org.jboss.as.server.moduleservice.ServiceModuleLoader Maven / Gradle / Ivy
/*
* JBoss, Home of Professional Open Source
* Copyright 2010, Red Hat Inc., and individual contributors as indicated
* by the @authors tag. See the copyright.txt in the distribution for a
* full listing of individual contributors.
*
* This 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 software 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.
*
* You should have received a copy of the GNU Lesser General Public
* License along with this software; if not, write to the Free
* Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
* 02110-1301 USA, or see the FSF site: http://www.fsf.org.
*/
package org.jboss.as.server.moduleservice;
import java.util.concurrent.CountDownLatch;
import org.jboss.as.server.Bootstrap;
import org.jboss.as.server.ServerLogger;
import org.jboss.as.server.ServerMessages;
import org.jboss.as.server.Services;
import org.jboss.modules.Module;
import org.jboss.modules.ModuleIdentifier;
import org.jboss.modules.ModuleLoadException;
import org.jboss.modules.ModuleLoader;
import org.jboss.modules.ModuleSpec;
import org.jboss.msc.service.AbstractServiceListener;
import org.jboss.msc.service.Service;
import org.jboss.msc.service.ServiceBuilder;
import org.jboss.msc.service.ServiceContainer;
import org.jboss.msc.service.ServiceController;
import org.jboss.msc.service.ServiceController.State;
import org.jboss.msc.service.ServiceName;
import org.jboss.msc.service.ServiceTarget;
import org.jboss.msc.service.StartContext;
import org.jboss.msc.service.StartException;
import org.jboss.msc.service.StopContext;
import org.jboss.msc.service.ValueService;
import org.jboss.msc.value.ImmediateValue;
/**
* {@link ModuleLoader} that loads module definitions from msc services. Module specs are looked up in msc services that
* correspond to the module names.
*
* Modules are automatically removed when the corresponding service comes down.
*
* @author Stuart Douglas
*
*/
public class ServiceModuleLoader extends ModuleLoader implements Service {
// Provide logging
private static final ServerLogger log = ServerLogger.MODULE_SERVICE_LOGGER;
/**
* Listener class that atomically retrieves the moduleSpec, and automatically removes the Module when the module spec
* service is removed
*
* @author Stuart Douglas
*
*/
private class ModuleSpecLoadListener extends AbstractServiceListener {
private final CountDownLatch latch = new CountDownLatch(1);
private volatile StartException startException;
private volatile ModuleSpec moduleSpec;
private ModuleSpecLoadListener(ModuleIdentifier identifier) {
}
@Override
public void listenerAdded(ServiceController extends ModuleDefinition> controller) {
log.tracef("listenerAdded: %s", controller);
State state = controller.getState();
if (state == State.UP || state == State.START_FAILED) {
done(controller, controller.getStartException());
}
}
@Override
public void transition(final ServiceController extends ModuleDefinition> controller, final ServiceController.Transition transition) {
switch (transition) {
case STARTING_to_UP:
log.tracef("serviceStarted: %s", controller);
done(controller, null);
break;
case STARTING_to_START_FAILED:
log.tracef(controller.getStartException(), "serviceFailed: %s", controller);
done(controller, controller.getStartException());
break;
case STOP_REQUESTED_to_STOPPING: {
log.tracef("serviceStopping: %s", controller);
ModuleSpec moduleSpec = this.moduleSpec;
ModuleIdentifier identifier = moduleSpec.getModuleIdentifier();
Module module = findLoadedModuleLocal(identifier);
if(module != null)
unloadModuleLocal(module);
// TODO: what if the service is restarted?
controller.removeListener(this);
break;
}
}
}
private void done(ServiceController extends ModuleDefinition> controller, StartException reason) {
startException = reason;
if (startException == null) {
moduleSpec = controller.getValue().getModuleSpec();
}
latch.countDown();
}
public ModuleSpec getModuleSpec() throws ModuleLoadException {
if (startException != null)
throw new ModuleLoadException(startException.getCause());
return moduleSpec;
}
}
public static final ServiceName MODULE_SPEC_SERVICE_PREFIX = ServiceName.JBOSS.append("module", "spec", "service");
public static final ServiceName MODULE_SERVICE_PREFIX = ServiceName.JBOSS.append("module", "service");
public static final ServiceName MODULE_RESOLVED_SERVICE_PREFIX = ServiceName.of("module", "resolved", "service");
public static final String MODULE_PREFIX = "deployment.";
private final ModuleLoader mainModuleLoader;
private volatile ServiceContainer serviceContainer;
public ServiceModuleLoader(ModuleLoader mainModuleLoader) {
this.mainModuleLoader = mainModuleLoader;
}
@Override
protected Module preloadModule(final ModuleIdentifier identifier) throws ModuleLoadException {
if (identifier.getName().startsWith(MODULE_PREFIX)) {
return super.preloadModule(identifier);
} else {
return preloadModule(identifier, mainModuleLoader);
}
}
@SuppressWarnings("unchecked")
@Override
public ModuleSpec findModule(ModuleIdentifier identifier) throws ModuleLoadException {
ServiceController controller = (ServiceController) serviceContainer.getService(moduleSpecServiceName(identifier));
if (controller == null) {
ServerLogger.MODULE_SERVICE_LOGGER.debugf("Could not load module '%s' as corresponding module spec service '%s' was not found", identifier, identifier);
return null;
}
ModuleSpecLoadListener listener = new ModuleSpecLoadListener(identifier);
controller.addListener(listener);
return listener.getModuleSpec();
}
@Override
public String toString() {
return "Service Module Loader";
}
@Override
public synchronized void start(StartContext context) throws StartException {
if (serviceContainer != null) {
throw ServerMessages.MESSAGES.serviceModuleLoaderAlreadyStarted();
}
serviceContainer = context.getController().getServiceContainer();
}
@Override
public synchronized void stop(StopContext context) {
if (serviceContainer == null) {
throw ServerMessages.MESSAGES.serviceModuleLoaderAlreadyStopped();
}
serviceContainer = null;
}
@Override
public ServiceModuleLoader getValue() throws IllegalStateException, IllegalArgumentException {
return this;
}
public void relinkModule(Module module) throws ModuleLoadException {
relink(module);
}
public static void addService(final ServiceTarget serviceTarget, final Bootstrap.Configuration configuration) {
final Service service = new ServiceModuleLoader(configuration.getModuleLoader());
final ServiceBuilder> serviceBuilder = serviceTarget.addService(Services.JBOSS_SERVICE_MODULE_LOADER, service);
serviceBuilder.install();
}
/**
* Returns the corresponding ModuleSpec service name for the given module.
*
* @param identifier The module identifier
* @return The service name of the ModuleSpec service
*/
public static ServiceName moduleSpecServiceName(ModuleIdentifier identifier) {
if (!isDynamicModule(identifier)) {
throw ServerMessages.MESSAGES.missingModulePrefix(identifier, MODULE_PREFIX);
}
return MODULE_SPEC_SERVICE_PREFIX.append(identifier.getName()).append(identifier.getSlot());
}
public static void installModuleResolvedService(ServiceTarget serviceTarget, ModuleIdentifier identifier) {
final ValueService resolvedService = new ValueService(new ImmediateValue(identifier));
serviceTarget.addService(ServiceModuleLoader.moduleResolvedServiceName(identifier), resolvedService)
.addDependency(moduleSpecServiceName(identifier))
.install();
}
/**
* Returns the corresponding module resolved service name for the given module.
*
* The module resolved service is basically a latch that prevents the module from being loaded
* until all the transitive dependencies that it depends upon have have their module spec services
* come up.
*
* @param identifier The module identifier
* @return The service name of the ModuleSpec service
*/
public static ServiceName moduleResolvedServiceName(ModuleIdentifier identifier) {
if (!isDynamicModule(identifier)) {
throw ServerMessages.MESSAGES.missingModulePrefix(identifier, MODULE_PREFIX);
}
return MODULE_RESOLVED_SERVICE_PREFIX.append(identifier.getName()).append(identifier.getSlot());
}
/**
* Returns true if the module identifier is a dynamic module that will be loaded by this module loader
*/
public static boolean isDynamicModule(ModuleIdentifier identifier) {
return identifier.getName().startsWith(MODULE_PREFIX);
}
/**
* Returns the corresponding ModuleLoadService service name for the given module.
*
* @param identifier The module identifier
* @return The service name of the ModuleLoadService service
*/
public static ServiceName moduleServiceName(ModuleIdentifier identifier) {
if (!identifier.getName().startsWith(MODULE_PREFIX)) {
throw ServerMessages.MESSAGES.missingModulePrefix(identifier, MODULE_PREFIX);
}
return MODULE_SERVICE_PREFIX.append(identifier.getName()).append(identifier.getSlot());
}
}