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.PropertiesUtil;
038 import com.liferay.portal.kernel.util.PropsKeys;
039 import com.liferay.portal.kernel.util.ReleaseInfo;
040 import com.liferay.portal.kernel.util.StringBundler;
041 import com.liferay.portal.kernel.util.StringPool;
042 import com.liferay.portal.kernel.util.StringUtil;
043 import com.liferay.portal.kernel.util.Time;
044 import com.liferay.portal.kernel.util.Validator;
045 import com.liferay.portal.kernel.xml.Attribute;
046 import com.liferay.portal.kernel.xml.Document;
047 import com.liferay.portal.kernel.xml.DocumentException;
048 import com.liferay.portal.kernel.xml.Element;
049 import com.liferay.portal.kernel.xml.SAXReaderUtil;
050 import com.liferay.portal.model.CompanyConstants;
051 import com.liferay.portal.model.Plugin;
052 import com.liferay.portal.util.HttpImpl;
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.Locale;
071 import java.util.Map;
072 import java.util.Properties;
073 import java.util.Set;
074 import java.util.TreeSet;
075 import java.util.jar.Attributes;
076 import java.util.jar.Manifest;
077
078 import javax.servlet.ServletContext;
079 import javax.servlet.http.HttpServletResponse;
080
081 import org.apache.commons.httpclient.HostConfiguration;
082 import org.apache.commons.httpclient.HttpClient;
083 import org.apache.commons.httpclient.methods.GetMethod;
084 import org.apache.commons.lang.time.StopWatch;
085
086
091 public class PluginPackageUtil {
092
093 public static final String REPOSITORY_XML_FILENAME_EXTENSION =
094 "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[] getStatusAndInstalledVersion(
159 PluginPackage pluginPackage) {
160
161 return _instance._getStatusAndInstalledVersion(pluginPackage);
162 }
163
164 public static String[] getRepositoryURLs() throws SystemException {
165 return _instance._getRepositoryURLs();
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 =
325 _getRepository(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 =
356 _getRepository(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 =
423 _getRepository(repositoryURL);
424
425 return repository.findPluginByArtifactURL(url);
426 }
427 catch (PluginPackageException pe) {
428 _log.error("Unable to load repository " + repositoryURL, pe);
429 }
430 }
431
432 return null;
433 }
434
435 private RemotePluginPackageRepository _getRepository(
436 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 = (HttpImpl)HttpUtil.getHttp();
649
650 HostConfiguration hostConfiguration = httpImpl.getHostConfiguration(
651 pluginsXmlURL);
652
653 HttpClient httpClient = httpImpl.getClient(hostConfiguration);
654
655 httpImpl.proxifyState(httpClient.getState(), hostConfiguration);
656
657 GetMethod getFileMethod = new GetMethod(pluginsXmlURL);
658
659 byte[] bytes = null;
660
661 try {
662 int responseCode = httpClient.executeMethod(
663 hostConfiguration, getFileMethod);
664
665 if (responseCode != HttpServletResponse.SC_OK) {
666 if (_log.isDebugEnabled()) {
667 _log.debug(
668 "A repository for version " +
669 ReleaseInfo.getVersion() + " was not found. " +
670 "Checking general repository");
671 }
672
673 sb.setIndex(0);
674
675 sb.append(repositoryURL);
676 sb.append(StringPool.SLASH);
677 sb.append(REPOSITORY_XML_FILENAME_PREFIX);
678 sb.append(StringPool.PERIOD);
679 sb.append(REPOSITORY_XML_FILENAME_EXTENSION);
680
681 pluginsXmlURL = sb.toString();
682
683 getFileMethod.releaseConnection();
684
685 getFileMethod = new GetMethod(pluginsXmlURL);
686
687 responseCode = httpClient.executeMethod(
688 hostConfiguration, getFileMethod);
689
690 if (responseCode != HttpServletResponse.SC_OK) {
691 throw new PluginPackageException(
692 "Unable to download file " + pluginsXmlURL +
693 " because of response code " + responseCode);
694 }
695 }
696
697 bytes = getFileMethod.getResponseBody();
698 }
699 finally {
700 getFileMethod.releaseConnection();
701 }
702
703 if ((bytes != null) && (bytes.length > 0)) {
704 repository = _parseRepositoryXml(
705 new String(bytes), repositoryURL);
706
707 _repositoryCache.put(repositoryURL, repository);
708 _availableTagsCache.addAll(repository.getTags());
709 _lastUpdateDate = new Date();
710 _updateAvailable = null;
711
712 return repository;
713 }
714 else {
715 _lastUpdateDate = new Date();
716
717 throw new PluginPackageException("Download returned 0 bytes");
718 }
719 }
720 catch (MalformedURLException mue) {
721 _repositoryCache.remove(repositoryURL);
722
723 throw new PluginPackageException(
724 "Invalid URL " + pluginsXmlURL, mue);
725 }
726 catch (IOException ioe) {
727 _repositoryCache.remove(repositoryURL);
728
729 throw new PluginPackageException(
730 "Unable to communicate with repository " + repositoryURL, ioe);
731 }
732 catch (DocumentException de) {
733 _repositoryCache.remove(repositoryURL);
734
735 throw new PluginPackageException(
736 "Unable to parse plugin list for repository " + repositoryURL,
737 de);
738 }
739 }
740
741 private RemotePluginPackageRepository _parseRepositoryXml(
742 String xml, String repositoryURL)
743 throws DocumentException, PortalException {
744
745 List<String> supportedPluginTypes = Arrays.asList(getSupportedTypes());
746
747 if (_log.isDebugEnabled()) {
748 _log.debug(
749 "Loading plugin repository " + repositoryURL + ":\n" + xml);
750 }
751
752 RemotePluginPackageRepository pluginPackageRepository =
753 new RemotePluginPackageRepository(repositoryURL);
754
755 if (xml == null) {
756 return pluginPackageRepository;
757 }
758
759 Document document = SAXReaderUtil.read(xml);
760
761 Element rootElement = document.getRootElement();
762
763 Properties settings = _readProperties(
764 rootElement.element("settings"), "setting");
765
766 pluginPackageRepository.setSettings(settings);
767
768 List<Element> pluginPackageElements = rootElement.elements(
769 "plugin-package");
770
771 for (Element pluginPackageElement : pluginPackageElements) {
772 PluginPackage pluginPackage = _readPluginPackageXml(
773 pluginPackageElement);
774
775 if (!_isCurrentVersionSupported(
776 pluginPackage.getLiferayVersions())) {
777
778 continue;
779 }
780
781 boolean containsSupportedTypes = false;
782
783 List<String> pluginTypes = pluginPackage.getTypes();
784
785 for (String pluginType : pluginTypes) {
786 if (supportedPluginTypes.contains(pluginType)) {
787 containsSupportedTypes = true;
788
789 break;
790 }
791 }
792
793 if (!containsSupportedTypes) {
794 continue;
795 }
796
797 pluginPackage.setRepository(pluginPackageRepository);
798
799 pluginPackageRepository.addPluginPackage(pluginPackage);
800
801 _indexPluginPackage(pluginPackage);
802 }
803
804 return pluginPackageRepository;
805 }
806
807 private Date _readDate(String text) {
808 if (Validator.isNotNull(text)) {
809 DateFormat dateFormat = DateFormatFactoryUtil.getSimpleDateFormat(
810 Time.RFC822_FORMAT, Locale.US);
811
812 try {
813 return dateFormat.parse(text);
814 }
815 catch (Exception e) {
816 if (_log.isWarnEnabled()) {
817 _log.warn("Unable to parse date " + text);
818 }
819 }
820 }
821
822 return new Date();
823 }
824
825 private String _readHtml(String text) {
826 return GetterUtil.getString(text);
827 }
828
829 private List<License> _readLicenseList(Element parentElement, String name) {
830 List<License> licenses = new ArrayList<License>();
831
832 for (Element licenseElement : parentElement.elements(name)) {
833 License license = new License();
834
835 license.setName(licenseElement.getText());
836
837 Attribute osiApproved = licenseElement.attribute("osi-approved");
838
839 if (osiApproved != null) {
840 license.setOsiApproved(
841 GetterUtil.getBoolean(osiApproved.getText()));
842 }
843
844 Attribute url = licenseElement.attribute("url");
845
846 if (url != null) {
847 license.setUrl(url.getText());
848 }
849
850 licenses.add(license);
851 }
852
853 return licenses;
854 }
855
856 private List<String> _readList(Element parentElement, String name) {
857 List<String> list = new ArrayList<String>();
858
859 if (parentElement == null) {
860 return list;
861 }
862
863 for (Element element : parentElement.elements(name)) {
864 String text = element.getText().trim().toLowerCase();
865
866 list.add(text);
867 }
868
869 return list;
870 }
871
872 private PluginPackage _readPluginPackageProperties(
873 String displayName, Properties properties) {
874
875 int pos = displayName.indexOf("-portlet");
876
877 String pluginType = Plugin.TYPE_PORTLET;
878
879 if (pos == -1) {
880 pos = displayName.indexOf("-ext");
881
882 pluginType = Plugin.TYPE_EXT;
883 }
884
885 if (pos == -1) {
886 pos = displayName.indexOf("-hook");
887
888 pluginType = Plugin.TYPE_HOOK;
889 }
890
891 if (pos == -1) {
892 pos = displayName.indexOf("-layouttpl");
893
894 pluginType = Plugin.TYPE_LAYOUT_TEMPLATE;
895 }
896
897 if (pos == -1) {
898 pos = displayName.indexOf("-theme");
899
900 pluginType = Plugin.TYPE_THEME;
901 }
902
903 if (pos == -1) {
904 pos = displayName.indexOf("-web");
905
906 pluginType = Plugin.TYPE_WEB;
907 }
908
909 if (pos == -1) {
910 return null;
911 }
912
913 String displayPrefix = displayName.substring(0, pos);
914
915 String moduleGroupId = GetterUtil.getString(
916 properties.getProperty("module-group-id"));
917 String moduleArtifactId = displayPrefix + "-" + pluginType;
918
919 String moduleVersion = null;
920
921 int moduleVersionPos = pos + pluginType.length() + 2;
922
923 if (displayName.length() > moduleVersionPos) {
924 moduleVersion = displayName.substring(moduleVersionPos);
925 }
926 else {
927 moduleVersion = ReleaseInfo.getVersion();
928 }
929
930 String moduleId =
931 moduleGroupId + "/" + moduleArtifactId + "/" + moduleVersion +
932 "/war";
933
934 String pluginName = GetterUtil.getString(
935 properties.getProperty("name"));
936
937 String deploymentContext = GetterUtil.getString(
938 properties.getProperty("recommended-deployment-context"),
939 moduleArtifactId);
940
941 String author = GetterUtil.getString(properties.getProperty("author"));
942
943 List<String> types = new ArrayList<String>();
944
945 types.add(pluginType);
946
947 List<License> licenses = new ArrayList<License>();
948
949 String[] licensesArray = StringUtil.split(
950 properties.getProperty("licenses"));
951
952 for (int i = 0; i < licensesArray.length; i++) {
953 License license = new License();
954
955 license.setName(licensesArray[i].trim());
956 license.setOsiApproved(true);
957
958 licenses.add(license);
959 }
960
961 List<String> liferayVersions = new ArrayList<String>();
962
963 String[] liferayVersionsArray = StringUtil.split(
964 properties.getProperty("liferay-versions"));
965
966 for (String liferayVersion : liferayVersionsArray) {
967 liferayVersions.add(liferayVersion.trim());
968 }
969
970 if (liferayVersions.size() == 0) {
971 liferayVersions.add(ReleaseInfo.getVersion() + "+");
972 }
973
974 List<String> tags = new ArrayList<String>();
975
976 String[] tagsArray = StringUtil.split(properties.getProperty("tags"));
977
978 for (String tag : tagsArray) {
979 tags.add(tag.trim());
980 }
981
982 String shortDescription = GetterUtil.getString(
983 properties.getProperty("short-description"));
984 String longDescription = GetterUtil.getString(
985 properties.getProperty("long-description"));
986 String changeLog = GetterUtil.getString(
987 properties.getProperty("change-log"));
988 String pageURL = GetterUtil.getString(
989 properties.getProperty("page-url"));
990 String downloadURL = GetterUtil.getString(
991 properties.getProperty("download-url"));
992
993 PluginPackage pluginPackage = new PluginPackageImpl(moduleId);
994
995 pluginPackage.setName(pluginName);
996 pluginPackage.setRecommendedDeploymentContext(deploymentContext);
997
998 pluginPackage.setAuthor(author);
999 pluginPackage.setTypes(types);
1000 pluginPackage.setLicenses(licenses);
1001 pluginPackage.setLiferayVersions(liferayVersions);
1002 pluginPackage.setTags(tags);
1003 pluginPackage.setShortDescription(shortDescription);
1004 pluginPackage.setLongDescription(longDescription);
1005 pluginPackage.setChangeLog(changeLog);
1006
1007 pluginPackage.setPageURL(pageURL);
1008 pluginPackage.setDownloadURL(downloadURL);
1009
1010
1011 return pluginPackage;
1012 }
1013
1014 private PluginPackage _readPluginPackageServletManifest(
1015 ServletContext servletContext)
1016 throws IOException {
1017 Attributes attributes = null;
1018
1019 String servletContextName = servletContext.getServletContextName();
1020
1021 InputStream inputStream = servletContext.getResourceAsStream(
1022 "/META-INF/MANIFEST.MF");
1023
1024 if (inputStream != null) {
1025 Manifest manifest = new Manifest(inputStream);
1026
1027 attributes = manifest.getMainAttributes();
1028 }
1029 else {
1030 attributes = new Attributes();
1031 }
1032
1033 String artifactGroupId = attributes.getValue(
1034 "Implementation-Vendor-Id");
1035
1036 if (Validator.isNull(artifactGroupId)) {
1037 artifactGroupId = attributes.getValue("Implementation-Vendor");
1038 }
1039
1040 if (Validator.isNull(artifactGroupId)) {
1041 artifactGroupId = GetterUtil.getString(
1042 attributes.getValue("Bundle-Vendor"), servletContextName);
1043 }
1044
1045 String artifactId = attributes.getValue("Implementation-Title");
1046
1047 if (Validator.isNull(artifactId)) {
1048 artifactId = GetterUtil.getString(
1049 attributes.getValue("Bundle-Name"), servletContextName);
1050 }
1051
1052 String version = attributes.getValue("Implementation-Version");
1053
1054 if (Validator.isNull(version)) {
1055 version = GetterUtil.getString(
1056 attributes.getValue("Bundle-Version"), Version.UNKNOWN);
1057 }
1058
1059 if (version.equals(Version.UNKNOWN) && _log.isWarnEnabled()) {
1060 _log.warn(
1061 "Plugin package on context " + servletContextName +
1062 " cannot be tracked because this WAR does not contain a " +
1063 "liferay-plugin-package.xml file");
1064 }
1065
1066 PluginPackage pluginPackage = new PluginPackageImpl(
1067 artifactGroupId + StringPool.SLASH + artifactId + StringPool.SLASH +
1068 version + StringPool.SLASH + "war");
1069
1070 pluginPackage.setName(artifactId);
1071
1072 String shortDescription = attributes.getValue("Bundle-Description");
1073
1074 if (Validator.isNotNull(shortDescription)) {
1075 pluginPackage.setShortDescription(shortDescription);
1076 }
1077
1078 String pageURL = attributes.getValue("Bundle-DocURL");
1079
1080 if (Validator.isNotNull(pageURL)) {
1081 pluginPackage.setPageURL(pageURL);
1082 }
1083
1084 return pluginPackage;
1085 }
1086
1087 private PluginPackage _readPluginPackageServletContext(
1088 ServletContext servletContext)
1089 throws DocumentException, IOException {
1090
1091 String servletContextName = servletContext.getServletContextName();
1092
1093 String xml = HttpUtil.URLtoString(
1094 servletContext.getResource("/WEB-INF/liferay-plugin-package.xml"));
1095
1096 if (_log.isInfoEnabled()) {
1097 if (servletContextName == null) {
1098 _log.info("Reading plugin package for the root context");
1099 }
1100 else {
1101 _log.info("Reading plugin package for " + servletContextName);
1102 }
1103 }
1104
1105 PluginPackage pluginPackage = null;
1106
1107 if (xml == null) {
1108 String propertiesString = HttpUtil.URLtoString(
1109 servletContext.getResource(
1110 "/WEB-INF/liferay-plugin-package.properties"));
1111
1112 if (propertiesString != null) {
1113 if (_log.isDebugEnabled()) {
1114 _log.debug(
1115 "Reading plugin package from " +
1116 "liferay-plugin-package.properties");
1117 }
1118
1119 Properties properties = PropertiesUtil.load(propertiesString);
1120
1121 String displayName = servletContextName;
1122
1123 if (displayName.startsWith(StringPool.SLASH)) {
1124 displayName = displayName.substring(1);
1125 }
1126
1127 pluginPackage = _readPluginPackageProperties(
1128 displayName, properties);
1129 }
1130
1131 if (pluginPackage == null) {
1132 if (_log.isDebugEnabled()) {
1133 _log.debug("Reading plugin package from MANIFEST.MF");
1134 }
1135
1136 pluginPackage =_readPluginPackageServletManifest(
1137 servletContext);
1138 }
1139 }
1140 else {
1141 if (_log.isDebugEnabled()) {
1142 _log.debug(
1143 "Reading plugin package from liferay-plugin-package.xml");
1144 }
1145
1146 pluginPackage = _readPluginPackageXml(xml);
1147 }
1148
1149 pluginPackage.setContext(servletContextName);
1150
1151 return pluginPackage;
1152 }
1153
1154 private PluginPackage _readPluginPackageXml(Element pluginPackageElement) {
1155 String name = pluginPackageElement.elementText("name");
1156
1157 if (_log.isDebugEnabled()) {
1158 _log.debug("Reading pluginPackage definition " + name);
1159 }
1160
1161 PluginPackage pluginPackage = new PluginPackageImpl(
1162 GetterUtil.getString(
1163 pluginPackageElement.elementText("module-id")));
1164
1165 List<String> liferayVersions = _readList(
1166 pluginPackageElement.element("liferay-versions"),
1167 "liferay-version");
1168
1169 List<String> types = _readList(
1170 pluginPackageElement.element("types"), "type");
1171
1172 pluginPackage.setName(_readText(name));
1173 pluginPackage.setRecommendedDeploymentContext(
1174 _readText(
1175 pluginPackageElement.elementText(
1176 "recommended-deployment-context")));
1177 pluginPackage.setModifiedDate(
1178 _readDate(pluginPackageElement.elementText("modified-date")));
1179 pluginPackage.setAuthor(
1180 _readText(pluginPackageElement.elementText("author")));
1181 pluginPackage.setTypes(types);
1182 pluginPackage.setLicenses(
1183 _readLicenseList(
1184 pluginPackageElement.element("licenses"), "license"));
1185 pluginPackage.setLiferayVersions(liferayVersions);
1186 pluginPackage.setTags(
1187 _readList(pluginPackageElement.element("tags"), "tag"));
1188 pluginPackage.setShortDescription(
1189 _readText(pluginPackageElement.elementText("short-description")));
1190 pluginPackage.setLongDescription(
1191 _readHtml(pluginPackageElement.elementText("long-description")));
1192 pluginPackage.setChangeLog(
1193 _readHtml(pluginPackageElement.elementText("change-log")));
1194 pluginPackage.setScreenshots(
1195 _readScreenshots(pluginPackageElement.element("screenshots")));
1196 pluginPackage.setPageURL(
1197 _readText(pluginPackageElement.elementText("page-url")));
1198 pluginPackage.setDownloadURL(
1199 _readText(pluginPackageElement.elementText("download-url")));
1200 pluginPackage.setDeploymentSettings(
1201 _readProperties(
1202 pluginPackageElement.element("deployment-settings"),
1203 "setting"));
1204
1205 return pluginPackage;
1206 }
1207
1208 private PluginPackage _readPluginPackageXml(String xml)
1209 throws DocumentException {
1210
1211 Document document = SAXReaderUtil.read(xml);
1212
1213 Element rootElement = document.getRootElement();
1214
1215 return _readPluginPackageXml(rootElement);
1216 }
1217
1218 private Properties _readProperties(Element parentElement, String name) {
1219 Properties properties = new Properties();
1220
1221 if (parentElement == null) {
1222 return properties;
1223 }
1224
1225 for (Element element : parentElement.elements(name)) {
1226 properties.setProperty(
1227 element.attributeValue("name"),
1228 element.attributeValue("value"));
1229 }
1230
1231 return properties;
1232 }
1233
1234 private List<Screenshot> _readScreenshots(Element parentElement) {
1235 List<Screenshot> screenshots = new ArrayList<Screenshot>();
1236
1237 if (parentElement == null) {
1238 return screenshots;
1239 }
1240
1241 for (Element screenshotElement : parentElement.elements("screenshot")) {
1242 Screenshot screenshot = new Screenshot();
1243
1244 screenshot.setThumbnailURL(
1245 screenshotElement.elementText("thumbnail-url"));
1246 screenshot.setLargeImageURL(
1247 screenshotElement.elementText("large-image-url"));
1248
1249 screenshots.add(screenshot);
1250 }
1251
1252 return screenshots;
1253 }
1254
1255 private String _readText(String text) {
1256 return HtmlUtil.extractText(GetterUtil.getString(text));
1257 }
1258
1259 private void _refreshUpdatesAvailableCache() {
1260 _updateAvailable = null;
1261 }
1262
1263 private void _registerInstalledPluginPackage(PluginPackage pluginPackage)
1264 throws PortalException {
1265
1266 _installedPluginPackages.addPluginPackage(pluginPackage);
1267
1268 _updateAvailable = null;
1269
1270 _indexPluginPackage(pluginPackage);
1271 }
1272
1273 private void _registerPluginPackageInstallation(String preliminaryContext) {
1274 _installedPluginPackages.registerPluginPackageInstallation(
1275 preliminaryContext);
1276 }
1277
1278 private RepositoryReport _reloadRepositories()
1279 throws PortalException, SystemException {
1280
1281 if (_log.isInfoEnabled()) {
1282 _log.info("Reloading repositories");
1283 }
1284
1285 RepositoryReport repositoryReport = new RepositoryReport();
1286
1287 String[] repositoryURLs = _getRepositoryURLs();
1288
1289 for (int i = 0; i < repositoryURLs.length; i++) {
1290 String repositoryURL = repositoryURLs[i];
1291
1292 try {
1293 _loadRepository(repositoryURL);
1294
1295 repositoryReport.addSuccess(repositoryURL);
1296 }
1297 catch (PluginPackageException pe) {
1298 repositoryReport.addError(repositoryURL, pe);
1299
1300 _log.error(
1301 "Unable to load repository " + repositoryURL + " " +
1302 pe.toString());
1303 }
1304
1305 }
1306
1307 Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);
1308
1309 indexer.reindex(new String[0]);
1310
1311 return repositoryReport;
1312 }
1313
1314 private Hits _search(
1315 String keywords, String type, String tag, String license,
1316 String repositoryURL, String status, int start, int end)
1317 throws PortalException, SystemException {
1318
1319 _checkRepositories(repositoryURL);
1320
1321 Map<String, Serializable> attributes =
1322 new HashMap<String, Serializable>();
1323
1324 attributes.put("license", license);
1325 attributes.put("repositoryURL", repositoryURL);
1326 attributes.put("status", status);
1327 attributes.put("tag", tag);
1328 attributes.put("type", type);
1329
1330 SearchContext searchContext = new SearchContext();
1331
1332 searchContext.setAttributes(attributes);
1333 searchContext.setCompanyId(CompanyConstants.SYSTEM);
1334 searchContext.setEnd(end);
1335 searchContext.setKeywords(keywords);
1336
1337 QueryConfig queryConfig = new QueryConfig();
1338
1339 queryConfig.setHighlightEnabled(false);
1340 queryConfig.setScoreEnabled(false);
1341
1342 searchContext.setQueryConfig(queryConfig);
1343
1344 searchContext.setStart(start);
1345
1346 Indexer indexer = IndexerRegistryUtil.getIndexer(PluginPackage.class);
1347
1348 return indexer.search(searchContext);
1349 }
1350
1351 private void _unregisterInstalledPluginPackage(
1352 PluginPackage pluginPackage)
1353 throws PortalException, SystemException {
1354
1355 _installedPluginPackages.removePluginPackage(pluginPackage);
1356
1357 try {
1358 List<PluginPackage> pluginPackages = _getAvailablePluginPackages(
1359 pluginPackage.getGroupId(), pluginPackage.getArtifactId());
1360
1361 for (PluginPackage availablePackage : pluginPackages) {
1362 _indexPluginPackage(availablePackage);
1363 }
1364 }
1365 catch (PluginPackageException ppe) {
1366 if (_log.isWarnEnabled()) {
1367 _log.warn(
1368 "Unable to reindex unistalled package " +
1369 pluginPackage.getContext() + ": " + ppe.getMessage());
1370 }
1371 }
1372 }
1373
1374 private void _updateInstallingPluginPackage(
1375 String preliminaryContext, PluginPackage pluginPackage) {
1376
1377 _installedPluginPackages.unregisterPluginPackageInstallation(
1378 preliminaryContext);
1379 _installedPluginPackages.registerPluginPackageInstallation(
1380 pluginPackage);
1381 }
1382
1383 private static Log _log = LogFactoryUtil.getLog(PluginPackageUtil.class);
1384
1385 private static PluginPackageUtil _instance = new PluginPackageUtil();
1386
1387 private Set<String> _availableTagsCache;
1388 private LocalPluginPackageRepository _installedPluginPackages;
1389 private Date _lastUpdateDate;
1390 private Map<String, RemotePluginPackageRepository> _repositoryCache;
1391 private boolean _settingUpdateAvailable;
1392 private Boolean _updateAvailable;
1393
1394 private class UpdateAvailableRunner implements Runnable {
1395
1396 public void run() {
1397 try {
1398 setUpdateAvailable();
1399 }
1400 catch (Exception e) {
1401 if (_log.isWarnEnabled()) {
1402 _log.warn(e.getMessage());
1403 }
1404 }
1405 }
1406
1407 protected void setUpdateAvailable() throws Exception {
1408 StopWatch stopWatch = null;
1409
1410 if (_log.isInfoEnabled()) {
1411 _log.info("Checking for available updates");
1412
1413 stopWatch = new StopWatch();
1414
1415 stopWatch.start();
1416 }
1417
1418 for (PluginPackage pluginPackage :
1419 _installedPluginPackages.getPluginPackages()) {
1420
1421 PluginPackage availablePluginPackage = null;
1422
1423 if (_isIgnored(pluginPackage)) {
1424 continue;
1425 }
1426
1427 availablePluginPackage =
1428 PluginPackageUtil.getLatestAvailablePluginPackage(
1429 pluginPackage.getGroupId(),
1430 pluginPackage.getArtifactId());
1431
1432 if (availablePluginPackage == null) {
1433 continue;
1434 }
1435
1436 Version availablePluginPackageVersion = Version.getInstance(
1437 availablePluginPackage.getVersion());
1438
1439 if (availablePluginPackageVersion.isLaterVersionThan(
1440 pluginPackage.getVersion())) {
1441
1442 _updateAvailable = Boolean.TRUE;
1443
1444 break;
1445 }
1446 }
1447
1448 if (_updateAvailable == null) {
1449 _updateAvailable = Boolean.FALSE;
1450 }
1451
1452 _settingUpdateAvailable = false;
1453
1454 if (_log.isInfoEnabled()) {
1455 _log.info(
1456 "Finished checking for available updates in " +
1457 stopWatch.getTime() + " ms");
1458 }
1459 }
1460 }
1461
1462 }