001    /**
002     * Copyright (c) 2000-2010 Liferay, Inc. All rights reserved.
003     *
004     * This library is free software; you can redistribute it and/or modify it under
005     * the terms of the GNU Lesser General Public License as published by the Free
006     * Software Foundation; either version 2.1 of the License, or (at your option)
007     * any later version.
008     *
009     * This library is distributed in the hope that it will be useful, but WITHOUT
010     * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
011     * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more
012     * details.
013     */
014    
015    package com.liferay.portal.plugin;
016    
017    import com.liferay.portal.kernel.exception.PortalException;
018    import com.liferay.portal.kernel.exception.SystemException;
019    import com.liferay.portal.kernel.log.Log;
020    import com.liferay.portal.kernel.log.LogFactoryUtil;
021    import com.liferay.portal.kernel.plugin.License;
022    import com.liferay.portal.kernel.plugin.PluginPackage;
023    import com.liferay.portal.kernel.plugin.RemotePluginPackageRepository;
024    import com.liferay.portal.kernel.plugin.Screenshot;
025    import com.liferay.portal.kernel.plugin.Version;
026    import com.liferay.portal.kernel.search.Hits;
027    import com.liferay.portal.kernel.search.Indexer;
028    import com.liferay.portal.kernel.search.IndexerRegistryUtil;
029    import com.liferay.portal.kernel.search.SearchContext;
030    import com.liferay.portal.kernel.util.ArrayUtil;
031    import com.liferay.portal.kernel.util.DateFormatFactoryUtil;
032    import com.liferay.portal.kernel.util.GetterUtil;
033    import com.liferay.portal.kernel.util.HtmlUtil;
034    import com.liferay.portal.kernel.util.Http;
035    import com.liferay.portal.kernel.util.HttpUtil;
036    import com.liferay.portal.kernel.util.PropsKeys;
037    import com.liferay.portal.kernel.util.ReleaseInfo;
038    import com.liferay.portal.kernel.util.StringBundler;
039    import com.liferay.portal.kernel.util.StringPool;
040    import com.liferay.portal.kernel.util.StringUtil;
041    import com.liferay.portal.kernel.util.Time;
042    import com.liferay.portal.kernel.util.Validator;
043    import com.liferay.portal.kernel.xml.Attribute;
044    import com.liferay.portal.kernel.xml.Document;
045    import com.liferay.portal.kernel.xml.DocumentException;
046    import com.liferay.portal.kernel.xml.Element;
047    import com.liferay.portal.kernel.xml.SAXReaderUtil;
048    import com.liferay.portal.model.CompanyConstants;
049    import com.liferay.portal.model.Plugin;
050    import com.liferay.portal.util.HttpImpl;
051    import com.liferay.portal.util.PrefsPropsUtil;
052    import com.liferay.portal.util.PropsValues;
053    
054    import java.io.IOException;
055    import java.io.Serializable;
056    
057    import java.net.MalformedURLException;
058    
059    import java.text.DateFormat;
060    
061    import java.util.ArrayList;
062    import java.util.Arrays;
063    import java.util.Collection;
064    import java.util.Date;
065    import java.util.HashMap;
066    import java.util.Iterator;
067    import java.util.List;
068    import java.util.Locale;
069    import java.util.Map;
070    import java.util.Properties;
071    import java.util.Set;
072    import java.util.TreeSet;
073    
074    import javax.servlet.http.HttpServletResponse;
075    
076    import org.apache.commons.httpclient.HostConfiguration;
077    import org.apache.commons.httpclient.HttpClient;
078    import org.apache.commons.httpclient.methods.GetMethod;
079    import org.apache.commons.lang.time.StopWatch;
080    
081    /**
082     * @author Jorge Ferrer
083     * @author Brian Wing Shun Chan
084     * @author Sandeep Soni
085     */
086    public class PluginPackageUtil {
087    
088            public static final String REPOSITORY_XML_FILENAME_EXTENSION =
089                    "xml";
090    
091            public static final String REPOSITORY_XML_FILENAME_PREFIX =
092                    "liferay-plugin-repository";
093    
094            public static void endPluginPackageInstallation(String preliminaryContext) {
095                    _instance._endPluginPackageInstallation(preliminaryContext);
096            }
097    
098            public static List<PluginPackage> getAllAvailablePluginPackages()
099                    throws PortalException, SystemException {
100    
101                    return _instance._getAllAvailablePluginPackages();
102            }
103    
104            public static Collection<String> getAvailableTags() {
105                    return _instance._getAvailableTags();
106            }
107    
108            public static List<PluginPackage> getInstalledPluginPackages() {
109                    return _instance._getInstalledPluginPackages();
110            }
111    
112            public static Date getLastUpdateDate() {
113                    return _instance._getLastUpdateDate();
114            }
115    
116            public static PluginPackage getLatestAvailablePluginPackage(
117                            String groupId, String artifactId)
118                    throws PortalException, SystemException {
119    
120                    return _instance._getLatestAvailablePluginPackage(groupId, artifactId);
121            }
122    
123            public static PluginPackage getLatestInstalledPluginPackage(
124                    String groupId, String artifactId) {
125    
126                    return _instance._getLatestInstalledPluginPackage(groupId, artifactId);
127            }
128    
129            public static PluginPackage getPluginPackageByModuleId(
130                            String moduleId, String repositoryURL)
131                    throws PortalException, SystemException {
132    
133                    return _instance._getPluginPackageByModuleId(moduleId, repositoryURL);
134            }
135    
136            public static PluginPackage getPluginPackageByURL(String url)
137                    throws PortalException, SystemException {
138    
139                    return _instance._getPluginPackageByURL(url);
140            }
141    
142            public static RemotePluginPackageRepository getRepository(
143                            String repositoryURL)
144                    throws PortalException, SystemException {
145    
146                    return _instance._getRepository(repositoryURL);
147            }
148    
149            public static String[] getStatusAndInstalledVersion(
150                    PluginPackage pluginPackage) {
151    
152                    return _instance._getStatusAndInstalledVersion(pluginPackage);
153            }
154    
155            public static String[] getRepositoryURLs() throws SystemException {
156                    return _instance._getRepositoryURLs();
157            }
158    
159            public static String[] getSupportedTypes() {
160                    return _instance._getSupportedTypes();
161            }
162    
163            public static boolean isCurrentVersionSupported(List<String> versions) {
164                    return _instance._isCurrentVersionSupported(versions);
165            }
166    
167            public static boolean isIgnored(PluginPackage pluginPackage)
168                    throws SystemException {
169    
170                    return _instance._isIgnored(pluginPackage);
171            }
172    
173            public static boolean isInstallationInProcess(String context) {
174                    return _instance._isInstallationInProcess(context);
175            }
176    
177            public static boolean isTrusted(String repositoryURL)
178                    throws SystemException {
179    
180                    return _instance._isTrusted(repositoryURL);
181            }
182    
183            public static boolean isUpdateAvailable() throws SystemException {
184                    return _instance._isUpdateAvailable();
185            }
186    
187            public static PluginPackage readPluginPackageProperties(
188                    String displayName, Properties props) {
189    
190                    return _instance._readPluginPackageProperties(displayName, props);
191            }
192    
193            public static PluginPackage readPluginPackageXml(Element pluginPackageEl) {
194                    return _instance._readPluginPackageXml(pluginPackageEl);
195            }
196    
197            public static PluginPackage readPluginPackageXml(String xml)
198                    throws DocumentException {
199    
200                    return _instance._readPluginPackageXml(xml);
201            }
202    
203            public static void refreshUpdatesAvailableCache() {
204                    _instance._refreshUpdatesAvailableCache();
205            }
206    
207            public static void registerInstalledPluginPackage(
208                            PluginPackage pluginPackage)
209                    throws PortalException {
210    
211                    _instance._registerInstalledPluginPackage(pluginPackage);
212            }
213    
214            public static void registerPluginPackageInstallation(
215                    String preliminaryContext) {
216    
217                    _instance._registerPluginPackageInstallation(preliminaryContext);
218            }
219    
220            public static RepositoryReport reloadRepositories()
221                    throws PortalException, SystemException {
222    
223                    return _instance._reloadRepositories();
224            }
225    
226            public static Hits search(
227                            String keywords, String type, String tag, String license,
228                            String repositoryURL, String status, int start, int end)
229                    throws PortalException, SystemException {
230    
231                    return _instance._search(
232                            keywords, type, tag, license, repositoryURL, status, start, end);
233            }
234    
235            public static void unregisterInstalledPluginPackage(
236                            PluginPackage pluginPackage)
237                    throws PortalException, SystemException {
238    
239                    _instance._unregisterInstalledPluginPackage(pluginPackage);
240            }
241    
242            public static void updateInstallingPluginPackage(
243                    String preliminaryContext, PluginPackage pluginPackage) {
244    
245                    _instance._updateInstallingPluginPackage(
246                            preliminaryContext, pluginPackage);
247            }
248    
249            private PluginPackageUtil() {
250                    _installedPluginPackages = new LocalPluginPackageRepository();
251                    _repositoryCache = new HashMap<String, RemotePluginPackageRepository>();
252                    _availableTagsCache = new TreeSet<String>();
253            }
254    
255            private void _checkRepositories(String repositoryURL)
256                    throws PortalException, SystemException {
257    
258                    String[] repositoryURLs = null;
259    
260                    if (Validator.isNotNull(repositoryURL)) {
261                            repositoryURLs = new String[] {repositoryURL};
262                    }
263                    else {
264                            repositoryURLs = _getRepositoryURLs();
265                    }
266    
267                    for (int i = 0; i < repositoryURLs.length; i++) {
268                            _getRepository(repositoryURLs[i]);
269                    }
270            }
271    
272            private void _endPluginPackageInstallation(String preliminaryContext) {
273                    _installedPluginPackages.unregisterPluginPackageInstallation(
274                            preliminaryContext);
275            }
276    
277            private PluginPackage _findLatestVersion(
278                    List<PluginPackage> pluginPackages) {
279    
280                    PluginPackage latestPluginPackage = null;
281    
282                    for (PluginPackage pluginPackage : pluginPackages) {
283                            if ((latestPluginPackage == null) ||
284                                    (pluginPackage.isLaterVersionThan(latestPluginPackage))) {
285    
286                                    latestPluginPackage = pluginPackage;
287                            }
288                    }
289    
290                    return latestPluginPackage;
291            }
292    
293            private List<PluginPackage> _getAllAvailablePluginPackages()
294                    throws PortalException, SystemException {
295    
296                    List<PluginPackage> pluginPackages = new ArrayList<PluginPackage>();
297    
298                    String[] repositoryURLs = _getRepositoryURLs();
299    
300                    for (int i = 0; i < repositoryURLs.length; i++) {
301                            try {
302                                    RemotePluginPackageRepository repository =
303                                            _getRepository(repositoryURLs[i]);
304    
305                                    pluginPackages.addAll(repository.getPluginPackages());
306                            }
307                            catch (PluginPackageException ppe) {
308                                    String message = ppe.getMessage();
309    
310                                    if (message.startsWith("Unable to communicate")) {
311                                            if (_log.isWarnEnabled()) {
312                                                    _log.warn(message);
313                                            }
314                                    }
315                                    else {
316                                            _log.error(message);
317                                    }
318                            }
319                    }
320    
321                    return pluginPackages;
322            }
323    
324            private List<PluginPackage> _getAvailablePluginPackages(
325                            String groupId, String artifactId)
326                    throws PortalException, SystemException {
327    
328                    List<PluginPackage> pluginPackages = new ArrayList<PluginPackage>();
329    
330                    String[] repositoryURLs = _getRepositoryURLs();
331    
332                    for (int i = 0; i < repositoryURLs.length; i++) {
333                            RemotePluginPackageRepository repository =
334                                    _getRepository(repositoryURLs[i]);
335    
336                            List<PluginPackage> curPluginPackages =
337                                    repository.findPluginsByGroupIdAndArtifactId(
338                                            groupId, artifactId);
339    
340                            if (curPluginPackages != null) {
341                                    pluginPackages.addAll(curPluginPackages);
342                            }
343                    }
344    
345                    return pluginPackages;
346            }
347    
348            private Collection<String> _getAvailableTags() {
349                    return _availableTagsCache;
350            }
351    
352            private List<PluginPackage> _getInstalledPluginPackages() {
353                    return _installedPluginPackages.getSortedPluginPackages();
354            }
355    
356            private Date _getLastUpdateDate() {
357                    return _lastUpdateDate;
358            }
359    
360            private PluginPackage _getLatestAvailablePluginPackage(
361                            String groupId, String artifactId)
362                    throws PortalException, SystemException {
363    
364                    List<PluginPackage> pluginPackages = _getAvailablePluginPackages(
365                            groupId, artifactId);
366    
367                    return _findLatestVersion(pluginPackages);
368            }
369    
370            private PluginPackage _getLatestInstalledPluginPackage(
371                    String groupId, String artifactId) {
372    
373                    return _installedPluginPackages.getLatestPluginPackage(
374                            groupId, artifactId);
375            }
376    
377            private PluginPackage _getPluginPackageByModuleId(
378                            String moduleId, String repositoryURL)
379                    throws PortalException, SystemException {
380    
381                    RemotePluginPackageRepository repository = _getRepository(
382                            repositoryURL);
383    
384                    return repository.findPluginPackageByModuleId(moduleId);
385            }
386    
387            private PluginPackage _getPluginPackageByURL(String url)
388                    throws PortalException, SystemException {
389    
390                    String[] repositoryURLs = _getRepositoryURLs();
391    
392                    for (int i = 0; i < repositoryURLs.length; i++) {
393                            String repositoryURL = repositoryURLs[i];
394    
395                            try {
396                                    RemotePluginPackageRepository repository =
397                                            _getRepository(repositoryURL);
398    
399                                    return repository.findPluginByArtifactURL(url);
400                            }
401                            catch (PluginPackageException pe) {
402                                    _log.error("Unable to load repository " + repositoryURL, pe);
403                            }
404                    }
405    
406                    return null;
407            }
408    
409            private RemotePluginPackageRepository _getRepository(
410                            String repositoryURL)
411                    throws PortalException, SystemException {
412    
413                    RemotePluginPackageRepository repository = _repositoryCache.get(
414                            repositoryURL);
415    
416                    if (repository != null) {
417                            return repository;
418                    }
419    
420                    return _loadRepository(repositoryURL);
421            }
422    
423            private String[] _getRepositoryURLs() throws PluginPackageException {
424                    try {
425                            String[] trusted = PrefsPropsUtil.getStringArray(
426                                    PropsKeys.PLUGIN_REPOSITORIES_TRUSTED, StringPool.NEW_LINE,
427                                    PropsValues.PLUGIN_REPOSITORIES_TRUSTED);
428                            String[] untrusted = PrefsPropsUtil.getStringArray(
429                                    PropsKeys.PLUGIN_REPOSITORIES_UNTRUSTED, StringPool.NEW_LINE,
430                                    PropsValues.PLUGIN_REPOSITORIES_UNTRUSTED);
431    
432                            return ArrayUtil.append(trusted, untrusted);
433                    }
434                    catch (Exception e) {
435                            throw new PluginPackageException(
436                                    "Unable to read repository list", e);
437                    }
438            }
439    
440            private String[] _getStatusAndInstalledVersion(
441                    PluginPackage pluginPackage) {
442    
443                    PluginPackage installedPluginPackage =
444                            _installedPluginPackages.getLatestPluginPackage(
445                                    pluginPackage.getGroupId(), pluginPackage.getArtifactId());
446    
447                    String status = null;
448                    String installedVersion = null;
449    
450                    if (installedPluginPackage == null) {
451                            status = PluginPackageImpl.STATUS_NOT_INSTALLED;
452                    }
453                    else {
454                            installedVersion = installedPluginPackage.getVersion();
455    
456                            if (installedPluginPackage.isLaterVersionThan(pluginPackage)) {
457                                    status = PluginPackageImpl.STATUS_NEWER_VERSION_INSTALLED;
458                            }
459                            else if (installedPluginPackage.isPreviousVersionThan(
460                                                    pluginPackage)) {
461    
462                                    status = PluginPackageImpl.STATUS_OLDER_VERSION_INSTALLED;
463                            }
464                            else {
465                                    status = PluginPackageImpl.STATUS_SAME_VERSION_INSTALLED;
466                            }
467                    }
468    
469                    return new String[] {status, installedVersion};
470            }
471    
472            private String[] _getSupportedTypes() {
473                    return PropsValues.PLUGIN_TYPES;
474            }
475    
476            private void _indexPluginPackage(PluginPackage pluginPackage)
477                    throws PortalException {
478    
479                    Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);
480    
481                    indexer.reindex(pluginPackage);
482            }
483    
484            private boolean _isCurrentVersionSupported(List<String> versions) {
485                    Version currentVersion = Version.getInstance(ReleaseInfo.getVersion());
486    
487                    for (String version : versions) {
488                            Version supportedVersion = Version.getInstance(version);
489    
490                            if (supportedVersion.includes(currentVersion)) {
491                                    return true;
492                            }
493                    }
494    
495                    return false;
496            }
497    
498            private boolean _isIgnored(PluginPackage pluginPackage)
499                    throws SystemException {
500    
501                    String packageId = pluginPackage.getPackageId();
502    
503                    String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
504                            PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
505                            StringPool.NEW_LINE,
506                            PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
507    
508                    for (int i = 0; i < pluginPackagesIgnored.length; i++) {
509                            String curPluginPackagesIgnored = pluginPackagesIgnored[i];
510    
511                            if (curPluginPackagesIgnored.endsWith(StringPool.STAR)) {
512                                    String prefix = curPluginPackagesIgnored.substring(
513                                            0, curPluginPackagesIgnored.length() - 2);
514    
515                                    if (packageId.startsWith(prefix)) {
516                                            return true;
517                                    }
518                            }
519                            else {
520                                    if (packageId.equals(curPluginPackagesIgnored)) {
521                                            return true;
522                                    }
523                            }
524                    }
525    
526                    return false;
527            }
528    
529            private boolean _isInstallationInProcess(String context) {
530                    if (_installedPluginPackages.getInstallingPluginPackage(
531                                    context) != null) {
532    
533                            return true;
534                    }
535                    else {
536                            return false;
537                    }
538            }
539    
540            private boolean _isTrusted(String repositoryURL)
541                    throws PluginPackageException {
542    
543                    try {
544                            String[] trusted = PrefsPropsUtil.getStringArray(
545                                    PropsKeys.PLUGIN_REPOSITORIES_TRUSTED, StringPool.NEW_LINE,
546                                    PropsValues.PLUGIN_REPOSITORIES_TRUSTED);
547    
548                            if (ArrayUtil.contains(trusted, repositoryURL)) {
549                                    return true;
550                            }
551                            else {
552                                    return false;
553                            }
554                    }
555                    catch (Exception e) {
556                            throw new PluginPackageException(
557                                    "Unable to read repository list", e);
558                    }
559            }
560    
561            private boolean _isUpdateAvailable() throws SystemException {
562                    if (!PrefsPropsUtil.getBoolean(
563                                    PropsKeys.PLUGIN_NOTIFICATIONS_ENABLED,
564                                    PropsValues.PLUGIN_NOTIFICATIONS_ENABLED)) {
565    
566                            return false;
567                    }
568    
569                    if (_updateAvailable != null) {
570                            return _updateAvailable.booleanValue();
571                    }
572                    else if (!_settingUpdateAvailable) {
573                            _settingUpdateAvailable = true;
574    
575                            Thread indexerThread = new Thread(
576                                    new UpdateAvailableRunner(), PluginPackageUtil.class.getName());
577    
578                            indexerThread.setPriority(Thread.MIN_PRIORITY);
579    
580                            indexerThread.start();
581                    }
582    
583                    return false;
584            }
585    
586            private RemotePluginPackageRepository _loadRepository(String repositoryURL)
587                    throws PluginPackageException, PortalException {
588    
589                    RemotePluginPackageRepository repository = null;
590    
591                    StringBundler sb = new StringBundler(8);
592    
593                    if (!repositoryURL.startsWith(Http.HTTP_WITH_SLASH) &&
594                            !repositoryURL.startsWith(Http.HTTPS_WITH_SLASH)) {
595    
596                            sb.append(Http.HTTP_WITH_SLASH);
597                    }
598    
599                    sb.append(repositoryURL);
600                    sb.append(StringPool.SLASH);
601                    sb.append(REPOSITORY_XML_FILENAME_PREFIX);
602                    sb.append(StringPool.DASH);
603                    sb.append(ReleaseInfo.getVersion());
604                    sb.append(StringPool.PERIOD);
605                    sb.append(REPOSITORY_XML_FILENAME_EXTENSION);
606    
607                    String pluginsXmlURL = sb.toString();
608    
609                    try {
610                            HttpImpl httpImpl = (HttpImpl)HttpUtil.getHttp();
611    
612                            HostConfiguration hostConfig = httpImpl.getHostConfig(
613                                    pluginsXmlURL);
614    
615                            HttpClient client = httpImpl.getClient(hostConfig);
616    
617                            GetMethod getFileMethod = new GetMethod(pluginsXmlURL);
618    
619                            byte[] bytes = null;
620    
621                            try {
622                                    int responseCode = client.executeMethod(
623                                            hostConfig, getFileMethod);
624    
625                                    if (responseCode != HttpServletResponse.SC_OK) {
626                                            if (_log.isDebugEnabled()) {
627                                                    _log.debug(
628                                                            "A repository for version " +
629                                                                    ReleaseInfo.getVersion() + " was not found. " +
630                                                                            "Checking general repository");
631                                            }
632    
633                                            sb.setIndex(0);
634    
635                                            sb.append(repositoryURL);
636                                            sb.append(StringPool.SLASH);
637                                            sb.append(REPOSITORY_XML_FILENAME_PREFIX);
638                                            sb.append(StringPool.PERIOD);
639                                            sb.append(REPOSITORY_XML_FILENAME_EXTENSION);
640    
641                                            pluginsXmlURL = sb.toString();
642    
643                                            getFileMethod.releaseConnection();
644    
645                                            getFileMethod = new GetMethod(pluginsXmlURL);
646    
647                                            responseCode = client.executeMethod(
648                                                    hostConfig, getFileMethod);
649    
650                                            if (responseCode != HttpServletResponse.SC_OK) {
651                                                    throw new PluginPackageException(
652                                                            "Unable to download file " + pluginsXmlURL +
653                                                                    " because of response code " + responseCode);
654                                            }
655                                    }
656    
657                                    bytes = getFileMethod.getResponseBody();
658                            }
659                            finally {
660                                    getFileMethod.releaseConnection();
661                            }
662    
663                            if ((bytes != null) && (bytes.length > 0)) {
664                                    repository = _parseRepositoryXml(
665                                            new String(bytes), repositoryURL);
666    
667                                    _repositoryCache.put(repositoryURL, repository);
668                                    _availableTagsCache.addAll(repository.getTags());
669                                    _lastUpdateDate = new Date();
670                                    _updateAvailable = null;
671    
672                                    return repository;
673                            }
674                            else {
675                                    _lastUpdateDate = new Date();
676    
677                                    throw new PluginPackageException("Download returned 0 bytes");
678                            }
679                    }
680                    catch (MalformedURLException mue) {
681                            _repositoryCache.remove(repositoryURL);
682    
683                            throw new PluginPackageException(
684                                    "Invalid URL " + pluginsXmlURL, mue);
685                    }
686                    catch (IOException ioe) {
687                            _repositoryCache.remove(repositoryURL);
688    
689                            throw new PluginPackageException(
690                                    "Unable to communicate with repository " + repositoryURL, ioe);
691                    }
692                    catch (DocumentException de) {
693                            _repositoryCache.remove(repositoryURL);
694    
695                            throw new PluginPackageException(
696                                    "Unable to parse plugin list for repository " + repositoryURL,
697                                    de);
698                    }
699            }
700    
701            private RemotePluginPackageRepository _parseRepositoryXml(
702                            String xml, String repositoryURL)
703                    throws DocumentException, PortalException {
704    
705                    List<String> supportedPluginTypes = Arrays.asList(getSupportedTypes());
706    
707                    if (_log.isDebugEnabled()) {
708                            _log.debug(
709                                    "Loading plugin repository " + repositoryURL + ":\n" + xml);
710                    }
711    
712                    RemotePluginPackageRepository pluginPackageRepository =
713                            new RemotePluginPackageRepository(repositoryURL);
714    
715                    if (xml == null) {
716                            return pluginPackageRepository;
717                    }
718    
719                    Document doc = SAXReaderUtil.read(xml);
720    
721                    Element root = doc.getRootElement();
722    
723                    Properties settings = _readProperties(
724                            root.element("settings"), "setting");
725    
726                    pluginPackageRepository.setSettings(settings);
727    
728                    Iterator<Element> itr1 = root.elements("plugin-package").iterator();
729    
730                    while (itr1.hasNext()) {
731                            Element pluginPackageEl = itr1.next();
732    
733                            PluginPackage pluginPackage = _readPluginPackageXml(
734                                    pluginPackageEl);
735    
736                            if (!_isCurrentVersionSupported(
737                                            pluginPackage.getLiferayVersions())) {
738    
739                                    continue;
740                            }
741    
742                            Iterator<String> itr2 = pluginPackage.getTypes().iterator();
743    
744                            boolean containsSupportedTypes = false;
745    
746                            while (itr2.hasNext()) {
747                                    String type = itr2.next();
748    
749                                    if (supportedPluginTypes.contains(type)) {
750                                            containsSupportedTypes = true;
751    
752                                            break;
753                                    }
754                            }
755    
756                            if (!containsSupportedTypes) {
757                                    continue;
758                            }
759    
760                            pluginPackage.setRepository(pluginPackageRepository);
761    
762                            pluginPackageRepository.addPluginPackage(pluginPackage);
763    
764                            _indexPluginPackage(pluginPackage);
765                    }
766    
767                    return pluginPackageRepository;
768            }
769    
770            private Date _readDate(String text) {
771                    if (Validator.isNotNull(text)) {
772                            DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat(
773                                    Time.RFC822_FORMAT, Locale.US);
774    
775                            try {
776                                    return dateFormat.parse(text);
777                            }
778                            catch (Exception e) {
779                                    if (_log.isWarnEnabled()) {
780                                            _log.warn("Unable to parse date " + text);
781                                    }
782                            }
783                    }
784    
785                    return new Date();
786            }
787    
788            private String _readHtml(String text) {
789                    return GetterUtil.getString(text);
790            }
791    
792            private List<License> _readLicenseList(Element parentEL, String name) {
793                    List<License> licenses = new ArrayList<License>();
794    
795                    Iterator<Element> itr = parentEL.elements(name).iterator();
796    
797                    while (itr.hasNext()) {
798                            Element licenseEl = itr.next();
799    
800                            License license = new License();
801    
802                            license.setName(licenseEl.getText());
803    
804                            Attribute osiApproved = licenseEl.attribute("osi-approved");
805    
806                            if (osiApproved != null) {
807                                    license.setOsiApproved(
808                                            GetterUtil.getBoolean(osiApproved.getText()));
809                            }
810    
811                            Attribute url = licenseEl.attribute("url");
812    
813                            if (url != null) {
814                                    license.setUrl(url.getText());
815                            }
816    
817                            licenses.add(license);
818                    }
819    
820                    return licenses;
821            }
822            private List<String> _readList(Element parentEl, String name) {
823                    List<String> result = new ArrayList<String>();
824    
825                    if (parentEl != null) {
826                            Iterator<Element> itr = parentEl.elements(name).iterator();
827    
828                            while (itr.hasNext()) {
829                                    Element el = itr.next();
830    
831                                    String text = el.getText().trim().toLowerCase();
832    
833                                    result.add(text);
834                            }
835                    }
836    
837                    return result;
838            }
839            private PluginPackage _readPluginPackageProperties(
840                    String displayName, Properties properties) {
841    
842                    int pos = displayName.indexOf("-portlet");
843    
844                    String pluginType = Plugin.TYPE_PORTLET;
845    
846                    if (pos == -1) {
847                            pos = displayName.indexOf("-ext");
848    
849                            pluginType = Plugin.TYPE_EXT;
850                    }
851    
852                    if (pos == -1) {
853                            pos = displayName.indexOf("-hook");
854    
855                            pluginType = Plugin.TYPE_HOOK;
856                    }
857    
858                    if (pos == -1) {
859                            pos = displayName.indexOf("-layouttpl");
860    
861                            pluginType = Plugin.TYPE_LAYOUT_TEMPLATE;
862                    }
863    
864                    if (pos == -1) {
865                            pos = displayName.indexOf("-theme");
866    
867                            pluginType = Plugin.TYPE_THEME;
868                    }
869    
870                    if (pos == -1) {
871                            pos = displayName.indexOf("-web");
872    
873                            pluginType = Plugin.TYPE_WEB;
874                    }
875    
876                    if (pos == -1) {
877                            return null;
878                    }
879    
880                    String displayPrefix = displayName.substring(0, pos);
881    
882                    String moduleGroupId = GetterUtil.getString(
883                            properties.getProperty("module-group-id"));
884                    String moduleArtifactId = displayPrefix + "-" + pluginType;
885    
886                    String moduleVersion = null;
887    
888                    int moduleVersionPos = pos + pluginType.length() + 2;
889    
890                    if (displayName.length() > moduleVersionPos) {
891                            moduleVersion = displayName.substring(moduleVersionPos);
892                    }
893                    else {
894                            moduleVersion = ReleaseInfo.getVersion();
895                    }
896    
897                    String moduleId =
898                            moduleGroupId + "/" + moduleArtifactId + "/" + moduleVersion +
899                                    "/war";
900    
901                    String pluginName = GetterUtil.getString(
902                            properties.getProperty("name"));
903    
904                    String deploymentContext = GetterUtil.getString(
905                            properties.getProperty("recommended-deployment-context"),
906                            moduleArtifactId);
907    
908                    String author = GetterUtil.getString(properties.getProperty("author"));
909    
910                    List<String> types = new ArrayList<String>();
911    
912                    types.add(pluginType);
913    
914                    List<License> licenses = new ArrayList<License>();
915    
916                    String[] licensesArray = StringUtil.split(
917                            properties.getProperty("licenses"));
918    
919                    for (int i = 0; i < licensesArray.length; i++) {
920                            License license = new License();
921    
922                            license.setName(licensesArray[i].trim());
923                            license.setOsiApproved(true);
924    
925                            licenses.add(license);
926                    }
927    
928                    List<String> liferayVersions = new ArrayList<String>();
929    
930                    String[] liferayVersionsArray = StringUtil.split(
931                            properties.getProperty("liferay-versions"));
932    
933                    for (String liferayVersion : liferayVersionsArray) {
934                            liferayVersions.add(liferayVersion.trim());
935                    }
936    
937                    if (liferayVersions.size() == 0) {
938                            liferayVersions.add(ReleaseInfo.getVersion() + "+");
939                    }
940    
941                    List<String> tags = new ArrayList<String>();
942    
943                    String[] tagsArray = StringUtil.split(properties.getProperty("tags"));
944    
945                    for (String tag : tagsArray) {
946                            tags.add(tag.trim());
947                    }
948    
949                    String shortDescription = GetterUtil.getString(
950                            properties.getProperty("short-description"));
951                    String longDescription = GetterUtil.getString(
952                            properties.getProperty("long-description"));
953                    String changeLog = GetterUtil.getString(
954                            properties.getProperty("change-log"));
955                    String pageURL = GetterUtil.getString(
956                            properties.getProperty("page-url"));
957                    String downloadURL = GetterUtil.getString(
958                            properties.getProperty("download-url"));
959    
960                    PluginPackage pluginPackage = new PluginPackageImpl(moduleId);
961    
962                    pluginPackage.setName(pluginName);
963                    pluginPackage.setRecommendedDeploymentContext(deploymentContext);
964                    //pluginPackage.setModifiedDate(null);
965                    pluginPackage.setAuthor(author);
966                    pluginPackage.setTypes(types);
967                    pluginPackage.setLicenses(licenses);
968                    pluginPackage.setLiferayVersions(liferayVersions);
969                    pluginPackage.setTags(tags);
970                    pluginPackage.setShortDescription(shortDescription);
971                    pluginPackage.setLongDescription(longDescription);
972                    pluginPackage.setChangeLog(changeLog);
973                    //pluginPackage.setScreenshots(null);
974                    pluginPackage.setPageURL(pageURL);
975                    pluginPackage.setDownloadURL(downloadURL);
976                    //pluginPackage.setDeploymentSettings(null);
977    
978                    return pluginPackage;
979            }
980            private PluginPackage _readPluginPackageXml(Element pluginPackageEl) {
981                    String name = pluginPackageEl.elementText("name");
982    
983                    if (_log.isDebugEnabled()) {
984                            _log.debug("Reading pluginPackage definition " + name);
985                    }
986    
987                    PluginPackage pluginPackage = new PluginPackageImpl(
988                            GetterUtil.getString(pluginPackageEl.elementText("module-id")));
989    
990                    List<String> liferayVersions = _readList(
991                            pluginPackageEl.element("liferay-versions"), "liferay-version");
992    
993                    List<String> types = _readList(
994                            pluginPackageEl.element("types"), "type");
995    
996                    pluginPackage.setName(_readText(name));
997                    pluginPackage.setRecommendedDeploymentContext(
998                            _readText(
999                                    pluginPackageEl.elementText("recommended-deployment-context")));
1000                    pluginPackage.setModifiedDate(
1001                            _readDate(pluginPackageEl.elementText("modified-date")));
1002                    pluginPackage.setAuthor(
1003                            _readText(pluginPackageEl.elementText("author")));
1004                    pluginPackage.setTypes(types);
1005                    pluginPackage.setLicenses(
1006                            _readLicenseList(
1007                                    pluginPackageEl.element("licenses"), "license"));
1008                    pluginPackage.setLiferayVersions(liferayVersions);
1009                    pluginPackage.setTags(
1010                            _readList(pluginPackageEl.element("tags"), "tag"));
1011                    pluginPackage.setShortDescription(
1012                            _readText(pluginPackageEl.elementText("short-description")));
1013                    pluginPackage.setLongDescription(
1014                            _readHtml(pluginPackageEl.elementText("long-description")));
1015                    pluginPackage.setChangeLog(
1016                            _readHtml(pluginPackageEl.elementText("change-log")));
1017                    pluginPackage.setScreenshots(
1018                            _readScreenshots(pluginPackageEl.element("screenshots")));
1019                    pluginPackage.setPageURL(
1020                            _readText(pluginPackageEl.elementText("page-url")));
1021                    pluginPackage.setDownloadURL(
1022                            _readText(pluginPackageEl.elementText("download-url")));
1023                    pluginPackage.setDeploymentSettings(
1024                            _readProperties(
1025                                    pluginPackageEl.element("deployment-settings"), "setting"));
1026    
1027                    return pluginPackage;
1028            }
1029            private PluginPackage _readPluginPackageXml(String xml)
1030                    throws DocumentException {
1031    
1032                    Document doc = SAXReaderUtil.read(xml);
1033    
1034                    Element root = doc.getRootElement();
1035    
1036                    return _readPluginPackageXml(root);
1037            }
1038            private Properties _readProperties(Element parentEl, String name) {
1039                    Properties result = new Properties();
1040    
1041                    if (parentEl != null) {
1042                            Iterator<Element> itr = parentEl.elements(name).iterator();
1043    
1044                            while (itr.hasNext()) {
1045                                    Element el = itr.next();
1046    
1047                                    result.setProperty(
1048                                            el.attribute("name").getValue(),
1049                                            el.attribute("value").getValue());
1050                            }
1051                    }
1052    
1053                    return result;
1054            }
1055    
1056            private List<Screenshot> _readScreenshots(Element parentEl) {
1057                    List<Screenshot> screenshots = new ArrayList<Screenshot>();
1058    
1059                    if (parentEl != null) {
1060                            Iterator<Element> itr = parentEl.elements("screenshot").iterator();
1061    
1062                            while (itr.hasNext()) {
1063                                    Element screenshotEl = itr.next();
1064    
1065                                    Screenshot screenshot = new Screenshot();
1066    
1067                                    screenshot.setThumbnailURL(
1068                                            screenshotEl.element("thumbnail-url").getText());
1069                                    screenshot.setLargeImageURL(
1070                                            screenshotEl.element("large-image-url").getText());
1071    
1072                                    screenshots.add(screenshot);
1073                            }
1074                    }
1075    
1076                    return screenshots;
1077            }
1078    
1079            private String _readText(String text) {
1080                    return HtmlUtil.extractText(GetterUtil.getString(text));
1081            }
1082    
1083            private void _refreshUpdatesAvailableCache() {
1084                    _updateAvailable = null;
1085            }
1086    
1087            private void _registerInstalledPluginPackage(
1088                            PluginPackage pluginPackage)
1089                    throws PortalException {
1090    
1091                    _installedPluginPackages.addPluginPackage(pluginPackage);
1092    
1093                    _updateAvailable = null;
1094    
1095                    _indexPluginPackage(pluginPackage);
1096            }
1097    
1098            private void _registerPluginPackageInstallation(
1099                    String preliminaryContext) {
1100    
1101                    _installedPluginPackages.registerPluginPackageInstallation(
1102                            preliminaryContext);
1103            }
1104    
1105            private RepositoryReport _reloadRepositories()
1106                    throws PortalException, SystemException {
1107    
1108                    if (_log.isInfoEnabled()) {
1109                            _log.info("Reloading repositories");
1110                    }
1111    
1112                    RepositoryReport repositoryReport = new RepositoryReport();
1113    
1114                    String[] repositoryURLs = _getRepositoryURLs();
1115    
1116                    for (int i = 0; i < repositoryURLs.length; i++) {
1117                            String repositoryURL = repositoryURLs[i];
1118    
1119                            try {
1120                                    _loadRepository(repositoryURL);
1121    
1122                                    repositoryReport.addSuccess(repositoryURL);
1123                            }
1124                            catch (PluginPackageException pe) {
1125                                    repositoryReport.addError(repositoryURL, pe);
1126    
1127                                    _log.error(
1128                                            "Unable to load repository " + repositoryURL + " " +
1129                                                    pe.toString());
1130                            }
1131    
1132                    }
1133    
1134                    Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);
1135    
1136                    indexer.reindex(new String[0]);
1137    
1138                    return repositoryReport;
1139            }
1140    
1141            private Hits _search(
1142                            String keywords, String type, String tag, String license,
1143                            String repositoryURL, String status, int start, int end)
1144                    throws PortalException, SystemException {
1145    
1146                    _checkRepositories(repositoryURL);
1147    
1148                    Map<String, Serializable> attributes =
1149                            new HashMap<String, Serializable>();
1150    
1151                    attributes.put("license", license);
1152                    attributes.put("repositoryURL", repositoryURL);
1153                    attributes.put("status", status);
1154                    attributes.put("tag", tag);
1155                    attributes.put("type", type);
1156    
1157                    SearchContext searchContext = new SearchContext();
1158    
1159                    searchContext.setAttributes(attributes);
1160                    searchContext.setCompanyId(CompanyConstants.SYSTEM);
1161                    searchContext.setEnd(end);
1162                    searchContext.setKeywords(keywords);
1163                    searchContext.setStart(start);
1164    
1165                    Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);
1166    
1167                    return indexer.search(searchContext);
1168            }
1169    
1170            private void _unregisterInstalledPluginPackage(
1171                            PluginPackage pluginPackage)
1172                    throws PortalException, SystemException {
1173    
1174                    _installedPluginPackages.removePluginPackage(pluginPackage);
1175    
1176                    try {
1177                            List<PluginPackage> pluginPackages = _getAvailablePluginPackages(
1178                                    pluginPackage.getGroupId(), pluginPackage.getArtifactId());
1179    
1180                            for (PluginPackage availablePackage : pluginPackages) {
1181                                    _indexPluginPackage(availablePackage);
1182                            }
1183                    }
1184                    catch (PluginPackageException ppe) {
1185                            if (_log.isWarnEnabled()) {
1186                                    _log.warn(
1187                                            "Unable to reindex unistalled package " +
1188                                                    pluginPackage.getContext() + ": " + ppe.getMessage());
1189                            }
1190                    }
1191            }
1192    
1193            private void _updateInstallingPluginPackage(
1194                    String preliminaryContext, PluginPackage pluginPackage) {
1195    
1196                    _installedPluginPackages.unregisterPluginPackageInstallation(
1197                            preliminaryContext);
1198                    _installedPluginPackages.registerPluginPackageInstallation(
1199                            pluginPackage);
1200            }
1201    
1202            private static Log _log = LogFactoryUtil.getLog(PluginPackageUtil.class);
1203    
1204            private static PluginPackageUtil _instance = new PluginPackageUtil();
1205    
1206            private Set<String> _availableTagsCache;
1207            private LocalPluginPackageRepository _installedPluginPackages;
1208            private Date _lastUpdateDate;
1209            private Map<String, RemotePluginPackageRepository> _repositoryCache;
1210            private boolean _settingUpdateAvailable;
1211            private Boolean _updateAvailable;
1212    
1213            private class UpdateAvailableRunner implements Runnable {
1214    
1215                    public void run() {
1216                            try {
1217                                    setUpdateAvailable();
1218                            }
1219                            catch (Exception e) {
1220                                    if (_log.isWarnEnabled()) {
1221                                            _log.warn(e.getMessage());
1222                                    }
1223                            }
1224                    }
1225    
1226                    protected void setUpdateAvailable() throws Exception {
1227                            StopWatch stopWatch = null;
1228    
1229                            if (_log.isInfoEnabled()) {
1230                                    _log.info("Checking for available updates");
1231    
1232                                    stopWatch = new StopWatch();
1233    
1234                                    stopWatch.start();
1235                            }
1236    
1237                            for (PluginPackage pluginPackage :
1238                                            _installedPluginPackages.getPluginPackages()) {
1239    
1240                                    PluginPackage availablePluginPackage = null;
1241    
1242                                    if (_isIgnored(pluginPackage)) {
1243                                            continue;
1244                                    }
1245    
1246                                    availablePluginPackage =
1247                                            PluginPackageUtil.getLatestAvailablePluginPackage(
1248                                                    pluginPackage.getGroupId(),
1249                                                    pluginPackage.getArtifactId());
1250    
1251                                    if (availablePluginPackage == null) {
1252                                            continue;
1253                                    }
1254    
1255                                    Version availablePluginPackageVersion = Version.getInstance(
1256                                            availablePluginPackage.getVersion());
1257    
1258                                    if (availablePluginPackageVersion.isLaterVersionThan(
1259                                                    pluginPackage.getVersion())) {
1260    
1261                                            _updateAvailable = Boolean.TRUE;
1262    
1263                                            break;
1264                                    }
1265                            }
1266    
1267                            if (_updateAvailable == null) {
1268                                    _updateAvailable = Boolean.FALSE;
1269                            }
1270    
1271                            _settingUpdateAvailable = false;
1272    
1273                            if (_log.isInfoEnabled()) {
1274                                    _log.info(
1275                                            "Finished checking for available updates in " +
1276                                                    stopWatch.getTime() + " ms");
1277                            }
1278                    }
1279            }
1280    
1281    }