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

org.wisdom.test.parents.DependencyInjector Maven / Gradle / Ivy

There is a newer version: 0.10.0
Show newest version
/*
 * #%L
 * Wisdom-Framework
 * %%
 * Copyright (C) 2013 - 2014 Wisdom Framework
 * %%
 * 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.
 * #L%
 */
package org.wisdom.test.parents;


import org.osgi.framework.BundleContext;
import org.osgi.framework.ServiceReference;
import org.ow2.chameleon.testing.helpers.OSGiHelper;
import org.wisdom.api.Controller;
import org.wisdom.api.router.Router;
import org.wisdom.api.templates.Template;

import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.List;

/**
 * Injects all @Inject fields found in the given object's class.
 * Support Bundle Context injection, Template injection and Service injection.
 *
 * Be aware that this class is heavily relying on reflection and type matching as we are in a different classloader.
 */
public class DependencyInjector {
    
    private static final String INJECTION_ERROR = "Cannot inject a template in ";
    private static final String VALUE = "value";
    
    private DependencyInjector(){
        //Hide implicit constructor
    }

    /**
     * Injects values in the annotated fields.
     * @param object the test object
     * @param helper the OSGi Helper letting us accessing services
     */
    public static void inject(Object object, OSGiHelper helper) {
        Field[] fields = object.getClass().getFields();
        for (Field field : fields) {
            if (field.getAnnotation(javax.inject.Inject.class) != null) {
                inject(object, field, helper);
            }
        }
        fields = object.getClass().getDeclaredFields();
        for (Field field : fields) {
            if (field.getAnnotation(javax.inject.Inject.class) != null) {
                inject(object, field, helper);
            }
        }
    }


    /**
     * Injects a value in the field 'field' from the object 'object'.
     * @param object the test object
     * @param field the field to inject.
     * @param helper the OSGi Helper letting us accessing services
     */
    public static void inject(Object object, Field field, OSGiHelper helper) {
        if (field.getType().getName().equals(BundleContext.class.getName())) {
            set(object, field, helper.getContext());
        } else if (field.getType().getName().equals(Template.class.getName())) {
            String name = readNameAnnotation(field);
            String filter = readFilterAnnotation(field);
            if (name == null  && filter == null) {
                throw new ExceptionInInitializerError(INJECTION_ERROR + field.getName() + ", " +
                        "the @Name annotation or @Filter annotation are required to indicate the template to inject");
            }
            if (name != null  && filter != null) {
                throw new ExceptionInInitializerError(INJECTION_ERROR + field.getName() + ", " +
                        "both @Name annotation and @Filter annotations are used to indicate the template to " +
                        "inject, please use only one");
            }
            if (name != null) {
                Object template = waitForService(helper, Template.class, "(name=" + name + ")");
                if (template == null) {
                    throw new ExceptionInInitializerError(INJECTION_ERROR+ field.getName() + ", " +
                            "cannot find a template with name=" + name);
                }
                set(object, field, template);
            }
            if (filter != null) {
                Object template = waitForService(helper, Template.class, filter);
                if (template == null) {
                    throw new ExceptionInInitializerError(INJECTION_ERROR + field.getName() + ", " +
                            "cannot find a template matching the given filter: " + filter);
                }
                set(object, field, template);
            }
        } else if (field.getType().getName().equals(Router.class.getName())) {
            Object router = waitForService(helper, Router.class, null);
            set(object, field, router);
        } else if (isController(field.getType())) {
            // Controller are identified by their classname (matching the factory.name).
            String filter = String.format("(factory.name=%s)", field.getType().getName());
            Object controller = waitForService(helper, Controller.class, filter);
            if (controller == null) {
                throw new ExceptionInInitializerError("Cannot inject a controller in '" + field.getName() + "' - " +
                        "cannot find a controller matching the given filter: " + filter);
            }
            set(object, field, controller);
        } else {
            // Service
            String filter = readFilterAnnotation(field);
            Object service = waitForService(helper, field.getType(), filter);
            if (service == null) {
                throw new ExceptionInInitializerError("Cannot inject a service in " + field.getName() + ", " +
                        "cannot find a service publishing " + field.getType().getName() + " matching the filter " +
                        filter);
            }
            set(object, field, service);
        }
    }

    private static boolean isController(Class type) {
        List classes = traverseHierarchy(type);
        return classes.contains(Controller.class.getName());
    }

    private static List traverseHierarchy(Class type) {
        List list = new ArrayList<>();
        list.add(type.getName());
        for (Class clazz : type.getInterfaces()) {
            list.addAll(traverseHierarchy(clazz));
        }
        if (type.getSuperclass() != null) {
            list.addAll(traverseHierarchy(type.getSuperclass()));
        }
        return list;
    }

    private static String readNameAnnotation(Field field) {
        // We can't access the annotation directly because of the classloading.
        // We retrieve the value by reflection.
        for (Annotation annotation : field.getAnnotations()) {
            if (annotation.annotationType().getName().endsWith(Name.class.getName())) {
                try {
                    Method method = annotation.getClass().getMethod(VALUE);
                    return (String) method.invoke(annotation);
                } catch (Exception e) { //NOSONAR
                    throw new ExceptionInInitializerError("Cannot retrieve the value of the @Name annotation");
                }
            }
        }
        return null;
    }

    private static String readFilterAnnotation(Field field) {
        // We can't access the annotation directly because of the classloading.
        // We retrieve the value by reflection.
        for (Annotation annotation : field.getAnnotations()) {
            if (annotation.annotationType().getName().endsWith(Filter.class.getName())) {
                try {
                    Method method = annotation.getClass().getMethod(VALUE);
                    return (String) method.invoke(annotation);
                } catch (Exception e) { //NOSONAR
                    throw new ExceptionInInitializerError("Cannot retrieve the value of the @Filter annotation");
                }
            }
        }
        return null;
    }

    private static void set(Object object, Field field, Object value) {
        field.setAccessible(true);
        try {
            field.set(object, value);
        } catch (IllegalAccessException e) {
            throw new RuntimeException("Error when injecting " + value + " in " + field, e);
        }
    }

    /**
     * This method is used until the new OSGi helper are released.
     * @param helper the helper
     * @param clazz the service interface
     * @param filter the filter (optional)
     * @return the service object, {@literal null} if not found
     */
    private static Object waitForService(OSGiHelper helper, Class clazz, String filter) {
        ServiceReference ref = helper.waitForService(clazz.getName(), filter, 10000, false);
        if (ref == null) {
            return null;
        } else {
            return helper.getServiceObject(ref);
        }
    }


}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy