/* * JBoss, Home of Professional Open Source * Copyright 2010, Red Hat Middleware LLC, and individual contributors * by the @authors tag. See the copyright.txt in the distribution for a * full listing of individual contributors. * * 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.jboss.arquillian.container.jsr88.remote_1_2; import java.io.InputStream; import java.util.concurrent.CyclicBarrier; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; import javax.enterprise.deploy.shared.CommandType; import javax.enterprise.deploy.shared.ModuleType; import javax.enterprise.deploy.shared.factories.DeploymentFactoryManager; import javax.enterprise.deploy.spi.DeploymentManager; import javax.enterprise.deploy.spi.Target; import javax.enterprise.deploy.spi.TargetModuleID; import javax.enterprise.deploy.spi.factories.DeploymentFactory; import javax.enterprise.deploy.spi.status.ProgressObject; import org.jboss.arquillian.spi.client.container.DeployableContainer; import org.jboss.arquillian.spi.client.container.DeploymentException; import org.jboss.arquillian.spi.client.container.LifecycleException; import org.jboss.arquillian.spi.client.deployment.Deployment; import org.jboss.arquillian.spi.client.protocol.metadata.HTTPContext; import org.jboss.arquillian.spi.client.protocol.metadata.ProtocolMetaData; import org.jboss.shrinkwrap.api.ArchivePath; import org.jboss.shrinkwrap.api.ArchivePaths; import org.jboss.shrinkwrap.api.exporter.ZipExporter; /** * A container implementation for a JSR 88-compliant container. * * <p>This class distributes (deploys) and undeploys ShrinkWrap * archives using the JSR-88 {@link DeploymentManager}. A connection * to the container is established during the start method and * released in the stop method. The deploy and undeploy methods use * {@link DeploymentManager#distribute(Target[], ModuleType, InputStream, InputStream)} and * {@link DeploymentManager#undeploy(TargetModuleID[])}, respectively.</p> * * <p>You can use this container implementation either through configuration * or extension. To use it via configuration, assign the deployment URI and * deployment factory class for the target container to the {@link JSR88Configuration} * object using the Arquillian configuration file (arquillian.xml). Alternatively, * you can choose to extend the {@link JSR88Configuration} to assign defaults for * these values. You should also specify the configuration class by overriding the * {@link JSR88CompliantRemoteContainer#getConfigurationClass()} * method.</p> * * <p>JSR 88 deploys the archive using an {@link InputStream}. The deployed * archive is assigned a random name. You can specify a custom name for * a war in the <code>module-name</code> element of the module deployment * descriptor (web.xml).</p> * * @author Dan Allen * @author Iskandar Salim * @see org.glassfish.admin.cli.resources.AddResources */ public class JSR88RemoteContainer<T extends JSR88Configuration> implements DeployableContainer<T> { public static final String HTTP_PROTOCOL = "http"; public static final ArchivePath MODULE_ID_STORE_PATH = ArchivePaths.create(".jsr88-module-id"); private static final CyclicBarrier PROGRESS_BARRIER = new CyclicBarrier(2); private static final Logger log = Logger.getLogger(JSR88RemoteContainer.class.getName()); private JSR88ModuleTypeMapper moduleTypeMapper; private DeploymentManager deploymentManager; private boolean moduleStarted = false; private T containerConfig; public JSR88RemoteContainer() { moduleTypeMapper = new JSR88ModuleTypeMapper(); } public void setup(T containerConfig) { this.containerConfig = containerConfig; } public void start() throws LifecycleException { try { initDeploymentManager(containerConfig.getDeploymentFactoryClass(), containerConfig.getDeploymentUri(), containerConfig.getDeploymentUsername(), containerConfig.getDeploymentPassword()); } catch (Exception e) { throw new LifecycleException("Could not connect to container", e); } } public void stop() throws LifecycleException { try { releaseDeploymentManager(); } catch (Exception e) { throw new LifecycleException("Could not release deployment manager", e); } } public ProtocolMetaData deploy(Deployment... deployments) throws DeploymentException { if (deploymentManager == null) { throw new DeploymentException("Could not deploy since deployment manager was not loaded"); } TargetModuleID moduleInfo = null; try { for(Deployment deployment : deployments) { if(deployment.isArchiveDeployment()) { PROGRESS_BARRIER.reset(); resetModuleStatus(); ProgressObject progress = deploymentManager.distribute( deploymentManager.getTargets(), moduleTypeMapper.getModuleType(deployment.getArchive()), deployment.getArchive().as(ZipExporter.class).exportAsInputStream(), null); progress.addProgressListener(new JSR88DeploymentListener(this, progress.getResultTargetModuleIDs(), CommandType.DISTRIBUTE)); waitForModuleToStart(); // QUESTION when is getResultTargetModuleIDs() > 0? moduleInfo = progress.getResultTargetModuleIDs()[0]; context.add(TargetModuleID.class, moduleInfo); } } } catch (Exception e) { throw new DeploymentException("Could not deploy archive", e); } if (moduleInfo == null || moduleInfo.getModuleID() == null) { throw new DeploymentException("Could not determine module id, likely because module did not deploy"); } try { // FIXME pass moduleId to ServletMethodExecutor since we can't guarantee anymore it's /test return new ProtocolMetaData().addContext( new HTTPContext(containerConfig.getRemoteServerAddress(), containerConfig.getRemoteServerHttpPort(), "/test")); } catch (Exception e) { throw new RuntimeException("Could not create ContainerMethodExecutor", e); } } public void undeploy(Deployment... deployments) throws DeploymentException { if (!moduleStarted) { log.info("Skipping undeploy since module is not deployed"); return; } if (deploymentManager == null) { throw new DeploymentException("Could not undeploy since deployment manager was not loaded"); } for(Deployment deployment : deployments) { if(deployment.isArchiveDeployment()) { try { PROGRESS_BARRIER.reset(); TargetModuleID moduleInfo = context.get(TargetModuleID.class); if (moduleInfo == null || moduleInfo.getModuleID() == null) { log.log(Level.INFO, "Skipping undeploy since module ID could not be determined"); return; } TargetModuleID[] availableModuleIDs = deploymentManager.getAvailableModules( moduleTypeMapper.getModuleType(deployment.getArchive()), getDeploymentManager().getTargets()); TargetModuleID moduleInfoMatch = null; for (TargetModuleID candidate : availableModuleIDs) { if (candidate.getModuleID().equals(moduleInfo.getModuleID())) { moduleInfoMatch = candidate; break; } } if (moduleInfoMatch != null) { TargetModuleID[] targetModuleIDs = { moduleInfoMatch }; ProgressObject progress = deploymentManager.undeploy(targetModuleIDs); progress.addProgressListener(new JSR88DeploymentListener(this, targetModuleIDs, CommandType.UNDEPLOY)); waitForModuleToUndeploy(); } else { resetModuleStatus(); log.info("Undeploy skipped since could not locate module in list of deployed modules"); } } catch (Exception e) { throw new DeploymentException("Could not undeploy module", e); } } } } public Class<T> getConfigurationClass() { return JSR88Configuration.class; } public DeploymentManager getDeploymentManager() { return deploymentManager; } protected DeploymentManager initDeploymentManager(String factoryClass, String uri, String username, String password) throws Exception { if (deploymentManager == null) { DeploymentFactoryManager dfm = DeploymentFactoryManager.getInstance(); dfm.registerDeploymentFactory( (DeploymentFactory) Class.forName(factoryClass).newInstance()); deploymentManager = dfm.getDeploymentManager(uri, username, password); } return deploymentManager; } protected void releaseDeploymentManager() { if (deploymentManager != null) { deploymentManager.release(); } } public boolean isModuleStarted() { return moduleStarted; } void moduleStarted(boolean status) { moduleStarted = status; if (PROGRESS_BARRIER.getNumberWaiting() > 0) { try { PROGRESS_BARRIER.await(); } catch (Exception e) { throw new RuntimeException("Failed to report module as " + (status ? "started" : "shutdown"), e); } } } protected void resetModuleStatus() { moduleStarted = false; } private void waitForModuleToStart() { if (!moduleStarted) { try { PROGRESS_BARRIER.await(containerConfig.getDeploymentTimeoutSeconds(), TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException("Module startup was interrupted or timed out", e); } } } private void waitForModuleToUndeploy() { if (moduleStarted) { try { PROGRESS_BARRIER.await(containerConfig.getDeploymentTimeoutSeconds(), TimeUnit.SECONDS); } catch (Exception e) { throw new RuntimeException("Module undeployment was interrupted or timed out", e); } } } }