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