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