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