org.mqnaas.core.impl.ApplicationInstance.java Source code

Java tutorial

Introduction

Here is the source code for org.mqnaas.core.impl.ApplicationInstance.java

Source

package org.mqnaas.core.impl;

/*
 * #%L
 * MQNaaS :: Core
 * %%
 * Copyright (C) 2007 - 2015 Fundaci Privada i2CAT, Internet i Innovaci a Catalunya
 * %%
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation, either version 3 of the
 * License, or (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Lesser Public License for more details.
 * 
 * You should have received a copy of the GNU General Lesser Public
 * License along with this program.  If not, see
 * <http://www.gnu.org/licenses/lgpl-3.0.html>.
 * #L%
 */

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Set;

import org.mqnaas.core.api.IApplication;
import org.mqnaas.core.api.IExecutionService;
import org.mqnaas.core.api.IResource;
import org.mqnaas.core.api.IService;
import org.mqnaas.core.api.annotations.DependingOn;
import org.mqnaas.core.impl.dependencies.ApplicationInstanceLifeCycleState;
import org.mqnaas.core.impl.utils.ReflectionUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import com.google.common.base.Predicate;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Iterables;
import com.google.common.collect.Multimap;

//TODO Redo the security and instantiation aspects

/**
 * It manages an application instance's
 * <ul>
 * <li><b>class</b>: to represent it as long as no instance exists)</li>
 * <li><b>instance</b>: which could be given when initializing</li>
 * <li><b>two types of dependencies</b>
 * <ol>
 * <li><i>pending</i>: the unresolved application dependencies</li>
 * <li><i>resolved</i>: already resolved application dependencies</li>
 * </ol>
 * </ul>
 * 
 * Dependencies are identified using {@link DependingOn} field annotation.
 * 
 * <p>
 * Provides a proxy of the instance to be used when injecting this application (through {@link ApplicationProxyHolder})
 * </p>
 * <p>
 * Provides the {@link IService}s for each {@link IApplication} interface implemented by the represented application class (through
 * {@link ApplicationProxyHolder}).
 * </p>
 * 
 * @author Georg Mansky-Kummert (i2CAT)
 * @author Isart Canyameres Gimenez (i2cat)
 */
public class ApplicationInstance {

    private final Logger log = LoggerFactory.getLogger(getClass());

    protected Class<? extends IApplication> clazz;

    private IApplication instance;

    private Dependencies dependencies;

    // this field is used through reflection (take caution when refactoring)
    private IExecutionService executionService;

    private ApplicationProxyHolder proxyHolder;

    private ApplicationInstanceLifeCycleState state;

    public ApplicationInstance(Class<? extends IApplication> clazz) {
        this(clazz, null);
    }

    public ApplicationInstance(Class<? extends IApplication> clazz, IApplication instance) {

        this.clazz = clazz;

        this.instance = instance;

        dependencies = new Dependencies(clazz, getInstance());

        proxyHolder = new ApplicationProxyHolder(clazz, getInstance());

        setState(ApplicationInstanceLifeCycleState.INSTANTIATED);
    }

    public Class<? extends IApplication> getClazz() {
        return clazz;
    }

    public IApplication getInstance() {
        if (instance == null) {
            try {
                instance = clazz.newInstance();
            } catch (InstantiationException e) {
                // ignore for now
                log.error("Failed to instantiate application " + clazz.getName(), e);
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // ignore for now
                log.error("Failed to instantiate application " + clazz.getName(), e);
                e.printStackTrace();
            }
        }

        return instance;
    }

    /**
     * @return the injectedDependencies. Returned collection is not the live one, but a copy.
     */
    public Collection<ApplicationInstance> getInjectedDependencies() {
        Collection<ApplicationInstance> injected = new ArrayList<ApplicationInstance>();
        for (Dependency resolved : dependencies.getResolvedDependencies()) {
            injected.add(resolved.getResolvedWith());
        }
        return injected;
    }

    /**
     * @return the state
     */
    public ApplicationInstanceLifeCycleState getState() {
        return state;
    }

