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