/* * Copyright 2013 The Sculptor Project Team, including the original * author or authors. * * 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.sculptor.framework.test; import java.io.IOException; import java.io.InputStream; import java.lang.reflect.Field; import java.util.Enumeration; import java.util.Properties; import javax.annotation.Resource; import javax.ejb.EJB; import javax.jms.Destination; import javax.jms.Message; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import org.junit.AfterClass; import org.junit.Before; import org.sculptor.framework.context.ServiceContext; import org.sculptor.framework.context.ServiceContextFactory; import org.sculptor.framework.test.ejbtestbean.messaging.MessagingTestLocal; import org.sculptor.framework.util.FactoryConfiguration; import org.slf4j.Logger; import org.slf4j.LoggerFactory; /** * Base class tests in a <a href="http://openejb.apache.org/">OpenEJB</a> environment. * <p> * Inject dependencies to EJBs with the ordinary <code>@EJB</code> annotation. * <p> * OpenEJB is initialized from properties loaded from classpath resource * <code>/openejb-test.properties</code>. * In case you need to destroy the container after each test (class) you should * add property <code>openejb.embedded.initialcontext.close=destroy</code>. * It is possible to override persistent unit properties this way also. * <p> * More information: * <ul> * <li><a href="http://openejb.apache.org/embedded-configuration.html">http://openejb.apache.org/embedded-configuration.html</a> * <li><a href="http://openejb.apache.org/properties-listing.html">http://openejb.apache.org/properties-listing.html</a> * <li><a href="http://openejb.apache.org/configuring-logging-in-tests.html">http://openejb.apache.org/configuring-logging-in-tests.html</a> * </ul> * * @author Patrik Nordwall */ public abstract class AbstractOpenEJBTest { /** Use SLF4J for OpenEJB logging */ static { System.setProperty("openejb.log.factory", "org.apache.openejb.util.Slf4jLogStreamFactory"); } static { ServiceContextFactory.setConfiguration(new FactoryConfiguration() { @Override public String getFactoryImplementationClassName() { return "org.sculptor.framework.context.JUnitServiceContextFactory"; } }); } private static InitialContext initialContext; private static Properties initalContextProperties; private final Logger log = LoggerFactory.getLogger(getClass()); private final int messageReplyTimeout = 10000; private final ServiceContext serviceContext = ServiceContextFactory.createServiceContext("JUnit"); private MessagingTestLocal messagingTestBean; public AbstractOpenEJBTest() { } @Before public void initialize() throws Exception { initOpenEjb(); initAnnotatedDependencies(); } @AfterClass public static void cleanup() { if ("destroy".equals(initalContextProperties.get("openejb.embedded.initialcontext.close"))) { closeOpenEjb(); } } public static void closeOpenEjb() { System.clearProperty(Context.INITIAL_CONTEXT_FACTORY); if (initialContext != null) { try { initialContext.close(); initialContext = null; } catch (NamingException ignore) { } } } protected void initOpenEjb() throws Exception { long t0 = System.currentTimeMillis(); if (initialContext == null) { initalContextProperties = createInitialContextProperties(); // Need to set this System property to be able to do new // InitialContext() System.setProperty(Context.INITIAL_CONTEXT_FACTORY, initalContextProperties.getProperty(Context.INITIAL_CONTEXT_FACTORY)); initialContext = new InitialContext(initalContextProperties); } messagingTestBean = lookup(getMessagingTestBeanJndiName()); if (messagingTestBean == null) { throw new IllegalStateException("Couldn't find " + getMessagingTestBeanJndiName()); } log.info("OpenEJB initialized in: " + (System.currentTimeMillis() - t0) + " ms"); } protected String getMessagingTestBeanJndiName() { return "MessagingTestBeanLocal"; } protected Properties createInitialContextProperties() { Properties defaultProperties = new Properties(); defaultProperties.put(Context.INITIAL_CONTEXT_FACTORY, "org.apache.openejb.client.LocalInitialContextFactory"); // defaultProperties.setProperty("openejb.embedded.initialcontext.close", // "destroy"); // alternative deployment descriptors prefixed with test defaultProperties.put("openejb.altdd.prefix", "test"); defaultProperties.put("openejb.deployments.classpath.include", ".*sculptor.framework.test.*"); additionalInitialContextProperties(defaultProperties); Properties fileProperties = loadInitialContextPropertiesFromFile(defaultProperties); return fileProperties; } /** * possible to override to include more, or change, InitialContext * Properties */ protected void additionalInitialContextProperties(Properties defaultProperties) { } @SuppressWarnings("unchecked") protected Properties loadInitialContextPropertiesFromFile(Properties defaultProperties) { String resourceName = "/openejb-test.properties"; InputStream resource = getClass().getResourceAsStream(resourceName); if (resource == null) { log.info("Didn't find properties file: " + resourceName); return defaultProperties; } else { Properties p = new Properties(); try { p.load(resource); } catch (IOException ignore) { log.warn("Couldn't load properties file: " + resourceName); } // it doesn't work to use defaultProperties as defaults to p for (Enumeration<String> iter = (Enumeration<String>) defaultProperties.propertyNames(); iter .hasMoreElements();) { String name = iter.nextElement(); Object value = p.get(name); if (value == null) { p.put(name, defaultProperties.get(name)); } else if ("".equals(value)) { p.remove(name); } } return p; } } @SuppressWarnings("unchecked") protected <T> T lookup(String name) throws NamingException { try { return (T) initialContext.lookup(name); } catch (NamingException e) { if (!name.endsWith("Local")) { try { return (T) initialContext.lookup(name + "Local"); } catch (NamingException ignore) { // throw first e } } // Retry with global lookup try { return (T) initialContext.lookup("openejb:Resource/" + name); } catch (NamingException ignore) { // throw first e } throw e; } } protected InitialContext getInitialContext() { return initialContext; } protected void initAnnotatedDependencies() throws NamingException { Field[] fields = getClass().getDeclaredFields(); for (Field each : fields) { initEjbAnnotatedDependency(each); initResouceAnnotatedDependency(each); } } protected void initEjbAnnotatedDependency(Field field) throws NamingException { EJB annotation = field.getAnnotation(EJB.class); if (annotation == null) { return; } String name = annotation.beanName(); if (name == null || name.equals("")) { name = field.getName(); } inject(field, name); } protected void initResouceAnnotatedDependency(Field field) throws NamingException { Resource annotation = field.getAnnotation(Resource.class); if (annotation == null) { return; } String name = annotation.mappedName(); if (name == null || name.equals("")) { name = field.getName(); } inject(field, name); } private void inject(Field field, String lookupName) throws NamingException { Object ejb = lookup(lookupName); try { field.setAccessible(true); field.set(this, ejb); } catch (Exception e) { throw new RuntimeException("Could not inject dependency for " + lookupName + ": " + e.getMessage()); } } protected ServiceContext getServiceContext() { return serviceContext; } /** * @return temporary queue for the reply */ protected Destination sendMessage(Destination destination, String message) { return messagingTestBean.sendMessage(destination, message); } /** * @return queue for the reply, either as specified replyTo in message, or a * temporary queue */ protected Destination sendMessage(Destination destination, Message message) { return messagingTestBean.sendMessage(destination, message); } protected Message waitForReply(Destination replyDestination) { return waitForReply(replyDestination, messageReplyTimeout); } protected Message waitForReply(Destination replyDestination, int timeoutMillis) { Message result = messagingTestBean.waitForReply(replyDestination, timeoutMillis); if (result == null) { throw new RuntimeException("No reply within timeout: " + timeoutMillis + " ms"); } // wait little bit longer for transaction to complete try { long waitTime = Math.max(timeoutMillis / 20, 1000); Thread.sleep(waitTime); } catch (InterruptedException e) { } return result; } }