    /**
     * @param state
     *            the state to set
     */
    public void setState(ApplicationInstanceLifeCycleState state) {
        this.state = state;
    }

    /**
     * Returns the currently pending dependencies, e.g. the missing capability classes.
     */
    public Collection<Class<? extends IApplication>> getPendingClasses() {
        return dependencies.getPendingClasses();
    }

    /**
     * Returns the currently resolved dependencies, e.g. the already initialized capability classes.
     */
    public Collection<Class<? extends IApplication>> getResolvedClasses() {
        return dependencies.getResolvedClasses();
    }

    public boolean isResolved() {
        return dependencies.isResolved();
    }

    /**
     * Resolves all dependencies that can be satisfied by the given {@link ApplicationInstance}.
     * 
     * @param potentialDependency
     * @return whether the internal state of this instance has changed after this call or not (after the call is using potentialDependency and was not
     *         before)
     */
    public boolean resolve(ApplicationInstance dependency) {
        Collection<Dependency> affected = dependencies.resolveDependencies(dependency);

        Dependency execServiceDep = getExecutionServiceDependency(affected);
        if (execServiceDep != null) {
            // execution service has been resolved
            proxyHolder.setExecutionService(executionService);
        }

        return !affected.isEmpty();
    }

    /**
     * Unresolves all dependencies that are being resolved with the given {@link CapabilityInstance}.
     * 
     * @param potentialDependency
     * @return whether the internal state of this instance has changed after this call or not (was using given potential dependency and after the call
     *         is no longer)
     */
    public boolean unresolve(ApplicationInstance dependency) {
        Collection<Dependency> affected = dependencies.unresolveDependencies(dependency);

        Dependency execServiceDep = getExecutionServiceDependency(affected);
        if (execServiceDep != null) {
            // execution service has been unresolved
            proxyHolder.setExecutionService(null);
        }
        return !affected.isEmpty();
    }

    /**
     * Unresolves all currently resolved dependencies
     * 
     */
    public void unresolveAllDependencies() {
        // Iterator-safe implementation for the following:
        // for (ApplicationInstance app: getInjectedDependencies()) unresolve(app);
        // Due to unresolve producing changes in the collections that backs up the foreach iterator, commented code is not safe
        // Copying injectedDependencies in another collection and iterate over that one solves the issue
        Collection<ApplicationInstance> injected = new HashSet<ApplicationInstance>(getInjectedDependencies());
        for (ApplicationInstance app : injected) {
            unresolve(app);
        }
    }

    // public void initServices() {
    // initInstanceServicesAndProxy(null);
    // }

    public void stopServices() {
        clearInstanceServicesAndProxy();
    }

    public Multimap<Class<? extends IApplication>, IService> getServices() {
        Multimap<Class<? extends IApplication>, IInternalService> internalServices = proxyHolder.getServices();
        Multimap<Class<? extends IApplication>, IService> services = ArrayListMultimap
                .create(internalServices.size(), 3);
        services.putAll(internalServices);
        return services;
    }

    /**
     * Returns all application interfaces implemented by the represented application
     * 
     */
    public Collection<Class<? extends IApplication>> getApplications() {
        Set<Class<? extends IApplication>> appsCopy = new HashSet<Class<? extends IApplication>>();
        appsCopy.addAll(getServices().keySet());
        return appsCopy;
    }

    /**
     * Sets the given {@link IResource} as the resource used when executing the services.
     * 
     * @param resource
     *            The resources used when executing services.
     */
    protected void setResource(IResource resource) {
        proxyHolder.setResource(resource);
    }

    protected void clearInstanceServicesAndProxy() {

        // 1. Clear proxy
        proxyHolder = null;

        // 2. Clear the instance
        instance = null;
    }

    public IApplication getProxy() {
        return proxyHolder.getProxy();
    }

    private Dependency getExecutionServiceDependency(Collection<Dependency> dependencies) {
        for (Dependency dep : dependencies) {
            if (dep.getInstance() == this && dep.getFieldType().equals(IExecutionService.class))
                return dep;
        }
        return null;
    }

