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