Enterprise Java
Tomcat Context JUnit @Rule
A first draft of a JUnit @Rule that create the test context. This can be used with the Spring context rule for this post to create a complete Spring context for integration tests.
import org.apache.commons.dbcp.BasicDataSource; import org.apache.log4j.Logger; import org.junit.rules.TestRule; import org.junit.runner.Description; import org.junit.runners.model.Statement; import org.w3c.dom.Document; import org.w3c.dom.Element; import org.w3c.dom.NodeList; import javax.naming.Context; import javax.naming.InitialContext; import javax.naming.NamingException; import javax.sql.DataSource; import javax.xml.parsers.DocumentBuilder; import javax.xml.parsers.DocumentBuilderFactory; import java.io.*; import java.lang.reflect.Method; import java.sql.Driver; import java.sql.DriverManager; /** * Creates an context for tests using an Apache Tomcat server.xml. * * https://blogs.oracle.com/randystuph/entry/injecting_jndi_datasources_for_junit * * @author alex.collins */ public class TomcatContextRule implements TestRule { public static final Logger LOGGER = Logger.getLogger(CatalinaContextRule.class); /** * Creates all the sub-contexts for a name. */ public static void createSubContexts(Context ctx, String name) { String subContext = ''; for (String x : name.substring(0, name.lastIndexOf('/')).split('/')) { subContext += x; try { ctx.createSubcontext(subContext); } catch (NamingException e) { // nop } subContext += '/'; } } private final File serverXml; public TomcatContextRule(File serverXml, Object target) { if (serverXml == null || !serverXml.isFile()) {throw new IllegalArgumentException();} if (target == null) {throw new IllegalArgumentException();} this.serverXml = serverXml; } public Statement apply(final Statement statement, Description description) { return new Statement() { @Override public void evaluate() throws Throwable { createInitialContext(); try { statement.evaluate(); } finally { destroyInitialContext(); } } }; } private void createInitialContext() throws Exception { LOGGER.info('creating context'); System.setProperty(Context.INITIAL_CONTEXT_FACTORY, org.apache.naming.java.javaURLContextFactory.class.getName()); System.setProperty(Context.URL_PKG_PREFIXES, 'org.apache.naming'); final InitialContext ic = new InitialContext(); createSubContexts(ic, 'java:/comp/env'); final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance(); final DocumentBuilder builder = factory.newDocumentBuilder(); final Document document = builder.parse(serverXml); // create Environment { final NodeList envs = document.getElementsByTagName('Environment'); for (int i = 0; i < envs.getLength(); i++) { final Element env = (Element)envs.item(i); // must be Element final String name = 'java:comp/env/' + env.getAttribute('name'); final Object instance = Class.forName(env.getAttribute('type')).getConstructor(String.class) .newInstance(env.getAttribute('value')); LOGGER.info('binding ' + name + ' <' + instance + '>'); createSubContexts(ic, name); ic.bind(name, instance); } } // Resource { final NodeList resources = document.getElementsByTagName('Resource'); for (int i = 0; i < resources.getLength(); i++) { final Element resource = (Element)resources.item(i); // must be Element final String name = 'java:comp/env/' + resource.getAttribute('name'); final Class<?> type = Class.forName(resource.getAttribute('type')); final Object instance; if (type.equals(DataSource.class)) { { @SuppressWarnings('unchecked') // this mus be driver? final Class<? extends Driver> driverClass = (Class<? extends Driver>) Class.forName(resource.getAttribute('driverClassName')); DriverManager.registerDriver(driverClass.newInstance()); } final BasicDataSource dataSource = new BasicDataSource(); // find all the bean attributes and set them use some reflection for (Method method : dataSource.getClass().getMethods()) { if (!method.getName().matches('^set.*')) {continue;} final String x = method.getName().substring(3, 4).toLowerCase() + method.getName().substring(4); if (!resource.hasAttribute(x)) {continue;} Class<?> y = method.getParameterTypes()[0]; // might be primitive if (y.isPrimitive()) { if (y.getName().equals('boolean')) y = Boolean.class; if (y.getName().equals('byte')) y = Byte.class; if (y.getName().equals('char')) y = Character.class; if (y.getName().equals('double')) y = Double.class; if (y.getName().equals('float')) y = Float.class; if (y.getName().equals('int')) y = Integer.class; if (y.getName().equals('long')) y = Long.class; if (y.getName().equals('short')) y = Short.class; if (y.getName().equals('void')) y = Void.class; } method.invoke(dataSource, y.getConstructor(String.class).newInstance(resource.getAttribute(x))); } instance = dataSource; } else { // not supported, yet... throw new AssertionError('type ' + type + ' not supported'); } LOGGER.info('binding ' + name + ' <' + instance + '>'); createSubContexts(ic, name); ic.bind(name, instance); } } } private void destroyInitialContext() { System.clearProperty(Context.INITIAL_CONTEXT_FACTORY); System.clearProperty(Context.URL_PKG_PREFIXES); LOGGER.info('context destroyed'); } }
For example:
@Rule public TestRule rules = RuleChain.outerRule(new CatalinaContextRule(new File(getClass().getResource('/server.xml').getFile()), this)) .around(new ContextRule(new String[] {'/applicationContext.xml'}, this));
This code is on Github.
Reference: Tomcat Context JUnit @Rule from our JCG partner Alex Collins at the Alex Collins ‘s blog blog.