/*
* JBoss, Home of Professional Open Source.
* Copyright 2009, Red Hat Middleware LLC, and individual contributors
* as indicated by the @author tags. See the copyright.txt file 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.bootstrap.impl.embedded.server;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import org.jboss.bootstrap.api.as.config.JBossASServerConfig;
import org.jboss.bootstrap.api.embedded.server.JBossASEmbeddedServer;
import org.jboss.bootstrap.api.lifecycle.LifecycleEventHandler;
import org.jboss.bootstrap.api.lifecycle.LifecycleState;
import org.jboss.bootstrap.impl.as.config.BasicJBossASServerConfig;
import org.jboss.bootstrap.impl.as.server.AbstractJBossASServerBase;
import org.jboss.bootstrap.impl.embedded.lifecycle.IgnoreXbUnorderedSequenceLifecycleEventHandler;
import org.jboss.bootstrap.impl.embedded.lifecycle.InitLoggingManagerLifecycleEventHandler;
import org.jboss.bootstrap.impl.embedded.lifecycle.SetRmiHostnameLifecycleEventHandler;
import org.jboss.dependency.spi.ControllerContext;
import org.jboss.dependency.spi.ControllerState;
import org.jboss.deployers.client.spi.Deployment;
import org.jboss.deployers.client.spi.main.MainDeployer;
import org.jboss.kernel.Kernel;
import org.jboss.kernel.spi.dependency.KernelController;
import org.jboss.logging.Logger;
import org.jboss.shrinkwrap.api.Archive;
import org.jboss.tmpdpl.api.container.DeploymentException;
import org.jboss.tmpdpl.api.deployable.Deployable;
import org.jboss.tmpdpl.impl.vdf.VfsVdfDeployableImpl;
import org.jboss.tmpdpl.spi.vdf.VdfDeployable;
/**
* JBossASEmbeddedServer
*
* Extension of the JBossAS Server implementation
* for use in Embedded environments
*
* @author <a href="mailto:andrew.rubinger@jboss.org">ALR</a>
* @version $Revision: $
*/
public class JBossASEmbeddedServerImpl extends AbstractJBossASServerBase<JBossASEmbeddedServer, JBossASServerConfig>
implements
JBossASEmbeddedServer
{
//-------------------------------------------------------------------------------------||
// Class Members ----------------------------------------------------------------------||
//-------------------------------------------------------------------------------------||
/**
* Logger
*/
private static final Logger log = Logger.getLogger(JBossASEmbeddedServerImpl.class);
/**
* Environment Variable name for JBOSS_HOME
*/
public static final String ENV_VAR_JBOSS_HOME = "JBOSS_HOME";
/**
* System property name for JBOSS_HOME
*/
public static final String SYS_PROP_JBOSS_HOME = "jboss.home";
/**
* Name under which the MainDeployer is registered with MC
*/
private static final String MC_NAME_MAIN_DEPLOYER = "MainDeployer";
//-------------------------------------------------------------------------------------||
// Instance Members -------------------------------------------------------------------||
//-------------------------------------------------------------------------------------||
/**
* A mapping of deployable types to their resultant deployments (so we can undeploy)
*/
private final Map<Deployable, Deployment> deployableToDeploymentMap = new ConcurrentHashMap<Deployable, Deployment>();
//-------------------------------------------------------------------------------------||
// Constructor ------------------------------------------------------------------------||
//-------------------------------------------------------------------------------------||
/**
* Constructor
*
* Creates a new instance, finding JBOSS_HOME from either:
*
* 1) Environment variable "JBOSS_HOME"
* 2) System property "jboss.home"
*
* ...with preference to the system property
*
* @throws IllegalStateException
*/
public JBossASEmbeddedServerImpl() throws IllegalStateException
{
// Call Super
super(JBossASEmbeddedServer.class);
// Get JBOSS_HOME
String jbossHome = SecurityActions.getSystemProperty(SYS_PROP_JBOSS_HOME);
if (jbossHome == null)
{
jbossHome = SecurityActions.getEnvironmentVariable(ENV_VAR_JBOSS_HOME);
log.info("Using " + ENV_VAR_JBOSS_HOME + " Environment Variable: " + jbossHome);
}
else
{
log.info("Using " + SYS_PROP_JBOSS_HOME + " System Property: " + jbossHome);
}
if (jbossHome == null)
{
throw new IllegalStateException("JBOSS_HOME must be specified, either via environment variable \""
+ ENV_VAR_JBOSS_HOME + "\" or system property \"" + SYS_PROP_JBOSS_HOME + "\".");
}
// Init
this.initialize(jbossHome);
}
/**
* Constructor
*
* @param Absolute location of JBOSS_HOME on the filesystem
* @throws IllegalArgumentException If jbossHome is not specified
*/
public JBossASEmbeddedServerImpl(final String jbossHome) throws IllegalArgumentException
{
// Call super
super(JBossASEmbeddedServer.class);
// Init
this.initialize(jbossHome);
}
//-------------------------------------------------------------------------------------||
// Required Implementations -----------------------------------------------------------||
//-------------------------------------------------------------------------------------||
/**
* @see org.jboss.tmpdpl.api.container.Container#deploy(org.jboss.tmpdpl.api.deployable.Deployable[])
*/
@Override
public void deploy(final Deployable... deployables) throws DeploymentException, IllegalArgumentException
{
// Precondition checks and obtain as VDF impls
final Collection<VdfDeployable> vdfDeployables = this.asVdfDeployables(deployables);
// Get the MainDeployer
//TODO Use ProfileService - EMB-39
final MainDeployer mainDeployer = this.getMainDeployer();
// Add each deployable
for (final VdfDeployable vdfDeployable : vdfDeployables)
{
/*
* Get the Deployers SPI Deployment
*/
// Declare
final Deployment deployment = vdfDeployable.getDeployment();
// Add to the MainDeployer
log.debug("Adding to " + mainDeployer + ": " + deployment);
try
{
// Add the deployment to the MainDeployer
mainDeployer.addDeployment(deployment);
}
catch (org.jboss.deployers.spi.DeploymentException de)
{
// Wrap in our own API's DeploymentException
throw new DeploymentException(de);
}
// Add to the map (so we can undeploy the deployable later)
this.deployableToDeploymentMap.put(vdfDeployable, deployment);
}
// Process and check
this.processAndCheckMainDeployer(mainDeployer);
}
/**
* @see org.jboss.tmpdpl.api.container.Container#undeploy(org.jboss.tmpdpl.api.deployable.Deployable[])
*/
@Override
public void undeploy(final Deployable... deployables) throws DeploymentException, IllegalArgumentException
{
/*
* Precondition checks
*/
// Ensure deployables are specified
if (deployables == null || deployables.length == 0)
{
throw new IllegalArgumentException("At least one deployable must be specified");
}
// Get the MainDeployer
//TODO Use ProfileService - EMB-39
final MainDeployer mainDeployer = this.getMainDeployer();
// Get the deployments for each deployable
for (final Deployable deployable : deployables)
{
final Deployment deployment = this.getDeployablesToDeploymentsMap().get(deployable);
if (deployment == null)
{
log.warn("Specified deployable " + deployable + " cannot be undeployed because it is not deployed.");
}
else
{
// Remove from MainDeployer
try
{
mainDeployer.removeDeployment(deployment);
}
catch (org.jboss.deployers.spi.DeploymentException de)
{
// Wrap in our own API's DeploymentException
throw new DeploymentException(de);
}
// Remove from Map
this.deployableToDeploymentMap.remove(deployable);
}
}
// Process and check
this.processAndCheckMainDeployer(mainDeployer);
}
/**
* @see org.jboss.bootstrap.impl.base.server.AbstractServer#getDefaultServerConfigClass()
*/
@Override
protected Class<? extends JBossASServerConfig> getDefaultServerConfigClass()
{
return BasicJBossASServerConfig.class;
}
/**
* @see org.jboss.tmpdpl.api.container.Container#deploy(org.jboss.shrinkwrap.api.Archive[])
*/
@Override
public void deploy(final Archive<?>... archives) throws DeploymentException, IllegalArgumentException
{
// Precondition checks and obtain as array of Deployables
final Deployable[] deployables = this.asDeployableArray(archives);
// Deploy
this.deploy(deployables);
}
/**
* @see org.jboss.tmpdpl.api.container.Container#undeploy(org.jboss.shrinkwrap.api.Archive[])
*/
@Override
public void undeploy(final Archive<?>... archives) throws DeploymentException, IllegalArgumentException
{
// Precondition checks and obtain as array of Deployables
final Deployable[] deployables = this.asDeployableArray(archives);
// Undeploy
this.undeploy(deployables);
}
//-------------------------------------------------------------------------------------||
// Internal Helper Methods ------------------------------------------------------------||
//-------------------------------------------------------------------------------------||
/**
* Centralizes initialization for the constructors
*
* @param jbossHome
* @throws IllegalArgumentException If JBOSS_HOME is not specified
*/
private void initialize(final String jbossHome) throws IllegalArgumentException
{
// Precondition check
if (jbossHome == null || jbossHome.length() == 0)
{
throw new IllegalArgumentException("JBOSS_HOME is required but was not specified");
}
// Set JBOSS_HOME
this.getConfiguration().jbossHome(jbossHome);
// Set some lifecycle handlers
@SuppressWarnings("deprecation")
final LifecycleEventHandler ignoreXbOrder = new IgnoreXbUnorderedSequenceLifecycleEventHandler();
this.registerEventHandler(LifecycleState.INITIALIZED, ignoreXbOrder);
@SuppressWarnings("deprecation")
final LifecycleEventHandler setRmiHostname = new SetRmiHostnameLifecycleEventHandler();
this.registerEventHandler(LifecycleState.INITIALIZED, setRmiHostname);
@SuppressWarnings("deprecation")
final LifecycleEventHandler initLoggingManager = new InitLoggingManagerLifecycleEventHandler();
this.registerEventHandler(LifecycleState.INITIALIZED, initLoggingManager);
//TODO
/*
* Do we need some handler for boot.log? Something like:
*/
log.warn(this + " has not installed any handler for a boot.log in $JBOSS_HOME/server/[name]/log");
// /*
// * Set boot log directory
// */
// final String sysPropBootLogDir = "jboss.boot.server.log.dir";
// final String sysPropLogDir = JBossASServerConfig.PROP_KEY_JBOSSAS_SERVER_LOG_DIR;
// String serverName = System.getProperty(JBossASServerConfig.PROP_KEY_JBOSSAS_SERVER_NAME);
// if (serverName == null || serverName.length() == 0)
// {
// serverName = JBossASConfigurationInitializer.VALUE_SERVER_NAME_DEFAULT;
// }
// final String manualBootLogDir = System.getProperty(sysPropBootLogDir);
// final String manualLogDir = System.getProperty(sysPropLogDir);
// // If nothing's been explicitly specified
// if (manualBootLogDir == null && manualLogDir == null)
// {
// // We default it
// final File jbossHomeFile = new File(jbossHome);
// final URL jbossHomeUrl = jbossHomeFile.toURI().toURL();
// final URL serverLog = new URL(jbossHomeUrl, "server/" + serverName + "/log/");
// final File serverLogFile = new File(serverLog.toURI());
// final String serverLogString = serverLogFile.getAbsolutePath();
// System.setProperty(sysPropBootLogDir, serverLogString);
// }
// // If we've got a manual log dir, use it
// else if (manualLogDir != null)
// {
// System.setProperty(sysPropBootLogDir, manualLogDir);
// }
}
/**
* Obtains the specified archives as an array of
* Deployables
*
* @param archives
* @throws IllegalArgumentException If the specified archives are not specified or not of acceptable
* (VFS) backing type
*/
private Deployable[] asDeployableArray(final Archive<?>... archives) throws IllegalArgumentException
{
// Precondition check
if (archives == null || archives.length == 0)
{
throw new IllegalArgumentException("At least one archive must be specified");
}
// Make a deployable from each archive
final Collection<Deployable> deployables = new ArrayList<Deployable>();
for (final Archive<?> archive : archives)
{
final Deployable deployable = VfsVdfDeployableImpl.create(archive);
deployables.add(deployable);
}
// Return
return deployables.toArray(new Deployable[]
{});
}
/**
* Obtains the MainDeployer via MC
* @return
* @deprecated EMB-39
*/
@Deprecated
protected final MainDeployer getMainDeployer()
{
// Get the MainDeployer
final Kernel kernel = this.getKernel();
final KernelController controller = kernel.getController();
final ControllerContext context = controller.getContext(MC_NAME_MAIN_DEPLOYER, ControllerState.INSTALLED);
if (context == null)
{
throw new IllegalStateException("Main deployer could not be found");
}
final Object target = context.getTarget();
final MainDeployer mainDeployer = MainDeployer.class.cast(target);
// Return
return mainDeployer;
}
/**
* Returns a view of the specified Deployables as {@link VdfDeployable}s.
*
* @param deployables
* @return
* @throws IllegalArgumentException If the deployables are either null, empty, or
* contain any non-VDF types
*/
private Collection<VdfDeployable> asVdfDeployables(final Deployable... deployables) throws IllegalArgumentException
{
return this.asExpectedTypeCollection(VdfDeployable.class, deployables);
}
/**
* Obtains the specified array of objects as a Collection of the specified
* type. Each object in the array must be assignable to the target type.
*
* @param <T>
* @param expectedType
* @param objects
* @return
* @throws IllegalArgumentException If the objects array is null or empty, or one of the objects
* in the array is not assignable to the specified type
*/
private <T> Collection<T> asExpectedTypeCollection(final Class<T> expectedType, final Object[] objects)
throws IllegalArgumentException
{
/*
* Precondition checks
*/
// Ensure objects are specified
assert objects != null && objects.length > 0 : "At least one object must be specified";
// Ensure all objects are of expected type
final Collection<T> typedCollection = new ArrayList<T>();
// For each object
for (final Object object : objects)
{
// Is of expected type?
final T typedObject;
if (expectedType.isAssignableFrom(object.getClass()))
{
typedObject = expectedType.cast(object);
}
else
{
throw new IllegalArgumentException("Unsupported" + "; must be of type \"" + expectedType.getName()
+ "\" but was " + object.getClass().getName());
}
// Add
typedCollection.add(typedObject);
}
// Return
return typedCollection;
}
/**
* Processes and checks the specified MainDeployer
* @param mainDeployer
* @throws DeploymentException
* @throws IllegalArgumentException If the mainDeployer is not specified
* @deprecated EMB-39
*/
@Deprecated
private void processAndCheckMainDeployer(final MainDeployer mainDeployer) throws DeploymentException,
IllegalArgumentException
{
// Precondition check
if (mainDeployer == null)
{
throw new IllegalArgumentException("mainDeployer must be specified");
}
// Process All
log.debug("Processing: " + mainDeployer);
mainDeployer.process();
// Check all completed OK
try
{
log.debug("Checking: " + mainDeployer);
mainDeployer.checkComplete();
}
catch (org.jboss.deployers.spi.DeploymentException de)
{
// Wrap in our own API's DeploymentException
throw new DeploymentException(de);
}
}
//-------------------------------------------------------------------------------------||
// Accessors / Mutators ---------------------------------------------------------------||
//-------------------------------------------------------------------------------------||
/**
* Returns an immutable view of the deployables/deployments map
* @return the Deployments
*/
protected final Map<Deployable, Deployment> getDeployablesToDeploymentsMap()
{
return Collections.unmodifiableMap(deployableToDeploymentMap);
}
}