From 37670a00a52b06c89d3ccd81188773218d1664b0 Mon Sep 17 00:00:00 2001 From: "James R. Perkins" Date: Tue, 12 Dec 2023 12:09:37 -0800 Subject: [PATCH] [WFARQ-140] Follow up on the application client support. Changed to require injection via @ArquillianResource annotation and minor clean up. https://issues.redhat.com/browse/WFARQ-140 Signed-off-by: James R. Perkins --- .../AbstractTargetsContainerProvider.java | 2 +- .../container/managed/AppClientProvider.java | 61 ++++ .../managed/AppClientTestEnricher.java | 120 -------- .../container/managed/AppClientWrapper.java | 277 +++++++++++------- .../ManagedContainerConfiguration.java | 71 ++--- .../managed/ManagedContainerExtension.java | 4 +- .../managed/ManagedDeployableContainer.java | 40 +-- .../container/app/AppClient2TestCase.java | 35 +-- .../container/app/AppClientTestCase.java | 24 +- 9 files changed, 315 insertions(+), 319 deletions(-) create mode 100644 container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientProvider.java delete mode 100644 container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientTestEnricher.java diff --git a/common/src/main/java/org/jboss/as/arquillian/container/AbstractTargetsContainerProvider.java b/common/src/main/java/org/jboss/as/arquillian/container/AbstractTargetsContainerProvider.java index 70dfb16a..ba5ed8cf 100644 --- a/common/src/main/java/org/jboss/as/arquillian/container/AbstractTargetsContainerProvider.java +++ b/common/src/main/java/org/jboss/as/arquillian/container/AbstractTargetsContainerProvider.java @@ -35,7 +35,7 @@ * * @author James R. Perkins */ -abstract class AbstractTargetsContainerProvider extends OperatesOnDeploymentAwareProvider { +public abstract class AbstractTargetsContainerProvider extends OperatesOnDeploymentAwareProvider { @Inject private Instance containerContext; diff --git a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientProvider.java b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientProvider.java new file mode 100644 index 00000000..a38cc288 --- /dev/null +++ b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientProvider.java @@ -0,0 +1,61 @@ +/* + * JBoss, Home of Professional Open Source. + * + * Copyright 2023 Red Hat, Inc., and individual contributors + * as indicated by the @author tags. + * + * 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.managed; + +import java.lang.annotation.Annotation; + +import org.jboss.arquillian.container.test.impl.enricher.resource.OperatesOnDeploymentAwareProvider; +import org.jboss.arquillian.core.api.Instance; +import org.jboss.arquillian.core.api.annotation.Inject; +import org.jboss.arquillian.test.api.ArquillianResource; +import org.jboss.as.arquillian.container.AbstractTargetsContainerProvider; + +/** + * {@link OperatesOnDeploymentAwareProvider} implementation to provide {@link AppClientWrapper} injection to + * {@link ArquillianResource} annotated fields. + * + * @author James R. Perkins + */ +public class AppClientProvider extends AbstractTargetsContainerProvider { + + @Inject + private Instance AppClientWrapper; + + /** + * {@inheritDoc} + * + * @see org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider#canProvide(java.lang.Class) + */ + @Override + public boolean canProvide(final Class type) { + return type.isAssignableFrom(AppClientWrapper.class); + } + + /** + * {@inheritDoc} + * + * @see org.jboss.arquillian.container.test.impl.enricher.resource.OperatesOnDeploymentAwareProvider#doLookup(org.jboss.arquillian.test.api.ArquillianResource, + * java.lang.annotation.Annotation[]) + */ + @Override + public Object doLookup(final ArquillianResource resource, final Annotation... qualifiers) { + return AppClientWrapper.get(); + } +} diff --git a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientTestEnricher.java b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientTestEnricher.java deleted file mode 100644 index d69214ca..00000000 --- a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientTestEnricher.java +++ /dev/null @@ -1,120 +0,0 @@ -/* - * Copyright 2023 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.managed; - -import java.lang.reflect.Field; -import java.lang.reflect.Method; -import java.util.ArrayList; -import java.util.List; - -import org.jboss.arquillian.container.spi.Container; -import org.jboss.arquillian.container.spi.ContainerRegistry; -import org.jboss.arquillian.container.spi.client.container.DeployableContainer; -import org.jboss.arquillian.container.spi.context.ContainerContext; -import org.jboss.arquillian.core.api.Instance; -import org.jboss.arquillian.core.api.annotation.Inject; -import org.jboss.arquillian.test.spi.TestEnricher; - -/** - * A TestEnricher that supports the injection of the AppClientWrapper application client container runner - */ -public class AppClientTestEnricher implements TestEnricher { - @Inject - private Instance containerContext; - - @Inject - private Instance containerRegistry; - - /** - * Support injection of the AppClientWrapper into a test instance - * - * @param testCase - test instance - */ - @Override - public void enrich(Object testCase) { - AppClientWrapper appClient = getAppClient(); - if (appClient != null) { - List appClientFields = getAppClientFields(testCase); - for (Field f : appClientFields) { - try { - f.set(testCase, appClient); - } catch (IllegalAccessException e) { - throw new RuntimeException("Could not set value on field " + f + " using " + testCase); - } - } - } - } - - /** - * Support injection of the AppClientWrapper into a test method - * - * @param method - test method - * @return array of method parameter values with any AppClientWrapper type set to the active - * {@link ManagedDeployableContainer#getAppClient()} - */ - @Override - public Object[] resolve(Method method) { - Object[] values = new Object[method.getParameterTypes().length]; - Class[] parameterTypes = method.getParameterTypes(); - for (int i = 0; i < parameterTypes.length; i++) { - if (parameterTypes[i].isAssignableFrom(AppClientWrapper.class)) { - values[i] = getAppClient(); - } - } - return values; - } - - /** - * Obtain the AppClientWrapper from the active ManagedDeployableContainer - * - * @return active AppClientWrapper if one exists, null otherwise - */ - private AppClientWrapper getAppClient() { - String containerID = containerContext.get().getActiveId(); - Container container = containerRegistry.get().getContainer(containerID); - DeployableContainer deployableContainer = container.getDeployableContainer(); - if (deployableContainer instanceof ManagedDeployableContainer) { - ManagedDeployableContainer mdContainer = (ManagedDeployableContainer) deployableContainer; - return mdContainer.getAppClient(); - } - return null; - } - - /** - * - * @param testCase test case instance - * @return fields of type assignable from AppClientWrapper - */ - private List getAppClientFields(Object testCase) { - Class testClass = testCase.getClass(); - Class nextTestClass = testClass; - List foundFields = new ArrayList(); - while (nextTestClass != Object.class) { - for (Field field : testClass.getDeclaredFields()) { - if (field.getType().isAssignableFrom(AppClientWrapper.class)) { - if (!field.canAccess(testCase)) { - field.setAccessible(true); - } - foundFields.add(field); - } - } - nextTestClass = nextTestClass.getSuperclass(); - } - - return foundFields; - } -} diff --git a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientWrapper.java b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientWrapper.java index 376e950a..2031ef0d 100644 --- a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientWrapper.java +++ b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/AppClientWrapper.java @@ -16,182 +16,251 @@ package org.jboss.as.arquillian.container.managed; -import java.io.BufferedReader; -import java.io.File; +import java.io.IOException; +import java.io.InputStream; import java.io.InputStreamReader; import java.nio.charset.StandardCharsets; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.ArrayList; +import java.util.List; import java.util.concurrent.BlockingQueue; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.TimeUnit; +import java.util.concurrent.locks.Lock; +import java.util.concurrent.locks.ReentrantLock; import org.jboss.as.arquillian.container.ParameterUtils; import org.jboss.logging.Logger; +import org.wildfly.plugin.core.ServerHelper; /** - * Fork of the wildfly integration testsuite application client container runner + * A wrapper for an application client process. Allows interacting with the application client process. * * @author Dominik Pospisil * @author Stuart Douglas + * @author James R. Perkins */ -public class AppClientWrapper { - private static final Logger LOGGER = Logger.getLogger(AppClientWrapper.class); +public class AppClientWrapper implements AutoCloseable { + private final BlockingQueue outputQueue = new LinkedBlockingQueue<>(); + private final ManagedContainerConfiguration config; + private final Logger log; + private final Lock lock; + private Process process; + private ExecutorService executorService; - private static final String outThreadHame = "APPCLIENT-out"; - private static final String errThreadHame = "APPCLIENT-err"; - - private Process appClientProcess; - private BufferedReader outputReader; - private BufferedReader errorReader; - private BlockingQueue outputQueue = new LinkedBlockingQueue(); - private ManagedContainerConfiguration config; - private Logger log; + private Future stdoutConsumer; + private Future stderrConsumer; /** - * Creates new CLI wrapper. If the connect parameter is set to true the CLI - * will connect to the server using connect command. - * + * Creates a new application client wrapper. * - * @param config - * @throws Exception + * @param config the configuration for the container + * @param log the logger to use */ - public AppClientWrapper(ManagedContainerConfiguration config) throws Exception { - this(config, LOGGER); - } - - public AppClientWrapper(ManagedContainerConfiguration config, Logger log) { + protected AppClientWrapper(final ManagedContainerConfiguration config, final Logger log) { this.config = config; this.log = log; + lock = new ReentrantLock(); } - public boolean waitForExit(long timeout, TimeUnit units) throws InterruptedException { - return appClientProcess.waitFor(timeout, units); + /** + * If the application client has started, causes the current thread to wait, if necessary, until the application + * client terminates or the specified wait has been reached. + * + *

