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