| /* |
| * 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.embedded; |
| |
| import org.apache.openejb.arquillian.common.ArquillianFilterRunner; |
| import org.apache.openejb.arquillian.common.Files; |
| import org.apache.openejb.arquillian.common.TestClassDiscoverer; |
| import org.apache.openejb.arquillian.common.TestObserver; |
| import org.apache.openejb.arquillian.common.TomEEContainer; |
| import org.apache.openejb.assembler.classic.AppInfo; |
| import org.apache.openejb.config.AdditionalBeanDiscoverer; |
| import org.apache.openejb.core.ParentClassLoaderFinder; |
| import org.apache.openejb.loader.SystemInstance; |
| import org.apache.openejb.spi.ContainerSystem; |
| import org.apache.tomee.embedded.Configuration; |
| import org.apache.tomee.embedded.Container; |
| import org.apache.webbeans.config.WebBeansContext; |
| import org.apache.webbeans.web.lifecycle.test.MockHttpSession; |
| 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.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.core.api.InstanceProducer; |
| import org.jboss.arquillian.core.api.annotation.Inject; |
| import org.jboss.arquillian.test.spi.annotation.SuiteScoped; |
| import org.jboss.shrinkwrap.api.Archive; |
| |
| import javax.enterprise.context.ConversationScoped; |
| import javax.enterprise.context.RequestScoped; |
| import javax.enterprise.context.SessionScoped; |
| import javax.servlet.http.HttpSession; |
| import java.io.File; |
| import java.util.HashMap; |
| import java.util.Map; |
| import java.util.concurrent.ConcurrentHashMap; |
| |
| public class EmbeddedTomEEContainer extends TomEEContainer<EmbeddedTomEEConfiguration> { |
| |
| private static final Map<Archive<?>, File> ARCHIVES = new ConcurrentHashMap<Archive<?>, File>(); |
| |
| private static final Map<String, MockHttpSession> SESSIONS = new ConcurrentHashMap<String, MockHttpSession>(); |
| |
| private Container container; |
| |
| @Inject |
| @SuiteScoped |
| private InstanceProducer<TestObserver.ClassLoaders> classLoader; |
| |
| @Override |
| public Class<EmbeddedTomEEConfiguration> getConfigurationClass() { |
| return EmbeddedTomEEConfiguration.class; |
| } |
| |
| @Override |
| public void setup(final EmbeddedTomEEConfiguration configuration) { |
| super.setup(configuration); |
| this.container = new Container(); |
| this.container.setup(this.convertConfiguration(configuration)); |
| } |
| |
| /* |
| * Not exactly as elegant as I'd like. Maybe we could have the EmbeddedServer configuration in openejb-core so all the adapters can use it. |
| * Depending on tomee-embedded is fine in this adapter, but less desirable in the others, as we'd get loads of stuff in the classpath we don't need. |
| */ |
| private Configuration convertConfiguration(final EmbeddedTomEEConfiguration tomeeConfiguration) { |
| final Configuration configuration = new Configuration(); |
| |
| configuration.setDir(tomeeConfiguration.getDir()); |
| configuration.setHttpPort(tomeeConfiguration.getHttpPort()); |
| configuration.setStopPort(tomeeConfiguration.getStopPort()); |
| configuration.setHost(tomeeConfiguration.getHost()); |
| configuration.setServerXml(tomeeConfiguration.getServerXml()); |
| configuration.setProperties(tomeeConfiguration.systemPropertiesAsProperties()); |
| configuration.setQuickSession(tomeeConfiguration.isQuickSession()); |
| configuration.setHttp2(tomeeConfiguration.isHttp2()); |
| |
| configuration.setSsl(tomeeConfiguration.isSsl()); |
| configuration.setHttpsPort(tomeeConfiguration.getHttpsPort()); |
| configuration.setKeystoreFile(tomeeConfiguration.getKeystoreFile()); |
| configuration.setKeystorePass(tomeeConfiguration.getKeystorePass()); |
| configuration.setKeyAlias(tomeeConfiguration.getKeyAlias()); |
| configuration.setKeystoreType(tomeeConfiguration.getKeystoreType()); |
| configuration.setClientAuth(tomeeConfiguration.getClientAuth()); |
| configuration.setKeyAlias(tomeeConfiguration.getKeyAlias()); |
| configuration.setSslProtocol(tomeeConfiguration.getSslProtocol()); |
| configuration.setWithEjbRemote(tomeeConfiguration.isWithEjbRemote()); |
| |
| configuration.setWebResourceCached(tomeeConfiguration.isWebResourcesCached()); |
| |
| if (tomeeConfiguration.getClasspathConfiguration() != null) { |
| configuration.loadFrom(tomeeConfiguration.getClasspathConfiguration()); |
| } |
| |
| if (tomeeConfiguration.getConfigurationCustomizers() != null) { |
| for (final String s : tomeeConfiguration.getConfigurationCustomizers().split(",")) { |
| final String trim = s.trim(); |
| if (!trim.isEmpty()) { |
| try { |
| final Class<?> type = Thread.currentThread().getContextClassLoader().loadClass(trim); |
| configuration.addCustomizer(Configuration.ConfigurationCustomizer.class.cast(type.newInstance())); |
| } catch (final Exception e) { |
| throw new IllegalArgumentException(e); |
| } |
| } |
| } |
| } |
| |
| if (tomeeConfiguration.getRoles() != null) { |
| configuration.setRoles(new HashMap<String, String>(Map.class.cast(tomeeConfiguration.getRolesAsProperties()))); |
| } |
| if (tomeeConfiguration.getUsers() != null) { |
| configuration.setUsers(new HashMap<String, String>(Map.class.cast(tomeeConfiguration.getUsersAsProperties()))); |
| } |
| |
| return configuration; |
| } |
| |
| @Override |
| public void start() throws LifecycleException { |
| try { |
| this.container.start(); |
| SystemInstance.get().setComponent(AdditionalBeanDiscoverer.class, new TestClassDiscoverer()); |
| // this property is not mandatory by default but depending the protocol it can be relevant so adding it by default |
| SystemInstance.get().setProperty("org.apache.openejb.servlet.filters", ArquillianFilterRunner.class.getName() + "=/ArquillianServletRunner"); |
| } catch (final Exception e) { |
| throw new LifecycleException("Something went wrong", e); |
| } |
| } |
| |
| @Override |
| public void stop() throws LifecycleException { |
| try { |
| this.container.stop(); |
| } catch (final Exception e) { |
| throw new LifecycleException("Unable to stop server", e); |
| } finally { |
| resetSerialization(); |
| } |
| } |
| |
| @Override |
| public ProtocolMetaData deploy(final Archive<?> archive) throws DeploymentException { |
| try { |
| /* don't do it since it should be configurable |
| final File tempDir = Files.createTempDir(); |
| final File file = new File(tempDir, name); |
| */ |
| final String name = archive.getName(); |
| final Dump dump = this.dumpFile(archive); |
| final File file = dump.getFile(); |
| |
| if (dump.isCreated() || !configuration.isSingleDeploymentByArchiveName(name)) { |
| ARCHIVES.put(archive, file); |
| final Thread current = Thread.currentThread(); |
| final ClassLoader loader = current.getContextClassLoader(); |
| current.setContextClassLoader(ParentClassLoaderFinder.Helper.get()); // multiple deployments, don't leak a loader |
| try { |
| this.container.deploy(name, file); |
| } finally { |
| current.setContextClassLoader(loader); |
| } |
| } |
| |
| final AppInfo info = this.container.getInfo(name); |
| final String context = this.getArchiveNameWithoutExtension(archive); |
| |
| final HTTPContext httpContext = new HTTPContext(this.configuration.getHost(), this.configuration.getHttpPort()); |
| httpContext.add(new Servlet("ArquillianServletRunner", "/" + context)); |
| this.addServlets(httpContext, info); |
| |
| startCdiContexts(name); // ensure tests can use request/session scopes even if we don't have a request |
| |
| TestObserver.ClassLoaders classLoaders = this.classLoader.get(); |
| if (classLoaders == null) { |
| classLoaders = new TestObserver.ClassLoaders(); |
| this.classLoader.set(classLoaders); |
| } |
| classLoaders.register(archive.getName(), SystemInstance.get().getComponent(ContainerSystem.class).getAppContext(info.appId).getClassLoader()); |
| |
| return new ProtocolMetaData().addContext(httpContext); |
| } catch (final Exception e) { |
| throw new DeploymentException("Unable to deploy", e); |
| } |
| } |
| |
| @Override |
| public void undeploy(final Archive<?> archive) throws DeploymentException { |
| final String name = archive.getName(); |
| stopCdiContexts(name); |
| if (configuration.isSingleDeploymentByArchiveName(name)) { |
| return; |
| } |
| |
| try { |
| this.container.undeploy(name); |
| } catch (final Exception e) { |
| throw new DeploymentException("Unable to undeploy", e); |
| } finally { |
| this.classLoader.get().unregister(archive.getName()); |
| |
| final File file = ARCHIVES.remove(archive); |
| if (!configuration.isSingleDumpByArchiveName()) { |
| final File folder = new File(file.getParentFile(), file.getName().substring(0, file.getName().length() - 4)); |
| if (folder.exists()) { |
| Files.delete(folder); |
| } |
| Files.delete(file); |
| final File parentFile = file.getParentFile(); |
| final File[] parentChildren = parentFile.listFiles(); |
| if (parentChildren == null || parentChildren.length == 0) { |
| Files.delete(file.getParentFile()); |
| } |
| } |
| } |
| } |
| |
| private void startCdiContexts(final String name) { |
| final WebBeansContext wbc = this.container.getAppContexts(name).getWebBeansContext(); |
| if (wbc != null && wbc.getBeanManagerImpl().isInUse()) { |
| final MockHttpSession session = new MockHttpSession(); |
| wbc.getContextsService().startContext(RequestScoped.class, null); |
| wbc.getContextsService().startContext(SessionScoped.class, session); |
| wbc.getContextsService().startContext(ConversationScoped.class, null); |
| |
| SESSIONS.put(name, session); |
| } |
| } |
| |
| private void stopCdiContexts(final String name) { |
| try { |
| final HttpSession session = SESSIONS.remove(name); |
| if (session != null) { |
| final WebBeansContext wbc = container.getAppContexts(container.getInfo(name).appId).getWebBeansContext(); |
| if (wbc != null && wbc.getBeanManagerImpl().isInUse()) { |
| wbc.getContextsService().startContext(RequestScoped.class, null); |
| wbc.getContextsService().startContext(SessionScoped.class, session); |
| wbc.getContextsService().startContext(ConversationScoped.class, null); |
| } |
| } |
| } catch (final Exception e) { |
| // no-op |
| } |
| } |
| } |