+ * If the application client has not started a value of {@code false} is returned and an error message logged + * indicating the application client has not started. + *

+ * + *

+ * If the application client has started and the process has terminated, this method returns {@code true} + * immediately. + *

+ * + * @param timeout the maximum time to wait + * @param unit the time unit of the {@code timeout} argument + * + * @return {@code true} if the application client process has exited and {@code false} if the waiting time elapsed + * before the process has exited + * + * @throws InterruptedException if the current thread is interrupted while waiting + * @throws NullPointerException if unit is null + * @see Process#waitFor(long, TimeUnit) + */ + @SuppressWarnings("UnusedReturnValue") + public boolean waitForExit(final long timeout, final TimeUnit unit) throws InterruptedException { + try { + lock.lock(); + if (process != null) { + try { + final boolean b = process.waitFor(timeout, unit); + process = null; + return b; + } finally { + close(); + } + } + } finally { + lock.unlock(); + } + log.warn("waitForExit was invoked before the process was started."); + return false; } /** - * Consumes all available output from App Client using the output queue filled by the process - * stanard out reader thread. + * Consumes all available output from application client using the output queue filled by the process + * standard out reader thread. * * @param timeout number of milliseconds to wait for each subsequent line - * @return array of App Client output lines + * + * @return list of application client output lines */ - public String[] readAll(final long timeout) { - ArrayList lines = new ArrayList(); + public List readAll(final long timeout) { + final List lines = new ArrayList<>(); String line = null; do { try { line = outputQueue.poll(timeout, TimeUnit.MILLISECONDS); if (line != null) lines.add(line); - } catch (InterruptedException ioe) { + } catch (InterruptedException ignore) { } } while (line != null); - return lines.toArray(new String[] {}); + return List.copyOf(lines); } /** - * Kills the app client + * Starts the application client in a new process and creates two thread to read the process output ({@code stdout}) + * and error streams ({@code stderr}). * - * @throws Exception + * @throws IOException if there is a failure to start the application client process */ - public synchronized void quit() throws Exception { - appClientProcess.destroy(); + public void run() throws IOException { try { - appClientProcess.waitFor(); - } catch (InterruptedException e) { - throw new RuntimeException(e); + lock.lock(); + if (process == null) { + process = new ProcessBuilder(getAppClientCommand()) + .start(); + executorService = Executors.newFixedThreadPool(2); + stdoutConsumer = executorService + .submit(new LogConsumer(outputQueue, process.getInputStream(), Logger.Level.INFO, process.pid())); + stderrConsumer = executorService + .submit(new LogConsumer(null, process.getErrorStream(), Logger.Level.ERROR, process.pid())); + } + } finally { + lock.unlock(); } } /** - * Starts the app client in a new process and creates two thread to read the process output - * and error streams. - * - * @throws Exception - on failure + * Kills the application client. */ - public void run() throws Exception { - appClientProcess = Runtime.getRuntime().exec(getAppClientCommand()); - outputReader = new BufferedReader(new InputStreamReader(appClientProcess.getInputStream(), StandardCharsets.UTF_8)); - errorReader = new BufferedReader(new InputStreamReader(appClientProcess.getErrorStream(), StandardCharsets.UTF_8)); - - final Thread readOutputThread = new Thread(this::readClientOut, outThreadHame); - readOutputThread.start(); - final Thread readErrorThread = new Thread(this::readClientErr, errThreadHame); - readErrorThread.start(); + @Override + public void close() { + try { + lock.lock(); + if (process != null) { + process.destroy(); + try { + process.waitFor(); + } catch (InterruptedException e) { + throw new RuntimeException(e); + } + } + stdoutConsumer.cancel(true); + stderrConsumer.cancel(true); + executorService.shutdownNow(); + } finally { + lock.unlock(); + } } - private String[] getAppClientCommand() throws Exception { - ArrayList cmd = new ArrayList<>(); + private List getAppClientCommand() { + final List cmd = new ArrayList<>(); final String archivePath = config.getClientAppEar(); final String clientArchiveName = config.getClientArchiveName(); - String jbossHome = config.getJbossHome(); + final String jbossHome = config.getJbossHome(); if (jbossHome == null) - throw new Exception("jbossHome config property is not set."); - if (!new File(jbossHome).exists()) - throw new Exception("AS dir from config jbossHome doesn't exist: " + jbossHome); + throw new IllegalArgumentException("jbossHome config property is not set."); + if (!ServerHelper.isValidHomeDirectory(jbossHome)) + throw new IllegalArgumentException("Server directory from config jbossHome doesn't exist: " + jbossHome); - String archiveArg = String.format("%s#%s", archivePath, clientArchiveName); + final String archiveArg = String.format("%s#%s", archivePath, clientArchiveName); - String client = config.getAppClientShForOS(); - String clientSh = String.format("%s%cbin%c%s", jbossHome, File.separatorChar, File.separatorChar, client); - cmd.add(clientSh); + final String client = config.resolveAppClientCommand(); + final Path clientExe = Path.of(jbossHome, "bin", client); + if (Files.notExists(clientExe)) { + throw new IllegalArgumentException("Could not find appclient executable " + clientExe); + } + cmd.add(clientExe.toString()); cmd.add(archiveArg); if (config.getClientArguments() != null) { cmd.addAll(ParameterUtils.splitParams(config.getClientArguments())); } log.info("AppClient cmd: " + cmd); - String[] cmdLine = new String[cmd.size()]; - cmd.toArray(cmdLine); - return cmdLine; + return cmd; } - private void readClientOut() { - if (outputReader == null) - return; + private class LogConsumer implements Runnable { + private final BlockingQueue queue; + private final InputStreamReader reader; + private final Logger.Level level; + private final long pid; - readClientProcess(outputReader, false); - synchronized (this) { - outputReader = null; + private LogConsumer(final BlockingQueue queue, final InputStream in, final Logger.Level level, final long pid) { + this.queue = queue; + this.reader = new InputStreamReader(in, StandardCharsets.UTF_8); + this.level = level; + this.pid = pid; } - } - - private void readClientErr() { - if (errorReader == null) - return; - readClientProcess(errorReader, true); - synchronized (this) { - errorReader = null; - } - } - /** - * Loop - */ - private void readClientProcess(BufferedReader reader, boolean errReader) { - try { - String line = reader.readLine(); - while (line != null) { - if (errReader) - errorLineReceived(line); - else - outputLineReceived(line); - line = reader.readLine(); + @Override + public void run() { + final StringBuilder buffer = new StringBuilder(); + final char[] inBuffer = new char[256]; + int len; + try { + while ((len = reader.read(inBuffer)) != -1) { + int mark = 0; + int i; + for (i = 0; i < len; i++) { + final char c = inBuffer[i]; + if (c == '\n') { + buffer.append(inBuffer, mark, i - mark); + log.log(level, buffer.toString()); + if (queue != null) { + queue.add(buffer.toString()); + } + buffer.setLength(0); + mark = i + 1; + } + } + buffer.append(inBuffer, mark, i - mark); + } + // If we're here, we should log the buffer if it's not empty + if (buffer.length() > 0) { + log.log(level, buffer.toString()); + if (queue != null) { + queue.add(buffer.toString()); + } + } + } catch (IOException e) { + if (buffer.length() > 0) { + log.errorf(e, "Failed to consume output from %s: %s", pid, buffer.toString()); + buffer.setLength(0); + } else { + log.errorf(e, "Failed to consume output from %s", pid); + } } - } catch (Exception e) { } } - private synchronized void outputLineReceived(String line) { - log.info("[" + outThreadHame + "] " + line); - outputQueue.add(line); - } - - private synchronized void errorLineReceived(String line) { - log.info("[" + errThreadHame + "] " + line); - } - } diff --git a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedContainerConfiguration.java b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedContainerConfiguration.java index 29352cd9..45cf3d0b 100644 --- a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedContainerConfiguration.java +++ b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedContainerConfiguration.java @@ -15,6 +15,8 @@ */ package org.jboss.as.arquillian.container.managed; +import java.nio.file.Files; +import java.nio.file.Path; import java.util.Locale; import java.util.Map; @@ -51,8 +53,7 @@ public class ManagedContainerConfiguration extends DistributionContainerConfigur // Application client container specific settings private String clientAppEar; private String clientArchiveName; - private String appClientSh = "appclient.sh"; - private boolean appClientShSet; + private String appClientCommand; private boolean runClient = true; private Map clientEnv = System.getenv(); @@ -70,6 +71,18 @@ public void validate() throws ConfigurationException { throw new ConfigurationException(String.format("Cannot define both a serverConfig and a readOnlyServerConfig: " + "serverConfig=%s - readOnlyServerConfig=%s", serverConfig, readOnlyServerConfig)); } + // Validate the server has been provisioned with the application client available if we're using the + // application client. + if (clientAppEar != null) { + if (getJbossHome() == null) { + throw new ConfigurationException("The jbossHome is required to be set if the clientAppEar is set."); + } + final String client = resolveAppClientCommand(); + final Path clientExe = Path.of(getJbossHome(), "bin", client); + if (Files.notExists(clientExe)) { + throw new ConfigurationException("Could not find appclient executable " + clientExe); + } + } } public String getJavaVmArguments() { @@ -198,13 +211,12 @@ public void setClientEnv(Map clientEnv) { this.clientEnv = clientEnv; } - public String getAppClientSh() { - return appClientSh; + public String getAppClientCommand() { + return appClientCommand; } - public void setAppClientSh(String appClientSh) { - this.appClientShSet = true; - this.appClientSh = appClientSh; + public void setAppClientCommand(String appClientCommand) { + this.appClientCommand = appClientCommand; } public boolean isRunClient() { @@ -216,43 +228,20 @@ public void setRunClient(boolean runClient) { } /** - * Get the appClientSh approriate for the current OS unless it was externally set + * Resolves the application client command appropriate for the current OS unless it was explicitly set. * - * @return appclient shell script default base on current OS + * @return application client shell script, default based on current OS */ - public String getAppClientShForOS() { - String clientSh = appClientSh; - if (appClientShSet) { - return clientSh; + public String resolveAppClientCommand() { + if (appClientCommand != null) { + return appClientCommand; } - OSType type = getOperatingSystemType(); - switch (type) { - case Linux: - case MacOS: - clientSh = "appclient.sh"; - break; - case Windows: - clientSh = "appclient.bat"; - break; - } - return clientSh; - } - - enum OSType { - Windows, - MacOS, - Linux - }; - - private static OSType getOperatingSystemType() { - OSType detectedOS = OSType.Linux; - String OS = System.getProperty("os.name", "generic").toLowerCase(Locale.ENGLISH); - if ((OS.indexOf("mac") >= 0) || (OS.indexOf("darwin") >= 0)) { - detectedOS = OSType.MacOS; - } else if (OS.indexOf("win") >= 0) { - detectedOS = OSType.Windows; - } - return detectedOS; + return isWindows() ? "appclient.bat" : "appclient.sh"; + } + + private static boolean isWindows() { + final String os = System.getProperty("os.name", "generic").toLowerCase(Locale.ROOT); + return os.contains("windows"); } } diff --git a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedContainerExtension.java b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedContainerExtension.java index 1677ed05..b49fe887 100644 --- a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedContainerExtension.java +++ b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedContainerExtension.java @@ -16,7 +16,7 @@ package org.jboss.as.arquillian.container.managed; import org.jboss.arquillian.container.spi.client.container.DeployableContainer; -import org.jboss.arquillian.test.spi.TestEnricher; +import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; import org.jboss.as.arquillian.container.CommonContainerExtension; /** @@ -32,6 +32,6 @@ public class ManagedContainerExtension extends CommonContainerExtension { public void register(ExtensionBuilder builder) { super.register(builder); builder.service(DeployableContainer.class, ManagedDeployableContainer.class); - builder.service(TestEnricher.class, AppClientTestEnricher.class); + builder.service(ResourceProvider.class, AppClientProvider.class); } } diff --git a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedDeployableContainer.java b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedDeployableContainer.java index 402f3a77..e9319f5b 100644 --- a/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedDeployableContainer.java +++ b/container-managed/src/main/java/org/jboss/as/arquillian/container/managed/ManagedDeployableContainer.java @@ -29,6 +29,9 @@ import java.util.regex.Pattern; import org.jboss.arquillian.container.spi.client.container.LifecycleException; +import org.jboss.arquillian.container.spi.context.annotation.ContainerScoped; +import org.jboss.arquillian.core.api.InstanceProducer; +import org.jboss.arquillian.core.api.annotation.Inject; import org.jboss.as.arquillian.container.CommonManagedDeployableContainer; import org.jboss.as.arquillian.container.ParameterUtils; import org.jboss.logging.Logger; @@ -51,6 +54,10 @@ public final class ManagedDeployableContainer extends CommonManagedDeployableCon static final String DATA_DIR = "data"; private final Logger log = Logger.getLogger(ManagedDeployableContainer.class); + + @Inject + @ContainerScoped + private InstanceProducer appClientWrapperProducer; private AppClientWrapper appClient; @Override @@ -58,10 +65,6 @@ public Class getConfigurationClass() { return ManagedContainerConfiguration.class; } - public AppClientWrapper getAppClient() { - return appClient; - } - @Override protected CommandBuilder createCommandBuilder(ManagedContainerConfiguration config) { final StandaloneCommandBuilder commandBuilder = StandaloneCommandBuilder.of(config.getJbossHome()); @@ -74,7 +77,7 @@ protected CommandBuilder createCommandBuilder(ManagedContainerConfiguration conf @SuppressWarnings("deprecation") String bundlesPath = config.getBundlePath(); if (bundlesPath != null && !bundlesPath.isEmpty()) { - log.warn("Bundles path is deprecated and no longer used."); + getLogger().warn("Bundles path is deprecated and no longer used."); } final String javaOpts = config.getJavaVmArguments(); @@ -131,25 +134,28 @@ protected CommandBuilder createCommandBuilder(ManagedContainerConfiguration conf return commandBuilder; } + @Override + public void setup(final ManagedContainerConfiguration config) { + super.setup(config); + if (config.getClientAppEar() != null) { + appClient = new AppClientWrapper(config, getLogger()); + appClientWrapperProducer.set(appClient); + } + } + protected void startInternal() throws LifecycleException { // Run the managed container startup super.startInternal(); - // If there is an appClientEar specified, setup the appClient - ManagedContainerConfiguration config = getContainerConfiguration(); - if (config.getClientAppEar() != null) { - appClient = new AppClientWrapper(config, log); + // If there is an appClientEar specified, run the app client + if (appClient != null) { try { // Launch the client container if the config says to if (getContainerConfiguration().isRunClient()) { appClient.run(); } } catch (Exception e) { - if (e instanceof LifecycleException) - throw (LifecycleException) e; - LifecycleException le = new LifecycleException(e.getMessage()); - le.initCause(e); - throw le; + throw new LifecycleException(e.getMessage(), e); } } } @@ -159,13 +165,11 @@ protected void stopInternal(Integer timeout) throws LifecycleException { super.stopInternal(timeout); try { if (appClient != null) { - appClient.quit(); + appClient.close(); appClient = null; } } catch (Exception e) { - LifecycleException le = new LifecycleException(e.getMessage()); - le.initCause(e); - throw le; + throw new LifecycleException(e.getMessage(), e); } } diff --git a/container-managed/src/test/java/org/jboss/as/arquillian/container/app/AppClient2TestCase.java b/container-managed/src/test/java/org/jboss/as/arquillian/container/app/AppClient2TestCase.java index 1c5da798..840488d2 100644 --- a/container-managed/src/test/java/org/jboss/as/arquillian/container/app/AppClient2TestCase.java +++ b/container-managed/src/test/java/org/jboss/as/arquillian/container/app/AppClient2TestCase.java @@ -16,14 +16,14 @@ package org.jboss.as.arquillian.container.app; import java.io.File; +import java.util.List; import java.util.concurrent.TimeUnit; -import jakarta.inject.Inject; - import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.container.test.api.TargetsContainer; import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.as.arquillian.container.managed.AppClientWrapper; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; @@ -36,12 +36,14 @@ /** * Test deployment of an application client ear and application using the extended managed container. - * This version only automatically starts the server and deploys the test EAR. The {@link #testAppClientRun(AppClientWrapper) } - * method explictly starts the application client with the injected AppClientWrapper and then validates its + * This version only automatically starts the server and deploys the test EAR. The {@link #testAppClientRun()} + * (AppClientWrapper) } + * method explicitly starts the application client with the injected AppClientWrapper and then validates its * output. - * + *