    @Override
    public String toString() {

        StringBuilder sb = new StringBuilder();
        sb.append("Application ").append(clazz.getSimpleName());
        sb.append(" [pending=(");
        int i = 0;
        for (Class<? extends IApplication> clazz : getPendingClasses()) {
            if (i > 0)
                sb.append(", ");
            sb.append(clazz.getSimpleName());
            i++;
        }

        sb.append("), resolved=(");
        i = 0;
        for (Class<? extends IApplication> clazz : getResolvedClasses()) {
            if (i > 0)
                sb.append(", ");
            sb.append(clazz.getSimpleName());
            i++;
        }

        sb.append(")]");

        return sb.toString();
    }

    /**
     * 
     * @author Georg Mansky-Kummert (i2CAT)
     * 
     */
    private class Dependencies extends ArrayList<Dependency> {

        private static final long serialVersionUID = 1L;

        public Dependencies(Class<? extends IApplication> clazz, IApplication instance) {

            super();

            for (Field field : ReflectionUtils.getAnnotationFields(clazz, DependingOn.class)) {

                if (!(IApplication.class.isAssignableFrom(field.getType()))) {
                    throw new IllegalArgumentException("In " + clazz.getName() + " " + field.getType().getName()
                            + " does not implement " + IApplication.class.getName()
                            + " and can therefore not be used as a dependency.");
                }

                // The following cast is safe, because it was explicitly checked above
                @SuppressWarnings("unchecked")
                Class<? extends IApplication> type = (Class<? extends IApplication>) field.getType();

                add(new Dependency(field, type, instance));
            }

            if (!IExecutionService.class.isAssignableFrom(clazz)) {
                // add executionService dependency
                try {
                    add(new Dependency(ApplicationInstance.class.getDeclaredField("executionService"),
                            IExecutionService.class, ApplicationInstance.this));
                } catch (SecurityException e) {
                    // This exception should never happen (a class should has access to its declared private fields)
                    log.error(
                            "Error populating application dependencies. Unable to define execution service dependency: ",
                            e);
                } catch (NoSuchFieldException e) {
                    // This exception should never happen (unless a programmer removes/renames executionService field)
                    log.error(
                            "Error populating application dependencies. Unable to define execution service dependency: ",
                            e);
                }
            }

        }

        public Collection<Class<? extends IApplication>> getPendingClasses() {
            Collection<Class<? extends IApplication>> pendingClasses = new HashSet<Class<? extends IApplication>>();
            for (Dependency dep : getPendingDependencies()) {
                pendingClasses.add(dep.getFieldType());
            }
            return pendingClasses;
        }

        public Collection<Class<? extends IApplication>> getResolvedClasses() {
            Collection<Class<? extends IApplication>> resolvedClasses = new HashSet<Class<? extends IApplication>>();
            for (Dependency dep : getResolvedDependencies()) {
                resolvedClasses.add(dep.getFieldType());
            }
            return resolvedClasses;
        }

        private Iterable<Dependency> getResolvedDependencies() {
            Predicate<Dependency> isResolved = new Predicate<Dependency>() {
                @Override
                public boolean apply(Dependency dep) {
                    return dep.isResolved();
                }
            };
            return Iterables.filter(this, isResolved);
        }

        private Iterable<Dependency> getPendingDependencies() {
            Predicate<Dependency> isPending = new Predicate<Dependency>() {
                @Override
                public boolean apply(Dependency dep) {
                    return !dep.isResolved();
                }
            };
            return Iterables.filter(this, isPending);
        }

        public boolean isResolved() {
            for (Dependency dep : this) {
                if (!dep.isResolved())
                    return false;
            }
            return true;
        }

