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.StringMaker;
33  import com.liferay.portal.kernel.util.StringPool;
34  import com.liferay.portal.kernel.util.StringUtil;
35  import com.liferay.portal.kernel.util.Validator;
36  import com.liferay.portal.lucene.LuceneFields;
37  import com.liferay.portal.lucene.LuceneUtil;
38  import com.liferay.portal.model.impl.CompanyImpl;
39  import com.liferay.portal.util.PortalUtil;
40  import com.liferay.portal.util.PrefsPropsUtil;
41  import com.liferay.portal.util.PropsUtil;
42  import com.liferay.portal.util.PropsValues;
43  import com.liferay.portal.util.ReleaseInfo;
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 Collection _getAvailableTags() {
335         return _availableTagsCache;
336     }
337 
338     private List _getInstalledPluginPackages() {
339         return _installedPluginPackages.getSortedPluginPackages();
340     }
341 
342     private PluginPackage _getLatestAvailablePluginPackage(
343             String groupId, String artifactId)
344         throws SystemException {
345 
346         List pluginPackages = new ArrayList();
347 
348         String[] repositoryURLs = _getRepositoryURLs();
349 
350         for (int i = 0; i < repositoryURLs.length; i++) {
351             RemotePluginPackageRepository repository =
352                 _getRepository(repositoryURLs[i]);
353 
354             List curPluginPackages =
355                 repository.findPluginsByGroupIdAndArtifactId(
356                     groupId, artifactId);
357 
358             if (curPluginPackages != null) {
359                 pluginPackages.addAll(curPluginPackages);
360             }
361         }
362 
363         return _findLatestVersion(pluginPackages);
364     }
365 
366     private PluginPackage _getLatestInstalledPluginPackage(
367         String groupId, String artifactId) {
368 
369         return _installedPluginPackages.getLatestPluginPackage(
370             groupId, artifactId);
371     }
372 
373     private Date _getLastUpdateDate() {
374         return _lastUpdateDate;
375     }
376 
377     private PluginPackage _getPluginPackageByModuleId(
378             String moduleId, String repositoryURL)
379         throws DocumentException, IOException, PluginPackageException {
380 
381         RemotePluginPackageRepository repository = _getRepository(
382             repositoryURL);
383 
384         return repository.findPluginPackageByModuleId(moduleId);
385     }
386 
387     private PluginPackage _getPluginPackageByURL(String url)
388         throws PluginPackageException {
389 
390         String[] repositoryURLs = _getRepositoryURLs();
391 
392         for (int i = 0; i < repositoryURLs.length; i++) {
393             String repositoryURL = repositoryURLs[i];
394 
395             try {
396                 RemotePluginPackageRepository repository =
397                     _getRepository(repositoryURL);
398 
399                 return repository.findPluginByArtifactURL(url);
400             }
401             catch (PluginPackageException pe) {
402                 _log.error("Unable to load repository " + repositoryURL, pe);
403             }
404         }
405 
406         return null;
407     }
408 
409     private RemotePluginPackageRepository _getRepository(
410             String repositoryURL)
411         throws PluginPackageException {
412 
413         RemotePluginPackageRepository repository =
414             (RemotePluginPackageRepository)_repositoryCache.get(repositoryURL);
415 
416         if (repository != null) {
417             return repository;
418         }
419 
420         return _loadRepository(repositoryURL);
421     }
422 
423     private String[] _getRepositoryURLs() throws PluginPackageException {
424         try {
425             String[] trusted = PrefsPropsUtil.getStringArray(
426                 PropsUtil.PLUGIN_REPOSITORIES_TRUSTED, StringPool.NEW_LINE,
427                 PropsValues.PLUGIN_REPOSITORIES_TRUSTED);
428             String[] untrusted = PrefsPropsUtil.getStringArray(
429                 PropsUtil.PLUGIN_REPOSITORIES_UNTRUSTED, StringPool.NEW_LINE,
430                 PropsValues.PLUGIN_REPOSITORIES_UNTRUSTED);
431 
432             return ArrayUtil.append(trusted, untrusted);
433         }
434         catch (Exception e) {
435             throw new PluginPackageException(
436                 "Unable to read repository list", e);
437         }
438     }
439 
440     private String[] _getStatusAndInstalledVersion(
441         PluginPackage pluginPackage) {
442 
443         PluginPackage installedPluginPackage =
444             _installedPluginPackages.getLatestPluginPackage(
445                 pluginPackage.getGroupId(), pluginPackage.getArtifactId());
446 
447         String status = null;
448         String installedVersion = null;
449 
450         if (installedPluginPackage == null) {
451             status = PluginPackageImpl.STATUS_NOT_INSTALLED;
452         }
453         else {
454             installedVersion = installedPluginPackage.getVersion();
455 
456             if (installedPluginPackage.isLaterVersionThan(pluginPackage)) {
457                 status = PluginPackageImpl.STATUS_NEWER_VERSION_INSTALLED;
458             }
459             else if (installedPluginPackage.isPreviousVersionThan(
460                         pluginPackage)) {
461 
462                 status = PluginPackageImpl.STATUS_OLDER_VERSION_INSTALLED;
463             }
464             else {
465                 status = PluginPackageImpl.STATUS_SAME_VERSION_INSTALLED;
466             }
467         }
468 
469         return new String[] {status, installedVersion};
470     }
471 
472     private String[] _getSupportedTypes() {
473         return PropsValues.PLUGIN_TYPES;
474     }
475 
476     private void _indexPluginPackage(PluginPackage pluginPackage) {
477         String[] statusAndInstalledVersion =
478             _getStatusAndInstalledVersion(pluginPackage);
479 
480         String status = statusAndInstalledVersion[0];
481         String installedVersion = statusAndInstalledVersion[1];
482 
483         try {
484             PluginPackageIndexer.updatePluginPackage(
485                 pluginPackage.getModuleId(), pluginPackage.getName(),
486                 pluginPackage.getVersion(), pluginPackage.getModifiedDate(),
487                 pluginPackage.getAuthor(), pluginPackage.getTypes(),
488                 pluginPackage.getTags(), pluginPackage.getLicenses(),
489                 pluginPackage.getLiferayVersions(),
490                 pluginPackage.getShortDescription(),
491                 pluginPackage.getLongDescription(),
492                 pluginPackage.getChangeLog(), pluginPackage.getPageURL(),
493                 pluginPackage.getRepositoryURL(), status, installedVersion);
494         }
495         catch (Exception e) {
496             _log.error("Error reindexing " + pluginPackage.getModuleId(), e);
497         }
498     }
499 
500     private boolean _isCurrentVersionSupported(List versions) {
501         Version currentVersion = Version.getInstance(ReleaseInfo.getVersion());
502 
503         for (int i = 0; i < versions.size(); i++) {
504             Version supportedVersion = Version.getInstance(
505                 (String)versions.get(i));
506 
507             if (supportedVersion.includes(currentVersion)) {
508                 return true;
509             }
510         }
511 
512         return false;
513     }
514 
515     private boolean _isIgnored(PluginPackage pluginPackage)
516         throws PortalException, SystemException {
517 
518         String packageId = pluginPackage.getPackageId();
519 
520         String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
521             PropsUtil.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
522             StringPool.NEW_LINE,
523             PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
524 
525         for (int i = 0; i < pluginPackagesIgnored.length; i++) {
526             String curPluginPackagesIgnored = pluginPackagesIgnored[i];
527 
528             if (curPluginPackagesIgnored.endsWith(StringPool.STAR)) {
529                 String prefix = curPluginPackagesIgnored.substring(
530                     0, curPluginPackagesIgnored.length() - 2);
531 
532                 if (packageId.startsWith(prefix)) {
533                     return true;
534                 }
535             }
536             else {
537                 if (packageId.equals(curPluginPackagesIgnored)) {
538                     return true;
539                 }
540             }
541         }
542 
543         return false;
544     }
545 
546     private boolean _isInstallationInProcess(String context) {
547         if (_installedPluginPackages.getInstallingPluginPackage(
548                 context) != null) {
549 
550             return true;
551         }
552         else {
553             return false;
554         }
555     }
556 
557     private boolean _isTrusted(String repositoryURL)
558         throws PluginPackageException {
559 
560         try {
561             String[] trusted = PrefsPropsUtil.getStringArray(
562                 PropsUtil.PLUGIN_REPOSITORIES_TRUSTED, StringPool.NEW_LINE,
563                 PropsValues.PLUGIN_REPOSITORIES_TRUSTED);
564 
565             if (ArrayUtil.contains(trusted, repositoryURL)) {
566                 return true;
567             }
568             else {
569                 return false;
570             }
571         }
572         catch (Exception e) {
573             throw new PluginPackageException(
574                 "Unable to read repository list", e);
575         }
576     }
577 
578     private boolean _isUpdateAvailable()
579         throws PortalException, SystemException {
580 
581         if (!PrefsPropsUtil.getBoolean(
582                 PropsUtil.PLUGIN_NOTIFICATIONS_ENABLED,
583                 PropsValues.PLUGIN_NOTIFICATIONS_ENABLED)) {
584 
585             return false;
586         }
587 
588         if (_updateAvailable != null) {
589             return _updateAvailable.booleanValue();
590         }
591         else if (!_settingUpdateAvailable) {
592             _settingUpdateAvailable = true;
593 
594             Thread indexerThread = new Thread(
595                 new UpdateAvailableRunner(), PluginPackageUtil.class.getName());
596 
597             indexerThread.setPriority(Thread.MIN_PRIORITY);
598 
599             indexerThread.start();
600         }
601 
602         return false;
603     }
604 
605     private RemotePluginPackageRepository _loadRepository(String repositoryURL)
606         throws PluginPackageException {
607 
608         RemotePluginPackageRepository repository = null;
609 
610         StringMaker sm = new StringMaker();
611 
612         sm.append(repositoryURL);
613         sm.append(StringPool.SLASH);
614         sm.append(REPOSITORY_XML_FILENAME_PREFIX);
615         sm.append(StringPool.DASH);
616         sm.append(ReleaseInfo.getVersion());
617         sm.append(StringPool.PERIOD);
618         sm.append(REPOSITORY_XML_FILENAME_EXTENSION);
619 
620         String pluginsXmlURL = sm.toString();
621 
622         try {
623             HostConfiguration hostConfig = Http.getHostConfig(pluginsXmlURL);
624 
625             HttpClient client = Http.getClient(hostConfig);
626 
627             GetMethod getFileMethod = new GetMethod(pluginsXmlURL);
628 
629             byte[] bytes = null;
630 
631             try {
632                 int responseCode = client.executeMethod(
633                     hostConfig, getFileMethod);
634 
635                 if (responseCode != 200) {
636                     if (_log.isDebugEnabled()) {
637                         _log.debug(
638                             "A repository for version " +
639                                 ReleaseInfo.getVersion() + " was not found. " +
640                                     "Checking general repository");
641                     }
642 
643                     sm = new StringMaker();
644 
645                     sm.append(repositoryURL);
646                     sm.append(StringPool.SLASH);
647                     sm.append(REPOSITORY_XML_FILENAME_PREFIX);
648                     sm.append(StringPool.PERIOD);
649                     sm.append(REPOSITORY_XML_FILENAME_EXTENSION);
650 
651                     pluginsXmlURL = sm.toString();
652 
653                     getFileMethod = new GetMethod(pluginsXmlURL);
654 
655                     responseCode = client.executeMethod(
656                         hostConfig, getFileMethod);
657 
658                     if (responseCode != 200) {
659                         throw new PluginPackageException(
660                             "Unable to download file " + pluginsXmlURL +
661                                 " because of response code " + responseCode);
662                     }
663                 }
664 
665                 bytes = getFileMethod.getResponseBody();
666             }
667             finally {
668                 getFileMethod.releaseConnection();
669             }
670 
671             if ((bytes != null) && (bytes.length > 0)) {
672                 repository = _parseRepositoryXml(
673                     new String(bytes), repositoryURL);
674 
675                 _repositoryCache.put(repositoryURL, repository);
676                 _availableTagsCache.addAll(repository.getTags());
677                 _lastUpdateDate = new Date();
678                 _updateAvailable = null;
679 
680                 return repository;
681             }
682             else {
683                 _lastUpdateDate = new Date();
684 
685                 throw new PluginPackageException("Download returned 0 bytes");
686             }
687         }
688         catch (MalformedURLException mue) {
689             _repositoryCache.remove(repositoryURL);
690 
691             throw new PluginPackageException(
692                 "Invalid URL " + pluginsXmlURL, mue);
693         }
694         catch (IOException ioe) {
695             _repositoryCache.remove(repositoryURL);
696 
697             throw new PluginPackageException(
698                 "Unable to communicate with repository " + repositoryURL, ioe);
699         }
700         catch (DocumentException de) {
701             _repositoryCache.remove(repositoryURL);
702 
703             throw new PluginPackageException(
704                 "Unable to parse plugin list for repository " + repositoryURL,
705                 de);
706         }
707     }
708 
709     private RemotePluginPackageRepository _parseRepositoryXml(
710             String xml, String repositoryURL)
711         throws DocumentException, IOException {
712 
713         List supportedPluginTypes = Arrays.asList(getSupportedTypes());
714 
715         if (_log.isDebugEnabled()) {
716             _log.debug(
717                 "Loading plugin repository " + repositoryURL + ":\n" + xml);
718         }
719 
720         RemotePluginPackageRepository pluginPackageRepository =
721             new RemotePluginPackageRepository(repositoryURL);
722 
723         if (xml == null) {
724             return pluginPackageRepository;
725         }
726 
727         Document doc = PortalUtil.readDocumentFromXML(xml);
728 
729         Element root = doc.getRootElement();
730 
731         Properties settings = _readProperties(
732             root.element("settings"), "setting");
733 
734         pluginPackageRepository.setSettings(settings);
735 
736         Iterator itr1 = root.elements("plugin-package").iterator();
737 
738         while (itr1.hasNext()) {
739             Element pluginPackageEl = (Element)itr1.next();
740 
741             PluginPackage pluginPackage = _readPluginPackageXml(
742                 pluginPackageEl);
743 
744             if (!_isCurrentVersionSupported(
745                     pluginPackage.getLiferayVersions())) {
746 
747                 continue;
748             }
749 
750             Iterator itr2 = pluginPackage.getTypes().iterator();
751 
752             boolean containsSupportedTypes = false;
753 
754             while (itr2.hasNext()) {
755                 String type = (String)itr2.next();
756 
757                 if (supportedPluginTypes.contains(type)) {
758                     containsSupportedTypes = true;
759 
760                     break;
761                 }
762             }
763 
764             if (!containsSupportedTypes) {
765                 continue;
766             }
767 
768             pluginPackage.setRepository(pluginPackageRepository);
769 
770             pluginPackageRepository.addPluginPackage(pluginPackage);
771 
772             _indexPluginPackage(pluginPackage);
773         }
774 
775         return pluginPackageRepository;
776     }
777 
778     private Date _readDate(String text) {
779         if (Validator.isNotNull(text)) {
780             DateFormat dateFormat = new SimpleDateFormat(
781                 Time.RFC822_FORMAT, Locale.US);
782 
783             try {
784                 return dateFormat.parse(text);
785             }
786             catch (Exception e) {
787                 if (_log.isWarnEnabled()) {
788                     _log.warn("Unable to parse date " + text);
789                 }
790             }
791         }
792 
793         return new Date();
794     }
795 
796     private String _readHtml(String text) {
797         return GetterUtil.getString(text);
798     }
799 
800     private List _readLicenseList(Element parent, String childTagName) {
801         List result = new ArrayList();
802 
803         Iterator itr = parent.elements(childTagName).iterator();
804 
805         while (itr.hasNext()) {
806             Element tagEl = (Element)itr.next();
807 
808             License license = new License();
809 
810             license.setName(tagEl.getText());
811 
812             Attribute osiApproved = tagEl.attribute("osi-approved");
813 
814             if (osiApproved != null) {
815                 license.setOsiApproved(
816                     GetterUtil.getBoolean(osiApproved.getText()));
817             }
818 
819             Attribute url = tagEl.attribute("url");
820 
821             if (url != null) {
822                 license.setUrl(url.getText());
823             }
824 
825             result.add(license);
826         }
827 
828         return result;
829     }
830 
831     private List _readList(Element parent, String childTagName) {
832         List result = new ArrayList();
833 
834         if (parent != null) {
835             Iterator itr = parent.elements(childTagName).iterator();
836 
837             while (itr.hasNext()) {
838                 Element element = (Element)itr.next();
839 
840                 String text = element.getText().trim().toLowerCase();
841 
842                 result.add(text);
843             }
844         }
845 
846         return result;
847     }
848 
849     private PluginPackage _readPluginPackageProps(
850         String displayName, Properties props) {
851 
852         int pos = displayName.indexOf("-portlet-");
853 
854         String pluginType = "portlet";
855 
856         if (pos == -1) {
857             pos = displayName.indexOf("-theme-");
858 
859             pluginType = "theme";
860         }
861 
862         if (pos == -1) {
863             return null;
864         }
865 
866         String displayPrefix = displayName.substring(0, pos);
867 
868         String moduleGroupId = GetterUtil.getString(
869             props.getProperty("module-group-id"));
870         String moduleArtifactId = displayPrefix + "-" + pluginType;
871         String moduleVersion = displayName.substring(
872             pos + pluginType.length() + 2);
873         String moduleId =
874             moduleGroupId + "/" + moduleArtifactId + "/" + moduleVersion +
875                 "/war";
876 
877         String pluginName = GetterUtil.getString(props.getProperty("name"));
878 
879         String deploymentContext = GetterUtil.getString(props.getProperty(
880             "recommended-deployment-context"), moduleArtifactId);
881 
882         String author = GetterUtil.getString(props.getProperty("author"));
883 
884         List types = new ArrayList();
885 
886         types.add(pluginType);
887 
888         List licenses = new ArrayList();
889 
890         String[] licensesArray = StringUtil.split(
891             props.getProperty("licenses"));
892 
893         for (int i = 0; i < licensesArray.length; i++) {
894             License license = new License();
895 
896             license.setName(licensesArray[i].trim());
897             license.setOsiApproved(true);
898 
899             licenses.add(license);
900         }
901 
902         List liferayVersions = new ArrayList();
903 
904         String[] liferayVersionsArray = StringUtil.split(
905             props.getProperty("liferay-versions"));
906 
907         for (int i = 0; i < liferayVersionsArray.length; i++) {
908             liferayVersions.add(liferayVersionsArray[i].trim());
909         }
910 
911         if (liferayVersions.size() == 0) {
912             liferayVersions.add(ReleaseInfo.getVersion() + "+");
913         }
914 
915         List tags = new ArrayList();
916 
917         String[] tagsArray = StringUtil.split(props.getProperty("tags"));
918 
919         for (int i = 0; i < tagsArray.length; i++) {
920             tags.add(tagsArray[i].trim());
921         }
922 
923         String shortDescription = GetterUtil.getString(
924             props.getProperty("short-description"));
925         String longDescription = GetterUtil.getString(
926             props.getProperty("long-description"));
927         String changeLog = GetterUtil.getString(
928             props.getProperty("change-log"));
929         String pageURL = GetterUtil.getString(props.getProperty("page-url"));
930         String downloadURL = GetterUtil.getString(
931             props.getProperty("download-url"));
932 
933         PluginPackage pluginPackage = new PluginPackageImpl(moduleId);
934 
935         pluginPackage.setName(pluginName);
936         pluginPackage.setRecommendedDeploymentContext(deploymentContext);
937         //pluginPackage.setModifiedDate(null);
938         pluginPackage.setAuthor(author);
939         pluginPackage.setTypes(types);
940         pluginPackage.setLicenses(licenses);
941         pluginPackage.setLiferayVersions(liferayVersions);
942         pluginPackage.setTags(tags);
943         pluginPackage.setShortDescription(shortDescription);
944         pluginPackage.setLongDescription(longDescription);
945         pluginPackage.setChangeLog(changeLog);
946         //pluginPackage.setScreenshots(null);
947         pluginPackage.setPageURL(pageURL);
948         pluginPackage.setDownloadURL(downloadURL);
949         //pluginPackage.setDeploymentSettings(null);
950 
951         return pluginPackage;
952     }
953 
954     private PluginPackage _readPluginPackageXml(String xml)
955         throws DocumentException {
956 
957         Document doc = PortalUtil.readDocumentFromXML(xml);
958 
959         Element root = doc.getRootElement();
960 
961         return _readPluginPackageXml(root);
962     }
963 
964     private PluginPackage _readPluginPackageXml(Element pluginPackageEl) {
965         String name = pluginPackageEl.elementText("name");
966 
967         if (_log.isDebugEnabled()) {
968             _log.debug("Reading pluginPackage definition " + name);
969         }
970 
971         PluginPackage pluginPackage = new PluginPackageImpl(
972             GetterUtil.getString(pluginPackageEl.elementText("module-id")));
973 
974         List liferayVersions = _readList(
975             pluginPackageEl.element("liferay-versions"), "liferay-version");
976 
977         List types = _readList(pluginPackageEl.element("types"), "type");
978 
979         pluginPackage.setName(_readText(name));
980         pluginPackage.setRecommendedDeploymentContext(
981             _readText(
982                 pluginPackageEl.elementText("recommended-deployment-context")));
983         pluginPackage.setModifiedDate(
984             _readDate(pluginPackageEl.elementText("modified-date")));
985         pluginPackage.setAuthor(
986             _readText(pluginPackageEl.elementText("author")));
987         pluginPackage.setTypes(types);
988         pluginPackage.setLicenses(
989             _readLicenseList(
990                 pluginPackageEl.element("licenses"), "license"));
991         pluginPackage.setLiferayVersions(liferayVersions);
992         pluginPackage.setTags(
993             _readList(pluginPackageEl.element("tags"), "tag"));
994         pluginPackage.setShortDescription(
995             _readText(pluginPackageEl.elementText("short-description")));
996         pluginPackage.setLongDescription(
997             _readHtml(pluginPackageEl.elementText("long-description")));
998         pluginPackage.setChangeLog(
999             _readHtml(pluginPackageEl.elementText("change-log")));
1000        pluginPackage.setScreenshots(
1001            _readScreenshots(pluginPackageEl.element("screenshots")));
1002        pluginPackage.setPageURL(
1003            _readText(pluginPackageEl.elementText("page-url")));
1004        pluginPackage.setDownloadURL(
1005            _readText(pluginPackageEl.elementText("download-url")));
1006        pluginPackage.setDeploymentSettings(
1007            _readProperties(
1008                pluginPackageEl.element("deployment-settings"), "setting"));
1009
1010        return pluginPackage;
1011    }
1012
1013    private Properties _readProperties(Element parent, String childTagName) {
1014        Properties result = new Properties();
1015
1016        if (parent != null) {
1017            Iterator itr = parent.elements(childTagName).iterator();
1018
1019            while (itr.hasNext()) {
1020                Element tagEl = (Element)itr.next();
1021
1022                result.setProperty(
1023                    tagEl.attribute("name").getValue(),
1024                    tagEl.attribute("value").getValue());
1025            }
1026        }
1027
1028        return result;
1029    }
1030
1031    private List _readScreenshots(Element parent) {
1032        List result = new ArrayList();
1033
1034        if (parent != null) {
1035            List screenshots = parent.elements("screenshot");
1036
1037            Iterator itr = screenshots.iterator();
1038
1039            while (itr.hasNext()) {
1040                Element screenshotEl = (Element)itr.next();
1041
1042                Screenshot screenshot = new Screenshot();
1043
1044                screenshot.setThumbnailURL(
1045                    screenshotEl.element("thumbnail-url").getText());
1046                screenshot.setLargeImageURL(
1047                    screenshotEl.element("large-image-url").getText());
1048
1049                result.add(screenshot);
1050            }
1051        }
1052
1053        return result;
1054    }
1055
1056    private String _readText(String text) {
1057        return Html.stripHtml(GetterUtil.getString(text));
1058    }
1059
1060    private void _refreshUpdatesAvailableCache() {
1061        _updateAvailable = null;
1062    }
1063
1064    private void _reIndex() throws SystemException {
1065        if (LuceneUtil.INDEX_READ_ONLY) {
1066            return;
1067        }
1068
1069        IndexWriter writer = null;
1070
1071        try {
1072            PluginPackageIndexer.cleanIndex();
1073
1074            writer = LuceneUtil.getWriter(CompanyImpl.SYSTEM);
1075
1076            Iterator itr = _getAllAvailablePluginPackages().iterator();
1077
1078            while (itr.hasNext()) {
1079                PluginPackage pluginPackage = (PluginPackage)itr.next();
1080
1081                String[] statusAndInstalledVersion =
1082                    _getStatusAndInstalledVersion(pluginPackage);
1083
1084                String status = statusAndInstalledVersion[0];
1085                String installedVersion = statusAndInstalledVersion[1];
1086
1087                org.apache.lucene.document.Document doc =
1088                    PluginPackageIndexer.getAddPluginPackageDocument(
1089                        pluginPackage.getModuleId(), pluginPackage.getName(),
1090                        pluginPackage.getVersion(),
1091                        pluginPackage.getModifiedDate(),
1092                        pluginPackage.getAuthor(), pluginPackage.getTypes(),
1093                        pluginPackage.getTags(), pluginPackage.getLicenses(),
1094                        pluginPackage.getLiferayVersions(),
1095                        pluginPackage.getShortDescription(),
1096                        pluginPackage.getLongDescription(),
1097                        pluginPackage.getChangeLog(),
1098                        pluginPackage.getPageURL(),
1099                        pluginPackage.getRepositoryURL(), status,
1100                    installedVersion);
1101
1102                writer.addDocument(doc);
1103            }
1104        }
1105        catch (SystemException se) {
1106            throw se;
1107        }
1108        catch (Exception e) {
1109            throw new SystemException(e);
1110        }
1111        finally {
1112            try {
1113                if (writer != null) {
1114                    LuceneUtil.write(CompanyImpl.SYSTEM);
1115                }
1116            }
1117            catch (Exception e) {
1118                _log.error(e);
1119            }
1120        }
1121    }
1122
1123    private RepositoryReport _reloadRepositories() throws SystemException {
1124        if (_log.isInfoEnabled()) {
1125            _log.info("Reloading repositories");
1126        }
1127
1128        RepositoryReport report = new RepositoryReport();
1129
1130        String[] repositoryURLs = _getRepositoryURLs();
1131
1132        for (int i = 0; i < repositoryURLs.length; i++) {
1133            String repositoryURL = repositoryURLs[i];
1134
1135            try {
1136                _loadRepository(repositoryURL);
1137
1138                report.addSuccess(repositoryURL);
1139            }
1140            catch(PluginPackageException pe) {
1141                report.addError(repositoryURL, pe);
1142
1143                _log.error(
1144                    "Unable to load repository " + repositoryURL + " " +
1145                        pe.toString());
1146            }
1147
1148        }
1149
1150        _reIndex();
1151
1152        return report;
1153    }
1154
1155    private void _registerInstalledPluginPackage(
1156        PluginPackage pluginPackage) {
1157
1158        _installedPluginPackages.addPluginPackage(pluginPackage);
1159
1160        _updateAvailable = null;
1161
1162        _indexPluginPackage(pluginPackage);
1163    }
1164
1165    private void _registerPluginPackageInstallation(
1166        String preliminaryContext) {
1167
1168        _installedPluginPackages.registerPluginPackageInstallation(
1169            preliminaryContext);
1170    }
1171
1172    private Hits _search(
1173            String keywords, String type, String tag, String license,
1174            String repositoryURL, String status)
1175        throws SystemException {
1176
1177        _checkRepositories(repositoryURL);
1178
1179        Searcher searcher = null;
1180
1181        try {
1182            HitsImpl hits = new HitsImpl();
1183
1184            BooleanQuery contextQuery = new BooleanQuery();
1185
1186            LuceneUtil.addRequiredTerm(
1187                contextQuery, LuceneFields.PORTLET_ID,
1188                PluginPackageIndexer.PORTLET_ID);
1189
1190            BooleanQuery fullQuery = new BooleanQuery();
1191
1192            fullQuery.add(contextQuery, BooleanClause.Occur.MUST);
1193
1194            if (Validator.isNotNull(keywords)) {
1195                BooleanQuery searchQuery = new BooleanQuery();
1196
1197                LuceneUtil.addTerm(searchQuery, LuceneFields.TITLE, keywords);
1198                LuceneUtil.addTerm(searchQuery, LuceneFields.CONTENT, keywords);
1199
1200                fullQuery.add(searchQuery, BooleanClause.Occur.MUST);
1201            }
1202
1203            if (Validator.isNotNull(type)) {
1204                BooleanQuery searchQuery = new BooleanQuery();
1205
1206                LuceneUtil.addExactTerm(searchQuery, "type", type);
1207
1208                fullQuery.add(searchQuery, BooleanClause.Occur.MUST);
1209            }
1210
1211            if (Validator.isNotNull(tag)) {
1212                BooleanQuery searchQuery = new BooleanQuery();
1213
1214                LuceneUtil.addExactTerm(searchQuery, "tag", tag);
1215
1216                fullQuery.add(searchQuery, BooleanClause.Occur.MUST);
1217            }
1218
1219            if (Validator.isNotNull(repositoryURL)) {
1220                BooleanQuery searchQuery = new BooleanQuery();
1221
1222                Query query = new TermQuery(
1223                    new Term("repositoryURL", repositoryURL));
1224
1225                searchQuery.add(query, BooleanClause.Occur.SHOULD);
1226
1227                fullQuery.add(searchQuery, BooleanClause.Occur.MUST);
1228            }
1229
1230            if (Validator.isNotNull(license)) {
1231                BooleanQuery searchQuery = new BooleanQuery();
1232
1233                LuceneUtil.addExactTerm(searchQuery, "license", license);
1234
1235                fullQuery.add(searchQuery, BooleanClause.Occur.MUST);
1236            }
1237
1238            if (Validator.isNotNull(status) && !status.equals("all")) {
1239                BooleanQuery searchQuery = new BooleanQuery();
1240
1241                if (status.equals(PluginPackageImpl.
1242                        STATUS_NOT_INSTALLED_OR_OLDER_VERSION_INSTALLED)) {
1243
1244                    LuceneUtil.addExactTerm(
1245                        searchQuery, "status",
1246                        PluginPackageImpl.STATUS_NOT_INSTALLED);
1247                    LuceneUtil.addExactTerm(
1248                        searchQuery, "status",
1249                        PluginPackageImpl.STATUS_OLDER_VERSION_INSTALLED);
1250                }
1251                else {
1252                    LuceneUtil.addExactTerm(searchQuery, "status", status);
1253                }
1254
1255                fullQuery.add(searchQuery, BooleanClause.Occur.MUST);
1256            }
1257
1258            searcher = LuceneUtil.getSearcher(CompanyImpl.SYSTEM);
1259
1260            hits.recordHits(searcher.search(fullQuery), searcher);
1261
1262            return hits;
1263        }
1264        catch (Exception e) {
1265            return LuceneUtil.closeSearcher(searcher, keywords, e);
1266        }
1267    }
1268
1269    private void _unregisterInstalledPluginPackage(
1270        PluginPackage pluginPackage) {
1271
1272        _installedPluginPackages.removePluginPackage(pluginPackage);
1273    }
1274
1275    private void _updateInstallingPluginPackage(
1276        String preliminaryContext, PluginPackage pluginPackage) {
1277
1278        _installedPluginPackages.unregisterPluginPackageInstallation(
1279            preliminaryContext);
1280        _installedPluginPackages.registerPluginPackageInstallation(
1281            pluginPackage);
1282    }
1283
1284    private static Log _log = LogFactory.getLog(PluginPackageUtil.class);
1285
1286    private static PluginPackageUtil _instance = new PluginPackageUtil();
1287
1288    private LocalPluginPackageRepository _installedPluginPackages;
1289    private Map _repositoryCache;
1290    private Set _availableTagsCache;
1291    private Date _lastUpdateDate;
1292    private Boolean _updateAvailable;
1293    private boolean _settingUpdateAvailable;
1294
1295    private class UpdateAvailableRunner implements Runnable {
1296
1297        public void run() {
1298            try {
1299                setUpdateAvailable();
1300            }
1301            catch (Exception e) {
1302                _log.error(e, e);
1303            }
1304        }
1305
1306        protected void setUpdateAvailable() throws Exception {
1307            StopWatch stopWatch = null;
1308
1309            if (_log.isInfoEnabled()) {
1310                _log.info("Checking for available updates");
1311
1312                stopWatch = new StopWatch();
1313
1314                stopWatch.start();
1315            }
1316
1317            Iterator itr =
1318                _installedPluginPackages.getPluginPackages().iterator();
1319
1320            while (itr.hasNext()) {
1321                PluginPackage pluginPackage = (PluginPackage)itr.next();
1322
1323                PluginPackage availablePluginPackage = null;
1324
1325                if (_isIgnored(pluginPackage)) {
1326                    continue;
1327                }
1328
1329                availablePluginPackage =
1330                    PluginPackageUtil.getLatestAvailablePluginPackage(
1331                        pluginPackage.getGroupId(),
1332                        pluginPackage.getArtifactId());
1333
1334                if (availablePluginPackage == null) {
1335                    continue;
1336                }
1337
1338                Version availablePluginPackageVersion = Version.getInstance(
1339                    availablePluginPackage.getVersion());
1340
1341                if (availablePluginPackageVersion.isLaterVersionThan(
1342                        pluginPackage.getVersion())) {
1343
1344                    _updateAvailable = Boolean.TRUE;
1345
1346                    break;
1347                }
1348            }
1349
1350            if (_updateAvailable == null) {
1351                _updateAvailable = Boolean.FALSE;
1352            }
1353
1354            _settingUpdateAvailable = false;
1355
1356            if (_log.isInfoEnabled()) {
1357                _log.info(
1358                    "Finished checking for available updates in " +
1359                        stopWatch.getTime() + " ms");
1360            }
1361        }
1362    }
1363
1364}