* To run in an IDE, set the -Darquillian.xml=appclient-arqullian.xml -Darqullian.launch=jboss-manual-client * properties the test VM arguments + *

*/ @RunWith(Arquillian.class) public class AppClient2TestCase { @@ -52,9 +54,9 @@ public class AppClient2TestCase { @TargetsContainer("jboss-manual-client") @Deployment(testable = false, name = "jboss-manual-client") public static EnterpriseArchive createDeployment() throws Exception { - final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class, "appClient" + ".ear"); + final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class, "appClient.ear"); - JavaArchive ejbJar = ShrinkWrap.create(JavaArchive.class, "myejb.jar") + final JavaArchive ejbJar = ShrinkWrap.create(JavaArchive.class, "myejb.jar") .addClasses(EjbBean.class, EjbBusiness.class); ear.addAsModule(ejbJar); @@ -63,20 +65,15 @@ public static EnterpriseArchive createDeployment() throws Exception { appClient.addAsManifestResource(new StringAsset("Main-Class: " + AppClientMain.class.getName() + "\n"), "MANIFEST.MF"); ear.addAsModule(appClient); - File archiveOnDisk = new File("target" + File.separator + ear.getName()); - if (archiveOnDisk.exists()) { - archiveOnDisk.delete(); - } + final File archiveOnDisk = new File("target" + File.separator + ear.getName()); final ZipExporter exporter = ear.as(ZipExporter.class); - exporter.exportTo(archiveOnDisk); - String archivePath = archiveOnDisk.getAbsolutePath(); - System.out.printf("archivePath: %s\n", archivePath); + exporter.exportTo(archiveOnDisk, true); return ear; } - @Inject - AppClientWrapper appClient; + @ArquillianResource + private AppClientWrapper appClient; /** * Launch the EE Application client container using the same EAR to validate access to the deployed EJB @@ -91,8 +88,8 @@ public void testAppClientRun() throws Exception { appClient.waitForExit(10, TimeUnit.SECONDS); System.out.println("AppClient exited"); - String[] output = appClient.readAll(1000); - System.out.printf("AppClient readAll returned %d lines\n", output.length); + List output = appClient.readAll(1000); + System.out.printf("AppClient readAll returned %d lines%n", output.size()); boolean sawStart = false, sawEnd = false, sawResult = false, sawSuccess = false, sawFailed = false; for (String line : output) { System.out.println(line); @@ -109,7 +106,7 @@ public void testAppClientRun() throws Exception { } } // Cleanup the app client - appClient.quit(); + appClient.close(); Assert.assertTrue("AppClientMain.begin was seen", sawStart); Assert.assertTrue("AppClientMain.end was seen", sawEnd); diff --git a/container-managed/src/test/java/org/jboss/as/arquillian/container/app/AppClientTestCase.java b/container-managed/src/test/java/org/jboss/as/arquillian/container/app/AppClientTestCase.java index 3e838e82..1a7e522a 100644 --- a/container-managed/src/test/java/org/jboss/as/arquillian/container/app/AppClientTestCase.java +++ b/container-managed/src/test/java/org/jboss/as/arquillian/container/app/AppClientTestCase.java @@ -16,11 +16,13 @@ package org.jboss.as.arquillian.container.app; import java.io.File; +import java.util.List; import org.jboss.arquillian.container.test.api.Deployment; import org.jboss.arquillian.container.test.api.RunAsClient; import org.jboss.arquillian.container.test.api.TargetsContainer; import org.jboss.arquillian.junit.Arquillian; +import org.jboss.arquillian.test.api.ArquillianResource; import org.jboss.as.arquillian.container.managed.AppClientWrapper; import org.jboss.shrinkwrap.api.ShrinkWrap; import org.jboss.shrinkwrap.api.asset.StringAsset; @@ -35,8 +37,9 @@ * Test deployment of an application client ear and application using the extended managed container. * This version automatically starts the application client container after starting the server and deploying * the test EAR. - * + *

* To run in an IDE, set the -Darquillian.xml=appclient-arqullian.xml property the test VM arguments + *

*/ @RunWith(Arquillian.class) public class AppClientTestCase { @@ -49,7 +52,7 @@ public class AppClientTestCase { public static EnterpriseArchive createDeployment() throws Exception { final EnterpriseArchive ear = ShrinkWrap.create(EnterpriseArchive.class, "appClient" + ".ear"); - JavaArchive ejbJar = ShrinkWrap.create(JavaArchive.class, "myejb.jar") + final JavaArchive ejbJar = ShrinkWrap.create(JavaArchive.class, "myejb.jar") .addClasses(EjbBean.class, EjbBusiness.class); ear.addAsModule(ejbJar); @@ -58,28 +61,21 @@ public static EnterpriseArchive createDeployment() throws Exception { appClient.addAsManifestResource(new StringAsset("Main-Class: " + AppClientMain.class.getName() + "\n"), "MANIFEST.MF"); ear.addAsModule(appClient); - File archiveOnDisk = new File("target" + File.separator + ear.getName()); - if (archiveOnDisk.exists()) { - archiveOnDisk.delete(); - } + final File archiveOnDisk = new File("target" + File.separator + ear.getName()); final ZipExporter exporter = ear.as(ZipExporter.class); - exporter.exportTo(archiveOnDisk); - String archivePath = archiveOnDisk.getAbsolutePath(); - System.out.printf("archivePath: %s\n", archivePath); - + exporter.exportTo(archiveOnDisk, true); return ear; } /** * Test using JBossModulesCommandBuilder * - * @throws Exception */ @Test @RunAsClient - public void testAppClientRunViaArq(AppClientWrapper appClient) throws Exception { - String[] output = appClient.readAll(1000); - System.out.printf("AppClient readAll returned %d lines\n", output.length); + public void testAppClientRunViaArq(@ArquillianResource AppClientWrapper appClient) { + final List output = appClient.readAll(1000); + System.out.printf("AppClient readAll returned %d lines%n", output.size()); boolean sawStart = false, sawEnd = false, sawResult = false, sawSuccess = false, sawFailed = false; for (String line : output) { System.out.println(line);