/*
* Licensed 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.aries.subsystem.core.internal;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collections;
import java.util.EnumSet;
import java.util.LinkedHashSet;
import java.util.List;
import org.apache.aries.subsystem.ContentHandler;
import org.apache.aries.subsystem.core.archive.DeploymentManifest;
import org.apache.aries.subsystem.core.archive.SubsystemContentHeader;
import org.osgi.framework.BundleException;
import org.osgi.framework.ServiceReference;
import org.osgi.framework.namespace.IdentityNamespace;
import org.osgi.framework.wiring.BundleRevision;
import org.osgi.resource.Resource;
import org.osgi.service.subsystem.Subsystem.State;
import org.osgi.service.subsystem.SubsystemConstants;
import org.osgi.service.subsystem.SubsystemException;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class StopAction extends AbstractAction {
private static final Logger logger = LoggerFactory.getLogger(StopAction.class);
public StopAction(BasicSubsystem requestor, BasicSubsystem target, boolean disableRootCheck) {
super(requestor, target, disableRootCheck);
}
@Override
public Object run() {
// Protect against re-entry now that cycles are supported.
if (!Activator.getInstance().getLockingStrategy().set(State.STOPPING, target)) {
return null;
}
try {
// We are now protected against re-entry.
// Acquire the global read lock to prevent installs and uninstalls
// but allow starts and stops.
Activator.getInstance().getLockingStrategy().readLock();
try {
// We are now protected against installs and uninstalls.
checkRoot();
// Compute affected subsystems. This is safe to do while only
// holding the read lock since we know that nothing can be added
// or removed.
LinkedHashSet<BasicSubsystem> subsystems = new LinkedHashSet<BasicSubsystem>();
subsystems.add(target);
List<Resource> resources = new ArrayList<Resource>(Activator.getInstance().getSubsystems().getResourcesReferencedBy(target));
for (Resource resource : resources) {
if (resource instanceof BasicSubsystem) {
subsystems.add((BasicSubsystem)resource);
}
}
// Acquire the global mutual exclusion lock while acquiring the
// state change locks of affected subsystems.
Activator.getInstance().getLockingStrategy().lock();
try {
// We are now protected against cycles.
// Acquire the state change locks of affected subsystems.
Activator.getInstance().getLockingStrategy().lock(subsystems);
}
finally {
// Release the global mutual exclusion lock as soon as possible.
Activator.getInstance().getLockingStrategy().unlock();
}
try {
// We are now protected against other starts and stops of the affected subsystems.
State state = target.getState();
if (EnumSet.of(State.INSTALLED, State.INSTALLING, State.RESOLVED).contains(state)) {
// INSTALLING is included because a subsystem may
// persist in this state without being locked when
// apache-aries-provision-dependencies:=resolve.
return null;
}
else if (EnumSet.of(State.INSTALL_FAILED, State.UNINSTALLED).contains(state)) {
throw new IllegalStateException("Cannot stop from state " + state);
}
target.setState(State.STOPPING);
SubsystemContentHeader header = target.getSubsystemManifest().getSubsystemContentHeader();
if (header != null) {
Collections.sort(resources, new StartResourceComparator(target.getSubsystemManifest().getSubsystemContentHeader()));
Collections.reverse(resources);
}
for (Resource resource : resources) {
// Don't stop the region context bundle.
if (Utils.isRegionContextBundle(resource))
continue;
try {
stopResource(resource);
}
catch (Exception e) {
logger.error("An error occurred while stopping resource " + resource + " of subsystem " + target, e);
}
}
// TODO Can we automatically assume it actually is resolved?
target.setState(State.RESOLVED);
try {
synchronized (target) {
target.setDeploymentManifest(new DeploymentManifest(
target.getDeploymentManifest(),
null,
target.isAutostart(),
target.getSubsystemId(),
SubsystemIdentifier.getLastId(),
target.getLocation(),
false,
false));
}
}
catch (Exception e) {
throw new SubsystemException(e);
}
}
finally {
// Release the state change locks of affected subsystems.
Activator.getInstance().getLockingStrategy().unlock(subsystems);
}
}
finally {
// Release the read lock.
Activator.getInstance().getLockingStrategy().readUnlock();
}
}
finally {
// Protection against re-entry no longer required.
Activator.getInstance().getLockingStrategy().unset(State.STOPPING, target);
}
return null;
}
private void stopBundleResource(Resource resource) throws BundleException {
if (target.isRoot())
return;
((BundleRevision)resource).getBundle().stop();
}
private void stopResource(Resource resource) throws BundleException, IOException {
if (Utils.getActiveUseCount(resource) > 0)
return;
String type = ResourceHelper.getTypeAttribute(resource);
if (SubsystemConstants.SUBSYSTEM_TYPE_APPLICATION.equals(type)
|| SubsystemConstants.SUBSYSTEM_TYPE_COMPOSITE.equals(type)
|| SubsystemConstants.SUBSYSTEM_TYPE_FEATURE.equals(type)) {
stopSubsystemResource(resource);
} else if (IdentityNamespace.TYPE_BUNDLE.equals(type)) {
stopBundleResource(resource);
} else if (IdentityNamespace.TYPE_FRAGMENT.equals(type)) {
return;
} else {
if (!stopCustomHandler(resource, type))
throw new SubsystemException("Unsupported resource type: " + type);
}
}
private boolean stopCustomHandler(Resource resource, String type) {
ServiceReference<ContentHandler> customHandlerRef = CustomResources.getCustomContentHandler(target, type);
if (customHandlerRef != null) {
ContentHandler customHandler = target.getBundleContext().getService(customHandlerRef);
if (customHandler != null) {
try {
customHandler.stop(ResourceHelper.getSymbolicNameAttribute(resource), type, target);
return true;
} finally {
target.getBundleContext().ungetService(customHandlerRef);
}
}
}
return false;
}
private void stopSubsystemResource(Resource resource) throws IOException {
new StopAction(target, (BasicSubsystem)resource, !((BasicSubsystem)resource).isRoot()).run();
}
}