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