http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/WARArquillianTest.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/WARArquillianTest.java b/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/WARArquillianTest.java deleted file mode 100644 index dccd95e..0000000 --- a/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/WARArquillianTest.java +++ /dev/null @@ -1,57 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb; - -import org.apache.openejb.loader.SystemInstance; -import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.arquillian.junit.Arquillian; -import org.jboss.shrinkwrap.api.ArchivePaths; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.EmptyAsset; -import org.jboss.shrinkwrap.api.spec.WebArchive; -import org.junit.Test; -import org.junit.runner.RunWith; - -import javax.inject.Inject; - -import static org.junit.Assert.assertNotNull; -import static org.junit.Assert.assertTrue; - -@RunWith(Arquillian.class) -public class WARArquillianTest { - @Inject - private ABean bean; - - @Deployment - public static WebArchive archive() { - return ShrinkWrap.create(WebArchive.class, WARArquillianTest.class.getSimpleName().concat(".war")) - .addClasses(ABean.class) - .addAsWebInfResource(EmptyAsset.INSTANCE, ArchivePaths.create("classes/META-INF/beans.xml")); - } - - @Test - public void checkItIsStarted() { - assertTrue(SystemInstance.isInitialized()); - } - - @Test - public void checkInjections() { - assertNotNull(bean); - } - - public static class ABean {} -}
http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/archive/SimpleArchive.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/archive/SimpleArchive.java b/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/archive/SimpleArchive.java deleted file mode 100644 index c20984b..0000000 --- a/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/archive/SimpleArchive.java +++ /dev/null @@ -1,50 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb.archive; - -import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.EmptyAsset; -import org.jboss.shrinkwrap.api.spec.WebArchive; - -import javax.annotation.PostConstruct; -import javax.ejb.Singleton; -import javax.ejb.Startup; - -public final class SimpleArchive { - private SimpleArchive() { - // no-op - } - - @Deployment - public static WebArchive war() { - return ShrinkWrap.create(WebArchive.class, "start.war") - .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") - .addClass(ASingleton.class); - } - - @Startup - @Singleton - public static class ASingleton { - public static boolean ok = false; - - @PostConstruct - public void init() { - ok = true; - } - } -} http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/archive/SimpleArchive2.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/archive/SimpleArchive2.java b/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/archive/SimpleArchive2.java deleted file mode 100644 index 3a99937..0000000 --- a/arquillian/arquillian-openejb-embedded-5/src/test/java/org/apache/openejb/arquillian/openejb/archive/SimpleArchive2.java +++ /dev/null @@ -1,40 +0,0 @@ -/** - * Licensed to the Apache Software Foundation (ASF) under one or more - * contributor license agreements. See the NOTICE file distributed with - * this work for additional information regarding copyright ownership. - * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb.archive; - -import org.jboss.arquillian.container.test.api.Deployment; -import org.jboss.shrinkwrap.api.ShrinkWrap; -import org.jboss.shrinkwrap.api.asset.EmptyAsset; -import org.jboss.shrinkwrap.api.spec.WebArchive; - -import javax.ejb.Singleton; - -public final class SimpleArchive2 { - private SimpleArchive2() { - // no-op - } - - @Deployment - public static WebArchive war() { - return ShrinkWrap.create(WebArchive.class, "start2.war") - .addAsWebInfResource(EmptyAsset.INSTANCE, "beans.xml") - .addClass(AnotherSingleton.class); - } - - @Singleton - public static class AnotherSingleton {} -} http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded-5/src/test/resources/a-pom.xml ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded-5/src/test/resources/a-pom.xml b/arquillian/arquillian-openejb-embedded-5/src/test/resources/a-pom.xml deleted file mode 100644 index bfdfea2..0000000 --- a/arquillian/arquillian-openejb-embedded-5/src/test/resources/a-pom.xml +++ /dev/null @@ -1,35 +0,0 @@ -<?xml version="1.0" encoding="UTF-8"?> -<!-- - - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You 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. ---> -<project xmlns="http://maven.apache.org/POM/4.0.0" - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> - <modelVersion>4.0.0</modelVersion> - - <groupId>fake</groupId> - <version>fake</version> - <artifactId>fake</artifactId> - - <dependencies> - <dependency> <!-- whatever it is, just to check we can use it --> - <groupId>info.cukes</groupId> - <artifactId>gherkin</artifactId> - <version>2.11.0</version> - </dependency> - </dependencies> -</project> http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded-5/src/test/resources/arquillian.xml ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded-5/src/test/resources/arquillian.xml b/arquillian/arquillian-openejb-embedded-5/src/test/resources/arquillian.xml deleted file mode 100644 index 463d72c..0000000 --- a/arquillian/arquillian-openejb-embedded-5/src/test/resources/arquillian.xml +++ /dev/null @@ -1,37 +0,0 @@ -<?xml version="1.0" encoding="UTF-8" standalone="yes"?> -<!-- - - Licensed to the Apache Software Foundation (ASF) under one or more - contributor license agreements. See the NOTICE file distributed with - this work for additional information regarding copyright ownership. - The ASF licenses this file to You 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. ---> -<arquillian - xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" - xsi:schemaLocation="http://jboss.org/schema/arquillian http://jboss.org/schema/arquillian/arquillian_1_0.xsd"> - <container qualifier="openejb" default="true"> - <configuration> - <property name="properties"> - # used to not have a single DataSource and be able to test the resource resolution - db1 = new://Resource?type=DataSource - db1.JdbcUrl = jdbc:hsqldb:mem:db1 - - java\:global/db = new://Resource?type=DataSource - java\:global/db.JdbcUrl = jdbc:hsqldb:mem:global - - # to check startup deployment - openejb.arquillian.predeploy-archives = org.apache.openejb.arquillian.openejb.archive.[SimpleArchive|SimpleArchive2] - </property> - </configuration> - </container> -</arquillian> http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded/pom.xml ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded/pom.xml b/arquillian/arquillian-openejb-embedded/pom.xml new file mode 100644 index 0000000..1cc2da7 --- /dev/null +++ b/arquillian/arquillian-openejb-embedded/pom.xml @@ -0,0 +1,175 @@ +<?xml version="1.0" encoding="UTF-8"?> +<!-- + + Licensed to the Apache Software Foundation (ASF) under one or more + contributor license agreements. See the NOTICE file distributed with + this work for additional information regarding copyright ownership. + The ASF licenses this file to You 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. +--> +<project xmlns="http://maven.apache.org/POM/4.0.0" + xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" + xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"> + <parent> + <artifactId>arquillian</artifactId> + <groupId>org.apache.tomee</groupId> + <version>7.0.0-SNAPSHOT</version> + </parent> + <modelVersion>4.0.0</modelVersion> + + <artifactId>arquillian-openejb-embedded</artifactId> + <name>OpenEJB :: Arquillian Adaptors Parent :: OpenEJB Container</name> + <version>7.0.0-SNAPSHOT</version> + + <dependencies> + <dependency> + <groupId>org.apache.tomee</groupId> + <artifactId>openejb-core</artifactId> + <version>${openejb.version}</version> + </dependency> + + <dependency> + <groupId>org.jboss.arquillian.container</groupId> + <artifactId>arquillian-container-spi</artifactId> + <version>${version.arquillian}</version> + <exclusions> + <exclusion> + <artifactId>shrinkwrap-descriptors-spi</artifactId> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + </exclusion> + <exclusion> + <artifactId>shrinkwrap-descriptors-api-base</artifactId> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + <artifactId>shrinkwrap-descriptors-spi</artifactId> + <version>${version.shrinkwrap.descriptor}</version> + </dependency> + + <dependency> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + <artifactId>shrinkwrap-descriptors-api-base</artifactId> + <version>${version.shrinkwrap.descriptor}</version> + </dependency> + + <dependency> + <groupId>org.jboss.arquillian.container</groupId> + <artifactId>arquillian-container-test-spi</artifactId> + <version>${version.arquillian}</version> + </dependency> + + <dependency> + <groupId>org.jboss.arquillian.junit</groupId> + <artifactId>arquillian-junit-container</artifactId> + <version>${version.arquillian}</version> + <exclusions> + <exclusion> + <artifactId>shrinkwrap-descriptors-spi</artifactId> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + <artifactId>shrinkwrap-descriptors-impl-javaee</artifactId> + <version>${version.shrinkwrap.descriptor}</version> + </dependency> + <dependency> + <groupId>org.jboss.shrinkwrap.descriptors</groupId> + <artifactId>shrinkwrap-descriptors-api-javaee</artifactId> + <version>${version.shrinkwrap.descriptor}</version> + </dependency> + <dependency> + <groupId>org.jboss.shrinkwrap</groupId> + <artifactId>shrinkwrap-api</artifactId> + <version>${version.shrinkwrap.shrinkwrap}</version> + </dependency> + <dependency> + <groupId>org.jboss.shrinkwrap</groupId> + <artifactId>shrinkwrap-impl-base</artifactId> + <version>${version.shrinkwrap.shrinkwrap}</version> + </dependency> + + <dependency> + <groupId>junit</groupId> + <artifactId>junit</artifactId> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.jboss.shrinkwrap.resolver</groupId> + <artifactId>shrinkwrap-resolver-impl-maven</artifactId> + <version>2.0.0</version> + <scope>test</scope> + </dependency> + + <dependency> + <groupId>org.apache.tomee</groupId> + <artifactId>arquillian-common</artifactId> + <version>${tomee.version}</version> + </dependency> + + <dependency> + <groupId>org.apache.tomee</groupId> + <artifactId>arquillian-openejb-transaction-provider</artifactId> + <version>${tomee.version}</version> + </dependency> + + <dependency> + <groupId>org.mockito</groupId> + <artifactId>mockito-core</artifactId> + <version>1.9.5</version> + <scope>test</scope> + <exclusions> + <exclusion> + <artifactId>hamcrest-core</artifactId> + <groupId>org.hamcrest</groupId> + </exclusion> + </exclusions> + </dependency> + + <dependency> + <groupId>info.cukes</groupId> + <artifactId>cucumber-core</artifactId> + <version>1.1.8</version> + <scope>provided</scope> + </dependency> + + <dependency> <!-- shouldn't be a compile/runtime dependency --> + <groupId>org.apache.tomee</groupId> + <artifactId>openejb-http</artifactId> + <version>${openejb.version}</version> + <scope>provided</scope> + </dependency> + + <dependency> + <groupId>org.apache.tomee</groupId> + <artifactId>openejb-cxf-rs</artifactId> + <version>${openejb.version}</version> + <scope>test</scope> + </dependency> + </dependencies> + + <build> + <plugins> + <plugin> + <groupId>org.apache.maven.plugins</groupId> + <artifactId>maven-surefire-plugin</artifactId> + </plugin> + </plugins> + </build> +</project> http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/Closeables.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/Closeables.java b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/Closeables.java new file mode 100644 index 0000000..5d093da --- /dev/null +++ b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/Closeables.java @@ -0,0 +1,38 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb; + +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; +import java.util.LinkedList; + +public class Closeables implements Closeable { + private final Collection<Closeable> closeables = new LinkedList<>(); + + public synchronized void add(final Closeable closeable) { + closeables.add(closeable); + } + + @Override + public synchronized void close() throws IOException { + for (final Closeable c : closeables) { + c.close(); + } + closeables.clear(); + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBArchiveProcessor.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBArchiveProcessor.java b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBArchiveProcessor.java new file mode 100644 index 0000000..1a75a69 --- /dev/null +++ b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBArchiveProcessor.java @@ -0,0 +1,620 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb; + +import org.apache.openejb.ClassLoaderUtil; +import org.apache.openejb.OpenEJBException; +import org.apache.openejb.OpenEJBRuntimeException; +import org.apache.openejb.cdi.CompositeBeans; +import org.apache.openejb.config.AppModule; +import org.apache.openejb.config.DeploymentLoader; +import org.apache.openejb.config.EjbModule; +import org.apache.openejb.config.FinderFactory; +import org.apache.openejb.config.ReadDescriptors; +import org.apache.openejb.config.WebModule; +import org.apache.openejb.config.WebappAggregatedArchive; +import org.apache.openejb.jee.Beans; +import org.apache.openejb.jee.EjbJar; +import org.apache.openejb.jee.ManagedBean; +import org.apache.openejb.jee.TransactionType; +import org.apache.openejb.jee.WebApp; +import org.apache.openejb.jee.WebApp$JAXB; +import org.apache.openejb.jee.oejb3.EjbDeployment; +import org.apache.openejb.jee.oejb3.OpenejbJar; +import org.apache.openejb.loader.IO; +import org.apache.openejb.sxc.Sxc; +import org.apache.openejb.util.classloader.URLClassLoaderFirst; +import org.apache.xbean.finder.archive.ClassesArchive; +import org.apache.xbean.finder.archive.CompositeArchive; +import org.apache.xbean.finder.archive.FilteredArchive; +import org.apache.xbean.finder.archive.JarArchive; +import org.jboss.arquillian.test.spi.TestClass; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.api.ArchivePath; +import org.jboss.shrinkwrap.api.Node; +import org.jboss.shrinkwrap.api.asset.ArchiveAsset; +import org.jboss.shrinkwrap.api.asset.Asset; +import org.jboss.shrinkwrap.api.asset.ClassLoaderAsset; +import org.jboss.shrinkwrap.api.asset.FileAsset; +import org.jboss.shrinkwrap.api.asset.UrlAsset; +import org.jboss.shrinkwrap.api.spec.EnterpriseArchive; +import org.jboss.shrinkwrap.api.spec.WebArchive; +import org.jboss.shrinkwrap.impl.base.filter.IncludeRegExpPaths; + +import java.io.File; +import java.io.IOException; +import java.io.InputStream; +import java.net.MalformedURLException; +import java.net.URL; +import java.net.URLClassLoader; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.LinkedList; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.logging.Level; +import java.util.logging.Logger; + +import static java.util.Arrays.asList; +import static org.apache.openejb.arquillian.openejb.reflection.Assets.EMPTY_LOADER; +import static org.apache.openejb.arquillian.openejb.reflection.Assets.get; + +// doesn't implement ApplicationArchiveProcessor anymore since in some cases +// (with some observers set for instance) +// it is not called before the deployment itself +// so it is like if it was skipped +public class OpenEJBArchiveProcessor { + private static final Logger LOGGER = Logger.getLogger(OpenEJBArchiveProcessor.class.getName()); + + private static final String META_INF = "META-INF/"; + private static final String WEB_INF = "WEB-INF/"; + private static final String EJB_JAR_XML = "ejb-jar.xml"; + + private static final String BEANS_XML = "beans.xml"; + private static final String VALIDATION_XML = "validation.xml"; + private static final String RESOURCES_XML = "resources.xml"; + private static final String PERSISTENCE_XML = "persistence.xml"; + private static final String OPENEJB_JAR_XML = "openejb-jar.xml"; + private static final String ENV_ENTRIES_PROPERTIES = "env-entries.properties"; + private static final String WEB_INF_CLASSES = "/WEB-INF/classes/"; + + public static AppModule createModule(final Archive<?> archive, final TestClass testClass, final Closeables closeables) { + final Class<?> javaClass; + if (testClass != null) { + javaClass = testClass.getJavaClass(); + } else { + javaClass = null; + } + + final ClassLoader parent; + if (javaClass == null) { + parent = Thread.currentThread().getContextClassLoader(); + } else { + parent = javaClass.getClassLoader(); + } + + final List<URL> additionalPaths = new ArrayList<>(); + CompositeArchive scannedArchive = null; + final Map<URL, List<String>> earMap = new HashMap<>(); + final Map<String, Object> altDD = new HashMap<>(); + final List<Archive> earLibsArchives = new ArrayList<>(); + final CompositeBeans earBeans = new CompositeBeans(); + + final boolean isEar = EnterpriseArchive.class.isInstance(archive); + final boolean isWebApp = WebArchive.class.isInstance(archive); + final String prefix = isWebApp ? WEB_INF : META_INF; + if (isEar || isWebApp) { + final Map<ArchivePath, Node> jars = archive.getContent(new IncludeRegExpPaths(isEar ? "/.*\\.jar" : "/WEB-INF/lib/.*\\.jar")); + scannedArchive = analyzeLibs(parent, additionalPaths, earMap, earLibsArchives, earBeans, jars, altDD); + } + + final URL[] urls = additionalPaths.toArray(new URL[additionalPaths.size()]); + + final URLClassLoaderFirst swParent = new URLClassLoaderFirst(urls, parent); + closeables.add(swParent); + + if (!isEar) { + earLibsArchives.add(archive); + } + final SWClassLoader loader = new SWClassLoader(swParent, earLibsArchives.toArray(new Archive<?>[earLibsArchives.size()])); + closeables.add(loader); + final URLClassLoader tempClassLoader = ClassLoaderUtil.createTempClassLoader(loader); + closeables.add(tempClassLoader); + + final AppModule appModule = new AppModule(loader, archive.getName()); + if (WEB_INF.equals(prefix)) { + appModule.setDelegateFirst(false); + appModule.setStandloneWebModule(); + + final WebModule webModule = new WebModule(createWebApp(archive), contextRoot(archive.getName()), loader, "", appModule.getModuleId()); + webModule.setUrls(additionalPaths); + appModule.getWebModules().add(webModule); + } else if (isEar) { // mainly for CDI TCKs + final FinderFactory.OpenEJBAnnotationFinder earLibFinder = new FinderFactory.OpenEJBAnnotationFinder(new SimpleWebappAggregatedArchive(tempClassLoader, scannedArchive, earMap)); + appModule.setEarLibFinder(earLibFinder); + + final EjbModule earCdiModule = new EjbModule(appModule.getClassLoader(), DeploymentLoader.EAR_SCOPED_CDI_BEANS + appModule.getModuleId(), new EjbJar(), new OpenejbJar()); + earCdiModule.setBeans(earBeans); + earCdiModule.setFinder(earLibFinder); + + final EjbJar ejbJar; + final AssetSource ejbJarXml = AssetSource.class.isInstance(altDD.get(EJB_JAR_XML)) ? AssetSource.class.cast(altDD.get(EJB_JAR_XML)) : null; + if (ejbJarXml != null) { + try { + ejbJar = ReadDescriptors.readEjbJar(ejbJarXml.get()); + } catch (final Exception e) { + throw new OpenEJBRuntimeException(e); + } + } else { + ejbJar = new EjbJar(); + } + + earCdiModule.setEjbJar(ejbJar); // EmptyEjbJar would prevent to add scanned EJBs but this is *here* an aggregator so we need to be able to do so + appModule.getEjbModules().add(earCdiModule); + + for (final Map.Entry<ArchivePath, Node> node : archive.getContent(new IncludeRegExpPaths("/.*\\.war")).entrySet()) { + final Asset asset = node.getValue().getAsset(); + if (ArchiveAsset.class.isInstance(asset)) { + final Archive<?> webArchive = ArchiveAsset.class.cast(asset).getArchive(); + if (WebArchive.class.isInstance(webArchive)) { + final Map<String, Object> webAltDD = new HashMap<>(); + final Node beansXml = findBeansXml(webArchive, WEB_INF); + + final List<URL> webappAdditionalPaths = new LinkedList<>(); + final CompositeBeans webAppBeansXml = new CompositeBeans(); + final List<Archive> webAppArchives = new LinkedList<Archive>(); + final Map<URL, List<String>> webAppClassesByUrl = new HashMap<URL, List<String>>(); + final CompositeArchive webAppArchive = analyzeLibs(parent, + webappAdditionalPaths, webAppClassesByUrl, + webAppArchives, webAppBeansXml, + webArchive.getContent(new IncludeRegExpPaths("/WEB-INF/lib/.*\\.jar")), + webAltDD); + + webAppArchives.add(webArchive); + final SWClassLoader webLoader = new SWClassLoader(parent, webAppArchives.toArray(new Archive<?>[webAppArchives.size()])); + closeables.add(webLoader); + + final FinderFactory.OpenEJBAnnotationFinder finder = new FinderFactory.OpenEJBAnnotationFinder( + finderArchive(beansXml, webArchive, webLoader, webAppArchive, webAppClassesByUrl, webAppBeansXml)); + + final String contextRoot = contextRoot(webArchive.getName()); + final WebModule webModule = new WebModule(createWebApp(webArchive), contextRoot, webLoader, "", appModule.getModuleId() + "_" + contextRoot); + webModule.setUrls(Collections.<URL>emptyList()); + webModule.setScannableUrls(Collections.<URL>emptyList()); + webModule.setFinder(finder); + + final EjbJar webEjbJar = createEjbJar(prefix, webArchive); + + final EjbModule ejbModule = new EjbModule(webLoader, webModule.getModuleId(), null, webEjbJar, new OpenejbJar()); + ejbModule.setBeans(webAppBeansXml); + ejbModule.getAltDDs().putAll(webAltDD); + ejbModule.getAltDDs().put("beans.xml", webAppBeansXml); + ejbModule.setFinder(finder); + ejbModule.setClassLoader(webLoader); + ejbModule.setWebapp(true); + + appModule.getEjbModules().add(ejbModule); + appModule.getWebModules().add(webModule); + + addPersistenceXml(archive, WEB_INF, appModule); + addOpenEJbJarXml(archive, WEB_INF, ejbModule); + addValidationXml(archive, WEB_INF, new HashMap<String, Object>(), ejbModule); + addResourcesXml(archive, WEB_INF, ejbModule); + addEnvEntries(archive, WEB_INF, appModule, ejbModule); + } + } + } + } + + if (isEar) { // adding the test class as lib class can break test if tested against the web part of the ear + addTestClassAsManagedBean(javaClass, tempClassLoader, appModule); + return appModule; + } + + // add the test as a managed bean to be able to inject into it easily + final Map<String, Object> testDD; + if (javaClass != null) { + final EjbModule testEjbModule = addTestClassAsManagedBean(javaClass, tempClassLoader, appModule); + testDD = testEjbModule.getAltDDs(); + } else { + testDD = new HashMap<>(); // ignore + } + + final EjbJar ejbJar = createEjbJar(prefix, archive); + + if (ejbJar.getModuleName() == null) { + final String name = archive.getName(); + if (name.endsWith("ar") && name.length() > 4) { + ejbJar.setModuleName(name.substring(0, name.length() - ".jar".length())); + } else { + ejbJar.setModuleName(name); + } + } + + final EjbModule ejbModule = new EjbModule(ejbJar); + ejbModule.setClassLoader(tempClassLoader); + + final Node beansXml = findBeansXml(archive, prefix); + + final FinderFactory.OpenEJBAnnotationFinder finder = new FinderFactory.OpenEJBAnnotationFinder( + finderArchive(beansXml, archive, loader, scannedArchive, earMap, earBeans)); + ejbModule.setFinder(finder); + ejbModule.setBeans(earBeans); + ejbModule.getAltDDs().put("beans.xml", earBeans); + + if (appModule.isWebapp()) { // war + appModule.getWebModules().iterator().next().setFinder(ejbModule.getFinder()); + } + appModule.getEjbModules().add(ejbModule); + + addPersistenceXml(archive, prefix, appModule); + addOpenEJbJarXml(archive, prefix, ejbModule); + addValidationXml(archive, prefix, testDD, ejbModule); + addResourcesXml(archive, prefix, ejbModule); + addEnvEntries(archive, prefix, appModule, ejbModule); + + if (!appModule.isWebapp()) { + appModule.getAdditionalLibraries().addAll(additionalPaths); + } + + if (archive.getName().endsWith(".jar")) { // otherwise global naming will be broken + appModule.setStandloneWebModule(); + } + return appModule; + } + + private static EjbJar createEjbJar(final String prefix, final Archive<?> webArchive) { + final EjbJar webEjbJar; + final Node webEjbJarXml = webArchive.get(prefix.concat(EJB_JAR_XML)); + if (webEjbJarXml != null) { + try { + webEjbJar = ReadDescriptors.readEjbJar(webEjbJarXml.getAsset().openStream()); + } catch (final OpenEJBException e) { + throw new OpenEJBRuntimeException(e); + } + } else { + webEjbJar = new EjbJar(); + } + return webEjbJar; + } + + private static EjbModule addTestClassAsManagedBean(Class<?> javaClass, URLClassLoader tempClassLoader, AppModule appModule) { + final EjbJar ejbJar = new EjbJar(); + final OpenejbJar openejbJar = new OpenejbJar(); + final String ejbName = appModule.getModuleId() + "_" + javaClass.getName(); + final ManagedBean bean = ejbJar.addEnterpriseBean(new ManagedBean(ejbName, javaClass.getName(), true)); + bean.localBean(); + bean.setTransactionType(TransactionType.BEAN); + final EjbDeployment ejbDeployment = openejbJar.addEjbDeployment(bean); + ejbDeployment.setDeploymentId(ejbName); + final EjbModule e = new EjbModule(ejbJar, openejbJar); + e.getProperties().setProperty("openejb.cdi.activated", "false"); + e.getProperties().setProperty("openejb.test.module", "true"); + e.setBeans(new Beans()); + e.setClassLoader(tempClassLoader); + appModule.getEjbModules().add(e); + return e; + } + + private static WebApp createWebApp(final Archive<?> archive) { + WebApp webApp; + final Node webXml = archive.get(WEB_INF + "web.xml"); + if (webXml == null) { + webApp = new WebApp(); + } else { + InputStream inputStream = null; + try { + inputStream = webXml.getAsset().openStream(); + webApp = Sxc.unmarshalJavaee(new WebApp$JAXB(), inputStream); + } catch (final Exception e) { + webApp = new WebApp(); + } finally { + IO.close(inputStream); + } + } + return webApp; + } + + private static CompositeArchive analyzeLibs(final ClassLoader parent, + final List<URL> additionalPaths, final Map<URL, List<String>> earMap, + final List<Archive> earLibsArchives, + final CompositeBeans earBeans, + final Map<ArchivePath, Node> jars, + final Map<String, Object> altDD) { + final List<org.apache.xbean.finder.archive.Archive> archives = new ArrayList<>(jars.size()); + for (final Map.Entry<ArchivePath, Node> node : jars.entrySet()) { + final Asset asset = node.getValue().getAsset(); + if (ArchiveAsset.class.isInstance(asset)) { + final Archive<?> libArchive = ArchiveAsset.class.cast(asset).getArchive(); + if (!isExcluded(libArchive.getName())) { + earLibsArchives.add(libArchive); + final List<Class<?>> earClasses = new ArrayList<>(); + final List<String> earClassNames = new ArrayList<>(); + final Map<ArchivePath, Node> content = libArchive.getContent(new IncludeRegExpPaths(".*.class")); + for (final Map.Entry<ArchivePath, Node> classNode : content.entrySet()) { + final String classname = name(classNode.getKey().get()); + try { + earClasses.add(parent.loadClass(classname)); + earClassNames.add(classname); + } catch (final ClassNotFoundException e) { + LOGGER.fine("Can't load class " + classname); + } catch (final NoClassDefFoundError ncdfe) { + // no-op + } + } + try { // ends with !/META-INF/beans.xml to force it to be used as a cdi module *with bda* + final Node beansNode = libArchive.get(META_INF + BEANS_XML); + final URL arUrl = new URL("jar:file://!/lib/" + libArchive.getName() + (beansNode != null ? "!/META-INF/beans.xml" : "")); + if (beansNode != null) { + try { + DeploymentLoader.doMerge(arUrl, earBeans, ReadDescriptors.readBeans(beansNode.getAsset().openStream())); + } catch (final OpenEJBException e) { + throw new IllegalArgumentException(e); + } + } + earMap.put(arUrl, earClassNames); + } catch (final MalformedURLException e) { + // no-op + } + archives.add(new ClassesArchive(earClasses)); + } + + final Node ejbJarXml = libArchive.get(META_INF + EJB_JAR_XML); + if (ejbJarXml != null) { // not super, we should merge them surely but ok for use cases we met until today + altDD.put("ejb-jar.xml", new AssetSource(ejbJarXml.getAsset(), null)); + } + } if (UrlAsset.class.isInstance(asset) || FileAsset.class.isInstance(asset)) { + try { + final URL url = UrlAsset.class.isInstance(asset) ? get(URL.class, "url", asset) : get(File.class, "file", asset).toURI().toURL(); + final List<String> classes = new ArrayList<String>(); + archives.add(new FilteredArchive( + new JarArchive(parent, url), + new WebappAggregatedArchive.ScanXmlSaverFilter(false, null, classes, null))); + additionalPaths.add(url); + + final URLClassLoader loader = new URLClassLoader(new URL[] { url }, EMPTY_LOADER); + for (final String beans : asList("META-INF/beans.xml", "/META-INF/beans.xml")) { + final URL u = loader.getResource(beans); + if (u != null) { + try { + DeploymentLoader.doMerge(u, earBeans, ReadDescriptors.readBeans(u.openStream())); + } catch (final OpenEJBException e) { + throw new IllegalArgumentException(e); + } catch (final IOException e) { + // no-op + } + earMap.put(u, classes); + } + } + try { + loader.close(); + } catch (final IOException e) { + // no-op + } + } catch (final MalformedURLException e) { + throw new IllegalArgumentException(e); + } + } + } + return new CompositeArchive(archives); + } + + private static Node findBeansXml(final Archive<?> archive, final String prefix) { + Node beansXml = archive.get(prefix.concat(BEANS_XML)); + if (beansXml == null && WEB_INF.equals(prefix)) { + beansXml = archive.get(WEB_INF_CLASSES.concat(META_INF).concat(BEANS_XML)); + } + return beansXml; + } + + private static void addPersistenceXml(final Archive<?> archive, final String prefix, final AppModule appModule) { + Node persistenceXml = archive.get(prefix.concat(PERSISTENCE_XML)); + if (persistenceXml == null && WEB_INF.equals(prefix)) { + persistenceXml = archive.get(WEB_INF_CLASSES.concat(META_INF).concat(PERSISTENCE_XML)); + } + if (persistenceXml != null) { + final Asset asset = persistenceXml.getAsset(); + if (UrlAsset.class.isInstance(asset)) { + appModule.getAltDDs().put(PERSISTENCE_XML, Arrays.asList(get(URL.class, "url", asset))); + } else if (FileAsset.class.isInstance(asset)) { + try { + appModule.getAltDDs().put(PERSISTENCE_XML, Arrays.asList(get(File.class, "file", asset).toURI().toURL())); + } catch (final MalformedURLException e) { + appModule.getAltDDs().put(PERSISTENCE_XML, Arrays.asList(new AssetSource(persistenceXml.getAsset(), null))); + } + } else if (ClassLoaderAsset.class.isInstance(asset)) { + final URL url = get(ClassLoader.class, "classLoader", asset).getResource(get(String.class, "resourceName", asset)); + if (url != null) { + appModule.getAltDDs().put(PERSISTENCE_XML, Arrays.asList(url)); + } else { + appModule.getAltDDs().put(PERSISTENCE_XML, Arrays.asList(new AssetSource(persistenceXml.getAsset(), null))); + } + } else { + appModule.getAltDDs().put(PERSISTENCE_XML, Arrays.asList(new AssetSource(persistenceXml.getAsset(), null))); + } + } + } + + private static void addOpenEJbJarXml(final Archive<?> archive, final String prefix, final EjbModule ejbModule) { + final Node openejbJarXml = archive.get(prefix.concat(OPENEJB_JAR_XML)); + if (openejbJarXml != null) { + ejbModule.getAltDDs().put(OPENEJB_JAR_XML, new AssetSource(openejbJarXml.getAsset(), null)); + } + } + + private static void addValidationXml(final Archive<?> archive, final String prefix, final Map<String, Object> testDD, final EjbModule ejbModule) { + Node validationXml = archive.get(prefix.concat(VALIDATION_XML)); + // bval tcks + if (validationXml == null && WEB_INF == prefix) { // we can use == here + validationXml = archive.get(WEB_INF_CLASSES.concat(META_INF).concat(VALIDATION_XML)); + } + if (validationXml != null) { + testDD.put(VALIDATION_XML, new AssetSource(validationXml.getAsset(), null)); // use same config otherwise behavior is weird + ejbModule.getAltDDs().put(VALIDATION_XML, new AssetSource(validationXml.getAsset(), null)); + } + } + + private static void addResourcesXml(final Archive<?> archive, final String prefix, final EjbModule ejbModule) { + final Node resourcesXml = archive.get(prefix.concat(RESOURCES_XML)); + if (resourcesXml != null) { + ejbModule.getAltDDs().put(RESOURCES_XML, new AssetSource(resourcesXml.getAsset(), null)); + } + } + + private static void addEnvEntries(final Archive<?> archive, final String prefix, final AppModule appModule, final EjbModule ejbModule) { + final Node envEntriesProperties = archive.get(prefix.concat(ENV_ENTRIES_PROPERTIES)); + if (envEntriesProperties != null) { + InputStream is = null; + final Properties properties = new Properties(); + try { + is = envEntriesProperties.getAsset().openStream(); + properties.load(is); + ejbModule.getAltDDs().put(ENV_ENTRIES_PROPERTIES, properties); + + // do it for test class too + appModule.getEjbModules().iterator().next().getAltDDs().put(ENV_ENTRIES_PROPERTIES, properties); + } catch (final Exception e) { + LOGGER.log(Level.SEVERE, "can't read env-entries.properties", e); + } finally { + IO.close(is); + } + } + } + + private static String contextRoot(final String name) { + if (name.endsWith(".war")) { + return name.substring(0, name.length() - ".war".length()); + } + return name; + } + + private static org.apache.xbean.finder.archive.Archive finderArchive( + final Node beansXml, final Archive<?> archive, + final ClassLoader cl, final CompositeArchive libs, + final Map<URL, List<String>> webAppClassesByUrl, + final CompositeBeans compositeBeans) { + final List<Class<?>> classes = new ArrayList<>(); + final Map<ArchivePath, Node> content = archive.getContent(new IncludeRegExpPaths(".*.class")); + for (final Map.Entry<ArchivePath, Node> node : content.entrySet()) { + final String classname = name(node.getKey().get()); + try { + classes.add(cl.loadClass(classname)); + } catch (final ClassNotFoundException e) { + LOGGER.fine("Can't load class " + classname); + if (LOGGER.isLoggable(Level.FINEST)) { + e.printStackTrace(System.err); + } + } catch (final NoClassDefFoundError ncdfe) { + // no-op + } + } + + final List<org.apache.xbean.finder.archive.Archive> archives = new ArrayList<>(); + + archives.add(new ClassesArchive(classes)); + if (beansXml != null) { + try { + final URL key = new URL("jar:file://!/WEB-INF/beans.xml"); // no host avoid host resolution in hashcode() + try { + DeploymentLoader.doMerge(key, compositeBeans, ReadDescriptors.readBeans(beansXml.getAsset().openStream())); + } catch (final OpenEJBException e) { + throw new IllegalArgumentException(e); + } + + final List<String> mainClasses = new ArrayList<>(); + for (final Class<?> clazz : classes) { + mainClasses.add(clazz.getName()); + } + + webAppClassesByUrl.put(key, mainClasses); + } catch (final MalformedURLException mue) { + // no-op + } + } + + if (libs != null) { + archives.add(libs); + } + + return new SimpleWebappAggregatedArchive(cl, new CompositeArchive(archives), webAppClassesByUrl); + } + + private static boolean isExcluded(final String archiveName) { + return "arquillian-junit.jar".equals(archiveName) || "arquillian-protocol.jar".equals(archiveName) + || "arquillian-core.jar".equals(archiveName); + } + + private static String name(final String raw) { + String name = raw; + if (name.startsWith(WEB_INF_CLASSES)) { + name = name.substring(WEB_INF_CLASSES.length() - 1); + } + name = name.replace('/', '.'); + return name.substring(1, name.length() - 6); + } + + private static final class AssetSource extends ReadDescriptors.UrlSource { + private Asset asset; + + private AssetSource(final Asset asset, final URL url) { + super(url); + this.asset = asset; + } + + @Override + public InputStream get() throws IOException { + return asset.openStream(); + } + } + + // mainly extended to be sure to reuse our tip about scanning for CDI + private static class SimpleWebappAggregatedArchive extends WebappAggregatedArchive { + private final CompositeArchive delegate; + private final Map<URL, List<String>> classesMap; + + public SimpleWebappAggregatedArchive(final ClassLoader cl, final CompositeArchive archive, final Map<URL, List<String>> map) { + super(cl, new HashMap<String, Object>(), new ArrayList<URL>()); + + delegate = archive; + classesMap = map; + } + + @Override + public Map<URL, List<String>> getClassesMap() { + return classesMap; + } + + @Override + public InputStream getBytecode(final String s) throws IOException, ClassNotFoundException { + return delegate.getBytecode(s); + } + + @Override + public Class<?> loadClass(final String s) throws ClassNotFoundException { + return delegate.loadClass(s); + } + + @Override + public Iterator<Entry> iterator() { + return delegate.iterator(); + } + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBConfiguration.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBConfiguration.java b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBConfiguration.java new file mode 100644 index 0000000..093caca --- /dev/null +++ b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBConfiguration.java @@ -0,0 +1,73 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb; + +import org.jboss.arquillian.config.descriptor.api.Multiline; +import org.jboss.arquillian.container.spi.ConfigurationException; +import org.jboss.arquillian.container.spi.client.container.ContainerConfiguration; + +import java.util.Collection; +import java.util.Collections; +import java.util.HashSet; + +import static java.util.Arrays.asList; + +public class OpenEJBConfiguration implements ContainerConfiguration { + private String properties = ""; + private String preloadClasses; + private boolean startDefaultScopes; + private Collection<String> singleDeploymentByArchiveName = Collections.emptyList(); + + @Override + public void validate() throws ConfigurationException { + // no-op + } + + public boolean isStartDefaultScopes() { + return startDefaultScopes; + } + + public void setStartDefaultScopes(final boolean startDefaultScopes) { + this.startDefaultScopes = startDefaultScopes; + } + + public String getProperties() { + return properties; + } + + @Multiline + public void setProperties(final String properties) { + this.properties = properties; + } + + public String getPreloadClasses() { + return preloadClasses; + } + + public void setPreloadClasses(final String preloadClasses) { + this.preloadClasses = preloadClasses; + } + + public boolean isSingleDeploymentByArchiveName(final String name) { + return singleDeploymentByArchiveName.contains(name) || singleDeploymentByArchiveName.contains("*") || singleDeploymentByArchiveName.contains("true"); + } + + public void setSingleDeploymentByArchiveName(final String singleDeploymentByArchiveName) { + this.singleDeploymentByArchiveName = singleDeploymentByArchiveName == null || singleDeploymentByArchiveName.trim().isEmpty() ? + Collections.<String>emptyList() : new HashSet<String>(asList(singleDeploymentByArchiveName.split(" *, *"))); + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java new file mode 100644 index 0000000..f999c20 --- /dev/null +++ b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBDeployableContainer.java @@ -0,0 +1,435 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb; + +import org.apache.openejb.AppContext; +import org.apache.openejb.BeanContext; +import org.apache.openejb.ModuleTestContext; +import org.apache.openejb.OpenEJB; +import org.apache.openejb.OpenEJBRuntimeException; +import org.apache.openejb.OpenEjbContainer; +import org.apache.openejb.arquillian.common.ArquillianUtil; +import org.apache.openejb.arquillian.openejb.server.ServiceManagers; +import org.apache.openejb.assembler.classic.AppInfo; +import org.apache.openejb.assembler.classic.Assembler; +import org.apache.openejb.assembler.classic.ClassListInfo; +import org.apache.openejb.assembler.classic.OpenEjbConfigurationFactory; +import org.apache.openejb.assembler.classic.ServletInfo; +import org.apache.openejb.assembler.classic.WebAppBuilder; +import org.apache.openejb.assembler.classic.WebAppInfo; +import org.apache.openejb.config.AppModule; +import org.apache.openejb.config.ConfigurationFactory; +import org.apache.openejb.config.DeploymentFilterable; +import org.apache.openejb.config.WebModule; +import org.apache.openejb.core.LocalInitialContext; +import org.apache.openejb.core.LocalInitialContextFactory; +import org.apache.openejb.core.WebContext; +import org.apache.openejb.loader.IO; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.server.httpd.session.SessionManager; +import org.apache.openejb.web.LightweightWebAppBuilder; +import org.apache.webbeans.web.lifecycle.test.MockHttpSession; +import org.apache.webbeans.web.lifecycle.test.MockServletContext; +import org.jboss.arquillian.container.spi.client.container.DeployableContainer; +import org.jboss.arquillian.container.spi.client.container.DeploymentException; +import org.jboss.arquillian.container.spi.client.container.LifecycleException; +import org.jboss.arquillian.container.spi.client.protocol.ProtocolDescription; +import org.jboss.arquillian.container.spi.client.protocol.metadata.HTTPContext; +import org.jboss.arquillian.container.spi.client.protocol.metadata.ProtocolMetaData; +import org.jboss.arquillian.container.spi.client.protocol.metadata.Servlet; +import org.jboss.arquillian.container.spi.context.annotation.DeploymentScoped; +import org.jboss.arquillian.core.api.Instance; +import org.jboss.arquillian.core.api.InstanceProducer; +import org.jboss.arquillian.core.api.annotation.Inject; +import org.jboss.arquillian.test.spi.TestClass; +import org.jboss.arquillian.test.spi.annotation.SuiteScoped; +import org.jboss.shrinkwrap.api.Archive; +import org.jboss.shrinkwrap.descriptor.api.Descriptor; + +import java.io.ByteArrayInputStream; +import java.io.Closeable; +import java.io.IOException; +import java.util.Collection; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Properties; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentMap; +import java.util.logging.Level; +import java.util.logging.Logger; +import javax.naming.Context; +import javax.naming.InitialContext; +import javax.naming.NamingException; +import javax.servlet.ServletContext; +import javax.servlet.http.HttpSession; + +import static org.apache.openejb.cdi.ScopeHelper.startContexts; +import static org.apache.openejb.cdi.ScopeHelper.stopContexts; + +public class OpenEJBDeployableContainer implements DeployableContainer<OpenEJBConfiguration> { + private static final Properties PROPERTIES = new Properties(); + + static { + // global properties + PROPERTIES.setProperty(Context.INITIAL_CONTEXT_FACTORY, LocalInitialContextFactory.class.getName()); + PROPERTIES.setProperty(LocalInitialContext.ON_CLOSE, LocalInitialContext.Close.DESTROY.name()); + PROPERTIES.setProperty(DeploymentFilterable.DEPLOYMENTS_CLASSPATH_PROPERTY, "false"); + try { + OpenEJBDeployableContainer.class.getClassLoader().loadClass("org.apache.openejb.server.ServiceManager"); + PROPERTIES.setProperty(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE, "true"); + } catch (final Exception e) { + // ignored + } + } + + private static final ConcurrentMap<String, DeploymentInfo> DEPLOYMENT_INFO = new ConcurrentHashMap<String, DeploymentInfo>(); + public static final AppContext NO_APP_CTX = new AppContext(null, SystemInstance.get(), null, null, null, false); + + // config + private Properties properties; + + // system + private Assembler assembler; + private InitialContext initialContext; + private ConfigurationFactory configurationFactory; + private Collection<Archive<?>> containerArchives; + + // suite + @Inject + @DeploymentScoped + private InstanceProducer<AppInfo> appInfoProducer; + + @Inject + @DeploymentScoped + private InstanceProducer<AppContext> appContextProducer; + + @Inject + @SuiteScoped + private InstanceProducer<Context> contextProducer; + + @Inject + @DeploymentScoped + private InstanceProducer<ServletContext> servletContextProducer; + + @Inject + @DeploymentScoped + private InstanceProducer<HttpSession> sessionProducer; + + @Inject + @DeploymentScoped + private InstanceProducer<Closeables> closeablesProducer; + + @Inject + @SuiteScoped + private InstanceProducer<ClassLoader> classLoader; + + @Inject + private Instance<Closeables> closeables; + + @Inject + private Instance<ServletContext> servletContext; + + @Inject + private Instance<HttpSession> session; + + @Inject + private Instance<AppInfo> info; + + @Inject + private Instance<AppContext> appContext; + + @Inject + private Instance<TestClass> testClass; + + private OpenEJBConfiguration configuration; + + @Override + public Class<OpenEJBConfiguration> getConfigurationClass() { + return OpenEJBConfiguration.class; + } + + @Override + public void setup(final OpenEJBConfiguration openEJBConfiguration) { + properties = new Properties(); + configuration = openEJBConfiguration; + + final ByteArrayInputStream bais = new ByteArrayInputStream(openEJBConfiguration.getProperties().getBytes()); + try { + properties.load(bais); + } catch (final IOException e) { + throw new OpenEJBRuntimeException(e); + } finally { + IO.close(bais); + } + + for (final Map.Entry<Object, Object> defaultKey : PROPERTIES.entrySet()) { + final String key = defaultKey.getKey().toString(); + if (!properties.containsKey(key)) { + properties.setProperty(key, defaultKey.getValue().toString()); + } + } + + ArquillianUtil.preLoadClassesAsynchronously(openEJBConfiguration.getPreloadClasses()); + } + + @Override + public void start() throws LifecycleException { + try { + initialContext = new InitialContext(properties); + } catch (final NamingException e) { + throw new LifecycleException("can't start the OpenEJB container", e); + } + + assembler = SystemInstance.get().getComponent(Assembler.class); + configurationFactory = (ConfigurationFactory) SystemInstance.get().getComponent(OpenEjbConfigurationFactory.class); + + if ("true".equalsIgnoreCase(PROPERTIES.getProperty(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE)) + && SystemInstance.get().getComponent(WebAppBuilder.class) == null) { + SystemInstance.get().setComponent(WebAppBuilder.class, new LightweightWebAppBuilder()); + } + + contextProducer.set(initialContext); + + containerArchives = ArquillianUtil.toDeploy(properties); + final Closeables globalScopeCloseables = new Closeables(); + SystemInstance.get().setComponent(Closeables.class, globalScopeCloseables); + for (final Archive<?> archive : containerArchives) { + try { + quickDeploy(archive, testClass.get(), globalScopeCloseables); + } catch (final DeploymentException e) { + Logger.getLogger(OpenEJBDeployableContainer.class.getName()).log(Level.SEVERE, e.getMessage(), e); + } + } + } + + @Override + public ProtocolMetaData deploy(final Archive<?> archive) throws DeploymentException { + final DeploymentInfo info; + try { + final Closeables cl = new Closeables(); + closeablesProducer.set(cl); + info = quickDeploy(archive, testClass.get(), cl); + + // try to switch module context jndi to let test use java:module naming + // we could put the managed bean in the war but then test class should respect all the + // container rules (CDI) which is not the case with this solution + if (archive.getName().endsWith(".war")) { + final List<BeanContext> beanContexts = info.appCtx.getBeanContexts(); + if (beanContexts.size() > 1) { + final Iterator<BeanContext> it = beanContexts.iterator(); + while (it.hasNext()) { + final BeanContext next = it.next(); + if (ModuleTestContext.class.isInstance(next.getModuleContext()) && BeanContext.Comp.class != next.getBeanClass()) { + for (final BeanContext b : beanContexts) { + if (b.getModuleContext() != next.getModuleContext()) { + ModuleTestContext.class.cast(next.getModuleContext()) + .setModuleJndiContextOverride(b.getModuleContext().getModuleJndiContext()); + break; + } + } + break; + } + } + } + } + + servletContextProducer.set(info.appServletContext); + sessionProducer.set(info.appSession); + appInfoProducer.set(info.appInfo); + appContextProducer.set(info.appCtx); + final ClassLoader loader = info.appCtx.getWebContexts().isEmpty() ? info.appCtx.getClassLoader() : info.appCtx.getWebContexts().iterator().next().getClassLoader(); + classLoader.set(loader == null ? info.appCtx.getClassLoader() : loader); + } catch (final Exception e) { + throw new DeploymentException("can't deploy " + archive.getName(), e); + } + + // if service manager is started allow @ArquillianResource URL injection + if (PROPERTIES.containsKey(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE)) { + final ProtocolMetaData metaData = ServiceManagers.protocolMetaData(appInfoProducer.get()); + HTTPContext http = null; + for (final WebAppInfo webapp : info.appInfo.webApps) { + for (final ServletInfo servletInfo : webapp.servlets) { + if (http == null) { + http = HTTPContext.class.cast(metaData.getContexts().iterator().next()); + http.add(new Servlet(servletInfo.servletName, webapp.contextRoot)); + } + } + for (final ClassListInfo classListInfo : webapp.webAnnotatedClasses) { + for (final String path : classListInfo.list) { + if (!path.contains("!")) { + continue; + } + if (http == null) { + http = HTTPContext.class.cast(metaData.getContexts().iterator().next()); + } + http.add(new Servlet(path.substring(path.lastIndexOf('!') + 2).replace(".class", "").replace("/", "."), webapp.contextRoot)); + } + } + } + if (metaData != null) { + return metaData; + } + } + return new ProtocolMetaData(); + } + + private DeploymentInfo quickDeploy(final Archive<?> archive, final TestClass testClass, final Closeables cls) throws DeploymentException { + final String name = archive.getName(); + DeploymentInfo info = DEPLOYMENT_INFO.get(name); + if (info == null) { + try { + final AppModule module = OpenEJBArchiveProcessor.createModule(archive, testClass, cls); + final AppInfo appInfo = configurationFactory.configureApplication(module); + + final WebAppBuilder webAppBuilder = SystemInstance.get().getComponent(WebAppBuilder.class); + final boolean isEmbeddedWebAppBuilder = webAppBuilder != null && LightweightWebAppBuilder.class.isInstance(webAppBuilder); + if (isEmbeddedWebAppBuilder) { + // for now we keep the same classloader, open to discussion if we should recreate it, not sure it does worth it + final LightweightWebAppBuilder lightweightWebAppBuilder = LightweightWebAppBuilder.class.cast(webAppBuilder); + for (final WebModule w : module.getWebModules()) { + final String moduleId = w.getModuleId(); + lightweightWebAppBuilder.setClassLoader(moduleId, w.getClassLoader()); + cls.add(new Closeable() { + @Override + public void close() throws IOException { + lightweightWebAppBuilder.removeClassLoader(moduleId); + } + }); + } + } + final AppContext appCtx = assembler.createApplication(appInfo, module.getClassLoader()); + if (isEmbeddedWebAppBuilder && PROPERTIES.containsKey(OpenEjbContainer.OPENEJB_EMBEDDED_REMOTABLE) && !appCtx.getWebContexts().isEmpty()) { + cls.add(new Closeable() { + @Override + public void close() throws IOException { + try { + final SessionManager sessionManager = SystemInstance.get().getComponent(SessionManager.class); + if (sessionManager != null) { + for (final WebContext web : appCtx.getWebContexts()) { + sessionManager.destroy(web); + } + } + } catch (final Throwable e) { + // no-op + } + } + }); + } + + final ServletContext appServletContext = new MockServletContext(); + final HttpSession appSession = new MockHttpSession(); + + if (configuration.isStartDefaultScopes()) { + startContexts(appCtx.getWebBeansContext().getContextsService(), appServletContext, appSession); + } + + info = new DeploymentInfo(appServletContext, appSession, appInfo, appCtx); + if (configuration.isSingleDeploymentByArchiveName(name)) { + DEPLOYMENT_INFO.putIfAbsent(name, info); + } + } catch (final Exception e) { + throw new DeploymentException("can't deploy " + name, e); + } + } + return info; + } + + @Override + public void undeploy(final Archive<?> archive) throws DeploymentException { + final Closeables cl = closeables.get(); + if (cl != null) { + try { + cl.close(); + } catch (final IOException e) { + // no-op + } + } + + // reset classloader for next text + // otherwise if it was closed something can fail + classLoader.set(OpenEJBDeployableContainer.class.getClassLoader()); + + final AppContext ctx = appContext.get(); + if (ctx == null) { + return; + } else { + appContextProducer.set(NO_APP_CTX); // release all references of the previous one - classloaders whatever arquillian Instance impl is etc + } + + try { + if (!configuration.isSingleDeploymentByArchiveName(archive.getName())) { + assembler.destroyApplication(info.get().path); + } + if (configuration.isStartDefaultScopes()) { + stopContexts(ctx.getWebBeansContext().getContextsService(), servletContext.get(), session.get()); + } + } catch (final Exception e) { + throw new DeploymentException("can't undeploy " + archive.getName(), e); + } + } + + @Override + public void stop() throws LifecycleException { + ArquillianUtil.undeploy(this, containerArchives); + + try { + if (initialContext != null) { + initialContext.close(); + } + Closeables closeables = SystemInstance.get().getComponent(Closeables.class); + if (closeables != null) { + closeables.close(); + } + } catch (final NamingException e) { + throw new LifecycleException("can't close the OpenEJB container", e); + } catch (final IOException e) { + // no-op: close() of classloaders, not a big deal at this moment + } finally { + OpenEJB.destroy(); + } + } + + @Override + public ProtocolDescription getDefaultProtocol() { + return new ProtocolDescription("Local"); + } + + @Override + public void deploy(final Descriptor descriptor) throws DeploymentException { + throw new UnsupportedOperationException(); + } + + @Override + public void undeploy(final Descriptor descriptor) throws DeploymentException { + throw new UnsupportedOperationException(); + } + + private static final class DeploymentInfo { + public final ServletContext appServletContext; + public final HttpSession appSession; + public final AppInfo appInfo; + public final AppContext appCtx; + + private DeploymentInfo(final ServletContext appServletContext, final HttpSession appSession, final AppInfo appInfo, final AppContext appCtx) { + this.appServletContext = appServletContext; + this.appSession = appSession; + this.appInfo = appInfo; + this.appCtx = appCtx; + } + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBExtension.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBExtension.java b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBExtension.java new file mode 100644 index 0000000..6bb944d --- /dev/null +++ b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBExtension.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb; + +import org.apache.openejb.arquillian.common.ArquillianUtil; +import org.apache.openejb.arquillian.common.TestObserver; +import org.apache.openejb.arquillian.common.deployment.DeploymentExceptionObserver; +import org.apache.openejb.arquillian.common.deployment.DeploymentExceptionProvider; +import org.apache.openejb.arquillian.transaction.OpenEJBTransactionProvider; +import org.apache.openejb.util.JuliLogStreamFactory; +import org.apache.openejb.util.LogCategory; +import org.jboss.arquillian.container.spi.client.container.DeployableContainer; +import org.jboss.arquillian.core.spi.LoadableExtension; +import org.jboss.arquillian.test.spi.TestEnricher; +import org.jboss.arquillian.test.spi.enricher.resource.ResourceProvider; +import org.jboss.arquillian.transaction.spi.provider.TransactionProvider; + +import java.util.Enumeration; +import java.util.logging.Handler; +import java.util.logging.LogManager; +import java.util.logging.Logger; + +public class OpenEJBExtension implements LoadableExtension { + private static final String OPENEJB_ADAPTER_NAME = "openejb"; + + static { // logging conf + if (ArquillianUtil.isCurrentAdapter(OPENEJB_ADAPTER_NAME)) { + if (System.getProperty("java.util.logging.config.class") == null || System.getProperty("java.util.logging.config.file") == null) { + final Enumeration<String> list = LogManager.getLogManager().getLoggerNames(); + while (list.hasMoreElements()) { + initLogger(list.nextElement()); + } + initLogger(LogCategory.OPENEJB.getName()); + } + } + } + + private static void initLogger(final String name) { + final Logger logger = Logger.getLogger(name); + final Handler[] handlers = logger.getHandlers(); + if (handlers != null) { + for (final Handler handler : handlers) { + logger.removeHandler(handler); + } + } + logger.setUseParentHandlers(false); + logger.addHandler(new JuliLogStreamFactory.OpenEJBSimpleLayoutHandler()); + + } + + @Override + public void register(final ExtensionBuilder extensionBuilder) { + if (ArquillianUtil.isCurrentAdapter(OPENEJB_ADAPTER_NAME)) { + extensionBuilder.service(DeployableContainer.class, OpenEJBDeployableContainer.class) + .service(TestEnricher.class, OpenEJBInjectionEnricher.class) + .service(ResourceProvider.class, DeploymentExceptionProvider.class) + .service(TransactionProvider.class, OpenEJBTransactionProvider.class) + .observer(TestObserver.class) + .observer(DeploymentExceptionObserver.class); + } + } +} http://git-wip-us.apache.org/repos/asf/tomee/blob/5b16879e/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBInjectionEnricher.java ---------------------------------------------------------------------- diff --git a/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBInjectionEnricher.java b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBInjectionEnricher.java new file mode 100644 index 0000000..a571381 --- /dev/null +++ b/arquillian/arquillian-openejb-embedded/src/main/java/org/apache/openejb/arquillian/openejb/OpenEJBInjectionEnricher.java @@ -0,0 +1,76 @@ +/* + * Licensed to the Apache Software Foundation (ASF) under one or more + * contributor license agreements. See the NOTICE file distributed with + * this work for additional information regarding copyright ownership. + * The ASF licenses this file to You 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.apache.openejb.arquillian.openejb; + +import org.apache.openejb.AppContext; +import org.apache.openejb.arquillian.common.enrichment.OpenEJBEnricher; +import org.apache.openejb.arquillian.common.mockito.MockitoEnricher; +import org.apache.openejb.loader.SystemInstance; +import org.apache.openejb.spi.ContainerSystem; +import org.apache.webbeans.inject.OWBInjector; +import org.jboss.arquillian.core.api.Instance; +import org.jboss.arquillian.core.api.annotation.Inject; +import org.jboss.arquillian.test.spi.TestClass; +import org.jboss.arquillian.test.spi.TestEnricher; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.AnnotatedType; +import javax.enterprise.inject.spi.BeanManager; +import java.lang.reflect.Method; +import java.util.List; + +public class OpenEJBInjectionEnricher implements TestEnricher { + @Inject + private Instance<AppContext> appContext; + + @Inject + private Instance<TestClass> testClass; + + @Override + public void enrich(final Object testInstance) { + if (!SystemInstance.isInitialized()) { + return; + } + + new MockitoEnricher().enrich(testInstance); + + final AppContext context = appContext.get(); + if (context != null) { + OpenEJBEnricher.enrich(testInstance, context); + } else { // try to enrich with all for deployment at startup feature - only once context can be used in a class + final List<AppContext> appContexts = SystemInstance.get().getComponent(ContainerSystem.class).getAppContexts(); + final Class<?> clazz = testInstance.getClass(); + for (final AppContext appContext : appContexts) { + try { + final BeanManager bm = appContext.getWebBeansContext().getBeanManagerImpl(); + final AnnotatedType<?> at = bm.createAnnotatedType(clazz); + bm.createInjectionTarget(at); + final CreationalContext<Object> cc = bm.createCreationalContext(null); + OWBInjector.inject(bm, testInstance, cc); + cc.release(); + } catch (final Exception e) { + // no-op + } + } + } + } + + @Override + public Object[] resolve(final Method method) { + return OpenEJBEnricher.resolve(appContext.get(), testClass.get(), method); + } +}