| /* |
| * 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.junit.context; |
| |
| import org.apache.openejb.OpenEJB; |
| import org.apache.openejb.OpenEJBRuntimeException; |
| import org.apache.openejb.api.LocalClient; |
| import org.apache.openejb.junit.ContextConfig; |
| import org.apache.openejb.junit.Property; |
| import org.apache.openejb.junit.RunTestAs; |
| import org.apache.openejb.junit.TestResource; |
| import org.apache.openejb.junit.TestResourceTypes; |
| import org.apache.openejb.loader.SystemInstance; |
| |
| import javax.naming.InitialContext; |
| import javax.naming.NamingException; |
| import java.io.FileNotFoundException; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.lang.reflect.Field; |
| import java.lang.reflect.Method; |
| import java.lang.reflect.Modifier; |
| import java.net.URLDecoder; |
| import java.util.Properties; |
| |
| /** |
| * To implement your own context, you need to create an implementation of TestContext |
| * which would configure the test when instructed. You can then use whatever method |
| * of configuration you choose. |
| */ |
| public class OpenEjbTestContext implements TestContext { |
| protected static final String REALM_PROPERTY_KEY = "openejb.authentication.realmName"; |
| |
| protected static final String LOGIN_CONFIG_RESOURCE = "/META-INF/openejb-test-login.config"; |
| |
| protected static final String DEFAULT_CONFIG_FILE_RESOURCE = "/META-INF/default-openejb-test-config.properties"; |
| |
| /** |
| * Properties object used to initialize InitialContext |
| */ |
| protected Properties contextConfig; |
| |
| /** |
| * Context's InitialContext |
| */ |
| private InitialContext initialContext; |
| |
| /** |
| * Test class |
| */ |
| private final Class<?> clazz; |
| |
| /** |
| * Method being run on test |
| */ |
| private Method method; |
| |
| /** |
| * Security role to execute as |
| */ |
| private String securityRole; |
| |
| /** |
| * Constructs a context for a class |
| * |
| * @param clazz |
| */ |
| public OpenEjbTestContext(final Class clazz) { |
| this(clazz, null); |
| } |
| |
| /** |
| * Constructs a context for a method |
| * |
| * @param method |
| */ |
| public OpenEjbTestContext(final Method method) { |
| this(method, null); |
| } |
| |
| /** |
| * Constructs a context for a class |
| * |
| * @param clazz |
| * @param securityRole |
| */ |
| public OpenEjbTestContext(final Class clazz, final String securityRole) { |
| this.clazz = clazz; |
| this.securityRole = securityRole; |
| } |
| |
| /** |
| * Constructs a context for a method |
| * |
| * @param method |
| * @param securityRole |
| */ |
| public OpenEjbTestContext(final Method method, final String securityRole) { |
| this.clazz = method.getDeclaringClass(); |
| this.method = method; |
| this.securityRole = securityRole; |
| } |
| |
| public void configureTest(final Object testObj) { |
| try { |
| if (testObj.getClass().isAnnotationPresent(LocalClient.class)) { |
| getInitialContext().bind("inject", testObj); |
| } |
| |
| // perform custom injections |
| performInjections(testObj); |
| } catch (final IOException e) { |
| throw new OpenEJBRuntimeException("Failed to load configuration.", e); |
| } catch (final NamingException e) { |
| throw new OpenEJBRuntimeException("Failed to configure object.", e); |
| } catch (final Exception e) { |
| throw new OpenEJBRuntimeException("Unknown error trying to configure object.", e); |
| } |
| } |
| |
| @Override |
| public void close() { |
| try { |
| initialContext.close(); |
| } catch (final NamingException e) { |
| // ignored |
| } |
| OpenEJB.destroy(); |
| SystemInstance.reset(); |
| } |
| |
| /** |
| * Returns this context's InitialContext, creating it if necessary. |
| * |
| * @return InitialContext for this TestContext |
| * @throws NamingException |
| */ |
| protected InitialContext getInitialContext() throws NamingException { |
| if (initialContext == null) { |
| // set the property for security realm RIGHT before we load the InitialContext |
| final String loginConfig = OpenEjbTestContext.class.getResource(LOGIN_CONFIG_RESOURCE).toExternalForm(); |
| System.setProperty("java.security.auth.login.config", URLDecoder.decode(loginConfig)); |
| |
| try { |
| final Properties config = getContextConfig(); |
| initialContext = new InitialContext(config); |
| } catch (final IOException e) { |
| throw new NamingException("Failed to load initial context configuration: " + e.getMessage()); |
| } |
| } |
| |
| return initialContext; |
| } |
| |
| /** |
| * Constructs the configuration needed to create the InitialContext. This will |
| * be determined from the class/method supplied to the constructor. |
| * |
| * @return |
| */ |
| protected Properties getContextConfig() throws IOException { |
| if (contextConfig != null) { |
| return contextConfig; |
| } |
| |
| final Properties env = new Properties(); |
| boolean loadedConfig = false; |
| |
| if (clazz.isAnnotationPresent(ContextConfig.class)) { |
| loadedConfig |= loadConfig(env, clazz.getAnnotation(ContextConfig.class)); |
| } |
| |
| if (method != null && method.isAnnotationPresent(ContextConfig.class)) { |
| loadedConfig |= loadConfig(env, method.getAnnotation(ContextConfig.class)); |
| } |
| |
| // no properties loaded, use the "default" configuration |
| if (!loadedConfig) { |
| final InputStream in = OpenEjbTestContext.class.getResourceAsStream(DEFAULT_CONFIG_FILE_RESOURCE); |
| if (in == null) { |
| throw new FileNotFoundException("Default configuration file not found. Specify configuration " + |
| "properties to initialize OpenEJB using @ContextConfig."); |
| } |
| env.load(in); |
| |
| // if it's still empty, something bad has happened, and OpenEJB won't initialize. Complain. |
| if (env.size() == 0) { |
| throw new IOException("Context configuration failed to load, so OpenEJB won't load either. Specify configuration " + |
| "properties for initializing OpenEJB using @ContextConfig."); |
| } |
| } |
| |
| configureSecurity(env); |
| |
| contextConfig = env; |
| return env; |
| } |
| |
| /** |
| * Interprets and loads InitialContext properties from the ContextConfig annotation |
| * |
| * @param env |
| * @param contextConfig |
| * @return true if any properties were loaded |
| */ |
| protected boolean loadConfig(final Properties env, final ContextConfig contextConfig) throws IOException { |
| boolean loadedConfig = false; |
| |
| loadedConfig = loadConfigFile(env, contextConfig); |
| loadedConfig |= loadConfigProperties(env, contextConfig); |
| |
| return loadedConfig; |
| } |
| |
| /** |
| * Loads the direct properties from the annotation configuration into the given Properties object |
| * |
| * @param env |
| * @param contextConfig |
| * @return true if any properties were loaded |
| */ |
| protected boolean loadConfigProperties(final Properties env, final ContextConfig contextConfig) { |
| boolean loadedConfig = false; |
| |
| if (contextConfig.properties().length > 0) { |
| for (final Property p : contextConfig.properties()) { |
| if (p.value() != null) { |
| loadedConfig = true; |
| Util.addProperty(env, p.value()); |
| } |
| } |
| } |
| |
| return loadedConfig; |
| } |
| |
| /** |
| * Loads the configuration file specified in the {@link org.apache.openejb.junit.ContextConfig} annotation |
| * into the specified Properties instance |
| * |
| * @param env |
| * @param contextConfig |
| * @return true if any properties were loaded |
| */ |
| protected boolean loadConfigFile(final Properties env, final ContextConfig contextConfig) |
| throws IOException { |
| // properties file |
| if (contextConfig.configFile().length() > 0) { |
| final InputStream in = clazz.getResourceAsStream(contextConfig.configFile()); |
| if (in == null) { |
| throw new FileNotFoundException("Cannot find resource '" + contextConfig.configFile() + "' in classpath: " + clazz.getName()); |
| } |
| env.load(in); |
| |
| return env.size() > 0; |
| } |
| |
| return false; |
| } |
| |
| /** |
| * Loads the security configuration into the given Properties object |
| * |
| * @param env |
| */ |
| protected void configureSecurity(final Properties env) { |
| // if a securityRole isn't already configured, use the RunTestAs annotation if available |
| if (securityRole == null) { |
| if (method != null && method.isAnnotationPresent(RunTestAs.class)) { |
| securityRole = method.getAnnotation(RunTestAs.class).value(); |
| } else if (clazz.isAnnotationPresent(RunTestAs.class)) { |
| securityRole = clazz.getAnnotation(RunTestAs.class).value(); |
| } |
| } |
| |
| if (securityRole != null) { |
| env.put(REALM_PROPERTY_KEY, "OpenEjbJunitSecurityRealm"); |
| env.put(InitialContext.SECURITY_PRINCIPAL, securityRole); |
| env.put(InitialContext.SECURITY_CREDENTIALS, "[no-password-needed]"); |
| } |
| } |
| |
| /** |
| * Performs any non-OpenEJB type injections on the test object. It will "prefer" |
| * a setter, and therefore I made it work on private fields as well. So if you |
| * are injecting to a private field and wish to have some control over it, create |
| * a setter according to the JavaBeans idioms. |
| * |
| * If the setter isn't found OR it fails, then an attempt will be made to set |
| * it directly, and a message will be printed when it fails. |
| */ |
| private void performInjections(final Object testObj) throws Exception { |
| for (final Field field : clazz.getDeclaredFields()) { |
| final Object injectValue = getInjectionValue(field); |
| |
| // no value determined, try next field |
| if (injectValue == null) { |
| continue; |
| } |
| |
| // now inject it through the setter |
| try { |
| final Method setterMethod = Util.findSetter(clazz, field, injectValue); |
| if (setterMethod != null) { |
| setterMethod.invoke(testObj, injectValue); |
| continue; |
| } |
| } catch (final Exception e) { |
| System.err.println("Failed to perform setter injection on: " + clazz.getCanonicalName() + "." + field.getName()); |
| e.printStackTrace(); |
| } |
| |
| // do direct injection |
| try { |
| if (!Modifier.isPublic(field.getModifiers())) { |
| field.setAccessible(true); |
| } |
| |
| field.set(testObj, injectValue); |
| } catch (final Exception e) { |
| throw new OpenEJBRuntimeException("Failed to inject on: " + clazz.getCanonicalName() + "." + field.getName(), e); |
| } |
| } |
| } |
| |
| /** |
| * Analyzes the field and returns any values which should be injected on it |
| * |
| * @param field |
| * @return reference to value to inject, or null if nothing should be injected |
| */ |
| protected Object getInjectionValue(final Field field) throws Exception { |
| // determine the value to inject |
| if (field.isAnnotationPresent(TestResource.class)) { |
| final TestResource resourceConfig = field.getAnnotation(TestResource.class); |
| final String resourceType = resourceConfig.value(); |
| |
| if (resourceType == null) { |
| throw new IllegalArgumentException("Null TestResource type '" + resourceType + |
| "' on field: " + clazz.getCanonicalName() + "." + field.getName()); |
| } else { |
| if (TestResourceTypes.CONTEXT_CONFIG.equals(resourceType)) { |
| return getContextConfig(); |
| } else if (TestResourceTypes.INITIALCONTEXT.equals(resourceType)) { |
| return getInitialContext(); |
| } else { |
| return getOtherTestResource(resourceConfig); |
| } |
| } |
| } |
| |
| return null; |
| } |
| |
| /** |
| * Override to perform custom resource types injection. This method will be called |
| * when whatever value was specified in the {@link TestResource} annotation wasn't |
| * understood by the {@link #performInjections(java.lang.Object) } method. This |
| * method will be called, supplying the annotation, and you can then interpret and |
| * create the value to be injected. By default this method just returns null. |
| * |
| * You can use this to inject values into annotated fields which contain custom |
| * values in their names. |
| * |
| * @param resourceConfig |
| * @return instance to inject into annotated field. |
| */ |
| protected Object getOtherTestResource(final TestResource resourceConfig) { |
| return null; |
| } |
| |
| /** |
| * @return the test class |
| */ |
| protected Class<?> getTestClass() { |
| return clazz; |
| } |
| |
| /** |
| * @return the test method for which this context was created |
| */ |
| protected Method getTestMethod() { |
| return method; |
| } |
| } |