/** * Copyright (c) 2000-present Liferay, Inc. All rights reserved. * * This library is free software; you can redistribute it and/or modify it under * the terms of the GNU Lesser General Public License as published by the Free * Software Foundation; either version 2.1 of the License, or (at your option) * any later version. * * This library is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more * details. */ package com.liferay.portal.upgrade.internal.release.osgi.commands; import com.liferay.osgi.service.tracker.collections.map.PropertyServiceReferenceComparator; import com.liferay.osgi.service.tracker.collections.map.PropertyServiceReferenceMapper; import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerMap; import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerMapFactory; import com.liferay.osgi.service.tracker.collections.map.ServiceTrackerMapListener; import com.liferay.portal.configuration.metatype.bnd.util.ConfigurableUtil; import com.liferay.portal.kernel.cache.CacheRegistryUtil; import com.liferay.portal.kernel.dao.db.DB; import com.liferay.portal.kernel.dao.db.DBContext; import com.liferay.portal.kernel.dao.db.DBManagerUtil; import com.liferay.portal.kernel.dao.db.DBProcessContext; import com.liferay.portal.kernel.model.Release; import com.liferay.portal.kernel.service.ReleaseLocalService; import com.liferay.portal.kernel.upgrade.UpgradeStep; import com.liferay.portal.kernel.util.StringBundler; import com.liferay.portal.kernel.util.Validator; import com.liferay.portal.output.stream.container.OutputStreamContainer; import com.liferay.portal.output.stream.container.OutputStreamContainerFactory; import com.liferay.portal.output.stream.container.OutputStreamContainerFactoryTracker; import com.liferay.portal.upgrade.internal.configuration.ReleaseManagerConfiguration; import com.liferay.portal.upgrade.internal.graph.ReleaseGraphManager; import com.liferay.portal.upgrade.internal.release.ReleasePublisher; import com.liferay.portal.upgrade.registry.UpgradeInfo; import java.io.IOException; import java.io.OutputStream; import java.util.Collections; import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Set; import org.apache.felix.utils.log.Logger; import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.service.component.annotations.Activate; import org.osgi.service.component.annotations.Component; import org.osgi.service.component.annotations.ConfigurationPolicy; import org.osgi.service.component.annotations.Deactivate; import org.osgi.service.component.annotations.Reference; import org.osgi.util.tracker.ServiceTrackerCustomizer; /** * @author Miguel Pastor * @author Carlos Sierra Andrés */ @Component( configurationPid = "com.liferay.portal.upgrade.internal.configuration.ReleaseManagerConfiguration", configurationPolicy = ConfigurationPolicy.OPTIONAL, immediate = true, property = { "osgi.command.function=check", "osgi.command.function=execute", "osgi.command.function=executeAll", "osgi.command.function=list", "osgi.command.scope=upgrade" }, service = ReleaseManagerOSGiCommands.class ) public class ReleaseManagerOSGiCommands { public void check() { Set<String> bundleSymbolicNames = _serviceTrackerMap.keySet(); for (String bundleSymbolicName : bundleSymbolicNames) { String schemaVersionString = getSchemaVersionString( bundleSymbolicName); ReleaseGraphManager releaseGraphManager = new ReleaseGraphManager( _serviceTrackerMap.getService(bundleSymbolicName)); List<List<UpgradeInfo>> upgradeInfosList = releaseGraphManager.getUpgradeInfosList(schemaVersionString); int size = upgradeInfosList.size(); if (size > 1) { System.out.println( "There are " + size + " possible end nodes for " + schemaVersionString); } if (size == 0) { continue; } StringBundler sb = new StringBundler(6); sb.append("There is an upgrade process available for "); sb.append(bundleSymbolicName); sb.append(" from "); sb.append(schemaVersionString); sb.append(" to "); List<UpgradeInfo> upgradeInfos = upgradeInfosList.get(0); UpgradeInfo lastUpgradeInfo = upgradeInfos.get( upgradeInfos.size() - 1); sb.append(lastUpgradeInfo.getToSchemaVersionString()); System.out.println(sb.toString()); } } public void execute(String bundleSymbolicName) { doExecute(bundleSymbolicName, _serviceTrackerMap); } public void execute(String bundleSymbolicName, String toVersionString) { String schemaVersionString = getSchemaVersionString(bundleSymbolicName); ReleaseGraphManager releaseGraphManager = new ReleaseGraphManager( _serviceTrackerMap.getService(bundleSymbolicName)); executeUpgradeInfos( bundleSymbolicName, releaseGraphManager.getUpgradeInfos( schemaVersionString, toVersionString)); } public void executeAll() { Set<String> upgradeThrewExceptionBundleSymbolicNames = new HashSet<>(); executeAll(upgradeThrewExceptionBundleSymbolicNames); if (upgradeThrewExceptionBundleSymbolicNames.isEmpty()) { System.out.println("All modules were successfully upgraded"); return; } StringBundler sb = new StringBundler( (upgradeThrewExceptionBundleSymbolicNames.size() * 3) + 3); sb.append("\nThe following modules had errors while upgrading:\n"); for (String upgradeThrewExceptionBundleSymbolicName : upgradeThrewExceptionBundleSymbolicNames) { sb.append("\t"); sb.append(upgradeThrewExceptionBundleSymbolicName); sb.append("\n"); } sb.append("Use the command upgrade:list <module name> to get more "); sb.append("details about the status of a specific upgrade."); System.out.println(sb.toString()); } public void list() { for (String bundleSymbolicName : _serviceTrackerMap.keySet()) { list(bundleSymbolicName); } } public void list(String bundleSymbolicName) { List<UpgradeInfo> upgradeProcesses = _serviceTrackerMap.getService( bundleSymbolicName); System.out.println( "Registered upgrade processes for " + bundleSymbolicName + " " + getSchemaVersionString(bundleSymbolicName)); for (UpgradeInfo upgradeProcess : upgradeProcesses) { System.out.println("\t" + upgradeProcess); } } @Reference(unbind = "-") public void setOutputStreamTracker( OutputStreamContainerFactoryTracker outputStreamContainerFactoryTracker) { _outputStreamContainerFactoryTracker = outputStreamContainerFactoryTracker; } @Activate protected void activate( final BundleContext bundleContext, Map<String, Object> properties) { _logger = new Logger(bundleContext); DB db = DBManagerUtil.getDB(); ServiceTrackerMapListener<String, UpgradeInfo, List<UpgradeInfo>> serviceTrackerMapListener = null; _releaseManagerConfiguration = ConfigurableUtil.createConfigurable( ReleaseManagerConfiguration.class, properties); if (_releaseManagerConfiguration.autoUpgrade()) { serviceTrackerMapListener = new UpgradeInfoServiceTrackerMapListener(); } _serviceTrackerMap = ServiceTrackerMapFactory.openMultiValueMap( bundleContext, UpgradeStep.class, "(&(upgrade.bundle.symbolic.name=*)(|(upgrade.db.type=any)" + "(upgrade.db.type=" + db.getDBType() + ")))", new PropertyServiceReferenceMapper<String, UpgradeStep>( "upgrade.bundle.symbolic.name"), new UpgradeServiceTrackerCustomizer(bundleContext), Collections.reverseOrder( new PropertyServiceReferenceComparator<UpgradeStep>( "upgrade.from.schema.version")), serviceTrackerMapListener); } @Deactivate protected void deactivate() { _serviceTrackerMap.close(); } protected void doExecute( String bundleSymbolicName, ServiceTrackerMap<String, List<UpgradeInfo>> serviceTrackerMap) { List<List<UpgradeInfo>> upgradeInfosList = getUpgradeInfosList( bundleSymbolicName, serviceTrackerMap); int size = upgradeInfosList.size(); if (size > 1) { throw new IllegalStateException( "There are " + size + " possible end nodes for " + getSchemaVersionString(bundleSymbolicName)); } if (size == 0) { return; } executeUpgradeInfos(bundleSymbolicName, upgradeInfosList.get(0)); } protected void executeAll( Set<String> upgradeThrewExceptionBundleSymbolicNames) { Set<String> upgradableBundleSymbolicNames = getUpgradableBundleSymbolicNames(); upgradableBundleSymbolicNames.removeAll( upgradeThrewExceptionBundleSymbolicNames); if (upgradableBundleSymbolicNames.isEmpty()) { return; } for (String upgradableBundleSymbolicName : upgradableBundleSymbolicNames) { try { doExecute(upgradableBundleSymbolicName, _serviceTrackerMap); } catch (Exception e) { upgradeThrewExceptionBundleSymbolicNames.add( upgradableBundleSymbolicName); } } executeAll(upgradeThrewExceptionBundleSymbolicNames); } protected void executeUpgradeInfos( final String bundleSymbolicName, final List<UpgradeInfo> upgradeInfos) { OutputStreamContainerFactory outputStreamContainerFactory = _outputStreamContainerFactoryTracker. getOutputStreamContainerFactory(); OutputStreamContainer outputStreamContainer = outputStreamContainerFactory.create( "upgrade-" + bundleSymbolicName); OutputStream outputStream = outputStreamContainer.getOutputStream(); _outputStreamContainerFactoryTracker.runWithSwappedLog( new UpgradeInfosRunnable( bundleSymbolicName, upgradeInfos, outputStream), outputStreamContainer.getDescription(), outputStream); try { outputStream.close(); } catch (IOException ioe) { throw new RuntimeException(ioe); } Release release = _releaseLocalService.fetchRelease(bundleSymbolicName); if (release != null) { _releasePublisher.publish(release); } } protected String getSchemaVersionString(String bundleSymbolicName) { Release release = _releaseLocalService.fetchRelease(bundleSymbolicName); if ((release == null) || Validator.isNull(release.getSchemaVersion())) { return "0.0.0"; } return release.getSchemaVersion(); } protected Set<String> getUpgradableBundleSymbolicNames() { Set<String> upgradableBundleSymbolicNames = new HashSet<>(); for (String bundleSymbolicName : _serviceTrackerMap.keySet()) { if (isUpgradable(bundleSymbolicName)) { upgradableBundleSymbolicNames.add(bundleSymbolicName); } } return upgradableBundleSymbolicNames; } protected List<List<UpgradeInfo>> getUpgradeInfosList( String bundleSymbolicName, ServiceTrackerMap<String, List<UpgradeInfo>> serviceTrackerMap) { String schemaVersionString = getSchemaVersionString(bundleSymbolicName); ReleaseGraphManager releaseGraphManager = new ReleaseGraphManager( serviceTrackerMap.getService(bundleSymbolicName)); return releaseGraphManager.getUpgradeInfosList(schemaVersionString); } protected boolean isUpgradable(String bundleSymbolicName) { List<List<UpgradeInfo>> upgradeInfosList = getUpgradeInfosList( bundleSymbolicName, _serviceTrackerMap); if (upgradeInfosList.size() == 1) { return true; } return false; } @Reference(unbind = "-") protected void setReleaseLocalService( ReleaseLocalService releaseLocalService) { _releaseLocalService = releaseLocalService; } @Reference(unbind = "-") protected void setReleasePublisher(ReleasePublisher releasePublisher) { _releasePublisher = releasePublisher; } private static Logger _logger; private OutputStreamContainerFactoryTracker _outputStreamContainerFactoryTracker; private ReleaseLocalService _releaseLocalService; private ReleaseManagerConfiguration _releaseManagerConfiguration; private ReleasePublisher _releasePublisher; private ServiceTrackerMap<String, List<UpgradeInfo>> _serviceTrackerMap; private static class UpgradeServiceTrackerCustomizer implements ServiceTrackerCustomizer<UpgradeStep, UpgradeInfo> { public UpgradeServiceTrackerCustomizer(BundleContext bundleContext) { _bundleContext = bundleContext; } @Override public UpgradeInfo addingService( ServiceReference<UpgradeStep> serviceReference) { String fromSchemaVersionString = (String)serviceReference.getProperty( "upgrade.from.schema.version"); String toSchemaVersionString = (String)serviceReference.getProperty( "upgrade.to.schema.version"); UpgradeStep upgradeStep = _bundleContext.getService( serviceReference); if (upgradeStep == null) { _logger.log( Logger.LOG_WARNING, "Skipping service " + serviceReference + " because it does not implement UpgradeStep"); return null; } return new UpgradeInfo( fromSchemaVersionString, toSchemaVersionString, upgradeStep); } @Override public void modifiedService( ServiceReference<UpgradeStep> serviceReference, UpgradeInfo upgradeInfo) { } @Override public void removedService( ServiceReference<UpgradeStep> serviceReference, UpgradeInfo upgradeInfo) { _bundleContext.ungetService(serviceReference); } private final BundleContext _bundleContext; } private class UpgradeInfoServiceTrackerMapListener implements ServiceTrackerMapListener <String, UpgradeInfo, List<UpgradeInfo>> { @Override public void keyEmitted( ServiceTrackerMap<String, List<UpgradeInfo>> serviceTrackerMap, final String key, UpgradeInfo upgradeInfo, List<UpgradeInfo> upgradeInfos) { doExecute(key, serviceTrackerMap); } @Override public void keyRemoved( ServiceTrackerMap<String, List<UpgradeInfo>> serviceTrackerMap, String key, UpgradeInfo upgradeInfo, List<UpgradeInfo> upgradeInfos) { } } private class UpgradeInfosRunnable implements Runnable { public UpgradeInfosRunnable( String bundleSymbolicName, List<UpgradeInfo> upgradeInfos, OutputStream outputStream) { _bundleSymbolicName = bundleSymbolicName; _upgradeInfos = upgradeInfos; _outputStream = outputStream; } @Override public void run() { for (UpgradeInfo upgradeInfo : _upgradeInfos) { UpgradeStep upgradeStep = upgradeInfo.getUpgradeStep(); try { upgradeStep.upgrade( new DBProcessContext() { @Override public DBContext getDBContext() { return new DBContext(); } @Override public OutputStream getOutputStream() { return _outputStream; } }); _releaseLocalService.updateRelease( _bundleSymbolicName, upgradeInfo.getToSchemaVersionString(), upgradeInfo.getFromSchemaVersionString()); } catch (Exception e) { throw new RuntimeException(e); } } CacheRegistryUtil.clear(); } private final String _bundleSymbolicName; private final OutputStream _outputStream; private final List<UpgradeInfo> _upgradeInfos; } }