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