/*
* 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.embedded.core.server;
import java.io.File;
import java.net.URISyntaxException;
import java.net.URL;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.Map.Entry;
import org.jboss.bootstrap.api.as.config.JBossASServerConfig;
import org.jboss.bootstrap.api.config.InvalidConfigurationException;
import org.jboss.bootstrap.api.lifecycle.LifecycleEventException;
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.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.deployers.vfs.spi.client.VFSDeploymentFactory;
import org.jboss.embedded.api.Deployable;
import org.jboss.embedded.api.DeploymentException;
import org.jboss.embedded.api.server.JBossASEmbeddedServer;
import org.jboss.embedded.core.deployable.DeployableArchive;
import org.jboss.embedded.core.deployable.DeployableFile;
import org.jboss.embedded.core.deployable.DeployableUrl;
import org.jboss.embedded.core.lifecycle.IgnoreXbUnorderedSequenceLifecycleEventHandler;
import org.jboss.embedded.core.lifecycle.InitLogManagerLevelsLifecycleEventHandler;
import org.jboss.embedded.core.lifecycle.InitLoggingManagerLifecycleEventHandler;
import org.jboss.embedded.core.lifecycle.NoXbRepeatableParticleHandlersLifecycleEventHandler;
import org.jboss.embedded.core.lifecycle.SetIPv4LifecycleEventHandler;
import org.jboss.embedded.core.lifecycle.SetRmiHostnameLifecycleEventHandler;
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.vfs.VFS;
import org.jboss.vfs.VirtualFile;
/**
* 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 {@link Deployable} types to their resultant {@link Deployment}s which have been deployed
* (so we can undeploy). Synchronized on "this" so that we can guarantee all operations are atomic
* (like adding all Deployments, removing Deployments, etc.) Using a java.util.Concurrent
* implementation alone won't suit this need.
*/
private final Map<Deployable, Deployment> deployed = new HashMap<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);
}
/**
* Constructor
*
* Creates a new instance, manually specifying JBOSS_HOME
*
* @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);
// Precondition check
if (jbossHome == null || jbossHome.length() == 0)
{
throw new IllegalArgumentException("JBOSS_HOME must be specified");
}
// Override JBOSS_HOME by setting the system property
SecurityActions.setJBossHomeSystemProperty(jbossHome);
}
//-------------------------------------------------------------------------------------||
// Required Implementations -----------------------------------------------------------||
//-------------------------------------------------------------------------------------||
/**
* @see org.jboss.bootstrap.impl.base.server.AbstractServer#getDefaultServerConfigClass()
*/
@Override
protected Class<? extends JBossASServerConfig> getDefaultServerConfigClass()
{
return BasicJBossASServerConfig.class;
}
/**
* {@inheritDoc}
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#deploy(java.net.URL[])
*/
@Override
public void deploy(final URL... urls) throws DeploymentException, IllegalArgumentException
{
// Make new Deployables
final Collection<Deployable> deployables = this.asDeployables(urls);
// Deploy
deploy(deployables);
}
/**
* {@inheritDoc}
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#deploy(java.io.File[])
*/
@Override
public void deploy(final File... files) throws DeploymentException, IllegalArgumentException
{
// Make new Deployables
final Collection<Deployable> deployables = this.asDeployables(files);
// Deploy
deploy(deployables);
}
/**
* {@inheritDoc}
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#deploy(org.jboss.shrinkwrap.api.Archive<?>[])
*/
@Override
public void deploy(final Archive<?>... archives) throws DeploymentException, IllegalArgumentException
{
// Make new Deployables
final Collection<Deployable> deployables = this.asDeployables(archives);
// Deploy
deploy(deployables);
}
/**
* {@inheritDoc}
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#deploy(org.jboss.embedded.api.Deployable[])
*/
@Override
public synchronized void deploy(final Deployable... deployables) throws DeploymentException,
IllegalArgumentException
{
// Precondition checks
if (deployables == null)
{
throw new IllegalArgumentException("deployables must not be null");
}
// Get the MainDeployer
//TODO Use ProfileService - EMB-39
final MainDeployer mainDeployer = this.getMainDeployer();
// Record the deployments to be added
final Map<Deployable, Deployment> deploymentsToBeAdded = new HashMap<Deployable, Deployment>();
// For all Deployables
for (final Deployable deployable : deployables)
{
// If already deployed
if (deployed.containsKey(deployable))
{
log.warn("Attempting to re-deploy " + deployable + "; ignoring.");
}
// Make a VFS Deployment
final URL target = deployable.getTarget();
final VirtualFile vf;
try
{
vf = VFS.getChild(target);
}
catch (final URISyntaxException e)
{
throw new RuntimeException("Could not create new VFS root from " + target, e);
}
final Deployment deployment = VFSDeploymentFactory.getInstance().createVFSDeployment(vf);
// Add to the MainDeployer
log.debug("Adding to " + mainDeployer + ": " + deployment);
try
{
// Add the deployment to the MainDeployer
mainDeployer.addDeployment(deployment);
deploymentsToBeAdded.put(deployable, deployment);
}
catch (final org.jboss.deployers.spi.DeploymentException de)
{
// Construct a new DeploymentException of this API
final DeploymentException wrappedException = new DeploymentException(de);
// Remove all previously-added deployables from further MainDeployer processing
final Iterator<Entry<Deployable, Deployment>> it = deploymentsToBeAdded.entrySet().iterator();
while (it.hasNext())
{
final Entry<Deployable, Deployment> next = it.next();
final Deployable thisDeployable = next.getKey();
try
{
mainDeployer.removeDeployment(deploymentsToBeAdded.get(thisDeployable));
}
catch (org.jboss.deployers.spi.DeploymentException e)
{
log.warn("Atomic deployment of " + Arrays.asList(deployables) + " failed due to " + de.getMessage()
+ ", and we could not remove " + thisDeployable + " from further processing");
}
}
// Throw the wrapped exception
throw wrappedException;
}
// Process and check
this.processAndCheckMainDeployer(mainDeployer);
// Record deployed
this.deployed.putAll(deploymentsToBeAdded);
}
}
/**
* @see org.jboss.tmpdpl.api.container.Container#undeploy(org.jboss.shrinkwrap.api.Archive[])
*/
@Override
public void undeploy(final Archive<?>... archives) throws DeploymentException, IllegalArgumentException
{
// Make new Deployables
final Collection<Deployable> deployables = this.asDeployables(archives);
// Undeploy
undeploy(deployables);
}
/**
* {@inheritDoc}
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#undeploy(java.net.URL[])
*/
@Override
public void undeploy(final URL... urls) throws DeploymentException, IllegalArgumentException
{
// Make new Deployables
final Collection<Deployable> deployables = this.asDeployables(urls);
// Undeploy
undeploy(deployables);
}
/**
* {@inheritDoc}
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#undeploy(java.io.File[])
*/
@Override
public void undeploy(final File... files) throws DeploymentException, IllegalArgumentException
{
// Make new Deployables
final Collection<Deployable> deployables = this.asDeployables(files);
// Undeploy
undeploy(deployables);
}
/**
* {@inheritDoc}
* Synchronized so that we may atomically remove all deployables in {@link JBossASEmbeddedServerImpl#deployed}
* without fear of intrusion from the outside (there is no atomic Map.removeAll)
*
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#undeploy(org.jboss.embedded.api.Deployable[])
*/
@Override
public synchronized 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();
// Record deployables to remove
final Collection<Deployable> deployablesToRemove = new ArrayList<Deployable>();
// Get the deployments for each deployable
for (final Deployable deployable : deployables)
{
final Deployment deployment = this.deployed.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);
}
// Mark to remove
deployablesToRemove.add(deployable);
}
}
// Process and check
this.processAndCheckMainDeployer(mainDeployer);
// Mark removed
for (final Deployable deployable : deployablesToRemove)
{
this.deployed.remove(deployable);
}
}
//-------------------------------------------------------------------------------------||
// Internal Helper Methods ------------------------------------------------------------||
//-------------------------------------------------------------------------------------||
/* (non-Javadoc)
* @see org.jboss.bootstrap.impl.as.server.AbstractJBossASServerBase#doInitialize()
*/
@Override
protected void doInitialize() throws IllegalStateException, InvalidConfigurationException, LifecycleEventException
{
// Invoke super
super.doInitialize();
// 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);
@SuppressWarnings("deprecation")
final LifecycleEventHandler ipv4Handler = new SetIPv4LifecycleEventHandler();
this.registerEventHandler(LifecycleState.INITIALIZED, ipv4Handler);
@SuppressWarnings("deprecation")
final LifecycleEventHandler repeatableParticleHandlers = new NoXbRepeatableParticleHandlersLifecycleEventHandler();
this.registerEventHandler(LifecycleState.INITIALIZED, repeatableParticleHandlers);
// Init Logging levels
//TODO Remove when we can JBLOGGING-37
this.registerEventHandler(InitLogManagerLevelsLifecycleEventHandler.INSTANCE, LifecycleState.INITIALIZED);
}
/**
* Represents the specified {@link URL}s as a {@link Collection} of
* {@link Deployable}s
*
* @param urls
* @return
* @throws IllegalArgumentException If the URLs are not specified
*/
private Collection<Deployable> asDeployables(final URL... urls) throws IllegalArgumentException
{
// Precondition checks
if (urls == null)
{
throw new IllegalArgumentException("urls must not be null");
}
// Make new Deployables
final Collection<Deployable> deployables = new ArrayList<Deployable>(urls.length);
for (final URL url : urls)
{
final Deployable deployable = new DeployableUrl(url);
deployables.add(deployable);
}
return deployables;
}
/**
* Represents the specified {@link Archive}s as a {@link Collection} of
* {@link Deployable}s
*
* @param archives
* @return
* @throws IllegalArgumentException If the URLs are not specified
*/
private Collection<Deployable> asDeployables(final Archive<?>... archives) throws IllegalArgumentException
{
// Precondition checks
if (archives == null)
{
throw new IllegalArgumentException("archives must not be null");
}
// Make new Deployables
final Collection<Deployable> deployables = new ArrayList<Deployable>(archives.length);
for (final Archive<?> archive : archives)
{
final Deployable deployable = new DeployableArchive(archive);
deployables.add(deployable);
}
return deployables;
}
/**
* Represents the specified {@link File}s as a {@link Collection} of
* {@link Deployable}s
*
* @param archives
* @return
* @throws IllegalArgumentException If the URLs are not specified
*/
private Collection<Deployable> asDeployables(final File... files) throws IllegalArgumentException
{
// Precondition checks
if (files == null)
{
throw new IllegalArgumentException("files must not be null");
}
// Make new Deployables
final Collection<Deployable> deployables = new ArrayList<Deployable>(files.length);
for (final File file : files)
{
final Deployable deployable = new DeployableFile(file);
deployables.add(deployable);
}
return deployables;
}
/**
* Deploys the specified {@link Deployable}s
*
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#deploy(org.jboss.embedded.api.Deployable[])
* @param deployables
* @throws DeploymentException
* @throws IllegalArgumentException
*/
private void deploy(final Collection<Deployable> deployables) throws DeploymentException, IllegalArgumentException
{
// Pass over as an array
final Deployable[] deployablesArray = deployables.toArray(new Deployable[]
{});
this.deploy(deployablesArray);
}
/**
* Undeploys the specified {@link Deployable}s
*
* @see org.jboss.embedded.api.server.JBossASEmbeddedServer#undeploy(Deployable...)
* @param deployables
* @throws DeploymentException
* @throws IllegalArgumentException
*/
private void undeploy(final Collection<Deployable> deployables) throws DeploymentException, IllegalArgumentException
{
// Pass over as an array
final Deployable[] deployablesArray = deployables.toArray(new Deployable[]
{});
this.undeploy(deployablesArray);
}
/**
* 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;
}
/**
* 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);
}
}
}