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