| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you under the Apache License, Version 2.0 (the |
| * "License"); you may not use this file except in compliance |
| * with the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, |
| * software distributed under the License is distributed on an |
| * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| * KIND, either express or implied. See the License for the |
| * specific language governing permissions and limitations |
| * under the License. |
| */ |
| package org.apache.sling.installer.core.impl.tasks; |
| |
| import java.text.MessageFormat; |
| import java.util.Iterator; |
| |
| import org.apache.sling.installer.api.InstallableResource; |
| import org.apache.sling.installer.api.ResourceChangeListener; |
| import org.apache.sling.installer.api.tasks.ChangeStateTask; |
| import org.apache.sling.installer.api.tasks.InstallTask; |
| import org.apache.sling.installer.api.tasks.InstallTaskFactory; |
| import org.apache.sling.installer.api.tasks.ResourceState; |
| import org.apache.sling.installer.api.tasks.RetryHandler; |
| import org.apache.sling.installer.api.tasks.TaskResource; |
| import org.apache.sling.installer.api.tasks.TaskResourceGroup; |
| import org.apache.sling.installer.core.impl.BundleBlackList; |
| import org.apache.sling.installer.core.impl.EntityResourceList; |
| import org.apache.sling.installer.core.impl.InternalService; |
| import org.apache.sling.installer.core.impl.PersistentResourceList; |
| import org.apache.sling.installer.core.impl.RegisteredResourceImpl; |
| import org.apache.sling.installer.core.impl.Util; |
| import org.osgi.framework.BundleContext; |
| import org.osgi.framework.BundleEvent; |
| import org.osgi.framework.BundleListener; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.FrameworkEvent; |
| import org.osgi.framework.FrameworkListener; |
| import org.osgi.framework.Version; |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| |
| /** |
| * Task creator for bundles |
| */ |
| public class BundleTaskCreator |
| implements InternalService, InstallTaskFactory, FrameworkListener, BundleListener { |
| |
| /** If this property is set, the bundle is installed if the currently installed version |
| * is the version specified by the property. |
| */ |
| private final static String FORCE_INSTALL_VERSION = "force.install.version"; |
| |
| /** The logger */ |
| private final Logger logger = LoggerFactory.getLogger(this.getClass()); |
| |
| /** Support service for the tasks */ |
| private TaskSupport taskSupport; |
| |
| /** The bundle context. */ |
| private BundleContext bundleContext; |
| |
| /** The retry handler. */ |
| private RetryHandler retryHandler; |
| |
| private BundleBlackList bundleBlacklist; |
| |
| private boolean isMultiVersion; |
| |
| /** |
| * @see org.apache.sling.installer.core.impl.InternalService#init(org.osgi.framework.BundleContext, org.apache.sling.installer.api.ResourceChangeListener, RetryHandler) |
| */ |
| @Override |
| public void init(final BundleContext bc, final ResourceChangeListener listener, final RetryHandler retryHandler) { |
| this.bundleContext = bc; |
| this.retryHandler = retryHandler; |
| |
| this.isMultiVersion = Boolean.TRUE.equals(Boolean.valueOf(bc.getProperty("sling.installer.multiversion"))); |
| this.bundleContext.addBundleListener(this); |
| this.bundleContext.addFrameworkListener(this); |
| |
| this.taskSupport = new TaskSupport(bc); |
| this.bundleBlacklist = new BundleBlackList(bc); |
| } |
| |
| /** |
| * @see org.apache.sling.installer.core.impl.InternalService#deactivate() |
| */ |
| @Override |
| public void deactivate() { |
| if ( this.bundleContext != null ) { |
| this.bundleContext.removeBundleListener(this); |
| this.bundleContext.removeFrameworkListener(this); |
| } |
| if ( this.taskSupport != null ) { |
| this.taskSupport = null; |
| } |
| } |
| |
| /** |
| * @see org.osgi.framework.FrameworkListener#frameworkEvent(org.osgi.framework.FrameworkEvent) |
| */ |
| @Override |
| public void frameworkEvent(final FrameworkEvent event) { |
| if ( event.getType() == FrameworkEvent.PACKAGES_REFRESHED ) { |
| logger.debug("Received FrameworkEvent triggering a retry of the installer: {}", event); |
| this.retryHandler.scheduleRetry(); |
| } |
| } |
| |
| /** |
| * @see org.osgi.framework.BundleListener#bundleChanged(org.osgi.framework.BundleEvent) |
| */ |
| @Override |
| public void bundleChanged(final BundleEvent event) { |
| final int t = event.getType(); |
| if (t == BundleEvent.INSTALLED || t == BundleEvent.RESOLVED || t == BundleEvent.STARTED || t == BundleEvent.UPDATED) { |
| logger.debug("Received BundleEvent triggering a retry of the installer: {}", event); |
| this.retryHandler.scheduleRetry(); |
| } |
| } |
| |
| /** |
| * @see org.apache.sling.installer.core.impl.InternalService#getDescription() |
| */ |
| @Override |
| public String getDescription() { |
| return "Apache Sling Bundle Install Task Factory"; |
| } |
| |
| /** |
| * Create a bundle task - install, update or remove |
| * |
| * @see org.apache.sling.installer.api.tasks.InstallTaskFactory#createTask(org.apache.sling.installer.api.tasks.TaskResourceGroup) |
| */ |
| @Override |
| public InstallTask createTask(final TaskResourceGroup resourceList) { |
| // quick check of the resource type. |
| final TaskResource toActivate = resourceList.getActiveResource(); |
| if ( toActivate.getType().equals(PersistentResourceList.RESTART_ACTIVE_BUNDLES_TYPE) ) { |
| return new RestartActiveBundlesTask(resourceList, this.taskSupport); |
| } |
| if ( !toActivate.getType().equals(InstallableResource.TYPE_BUNDLE) ) { |
| return null; |
| } |
| |
| // check if symbolic name and version is provided in the attributes |
| if ( toActivate.getAttribute(Constants.BUNDLE_SYMBOLICNAME) == null ) { |
| final Util.BundleHeaders headers = Util.readBundleHeaders(toActivate, logger); |
| if ( headers == null ) { |
| String message = MessageFormat.format("Resource of type bundle {0} is not really a bundle - manifest entries are missing.", toActivate); |
| logger.info(message); |
| return new ChangeStateTask(resourceList, ResourceState.IGNORED, message); |
| } |
| toActivate.setAttribute(Constants.BUNDLE_SYMBOLICNAME, headers.symbolicName); |
| toActivate.setAttribute(Constants.BUNDLE_VERSION, headers.version); |
| if ( headers.activationPolicy != null ) { |
| toActivate.setAttribute(Constants.BUNDLE_ACTIVATIONPOLICY, headers.activationPolicy); |
| } |
| } |
| final String symbolicName = (String)toActivate.getAttribute(Constants.BUNDLE_SYMBOLICNAME); |
| final boolean isInstallerCoreBundle = this.bundleContext.getBundle().getSymbolicName().equals(symbolicName); |
| |
| // Uninstall |
| final InstallTask result; |
| if (toActivate.getState() == ResourceState.UNINSTALL) { |
| // find the info with the exact version |
| final BundleInfo info = this.getBundleInfo( |
| symbolicName, |
| (String)toActivate.getAttribute(Constants.BUNDLE_VERSION)); |
| // Remove corresponding bundle if present and if we installed it |
| if ( info != null ) { |
| // if this is an uninstall, check if we have to install an older version |
| // in this case we should do an update instead of uninstall/install (!) |
| Iterator<TaskResource> candidatesIt = ((EntityResourceList)resourceList).getActiveResourceIterator(); |
| TaskResource second = null; |
| while (candidatesIt != null && second == null && candidatesIt.hasNext()) { |
| TaskResource candidate = candidatesIt.next(); |
| boolean sameVersion = toActivate.getVersion().equals(candidate.getVersion()); |
| if (!sameVersion) { |
| if (bundleBlacklist.isBlacklisted(symbolicName, candidate.getVersion())) { |
| String message = MessageFormat.format("Uninstalling blacklisted bundle {0} found at {1}", symbolicName, candidate.getURL()); |
| logger.info(message); |
| // blacklisted candidates should be uninstalled to no longer be taken into account anymore |
| ((RegisteredResourceImpl)candidate).setState(ResourceState.UNINSTALL, message); |
| } else { |
| second = candidate; |
| } |
| } |
| } |
| if ( second != null && |
| ( second.getState() == ResourceState.IGNORED || second.getState() == ResourceState.INSTALLED || second.getState() == ResourceState.INSTALL ) ) { |
| second.setAttribute(FORCE_INSTALL_VERSION, info.version.toString()); |
| BundleUtil.clearBundleStart(second); |
| logger.debug("Detected downgrade of bundle {}", symbolicName); |
| result = new ChangeStateTask(resourceList, ResourceState.UNINSTALLED, null); |
| } else { |
| // prevent uninstalling the installer itself! |
| if ( isInstallerCoreBundle ) { |
| logger.debug("Prevent completely uninstalling installer bundle {}", symbolicName); |
| result = new ChangeStateTask(resourceList, ResourceState.UNINSTALLED, null); |
| } else { |
| result = new BundleRemoveTask(resourceList, this.taskSupport); |
| } |
| } |
| } else { |
| logger.debug("Bundle {}:{} is not installed anymore - nothing to remove.", symbolicName, |
| toActivate.getAttribute(Constants.BUNDLE_VERSION)); |
| result = new ChangeStateTask(resourceList, ResourceState.UNINSTALLED, null); |
| } |
| |
| // Install |
| } else { |
| // check for installer and system update |
| final Integer asyncTaskCounter = (Integer)toActivate.getAttribute(InstallTask.ASYNC_ATTR_NAME); |
| if ( asyncTaskCounter != null ) { |
| if ( isInstallerCoreBundle ) { |
| result = new InstallerBundleUpdateTask(resourceList, this.taskSupport); |
| } else { |
| // system bundle |
| result = new ChangeStateTask(resourceList, ResourceState.INSTALLED, null, |
| new String[] {InstallTask.ASYNC_ATTR_NAME}, null); |
| } |
| } else { |
| final Version newVersion = new Version((String) toActivate.getAttribute(Constants.BUNDLE_VERSION)); |
| if (bundleBlacklist.isBlacklisted(symbolicName, newVersion)) { |
| String message = MessageFormat.format("Ignoring blacklisted bundle {0} found at {1}", symbolicName, toActivate.getURL()); |
| logger.info(message); |
| result = new ChangeStateTask(resourceList, ResourceState.IGNORED, message); |
| } else { |
| |
| // if not isMultiVersion for install and update, we want the bundle with the |
| // highest version - otherwise only check for very same version (potential updates of snapshots) |
| String bundleVersion = null; |
| if (this.isMultiVersion) { |
| bundleVersion = newVersion.toString(); |
| } |
| |
| final BundleInfo info = this.getBundleInfo(symbolicName, bundleVersion); |
| |
| // check if we should start the bundle as we installed it in the previous run |
| if (info == null) { |
| // bundle is not installed yet: install |
| result = new BundleInstallTask(resourceList, this.taskSupport); |
| } else if ( BundleUtil.isBundleStart(toActivate) ) { |
| result = new BundleStartTask(resourceList, info.id, this.taskSupport); |
| } else { |
| boolean doUpdate = false; |
| |
| final int compare = info.version.compareTo(newVersion); |
| if (compare < 0) { |
| // installed version is lower -> update |
| doUpdate = true; |
| } else if (compare > 0) { |
| final String forceVersion = (String) toActivate.getAttribute(FORCE_INSTALL_VERSION); |
| if ( forceVersion != null && info.version.compareTo(new Version(forceVersion)) == 0 ) { |
| doUpdate = true; |
| } else { |
| logger.debug("Bundle " + info.symbolicName + " " + newVersion |
| + " is not installed, bundle with higher version is already installed."); |
| } |
| } else if (compare == 0 && BundleInfo.isSnapshot(newVersion)) { |
| |
| // installed, same version but SNAPSHOT |
| doUpdate = true; |
| } |
| if (doUpdate) { |
| |
| logger.debug("Scheduling update of {}", toActivate); |
| // check if this is the system bundle |
| if ( Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName) ) { |
| result = new SystemBundleUpdateTask(resourceList, this.taskSupport); |
| // check if this is a installer update |
| } else if ( isInstallerCoreBundle ) { |
| result = new InstallerBundleUpdateTask(resourceList, this.taskSupport); |
| } else { |
| result = new BundleUpdateTask(resourceList, this.taskSupport); |
| } |
| } else if ( compare == 0 && (isInstallerCoreBundle || Constants.SYSTEM_BUNDLE_SYMBOLICNAME.equals(symbolicName)) ) { |
| // the installer core bundle / system bundle has been updated, just set state |
| result = new ChangeStateTask(resourceList, ResourceState.INSTALLED, "This is the system bundle, therefore nothing was actually done!"); |
| } else { |
| String message = MessageFormat.format("Nothing to install for {0}, same or newer version {1} already installed.", toActivate, newVersion); |
| logger.debug(message); |
| result = new ChangeStateTask(resourceList, ResourceState.IGNORED, message); |
| } |
| } |
| } |
| } |
| toActivate.setAttribute(FORCE_INSTALL_VERSION, null); |
| } |
| return result; |
| } |
| |
| protected BundleInfo getBundleInfo(final String symbolicName, final String version) { |
| return BundleInfo.getBundleInfo(this.bundleContext, symbolicName, version); |
| } |
| } |