org.jboss.as.arquillian.container.ServerSetupObserver Maven / Gradle / Ivy
/*
* Copyright 2015 Red Hat, Inc.
*
* 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.as.arquillian.container;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationTargetException;
import java.util.ArrayDeque;
import java.util.Deque;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Set;
import org.jboss.arquillian.container.spi.Container;
import org.jboss.arquillian.container.spi.client.deployment.DeploymentDescription;
import org.jboss.arquillian.container.spi.event.container.AfterUnDeploy;
import org.jboss.arquillian.container.spi.event.container.BeforeDeploy;
import org.jboss.arquillian.core.api.Instance;
import org.jboss.arquillian.core.api.annotation.Inject;
import org.jboss.arquillian.core.api.annotation.Observes;
import org.jboss.arquillian.test.spi.context.ClassContext;
import org.jboss.arquillian.test.spi.event.suite.AfterClass;
import org.jboss.arquillian.test.spi.event.suite.BeforeClass;
import org.jboss.as.arquillian.api.ServerSetup;
import org.jboss.as.arquillian.api.ServerSetupTask;
import org.jboss.logging.Logger;
/**
* Observes the {@link BeforeDeploy}, {@link AfterUnDeploy} and {@link AfterClass} lifecycle events to ensure
* {@linkplain ServerSetupTask setup tasks} are executed.
*
* @author Stuart Douglas
* @author James R. Perkins
*/
@SuppressWarnings({ "unused", "InstanceVariableMayNotBeInitialized" })
public class ServerSetupObserver {
private static final Logger log = Logger.getLogger(ServerSetupObserver.class);
@Inject
private Instance managementClient;
@Inject
private Instance classContextInstance;
private final Map setupTasks = new HashMap<>();
private boolean afterClassRun = false;
/**
* Observed only for state changes.
*
* @param beforeClass the lifecycle event
*/
public synchronized void handleBeforeClass(@Observes BeforeClass beforeClass) {
afterClassRun = false;
}
/**
* Executed before deployments to lazily execute the {@link ServerSetupTask#setup(ManagementClient, String)
* ServerSetupTask}.
*
* This is lazily loaded for manual mode tests. The server may not have been started at the
* {@link org.jboss.arquillian.test.spi.event.suite.BeforeClass BeforeClass} event.
*
*
* @param event the lifecycle event
* @param container the container the event is being invoked on
*
* @throws Throwable if an error occurs processing the event
*/
public synchronized void handleBeforeDeployment(@Observes BeforeDeploy event, Container container) throws Throwable {
final String containerName = container.getName();
if (setupTasks.containsKey(containerName)) {
setupTasks.get(containerName).deployments.add(event.getDeployment());
return;
}
final ClassContext classContext = classContextInstance.get();
if (classContext == null) {
return;
}
final Class> currentClass = classContext.getActiveId();
ServerSetup setup = currentClass.getAnnotation(ServerSetup.class);
if (setup == null) {
return;
}
final ManagementClient client = managementClient.get();
final ServerSetupTaskHolder holder = new ServerSetupTaskHolder(client);
holder.deployments.add(event.getDeployment());
setupTasks.put(containerName, holder);
holder.setup(setup, containerName);
}
/**
* Executed after the test class has completed. This ensures that any
* {@linkplain ServerSetupTask#tearDown(ManagementClient, String) tear down tasks} have been executed if all
* all deployments have been undeployed.
*
* @param afterClass the lifecycle event
*
* @throws Exception if an error occurs processing the event
*/
public synchronized void afterTestClass(@Observes AfterClass afterClass) throws Exception {
if (setupTasks.isEmpty()) {
return;
}
// Clean up any remaining tasks from unmanaged deployments
final Iterator> iter = setupTasks.entrySet().iterator();
while (iter.hasNext()) {
final Map.Entry entry = iter.next();
final ServerSetupTaskHolder holder = entry.getValue();
// Only tearDown if all deployments have been removed from the container
if (holder.deployments.isEmpty()) {
entry.getValue().tearDown(entry.getKey());
iter.remove();
}
}
afterClassRun = true;
}
/**
* Executed after each undeploy for the container.
*
* @param afterDeploy the lifecycle event
* @param container the container the event is being invoked on
*
* @throws Exception if an error occurs processing the event
*/
public synchronized void handleAfterUndeploy(@Observes AfterUnDeploy afterDeploy, final Container container)
throws Exception {
final String containerName = container.getName();
final ServerSetupTaskHolder holder = setupTasks.get(containerName);
if (holder == null) {
return;
}
// Remove the deployment
if (holder.deployments.remove(afterDeploy.getDeployment())) {
// If the deployments are now empty and the AfterClass has been invoked we need to ensure the tearDown() has
// happened. This should clean up any tasks left from managed deployments or unmanaged deployments that were
// not undeployed manually.
if (afterClassRun && holder.deployments.isEmpty()) {
holder.tearDown(containerName);
setupTasks.remove(containerName);
}
}
}
private static class ServerSetupTaskHolder {
private final ManagementClient client;
private final Deque setupTasks;
private final Set deployments;
private ServerSetupTaskHolder(final ManagementClient client) {
this.client = client;
setupTasks = new ArrayDeque<>();
deployments = new HashSet<>();
}
void setup(final ServerSetup setup, final String containerName)
throws NoSuchMethodException, IllegalAccessException, InvocationTargetException, InstantiationException {
final Class extends ServerSetupTask>[] classes = setup.value();
for (Class extends ServerSetupTask> clazz : classes) {
final Constructor extends ServerSetupTask> ctor = clazz.getDeclaredConstructor();
ctor.setAccessible(true);
final ServerSetupTask task = ctor.newInstance();
setupTasks.add(task);
try {
task.setup(client, containerName);
} catch (Throwable e) {
log.errorf(e, "Setup failed during setup. Offending class '%s'", task);
}
}
}
public void tearDown(final String containerName) {
if (client.isClosed()) {
log.errorf("The container '%s' may have been stopped. The management client has been closed and " +
"tearing down setup tasks is not possible.", containerName);
} else {
ServerSetupTask task;
while ((task = setupTasks.pollLast()) != null) {
try {
task.tearDown(client, containerName);
} catch (Throwable e) {
log.errorf(e, "Setup task failed during tear down. Offending class '%s'", task);
}
}
}
}
@Override
public String toString() {
return ServerSetupTaskHolder.class.getName() +
"[setupTasks=" +
setupTasks +
", deployments" +
deployments +
"]";
}
}
}