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

Java tutorial

Introduction

Here is the source code for org.mqnaas.core.impl.ApplicationProxyHolder.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.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;

import org.apache.commons.lang3.ClassUtils;
import org.mqnaas.core.api.IApplication;
import org.mqnaas.core.api.ICapability;
import org.mqnaas.core.api.IExecutionService;
import org.mqnaas.core.api.IResource;
import org.mqnaas.core.api.IService;

import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;

/**
 * Holds a proxy for all the interfaces implemented by given application that redirects all calls to the interfaces to the ExecutionService.
 * 
 * @author Isart Canyameres Gimenez (i2cat)
 * 
 */
public class ApplicationProxyHolder {

    private IApplication instance;

    private IExecutionService executionService;

    // Holds the services available from this application instance, ordered by the interfaces they belong to
    private Multimap<Class<? extends IApplication>, IInternalService> internalServices;

    private IApplication proxy;

    // InvocationHandler used by the proxy to execute the services offered by this application instance
    private ExecutionRelayingInvocationHandler invocationHandler;

    public ApplicationProxyHolder(Class<? extends IApplication> clazz, IApplication instance) {
        if (instance == null)
            throw new IllegalArgumentException("Argument instance must not be null");

        this.instance = instance;

        initProxyRelatedDataStructures(clazz, instance);
    }

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

    /**
     * Sets the given {@link IExecutionService} as the execution service to be used to execute the services
     * 
     * @param executionService
     *            The execution service to execute all services.
     */
    public void setExecutionService(IExecutionService executionService) {
        this.executionService = executionService;
    }

    /**
     * Returns a proxy for all the interfaces implemented by given application that redirects all calls to the interfaces to the ExecutionService.
     * 
     * @return proxy for all the interfaces implemented by given application that redirects all calls to the interfaces to the ExecutionService.
     */
    public IApplication getProxy() {
        return proxy;
    }

    /**
     * 
     * @return services available from this application instance, ordered by the interfaces they belong to
     */
    public Multimap<Class<? extends IApplication>, IInternalService> getServices() {
        return internalServices;
    }

    private void initProxyRelatedDataStructures(Class<? extends IApplication> clazz, IApplication instance) {

        internalServices = ArrayListMultimap.create();

        Collection<Class<? extends IApplication>> appClasses = computeApplications(clazz);
        // an application without interfaces is not able to offer services
        // applications are forced to implement interfaces extending IApplication in order to publish services
        if (appClasses.isEmpty())
            return;

        // 1. Create the services of the interfaces (backed by the instance)
        for (Class<? extends IApplication> interfaze : appClasses) {
            for (Method method : interfaze.getMethods()) {
                internalServices.put(interfaze, new Service(method, instance, interfaze));
            }
        }

        // 2. Create the InvocationHandler used by the proxy
        invocationHandler = new ExecutionRelayingInvocationHandler(internalServices.values());

        // 3. Create a proxy for all the interfaces implemented by the application to redirect all calls to the interfaces to the ExecutionService
        // we use the ClassLoader of instance because it is the only one that has for sure access to all (implemented) interfaces.
        proxy = (IApplication) Proxy.newProxyInstance(instance.getClass().getClassLoader(),
                appClasses.toArray(new Class[appClasses.size()]), invocationHandler);

        if (instance instanceof IExecutionService) {
            executionService = (IExecutionService) instance;
        }
    }

    /**
     * Retrieves interfaces extending IApplication which are implemented by given clazz
     * 
     * @param clazz
     * @return interfaces extending IApplication which are implemented by given clazz
     */
    private static Collection<Class<? extends IApplication>> computeApplications(
            Class<? extends IApplication> clazz) {

        Collection<Class<? extends IApplication>> applicationClasses = new ArrayList<Class<? extends IApplication>>();
        for (Class<?> interfaze : ClassUtils.getAllInterfaces(clazz)) {
            // Ignore the IApplication interface itself
            if (interfaze.equals(IApplication.class))
                continue;
            // Ignore the ICapability interface itself
            if (interfaze.equals(ICapability.class))
                continue;

            // Ignore all interfaces that do not extend IApplication
            if (!IApplication.class.isAssignableFrom(interfaze))
                continue;

            // Now do the cast: this one is safe because we explicitly checked it before
            @SuppressWarnings("unchecked")
            Class<? extends IApplication> applicationInterface = (Class<? extends IApplication>) interfaze;
            applicationClasses.add(applicationInterface);
        }

        return applicationClasses;
    }

    /**
     * Relays the call to a capability implementations (an OSGi service) to an MQNaaS Service.
     * 
     * @author Georg Mansky-Kummert (i2CAT)
     * 
     */
    private class ExecutionRelayingInvocationHandler implements InvocationHandler {

        private Map<Method, IInternalService> relays;

        public ExecutionRelayingInvocationHandler(Collection<IInternalService> relayedServices) {

            relays = new HashMap<Method, IInternalService>();

            for (IInternalService relayedService : relayedServices) {
                relays.put(relayedService.getMetadata().getMethod(), relayedService);
            }

        }

        /**
         * Sets the given {@link IResource} to all managed relayed {@link IService}s.
         * 
         * @param resource
         *            The Resource handed to the Services.
         */
        public void setResource(IResource resource) {

            for (IInternalService relayedService : relays.values()) {
                relayedService.setResource(resource);
            }
        }

        @Override
        public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {

            IInternalService service = relays.get(method);

            Object result;

            try {
                if (service == null) {
                    // A method was called for which no relay exists, e.g.
                    // toString(), it will be invoked directly
                    result = method.invoke(instance, args);
                } else {
                    if (service.getMetadata().getName().equals("execute") && IExecutionService.class
                            .isAssignableFrom(service.getMetadata().getApplicationClass())) {
                        // This avoid looping infinitely through proxy calls... TODO add more details
                        result = service.execute(args);
                    } else {
                        result = executionService.execute(service, args);
                    }
                }

            } catch (InvocationTargetException e) {
                throw e.getCause();
            }

            return result;
        }
    }

}