        /**
         * Unresolves all dependencies that are being resolved with the given {@link CapabilityInstance}.
         * 
         * @param potentialDependency
         * @return whether the internal state of this instance has changed after this call or not (was using given potential dependency and after the
         *         call is no longer)
         */
        private Collection<Dependency> unresolveDependencies(ApplicationInstance potentialDependency) {
            Collection<Dependency> affected = new ArrayList<Dependency>();

            for (Dependency dep : getResolvedDependencies()) {
                if (dep.getResolvedWith() == potentialDependency) {
                    // dependency is being resolved with potentialDependency
                    dep.unresolve();
                    affected.add(dep);
                }
            }
            return affected;
        }

        /**
         * Resolves all dependencies that can be satisfied by the given {@link ApplicationInstance}.
         * 
         * @param potentialDependency
         * @return whether the internal state of this instance has changed after this call or not (after the call is using potentialDependency and was
         *         not before)
         */
        private Collection<Dependency> resolveDependencies(ApplicationInstance potentialDependency) {

            Collection<Dependency> affected = new ArrayList<Dependency>();

            for (Class<? extends IApplication> capabilityClass : potentialDependency.getApplications()) {

                for (Dependency dep : getPendingDependencies()) {
                    if (dep.isResolvedBy(capabilityClass)) {
                        dep.resolveWith(potentialDependency);
                        affected.add(dep);
                    }
                }
            }
            return affected;
        }

    }

    /**
     * Represents a dependency in an instance that is to be resolved by an ApplicationInstance. Dependencies are identified by its declared type and
     * the field that holds it in the instance.
     * 
     * @author Isart Canyameres Gimenez (i2cat)
     * 
     */
    private class Dependency {

        // the field identifying this Dependency
        private Field field;
        // the type of this Dependency
        private Class<? extends IApplication> fieldType;
        // the instance having this Dependency
        private Object instance;

        // ApplicationInstance this Dependency is resolvedWith (the field is set with a proxy, not the ApplicationInstance itself)
        private ApplicationInstance resolvedWith;

        public Dependency(Field field, Class<? extends IApplication> type, Object instance) {
            this.field = field;
            this.fieldType = type;
            this.instance = instance;
        }

        /**
         * Tells whether this Dependency can be resolved with given capabilityClass or not.
         * 
         * @param capabilityClass
         * @return
         */
        public boolean isResolvedBy(Class<? extends IApplication> capabilityClass) {
            // FIXME shouldn't it be is assignable from?
            return getFieldType().equals(capabilityClass);
        }

        public void resolveWith(ApplicationInstance potentialDependency) {
            log.debug("Resolving dependency of field {}.{} with {}", clazz.getSimpleName(), field.getName(),
                    potentialDependency);
            resolve(potentialDependency.getProxy());
            setResolvedWith(potentialDependency);
        }

        public void unresolve() {
            log.debug("Unresolving dependency of field {}.{}", clazz.getSimpleName(), field.getName());
            resolve(null);
            setResolvedWith(null);
        }

        private void resolve(Object value) {
            // Initialize the field of the application or capability
            // TODO Security implications?
            try {
                field.setAccessible(true);
                field.set(instance, value);
            } catch (IllegalArgumentException e) {
                // ignore for now
                e.printStackTrace();
            } catch (IllegalAccessException e) {
                // ignore for now
                e.printStackTrace();
            }
        }

        /**
         * @return the fieldType
         */
        public Class<? extends IApplication> getFieldType() {
            return fieldType;
        }

        /**
         * @return the instance
         */
        public Object getInstance() {
            return instance;
        }

        /**
         * @return the resolvedWith
         */
        public ApplicationInstance getResolvedWith() {
            return resolvedWith;
        }

        /**
         * @param resolvedWith
         *            the resolvedWith to set
         */
        public void setResolvedWith(ApplicationInstance resolvedWith) {
            this.resolvedWith = resolvedWith;
        }

        public boolean isResolved() {
            return getResolvedWith() != null;
        }

        @Override
        public String toString() {
            StringBuilder sb = new StringBuilder();
            sb.append("Dependency [");
            sb.append("type=").append(fieldType);
            sb.append(", name=").append(field.getName());
            sb.append(", instance=").append(instance.getClass().getName());
            sb.append("]");
            return sb.toString();
        }

    }
}