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