All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.apache.openejb.junit.context.OpenEjbTestContext Maven / Gradle / Ivy

There is a newer version: 4.7.5
Show newest version
/*
 * 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 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 testClazz
     */
    public OpenEjbTestContext(final Class clazz) {
        this(clazz, null);
    }

    /**
     * Constructs a context for a method
     *
     * @param testClazz
     * @param method
     */
    public OpenEjbTestContext(final Method method) {
        this(method, null);
    }

    /**
     * Constructs a context for a class
     *
     * @param testClazz
     */
    public OpenEjbTestContext(final Class clazz, final String securityRole) {
        this.clazz = clazz;
        this.securityRole = securityRole;
    }

    /**
     * Constructs a context for a method
     *
     * @param testClazz
     * @param method
     */
    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
    protected void finalize() throws Throwable {
        try {
            this.close();
        } catch (final Exception e) {
            //no-op
        } finally {
            super.finalize();
        }
    }

    @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; } }





© 2015 - 2025 Weber Informatics LLC | Privacy Policy