001
014
015 package com.liferay.portal.plugin;
016
017 import com.liferay.portal.kernel.exception.PortalException;
018 import com.liferay.portal.kernel.log.Log;
019 import com.liferay.portal.kernel.log.LogFactoryUtil;
020 import com.liferay.portal.kernel.plugin.License;
021 import com.liferay.portal.kernel.plugin.PluginPackage;
022 import com.liferay.portal.kernel.plugin.RemotePluginPackageRepository;
023 import com.liferay.portal.kernel.plugin.Screenshot;
024 import com.liferay.portal.kernel.plugin.Version;
025 import com.liferay.portal.kernel.search.Hits;
026 import com.liferay.portal.kernel.search.Indexer;
027 import com.liferay.portal.kernel.search.IndexerRegistryUtil;
028 import com.liferay.portal.kernel.search.QueryConfig;
029 import com.liferay.portal.kernel.search.SearchContext;
030 import com.liferay.portal.kernel.util.ArrayUtil;
031 import com.liferay.portal.kernel.util.DateFormatFactoryUtil;
032 import com.liferay.portal.kernel.util.GetterUtil;
033 import com.liferay.portal.kernel.util.HtmlUtil;
034 import com.liferay.portal.kernel.util.Http;
035 import com.liferay.portal.kernel.util.HttpUtil;
036 import com.liferay.portal.kernel.util.ListUtil;
037 import com.liferay.portal.kernel.util.LocaleUtil;
038 import com.liferay.portal.kernel.util.PropertiesUtil;
039 import com.liferay.portal.kernel.util.PropsKeys;
040 import com.liferay.portal.kernel.util.ReleaseInfo;
041 import com.liferay.portal.kernel.util.StringBundler;
042 import com.liferay.portal.kernel.util.StringPool;
043 import com.liferay.portal.kernel.util.StringUtil;
044 import com.liferay.portal.kernel.util.Time;
045 import com.liferay.portal.kernel.util.Validator;
046 import com.liferay.portal.kernel.xml.Attribute;
047 import com.liferay.portal.kernel.xml.Document;
048 import com.liferay.portal.kernel.xml.DocumentException;
049 import com.liferay.portal.kernel.xml.Element;
050 import com.liferay.portal.kernel.xml.SAXReaderUtil;
051 import com.liferay.portal.model.CompanyConstants;
052 import com.liferay.portal.model.Plugin;
053 import com.liferay.portal.util.PrefsPropsUtil;
054 import com.liferay.portal.util.PropsValues;
055
056 import java.io.IOException;
057 import java.io.InputStream;
058 import java.io.Serializable;
059
060 import java.net.MalformedURLException;
061
062 import java.text.DateFormat;
063
064 import java.util.ArrayList;
065 import java.util.Arrays;
066 import java.util.Collection;
067 import java.util.Date;
068 import java.util.HashMap;
069 import java.util.List;
070 import java.util.Map;
071 import java.util.Properties;
072 import java.util.Set;
073 import java.util.TreeSet;
074 import java.util.jar.Attributes;
075 import java.util.jar.Manifest;
076
077 import javax.servlet.ServletContext;
078 import javax.servlet.http.HttpServletResponse;
079
080 import org.apache.commons.lang.time.StopWatch;
081
082
087 public class PluginPackageUtil {
088
089 public static void endPluginPackageInstallation(String preliminaryContext) {
090 _instance._endPluginPackageInstallation(preliminaryContext);
091 }
092
093 public static List<PluginPackage> getAllAvailablePluginPackages()
094 throws PortalException {
095
096 return _instance._getAllAvailablePluginPackages();
097 }
098
099 public static Collection<String> getAvailableTags() {
100 return _instance._getAvailableTags();
101 }
102
103 public static PluginPackage getInstalledPluginPackage(String context) {
104 return _instance._getInstalledPluginPackage(context);
105 }
106
107 public static List<PluginPackage> getInstalledPluginPackages() {
108 return _instance._getInstalledPluginPackages();
109 }
110
111 public static Date getLastUpdateDate() {
112 return _instance._getLastUpdateDate();
113 }
114
115 public static PluginPackage getLatestAvailablePluginPackage(
116 String groupId, String artifactId)
117 throws PortalException {
118
119 return _instance._getLatestAvailablePluginPackage(groupId, artifactId);
120 }
121
122 public static PluginPackage getLatestInstalledPluginPackage(
123 String groupId, String artifactId) {
124
125 return _instance._getLatestInstalledPluginPackage(groupId, artifactId);
126 }
127
128 public static PluginPackage getPluginPackageByModuleId(
129 String moduleId, String repositoryURL)
130 throws PortalException {
131
132 return _instance._getPluginPackageByModuleId(moduleId, repositoryURL);
133 }
134
135 public static PluginPackage getPluginPackageByURL(String url)
136 throws PortalException {
137
138 return _instance._getPluginPackageByURL(url);
139 }
140
141 public static RemotePluginPackageRepository getRepository(
142 String repositoryURL)
143 throws PortalException {
144
145 return _instance._getRepository(repositoryURL);
146 }
147
148 public static String[] getRepositoryURLs() {
149 return _instance._getRepositoryURLs();
150 }
151
152 public static String[] getStatusAndInstalledVersion(
153 PluginPackage pluginPackage) {
154
155 return _instance._getStatusAndInstalledVersion(pluginPackage);
156 }
157
158 public static String[] getSupportedTypes() {
159 return _instance._getSupportedTypes();
160 }
161
162 public static boolean isCurrentVersionSupported(List<String> versions) {
163 return _instance._isCurrentVersionSupported(versions);
164 }
165
166 public static boolean isIgnored(PluginPackage pluginPackage) {
167 return _instance._isIgnored(pluginPackage);
168 }
169
170 public static boolean isInstallationInProcess(String context) {
171 return _instance._isInstallationInProcess(context);
172 }
173
174 public static boolean isInstalled(String context) {
175 return _instance._isInstalled(context);
176 }
177
178 public static boolean isTrusted(String repositoryURL) {
179 return _instance._isTrusted(repositoryURL);
180 }
181
182 public static boolean isUpdateAvailable() {
183 return _instance._isUpdateAvailable();
184 }
185
186 public static PluginPackage readPluginPackageProperties(
187 String displayName, Properties properties) {
188
189 return _instance._readPluginPackageProperties(displayName, properties);
190 }
191
192 public static PluginPackage readPluginPackageServletContext(
193 ServletContext servletContext)
194 throws DocumentException, IOException {
195
196 return _instance._readPluginPackageServletContext(servletContext);
197 }
198
199 public static PluginPackage readPluginPackageXml(
200 Element pluginPackageElement) {
201
202 return _instance._readPluginPackageXml(pluginPackageElement);
203 }
204
205 public static PluginPackage readPluginPackageXml(String xml)
206 throws DocumentException {
207
208 return _instance._readPluginPackageXml(xml);
209 }
210
211 public static void refreshUpdatesAvailableCache() {
212 _instance._refreshUpdatesAvailableCache();
213 }
214
215 public static void registerInstalledPluginPackage(
216 PluginPackage pluginPackage)
217 throws PortalException {
218
219 _instance._registerInstalledPluginPackage(pluginPackage);
220 }
221
222 public static void registerPluginPackageInstallation(
223 String preliminaryContext) {
224
225 _instance._registerPluginPackageInstallation(preliminaryContext);
226 }
227
228 public static RepositoryReport reloadRepositories() throws PortalException {
229 return _instance._reloadRepositories();
230 }
231
232 public static Hits search(
233 String keywords, String type, String tag, String license,
234 String repositoryURL, String status, int start, int end)
235 throws PortalException {
236
237 return _instance._search(
238 keywords, type, tag, license, repositoryURL, status, start, end);
239 }
240
241 public static void unregisterInstalledPluginPackage(
242 PluginPackage pluginPackage)
243 throws PortalException {
244
245 _instance._unregisterInstalledPluginPackage(pluginPackage);
246 }
247
248 public static void updateInstallingPluginPackage(
249 String preliminaryContext, PluginPackage pluginPackage) {
250
251 _instance._updateInstallingPluginPackage(
252 preliminaryContext, pluginPackage);
253 }
254
255 private PluginPackageUtil() {
256 _installedPluginPackages = new LocalPluginPackageRepository();
257 _repositoryCache = new HashMap<String, RemotePluginPackageRepository>();
258 _availableTagsCache = new TreeSet<String>();
259 }
260
261 private void _checkRepositories(String repositoryURL)
262 throws PortalException {
263
264 String[] repositoryURLs = null;
265
266 if (Validator.isNotNull(repositoryURL)) {
267 repositoryURLs = new String[] {repositoryURL};
268 }
269 else {
270 repositoryURLs = _getRepositoryURLs();
271 }
272
273 for (int i = 0; i < repositoryURLs.length; i++) {
274 _getRepository(repositoryURLs[i]);
275 }
276 }
277
278 private void _endPluginPackageInstallation(String preliminaryContext) {
279 _installedPluginPackages.unregisterPluginPackageInstallation(
280 preliminaryContext);
281 }
282
283 private PluginPackage _findLatestVersion(
284 List<PluginPackage> pluginPackages) {
285
286 PluginPackage latestPluginPackage = null;
287
288 for (PluginPackage pluginPackage : pluginPackages) {
289 if ((latestPluginPackage == null) ||
290 pluginPackage.isLaterVersionThan(latestPluginPackage)) {
291
292 latestPluginPackage = pluginPackage;
293 }
294 }
295
296 return latestPluginPackage;
297 }
298
299 private List<PluginPackage> _getAllAvailablePluginPackages()
300 throws PortalException {
301
302 List<PluginPackage> pluginPackages = new ArrayList<PluginPackage>();
303
304 String[] repositoryURLs = _getRepositoryURLs();
305
306 for (int i = 0; i < repositoryURLs.length; i++) {
307 try {
308 RemotePluginPackageRepository repository = _getRepository(
309 repositoryURLs[i]);
310
311 pluginPackages.addAll(repository.getPluginPackages());
312 }
313 catch (PluginPackageException ppe) {
314 String message = ppe.getMessage();
315
316 if (message.startsWith("Unable to communicate")) {
317 if (_log.isWarnEnabled()) {
318 _log.warn(message);
319 }
320 }
321 else {
322 _log.error(message);
323 }
324 }
325 }
326
327 return pluginPackages;
328 }
329
330 private List<PluginPackage> _getAvailablePluginPackages(
331 String groupId, String artifactId)
332 throws PortalException {
333
334 List<PluginPackage> pluginPackages = new ArrayList<PluginPackage>();
335
336 String[] repositoryURLs = _getRepositoryURLs();
337
338 for (int i = 0; i < repositoryURLs.length; i++) {
339 RemotePluginPackageRepository repository = _getRepository(
340 repositoryURLs[i]);
341
342 List<PluginPackage> curPluginPackages =
343 repository.findPluginsByGroupIdAndArtifactId(
344 groupId, artifactId);
345
346 if (curPluginPackages != null) {
347 pluginPackages.addAll(curPluginPackages);
348 }
349 }
350
351 return pluginPackages;
352 }
353
354 private Collection<String> _getAvailableTags() {
355 return _availableTagsCache;
356 }
357
358 private PluginPackage _getInstalledPluginPackage(String context) {
359 return _installedPluginPackages.getPluginPackage(context);
360 }
361
362 private List<PluginPackage> _getInstalledPluginPackages() {
363 return _installedPluginPackages.getSortedPluginPackages();
364 }
365
366 private Date _getLastUpdateDate() {
367 return _lastUpdateDate;
368 }
369
370 private PluginPackage _getLatestAvailablePluginPackage(
371 String groupId, String artifactId)
372 throws PortalException {
373
374 List<PluginPackage> pluginPackages = _getAvailablePluginPackages(
375 groupId, artifactId);
376
377 return _findLatestVersion(pluginPackages);
378 }
379
380 private PluginPackage _getLatestInstalledPluginPackage(
381 String groupId, String artifactId) {
382
383 return _installedPluginPackages.getLatestPluginPackage(
384 groupId, artifactId);
385 }
386
387 private PluginPackage _getPluginPackageByModuleId(
388 String moduleId, String repositoryURL)
389 throws PortalException {
390
391 RemotePluginPackageRepository repository = _getRepository(
392 repositoryURL);
393
394 return repository.findPluginPackageByModuleId(moduleId);
395 }
396
397 private PluginPackage _getPluginPackageByURL(String url)
398 throws PortalException {
399
400 String[] repositoryURLs = _getRepositoryURLs();
401
402 for (int i = 0; i < repositoryURLs.length; i++) {
403 String repositoryURL = repositoryURLs[i];
404
405 try {
406 RemotePluginPackageRepository repository = _getRepository(
407 repositoryURL);
408
409 return repository.findPluginByArtifactURL(url);
410 }
411 catch (PluginPackageException ppe) {
412 _log.error("Unable to load repository " + repositoryURL, ppe);
413 }
414 }
415
416 return null;
417 }
418
419 private RemotePluginPackageRepository _getRepository(String repositoryURL)
420 throws PortalException {
421
422 RemotePluginPackageRepository repository = _repositoryCache.get(
423 repositoryURL);
424
425 if (repository != null) {
426 return repository;
427 }
428
429 return _loadRepository(repositoryURL);
430 }
431
432 private String[] _getRepositoryURLs() throws PluginPackageException {
433 try {
434 String[] trusted = PrefsPropsUtil.getStringArray(
435 PropsKeys.PLUGIN_REPOSITORIES_TRUSTED, StringPool.NEW_LINE,
436 PropsValues.PLUGIN_REPOSITORIES_TRUSTED);
437 String[] untrusted = PrefsPropsUtil.getStringArray(
438 PropsKeys.PLUGIN_REPOSITORIES_UNTRUSTED, StringPool.NEW_LINE,
439 PropsValues.PLUGIN_REPOSITORIES_UNTRUSTED);
440
441 return ArrayUtil.append(trusted, untrusted);
442 }
443 catch (Exception e) {
444 throw new PluginPackageException(
445 "Unable to read repository list", e);
446 }
447 }
448
449 private String[] _getStatusAndInstalledVersion(
450 PluginPackage pluginPackage) {
451
452 PluginPackage installedPluginPackage =
453 _installedPluginPackages.getLatestPluginPackage(
454 pluginPackage.getGroupId(), pluginPackage.getArtifactId());
455
456 String status = null;
457 String installedVersion = null;
458
459 if (installedPluginPackage == null) {
460 status = PluginPackageImpl.STATUS_NOT_INSTALLED;
461 }
462 else {
463 installedVersion = installedPluginPackage.getVersion();
464
465 if (installedPluginPackage.isLaterVersionThan(pluginPackage)) {
466 status = PluginPackageImpl.STATUS_NEWER_VERSION_INSTALLED;
467 }
468 else if (installedPluginPackage.isPreviousVersionThan(
469 pluginPackage)) {
470
471 status = PluginPackageImpl.STATUS_OLDER_VERSION_INSTALLED;
472 }
473 else {
474 status = PluginPackageImpl.STATUS_SAME_VERSION_INSTALLED;
475 }
476 }
477
478 return new String[] {status, installedVersion};
479 }
480
481 private String[] _getSupportedTypes() {
482 return PropsValues.PLUGIN_TYPES;
483 }
484
485 private void _indexPluginPackage(PluginPackage pluginPackage)
486 throws PortalException {
487
488 Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);
489
490 indexer.reindex(pluginPackage);
491 }
492
493 private boolean _isCurrentVersionSupported(List<String> versions) {
494 Version currentVersion = Version.getInstance(ReleaseInfo.getVersion());
495
496 for (String version : versions) {
497 Version supportedVersion = Version.getInstance(version);
498
499 if (supportedVersion.includes(currentVersion)) {
500 return true;
501 }
502 }
503
504 return false;
505 }
506
507 private boolean _isIgnored(PluginPackage pluginPackage) {
508 String packageId = pluginPackage.getPackageId();
509
510 String[] pluginPackagesIgnored = PrefsPropsUtil.getStringArray(
511 PropsKeys.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED,
512 StringPool.NEW_LINE,
513 PropsValues.PLUGIN_NOTIFICATIONS_PACKAGES_IGNORED);
514
515 for (int i = 0; i < pluginPackagesIgnored.length; i++) {
516 String curPluginPackagesIgnored = pluginPackagesIgnored[i];
517
518 if (curPluginPackagesIgnored.endsWith(StringPool.STAR)) {
519 String prefix = curPluginPackagesIgnored.substring(
520 0, curPluginPackagesIgnored.length() - 2);
521
522 if (packageId.startsWith(prefix)) {
523 return true;
524 }
525 }
526 else {
527 if (packageId.equals(curPluginPackagesIgnored)) {
528 return true;
529 }
530 }
531 }
532
533 return false;
534 }
535
536 private boolean _isInstallationInProcess(String context) {
537 if (_installedPluginPackages.getInstallingPluginPackage(
538 context) != null) {
539
540 return true;
541 }
542 else {
543 return false;
544 }
545 }
546
547 private boolean _isInstalled(String context) {
548 PluginPackage pluginPackage = _installedPluginPackages.getPluginPackage(
549 context);
550
551 if (pluginPackage != null) {
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() {
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, PortalException {
607
608 RemotePluginPackageRepository repository = null;
609
610 StringBundler sb = new StringBundler(8);
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(PluginPackage.REPOSITORY_XML_FILENAME_PREFIX);
621 sb.append(StringPool.DASH);
622 sb.append(ReleaseInfo.getVersion());
623 sb.append(StringPool.PERIOD);
624 sb.append(PluginPackage.REPOSITORY_XML_FILENAME_EXTENSION);
625
626 String pluginsXmlURL = sb.toString();
627
628 try {
629 Http.Options options = new Http.Options();
630
631 options.setLocation(pluginsXmlURL);
632 options.setPost(false);
633
634 byte[] bytes = HttpUtil.URLtoByteArray(options);
635
636 Http.Response response = options.getResponse();
637
638 int responseCode = response.getResponseCode();
639
640 if (responseCode != HttpServletResponse.SC_OK) {
641 if (_log.isDebugEnabled()) {
642 _log.debug(
643 "A repository for version " +
644 ReleaseInfo.getVersion() + " was not found. " +
645 "Checking general repository");
646 }
647
648 sb.setIndex(0);
649
650 sb.append(repositoryURL);
651 sb.append(StringPool.SLASH);
652 sb.append(PluginPackage.REPOSITORY_XML_FILENAME_PREFIX);
653 sb.append(StringPool.PERIOD);
654 sb.append(PluginPackage.REPOSITORY_XML_FILENAME_EXTENSION);
655
656 pluginsXmlURL = sb.toString();
657
658 options = new Http.Options();
659
660 options.setLocation(pluginsXmlURL);
661 options.setPost(false);
662
663 bytes = HttpUtil.URLtoByteArray(options);
664
665 response = options.getResponse();
666
667 if (responseCode != HttpServletResponse.SC_OK) {
668 throw new PluginPackageException(
669 "Unable to download file " + pluginsXmlURL +
670 " because of response code " + responseCode);
671 }
672 }
673
674 if (ArrayUtil.isNotEmpty(bytes)) {
675 repository = _parseRepositoryXml(
676 new String(bytes), repositoryURL);
677
678 _repositoryCache.put(repositoryURL, repository);
679 _availableTagsCache.addAll(repository.getTags());
680 _lastUpdateDate = new Date();
681 _updateAvailable = null;
682
683 return repository;
684 }
685
686 _lastUpdateDate = new Date();
687
688 throw new PluginPackageException("Download returned 0 bytes");
689 }
690 catch (MalformedURLException murle) {
691 _repositoryCache.remove(repositoryURL);
692
693 throw new PluginPackageException(
694 "Invalid URL " + pluginsXmlURL, murle);
695 }
696 catch (IOException ioe) {
697 _repositoryCache.remove(repositoryURL);
698
699 throw new PluginPackageException(
700 "Unable to communicate with repository " + repositoryURL, ioe);
701 }
702 catch (DocumentException de) {
703 _repositoryCache.remove(repositoryURL);
704
705 throw new PluginPackageException(
706 "Unable to parse plugin list for repository " + repositoryURL,
707 de);
708 }
709 }
710
711 private RemotePluginPackageRepository _parseRepositoryXml(
712 String xml, String repositoryURL)
713 throws DocumentException, PortalException {
714
715 List<String> supportedPluginTypes = Arrays.asList(getSupportedTypes());
716
717 if (_log.isDebugEnabled()) {
718 _log.debug(
719 "Loading plugin repository " + repositoryURL + ":\n" + xml);
720 }
721
722 RemotePluginPackageRepository pluginPackageRepository =
723 new RemotePluginPackageRepository(repositoryURL);
724
725 if (xml == null) {
726 return pluginPackageRepository;
727 }
728
729 Document document = SAXReaderUtil.read(xml);
730
731 Element rootElement = document.getRootElement();
732
733 Properties settings = _readProperties(
734 rootElement.element("settings"), "setting");
735
736 pluginPackageRepository.setSettings(settings);
737
738 List<Element> pluginPackageElements = rootElement.elements(
739 "plugin-package");
740
741 for (Element pluginPackageElement : pluginPackageElements) {
742 PluginPackage pluginPackage = _readPluginPackageXml(
743 pluginPackageElement);
744
745 if (!_isCurrentVersionSupported(
746 pluginPackage.getLiferayVersions())) {
747
748 continue;
749 }
750
751 boolean containsSupportedTypes = false;
752
753 List<String> pluginTypes = pluginPackage.getTypes();
754
755 for (String pluginType : pluginTypes) {
756 if (supportedPluginTypes.contains(pluginType)) {
757 containsSupportedTypes = true;
758
759 break;
760 }
761 }
762
763 if (!containsSupportedTypes) {
764 continue;
765 }
766
767 pluginPackage.setRepository(pluginPackageRepository);
768
769 pluginPackageRepository.addPluginPackage(pluginPackage);
770
771 _indexPluginPackage(pluginPackage);
772 }
773
774 return pluginPackageRepository;
775 }
776
777 private Date _readDate(String text) {
778 if (Validator.isNotNull(text)) {
779 DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat(
780 Time.RFC822_FORMAT, LocaleUtil.US);
781
782 try {
783 return dateFormat.parse(text);
784 }
785 catch (Exception e) {
786 if (_log.isWarnEnabled()) {
787 _log.warn("Unable to parse date " + text);
788 }
789 }
790 }
791
792 return new Date();
793 }
794
795 private String _readHtml(String text) {
796 return GetterUtil.getString(text);
797 }
798
799 private List<License> _readLicenseList(Element parentElement, String name) {
800 List<License> licenses = new ArrayList<License>();
801
802 for (Element licenseElement : parentElement.elements(name)) {
803 License license = new License();
804
805 license.setName(licenseElement.getText());
806
807 Attribute osiApproved = licenseElement.attribute("osi-approved");
808
809 if (osiApproved != null) {
810 license.setOsiApproved(
811 GetterUtil.getBoolean(osiApproved.getText()));
812 }
813
814 Attribute url = licenseElement.attribute("url");
815
816 if (url != null) {
817 license.setUrl(url.getText());
818 }
819
820 licenses.add(license);
821 }
822
823 return licenses;
824 }
825
826 private List<String> _readList(Element parentElement, String name) {
827 List<String> list = new ArrayList<String>();
828
829 if (parentElement == null) {
830 return list;
831 }
832
833 for (Element element : parentElement.elements(name)) {
834 String text = StringUtil.toLowerCase(element.getText().trim());
835
836 list.add(text);
837 }
838
839 return list;
840 }
841
842 private PluginPackage _readPluginPackageProperties(
843 String displayName, Properties properties) {
844
845 int pos = displayName.indexOf("-portlet");
846
847 String pluginType = Plugin.TYPE_PORTLET;
848
849 if (pos == -1) {
850 pos = displayName.indexOf("-ext");
851
852 pluginType = Plugin.TYPE_EXT;
853 }
854
855 if (pos == -1) {
856 pos = displayName.indexOf("-hook");
857
858 pluginType = Plugin.TYPE_HOOK;
859 }
860
861 if (pos == -1) {
862 pos = displayName.indexOf("-layouttpl");
863
864 pluginType = Plugin.TYPE_LAYOUT_TEMPLATE;
865 }
866
867 if (pos == -1) {
868 pos = displayName.indexOf("-theme");
869
870 pluginType = Plugin.TYPE_THEME;
871 }
872
873 if (pos == -1) {
874 pos = displayName.indexOf("-web");
875
876 pluginType = Plugin.TYPE_WEB;
877 }
878
879 if (pos == -1) {
880 return null;
881 }
882
883 String displayPrefix = displayName.substring(0, pos);
884
885 String moduleGroupId = GetterUtil.getString(
886 properties.getProperty("module-group-id"));
887 String moduleArtifactId = displayPrefix + "-" + pluginType;
888
889 String moduleVersion = GetterUtil.getString(
890 properties.getProperty("module-version"));
891
892 if (Validator.isNull(moduleVersion)) {
893 int moduleVersionPos = pos + pluginType.length() + 2;
894
895 if (displayName.length() > moduleVersionPos) {
896 moduleVersion = displayName.substring(moduleVersionPos);
897 }
898 else {
899 moduleVersion = ReleaseInfo.getVersion();
900 }
901 }
902
903 String moduleId =
904 moduleGroupId + "/" + moduleArtifactId + "/" + moduleVersion +
905 "/war";
906
907 String pluginName = GetterUtil.getString(
908 properties.getProperty("name"));
909
910 String deploymentContext = GetterUtil.getString(
911 properties.getProperty("recommended-deployment-context"),
912 moduleArtifactId);
913
914 String author = GetterUtil.getString(properties.getProperty("author"));
915
916 List<String> types = new ArrayList<String>();
917
918 types.add(pluginType);
919
920 List<License> licenses = new ArrayList<License>();
921
922 String[] licensesArray = StringUtil.split(
923 properties.getProperty("licenses"));
924
925 for (int i = 0; i < licensesArray.length; i++) {
926 License license = new License();
927
928 license.setName(licensesArray[i].trim());
929 license.setOsiApproved(true);
930
931 licenses.add(license);
932 }
933
934 List<String> liferayVersions = new ArrayList<String>();
935
936 String[] liferayVersionsArray = StringUtil.split(
937 properties.getProperty("liferay-versions"));
938
939 for (String liferayVersion : liferayVersionsArray) {
940 liferayVersions.add(liferayVersion.trim());
941 }
942
943 if (liferayVersions.isEmpty()) {
944 liferayVersions.add(ReleaseInfo.getVersion() + "+");
945 }
946
947 List<String> tags = new ArrayList<String>();
948
949 String[] tagsArray = StringUtil.split(properties.getProperty("tags"));
950
951 for (String tag : tagsArray) {
952 tags.add(tag.trim());
953 }
954
955 String shortDescription = GetterUtil.getString(
956 properties.getProperty("short-description"));
957 String longDescription = GetterUtil.getString(
958 properties.getProperty("long-description"));
959 String changeLog = GetterUtil.getString(
960 properties.getProperty("change-log"));
961 String pageURL = GetterUtil.getString(
962 properties.getProperty("page-url"));
963 String downloadURL = GetterUtil.getString(
964 properties.getProperty("download-url"));
965 List<String> requiredDeploymentContexts = ListUtil.fromArray(
966 StringUtil.split(
967 properties.getProperty("required-deployment-contexts")));
968
969 PluginPackage pluginPackage = new PluginPackageImpl(moduleId);
970
971 pluginPackage.setName(pluginName);
972 pluginPackage.setRecommendedDeploymentContext(deploymentContext);
973
974 pluginPackage.setAuthor(author);
975 pluginPackage.setTypes(types);
976 pluginPackage.setLicenses(licenses);
977 pluginPackage.setLiferayVersions(liferayVersions);
978 pluginPackage.setTags(tags);
979 pluginPackage.setShortDescription(shortDescription);
980 pluginPackage.setLongDescription(longDescription);
981 pluginPackage.setChangeLog(changeLog);
982
983 pluginPackage.setPageURL(pageURL);
984 pluginPackage.setDownloadURL(downloadURL);
985
986 pluginPackage.setRequiredDeploymentContexts(requiredDeploymentContexts);
987
988 return pluginPackage;
989 }
990
991
995 private PluginPackage _readPluginPackageServletContext(
996 ServletContext servletContext)
997 throws DocumentException, IOException {
998
999 String servletContextName = servletContext.getServletContextName();
1000
1001 if (_log.isInfoEnabled()) {
1002 if (servletContextName == null) {
1003 _log.info("Reading plugin package for the root context");
1004 }
1005 else {
1006 _log.info("Reading plugin package for " + servletContextName);
1007 }
1008 }
1009
1010 PluginPackage pluginPackage = null;
1011
1012 String xml = HttpUtil.URLtoString(
1013 servletContext.getResource("/WEB-INF/liferay-plugin-package.xml"));
1014
1015 if (xml != null) {
1016 pluginPackage = _readPluginPackageXml(xml);
1017 }
1018 else {
1019 String propertiesString = HttpUtil.URLtoString(
1020 servletContext.getResource(
1021 "/WEB-INF/liferay-plugin-package.properties"));
1022
1023 if (propertiesString != null) {
1024 if (_log.isDebugEnabled()) {
1025 _log.debug(
1026 "Reading plugin package from " +
1027 "liferay-plugin-package.properties");
1028 }
1029
1030 Properties properties = PropertiesUtil.load(propertiesString);
1031
1032 String displayName = servletContextName;
1033
1034 if (displayName.startsWith(StringPool.SLASH)) {
1035 displayName = displayName.substring(1);
1036 }
1037
1038 pluginPackage = _readPluginPackageProperties(
1039 displayName, properties);
1040 }
1041
1042 if (pluginPackage == null) {
1043 if (_log.isDebugEnabled()) {
1044 _log.debug("Reading plugin package from MANIFEST.MF");
1045 }
1046
1047 pluginPackage =_readPluginPackageServletManifest(
1048 servletContext);
1049 }
1050 }
1051
1052 pluginPackage.setContext(servletContextName);
1053
1054 return pluginPackage;
1055 }
1056
1057 private PluginPackage _readPluginPackageServletManifest(
1058 ServletContext servletContext)
1059 throws IOException {
1060
1061 Attributes attributes = null;
1062
1063 String servletContextName = servletContext.getServletContextName();
1064
1065 InputStream inputStream = servletContext.getResourceAsStream(
1066 "/META-INF/MANIFEST.MF");
1067
1068 if (inputStream != null) {
1069 Manifest manifest = new Manifest(inputStream);
1070
1071 attributes = manifest.getMainAttributes();
1072 }
1073 else {
1074 attributes = new Attributes();
1075 }
1076
1077 String artifactGroupId = attributes.getValue(
1078 "Implementation-Vendor-Id");
1079
1080 if (Validator.isNull(artifactGroupId)) {
1081 artifactGroupId = attributes.getValue("Implementation-Vendor");
1082 }
1083
1084 if (Validator.isNull(artifactGroupId)) {
1085 artifactGroupId = GetterUtil.getString(
1086 attributes.getValue("Bundle-Vendor"), servletContextName);
1087 }
1088
1089 String artifactId = attributes.getValue("Implementation-Title");
1090
1091 if (Validator.isNull(artifactId)) {
1092 artifactId = GetterUtil.getString(
1093 attributes.getValue("Bundle-Name"), servletContextName);
1094 }
1095
1096 String version = attributes.getValue("Implementation-Version");
1097
1098 if (Validator.isNull(version)) {
1099 version = GetterUtil.getString(
1100 attributes.getValue("Bundle-Version"), Version.UNKNOWN);
1101 }
1102
1103 if (version.equals(Version.UNKNOWN) && _log.isWarnEnabled()) {
1104 _log.warn(
1105 "Plugin package on context " + servletContextName +
1106 " cannot be tracked because this WAR does not contain a " +
1107 "liferay-plugin-package.xml file");
1108 }
1109
1110 PluginPackage pluginPackage = new PluginPackageImpl(
1111 artifactGroupId + StringPool.SLASH + artifactId + StringPool.SLASH +
1112 version + StringPool.SLASH + "war");
1113
1114 pluginPackage.setName(artifactId);
1115
1116 String shortDescription = attributes.getValue("Bundle-Description");
1117
1118 if (Validator.isNotNull(shortDescription)) {
1119 pluginPackage.setShortDescription(shortDescription);
1120 }
1121
1122 String pageURL = attributes.getValue("Bundle-DocURL");
1123
1124 if (Validator.isNotNull(pageURL)) {
1125 pluginPackage.setPageURL(pageURL);
1126 }
1127
1128 return pluginPackage;
1129 }
1130
1131 private PluginPackage _readPluginPackageXml(Element pluginPackageElement) {
1132 String name = pluginPackageElement.elementText("name");
1133
1134 if (_log.isDebugEnabled()) {
1135 _log.debug("Reading pluginPackage definition " + name);
1136 }
1137
1138 PluginPackage pluginPackage = new PluginPackageImpl(
1139 GetterUtil.getString(
1140 pluginPackageElement.elementText("module-id")));
1141
1142 List<String> liferayVersions = _readList(
1143 pluginPackageElement.element("liferay-versions"),
1144 "liferay-version");
1145
1146 List<String> types = _readList(
1147 pluginPackageElement.element("types"), "type");
1148
1149 if (types.contains("layout-template")) {
1150 types.remove("layout-template");
1151
1152 types.add(Plugin.TYPE_LAYOUT_TEMPLATE);
1153 }
1154
1155 pluginPackage.setName(_readText(name));
1156 pluginPackage.setRecommendedDeploymentContext(
1157 _readText(
1158 pluginPackageElement.elementText(
1159 "recommended-deployment-context")));
1160 pluginPackage.setRequiredDeploymentContexts(
1161 _readList(
1162 pluginPackageElement.element("required-deployment-contexts"),
1163 "required-deployment-context"));
1164 pluginPackage.setModifiedDate(
1165 _readDate(pluginPackageElement.elementText("modified-date")));
1166 pluginPackage.setAuthor(
1167 _readText(pluginPackageElement.elementText("author")));
1168 pluginPackage.setTypes(types);
1169 pluginPackage.setLicenses(
1170 _readLicenseList(
1171 pluginPackageElement.element("licenses"), "license"));
1172 pluginPackage.setLiferayVersions(liferayVersions);
1173 pluginPackage.setTags(
1174 _readList(pluginPackageElement.element("tags"), "tag"));
1175 pluginPackage.setShortDescription(
1176 _readText(pluginPackageElement.elementText("short-description")));
1177 pluginPackage.setLongDescription(
1178 _readHtml(pluginPackageElement.elementText("long-description")));
1179 pluginPackage.setChangeLog(
1180 _readHtml(pluginPackageElement.elementText("change-log")));
1181 pluginPackage.setScreenshots(
1182 _readScreenshots(pluginPackageElement.element("screenshots")));
1183 pluginPackage.setPageURL(
1184 _readText(pluginPackageElement.elementText("page-url")));
1185 pluginPackage.setDownloadURL(
1186 _readText(pluginPackageElement.elementText("download-url")));
1187 pluginPackage.setDeploymentSettings(
1188 _readProperties(
1189 pluginPackageElement.element("deployment-settings"),
1190 "setting"));
1191
1192 return pluginPackage;
1193 }
1194
1195 private PluginPackage _readPluginPackageXml(String xml)
1196 throws DocumentException {
1197
1198 Document document = SAXReaderUtil.read(xml);
1199
1200 Element rootElement = document.getRootElement();
1201
1202 return _readPluginPackageXml(rootElement);
1203 }
1204
1205 private Properties _readProperties(Element parentElement, String name) {
1206 Properties properties = new Properties();
1207
1208 if (parentElement == null) {
1209 return properties;
1210 }
1211
1212 for (Element element : parentElement.elements(name)) {
1213 properties.setProperty(
1214 element.attributeValue("name"),
1215 element.attributeValue("value"));
1216 }
1217
1218 return properties;
1219 }
1220
1221 private List<Screenshot> _readScreenshots(Element parentElement) {
1222 List<Screenshot> screenshots = new ArrayList<Screenshot>();
1223
1224 if (parentElement == null) {
1225 return screenshots;
1226 }
1227
1228 for (Element screenshotElement : parentElement.elements("screenshot")) {
1229 Screenshot screenshot = new Screenshot();
1230
1231 screenshot.setThumbnailURL(
1232 screenshotElement.elementText("thumbnail-url"));
1233 screenshot.setLargeImageURL(
1234 screenshotElement.elementText("large-image-url"));
1235
1236 screenshots.add(screenshot);
1237 }
1238
1239 return screenshots;
1240 }
1241
1242 private String _readText(String text) {
1243 return HtmlUtil.extractText(GetterUtil.getString(text));
1244 }
1245
1246 private void _refreshUpdatesAvailableCache() {
1247 _updateAvailable = null;
1248 }
1249
1250 private void _registerInstalledPluginPackage(PluginPackage pluginPackage)
1251 throws PortalException {
1252
1253 _installedPluginPackages.addPluginPackage(pluginPackage);
1254
1255 _updateAvailable = null;
1256
1257 _indexPluginPackage(pluginPackage);
1258 }
1259
1260 private void _registerPluginPackageInstallation(String preliminaryContext) {
1261 _installedPluginPackages.registerPluginPackageInstallation(
1262 preliminaryContext);
1263 }
1264
1265 private RepositoryReport _reloadRepositories() throws PortalException {
1266 if (_log.isInfoEnabled()) {
1267 _log.info("Reloading repositories");
1268 }
1269
1270 RepositoryReport repositoryReport = new RepositoryReport();
1271
1272 String[] repositoryURLs = _getRepositoryURLs();
1273
1274 for (int i = 0; i < repositoryURLs.length; i++) {
1275 String repositoryURL = repositoryURLs[i];
1276
1277 try {
1278 _loadRepository(repositoryURL);
1279
1280 repositoryReport.addSuccess(repositoryURL);
1281 }
1282 catch (PluginPackageException ppe) {
1283 repositoryReport.addError(repositoryURL, ppe);
1284
1285 _log.error(
1286 "Unable to load repository " + repositoryURL + " " +
1287 ppe.toString());
1288 }
1289 }
1290
1291 Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);
1292
1293 indexer.reindex(new String[0]);
1294
1295 return repositoryReport;
1296 }
1297
1298 private Hits _search(
1299 String keywords, String type, String tag, String license,
1300 String repositoryURL, String status, int start, int end)
1301 throws PortalException {
1302
1303 _checkRepositories(repositoryURL);
1304
1305 SearchContext searchContext = new SearchContext();
1306
1307 Map<String, Serializable> attributes =
1308 new HashMap<String, Serializable>();
1309
1310 attributes.put("license", license);
1311 attributes.put("repositoryURL", repositoryURL);
1312 attributes.put("status", status);
1313 attributes.put("tag", tag);
1314 attributes.put("type", type);
1315
1316 searchContext.setAttributes(attributes);
1317
1318 searchContext.setCompanyId(CompanyConstants.SYSTEM);
1319 searchContext.setEnd(end);
1320 searchContext.setKeywords(keywords);
1321
1322 QueryConfig queryConfig = new QueryConfig();
1323
1324 queryConfig.setHighlightEnabled(false);
1325 queryConfig.setScoreEnabled(false);
1326
1327 searchContext.setQueryConfig(queryConfig);
1328
1329 searchContext.setStart(start);
1330
1331 Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);
1332
1333 return indexer.search(searchContext);
1334 }
1335
1336 private void _unregisterInstalledPluginPackage(PluginPackage pluginPackage)
1337 throws PortalException {
1338
1339 _installedPluginPackages.removePluginPackage(pluginPackage);
1340
1341 try {
1342 List<PluginPackage> pluginPackages = _getAvailablePluginPackages(
1343 pluginPackage.getGroupId(), pluginPackage.getArtifactId());
1344
1345 for (PluginPackage availablePackage : pluginPackages) {
1346 _indexPluginPackage(availablePackage);
1347 }
1348 }
1349 catch (PluginPackageException ppe) {
1350 if (_log.isWarnEnabled()) {
1351 _log.warn(
1352 "Unable to reindex unistalled package " +
1353 pluginPackage.getContext() + ": " + ppe.getMessage());
1354 }
1355 }
1356 }
1357
1358 private void _updateInstallingPluginPackage(
1359 String preliminaryContext, PluginPackage pluginPackage) {
1360
1361 _installedPluginPackages.unregisterPluginPackageInstallation(
1362 preliminaryContext);
1363 _installedPluginPackages.registerPluginPackageInstallation(
1364 pluginPackage);
1365 }
1366
1367 private static Log _log = LogFactoryUtil.getLog(PluginPackageUtil.class);
1368
1369 private static PluginPackageUtil _instance = new PluginPackageUtil();
1370
1371 private Set<String> _availableTagsCache;
1372 private LocalPluginPackageRepository _installedPluginPackages;
1373 private Date _lastUpdateDate;
1374 private Map<String, RemotePluginPackageRepository> _repositoryCache;
1375 private boolean _settingUpdateAvailable;
1376 private Boolean _updateAvailable;
1377
1378 private class UpdateAvailableRunner implements Runnable {
1379
1380 @Override
1381 public void run() {
1382 try {
1383 setUpdateAvailable();
1384 }
1385 catch (Exception e) {
1386 if (_log.isWarnEnabled()) {
1387 _log.warn(e.getMessage());
1388 }
1389 }
1390 }
1391
1392 protected void setUpdateAvailable() throws Exception {
1393 StopWatch stopWatch = new StopWatch();
1394
1395 stopWatch.start();
1396
1397 if (_log.isInfoEnabled()) {
1398 _log.info("Checking for available updates");
1399 }
1400
1401 for (PluginPackage pluginPackage :
1402 _installedPluginPackages.getPluginPackages()) {
1403
1404 PluginPackage availablePluginPackage = null;
1405
1406 if (_isIgnored(pluginPackage)) {
1407 continue;
1408 }
1409
1410 availablePluginPackage =
1411 PluginPackageUtil.getLatestAvailablePluginPackage(
1412 pluginPackage.getGroupId(),
1413 pluginPackage.getArtifactId());
1414
1415 if (availablePluginPackage == null) {
1416 continue;
1417 }
1418
1419 Version availablePluginPackageVersion = Version.getInstance(
1420 availablePluginPackage.getVersion());
1421
1422 if (availablePluginPackageVersion.isLaterVersionThan(
1423 pluginPackage.getVersion())) {
1424
1425 _updateAvailable = Boolean.TRUE;
1426
1427 break;
1428 }
1429 }
1430
1431 if (_updateAvailable == null) {
1432 _updateAvailable = Boolean.FALSE;
1433 }
1434
1435 _settingUpdateAvailable = false;
1436
1437 if (_log.isInfoEnabled()) {
1438 _log.info(
1439 "Finished checking for available updates in " +
1440 stopWatch.getTime() + " ms");
1441 }
1442 }
1443
1444 }
1445
1446 }