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