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