| /* |
| * 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 org.apache.sling.installer.api.tasks.InstallationContext; |
| import org.apache.sling.installer.api.tasks.ResourceState; |
| import org.apache.sling.installer.api.tasks.TaskResourceGroup; |
| import org.osgi.framework.Bundle; |
| import org.osgi.framework.Constants; |
| import org.osgi.framework.Version; |
| import org.osgi.framework.startlevel.BundleStartLevel; |
| |
| /** Update a bundle from a RegisteredResource. Creates |
| * a bundleStartTask to restart the bundle if it was |
| * active before the update. |
| */ |
| public class BundleUpdateTask extends AbstractBundleTask { |
| |
| private static final String BUNDLE_UPDATE_ORDER = "50-"; |
| |
| private static final int MAX_RETRIES = 5; |
| |
| // keep track of retry attempts via temporary attribute stored in taskresource |
| private String ATTR_UPDATE_RETRY = "org.apache.sling.installer.core.impl.tasks.BundleUpdateTask.retrycount"; |
| |
| public BundleUpdateTask(final TaskResourceGroup r, |
| final TaskSupport creator) { |
| super(r, creator); |
| } |
| |
| /** |
| * Check if the bundle is active. |
| * This is true if the bundle has the active state or of the bundle |
| * is in the starting state and has the lazy activation policy. |
| * Or if the bundle is a fragment, it's considered active as well |
| */ |
| private boolean isBundleActive(final Bundle b) { |
| if ( BundleUtil.isBundleActive(b) ) { |
| return true; |
| } |
| final BundleStartLevel startLevelService = b.adapt(BundleStartLevel.class); |
| return startLevelService.isPersistentlyStarted(); |
| } |
| |
| /** |
| * @see org.apache.sling.installer.api.tasks.InstallTask#execute(org.apache.sling.installer.api.tasks.InstallationContext) |
| */ |
| @Override |
| public void execute(final InstallationContext ctx) { |
| final String symbolicName = (String)getResource().getAttribute(Constants.BUNDLE_SYMBOLICNAME); |
| final Bundle b = BundleInfo.getMatchingBundle(this.getBundleContext(), symbolicName, null); |
| if (b == null) { |
| String message = MessageFormat.format("Bundle to update ({0}) not found", symbolicName); |
| this.getLogger().debug(message); |
| this.setFinishedState(ResourceState.IGNORED, null, message); |
| return; |
| } |
| |
| final Version newVersion = new Version((String)getResource().getAttribute(Constants.BUNDLE_VERSION)); |
| |
| // Do not update if same version, unless snapshot |
| boolean snapshot = false; |
| final Version currentVersion = b.getVersion(); |
| snapshot = BundleInfo.isSnapshot(newVersion); |
| if (currentVersion.equals(newVersion) && !snapshot) { |
| // TODO : Isn't this already checked in the task creator? |
| String message = MessageFormat.format("Same version is already installed, and not a snapshot, ignoring update: {0}", getResource()); |
| this.getLogger().debug(message); |
| this.setFinishedState(ResourceState.INSTALLED, null, message); |
| return; |
| } |
| |
| try { |
| // If the bundle is active before the update - restart it once updated, but |
| // in sequence, not right now |
| final boolean reactivate = this.isBundleActive(b); |
| // if this is not a fragment, stop the bundle |
| final int state = b.getState(); |
| if (state == Bundle.ACTIVE || state == Bundle.STARTING) { |
| b.stop(); |
| } |
| |
| // update bundle |
| b.update(getResource().getInputStream()); |
| ctx.log("Updated bundle {} from resource {}", b, getResource()); |
| |
| setBundleLocation(getResource(), b.getLocation()); |
| // start level handling - after update to avoid starting the bundle |
| // just before the update |
| final BundleStartLevel startLevelService = b.adapt(BundleStartLevel.class); |
| final int newStartLevel = this.getBundleStartLevel(); |
| final int oldStartLevel = startLevelService.getStartLevel(); |
| if ( newStartLevel != oldStartLevel && newStartLevel != 0 ) { |
| startLevelService.setStartLevel(newStartLevel); |
| ctx.log("Set start level for bundle {} to {}", b, newStartLevel); |
| } |
| |
| if (reactivate) { |
| if ( BundleUtil.isSystemBundleFragment(b) ) { |
| this.setFinishedState(ResourceState.INSTALLED); |
| ctx.addTaskToCurrentCycle(new SystemBundleUpdateTask(null, this.getTaskSupport())); |
| } else if ( BundleUtil.getFragmentHostHeader(b) != null ) { |
| // if this is a fragment, we're done after a refresh of the host |
| final String fragmentHostHeader = BundleUtil.getFragmentHostHeader(b); |
| this.getLogger().debug("Need to do a refresh of the bundle's {} host", b); |
| for (final Bundle bundle : this.getBundleContext().getBundles()) { |
| if (fragmentHostHeader.equals(bundle.getSymbolicName())) { |
| this.getLogger().debug("Found host bundle for {} to refresh: {}", b, bundle); |
| RefreshBundlesTask.markBundleForRefresh(ctx, this.getTaskSupport(), bundle); |
| break; |
| } |
| } |
| this.setFinishedState(ResourceState.INSTALLED); |
| } else { |
| BundleUtil.markBundleStart(this.getResource()); |
| RefreshBundlesTask.markBundleForRefresh(ctx, this.getTaskSupport(), b); |
| ctx.addTaskToCurrentCycle(new BundleStartTask(this.getResourceGroup(), b.getBundleId(), this.getTaskSupport())); |
| } |
| } else { |
| this.setFinishedState(ResourceState.INSTALLED); |
| } |
| } catch (final Exception e) { |
| int retries = 0; |
| Object obj = getResource().getTemporaryAttribute(ATTR_UPDATE_RETRY); |
| if (obj instanceof Integer) { |
| retries = (Integer) obj; |
| } |
| getResource().setTemporaryAttribute(ATTR_UPDATE_RETRY, Integer.valueOf(++retries)); |
| if (retries > MAX_RETRIES) { |
| String message = MessageFormat.format("Removing failing update task due to {0} - unable to retry: {1}", |
| e.getLocalizedMessage(), this); |
| this.getLogger().error(message, e); |
| this.setFinishedState(ResourceState.IGNORED, null, message); |
| } else { |
| String message = MessageFormat.format("Failing update task due to {0} - will retry up to {1} more time(s) for {2} later", |
| e.getLocalizedMessage(), MAX_RETRIES - (retries - 1) , this); |
| this.getLogger().warn(message, e); |
| } |
| } |
| } |
| |
| @Override |
| public String getSortKey() { |
| return BUNDLE_UPDATE_ORDER + getSortableStartLevel() + "-" + getResource().getEntityId(); |
| } |
| |
| public Bundle getBundle(){ |
| final String symbolicName = (String)getResource().getAttribute(Constants.BUNDLE_SYMBOLICNAME); |
| return BundleInfo.getMatchingBundle(this.getBundleContext(), symbolicName, null); |
| } |
| |
| } |