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