Package org.apache.felix.ipojo.handler.temporal

Source Code of org.apache.felix.ipojo.handler.temporal.TemporalDependency$ProxyFactory

/*
* 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.felix.ipojo.handler.temporal;

import java.lang.reflect.Array;
import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;

import org.apache.felix.ipojo.FieldInterceptor;
import org.apache.felix.ipojo.MethodInterceptor;
import org.apache.felix.ipojo.Nullable;
import org.apache.felix.ipojo.PrimitiveHandler;
import org.apache.felix.ipojo.handler.temporal.ServiceUsage.Usage;
import org.apache.felix.ipojo.handlers.dependency.NullableObject;
import org.apache.felix.ipojo.util.DependencyModel;
import org.osgi.framework.BundleContext;
import org.osgi.framework.Filter;
import org.osgi.framework.ServiceReference;

/**
* Temporal dependency. A temporal dependency waits (block) for the availability
* of the service. If no provider arrives in the specified among of time, a
* runtime exception is thrown.
* @author <a href="mailto:dev@felix.apache.org">Felix Project Team</a>
*/
public class TemporalDependency extends DependencyModel implements
        FieldInterceptor, MethodInterceptor {

    /**
     * The timeout.
     */
    private long m_timeout;

    /**
     * The default implementation.
     */
    private String m_di;

    /**
     * The {@link Nullable} object or Default-Implementation instance if used.
     */
    private Object m_nullableObject;

    /**
     * The handler managing this dependency.
     */
    private PrimitiveHandler m_handler;

    /**
     * The timeout policy. Null injects null, {@link Nullable} injects a nullable object or
     * an array with a nullable object, Default-Implementation injects an object
     * created from the specified injected implementation or an array with it
     * Empty array inject an empty array (must be an aggregate dependency) No
     * policy (0) throw a runtime exception when the timeout occurs *
     */
    private int m_policy;
   
    /**
     * The dependency is injected as a collection.
     * The field must be of the {@link Collection} type
     */
    private boolean m_collection;
   
    /**
     * Enables the proxy mode.
     */
    private boolean m_proxy;
   
    /**
     * Service Usage (Thread Local).
     */
    private ServiceUsage m_usage;

    /**
     * The proxy object.
     * This field is used for scalar proxied temporal dependency.
     */
    private Object m_proxyObject;


    /**
     * Creates a temporal dependency.
     * @param spec the service specification
     * @param agg is the dependency aggregate ?
     * @param collection the dependency field is a collection
     * @param proxy enable the proxy-mode
     * @param filter the LDAP filter
     * @param context service context
     * @param timeout timeout
     * @param handler Handler managing this dependency
     * @param defaultImpl class used as default-implementation
     * @param policy onTimeout policy
     */
    public TemporalDependency(Class spec, boolean agg, boolean collection, boolean proxy, Filter filter,
            BundleContext context, long timeout, int policy,
            String defaultImpl, TemporalHandler handler) {
        super(spec, agg, true, filter, null,
                DependencyModel.DYNAMIC_BINDING_POLICY, context, handler, handler.getInstanceManager());
        m_di = defaultImpl;
        m_policy = policy;
        m_timeout = timeout;
        m_handler = handler;
        m_collection = collection;
        m_proxy = proxy;
        if (! proxy) { // No proxy => initialize the Thread local.
            m_usage = new ServiceUsage();
        } else if (proxy && ! agg) { // Scalar proxy => Create the proxy.
            ProxyFactory proxyFactory = new ProxyFactory(this.getClass().getClassLoader());
            m_proxyObject = proxyFactory.getProxy(getSpecification(), this);
        }
    }

    /**
     * The dependency has been reconfigured.
     * @param arg0 new service references
     * @param arg1 old service references
     * @see org.apache.felix.ipojo.util.DependencyModel#onDependencyReconfiguration(org.osgi.framework.ServiceReference[],
     *      org.osgi.framework.ServiceReference[])
     */
    public void onDependencyReconfiguration(ServiceReference[] arg0,
            ServiceReference[] arg1) {
        throw new UnsupportedOperationException(
                "Reconfiguration not yet supported");
    }

    /**
     * A provider arrives.
     * @param ref service reference of the new provider.
     * @see org.apache.felix.ipojo.util.DependencyModel#onServiceArrival(org.osgi.framework.ServiceReference)
     */
    public synchronized void onServiceArrival(ServiceReference ref) {
        // Notify if a thread is waiting.
        notifyAll();
    }

    /**
     * A provider leaves.
     * @param arg0 leaving service references.
     * @see org.apache.felix.ipojo.util.DependencyModel#onServiceDeparture(org.osgi.framework.ServiceReference)
     */
    public void onServiceDeparture(ServiceReference arg0) {  }
   
    /**
     * A provider is modified.
     * @param arg0 leaving service references.
     * @see org.apache.felix.ipojo.util.DependencyModel#onServiceDeparture(org.osgi.framework.ServiceReference)
     */
    public void onServiceModification(ServiceReference arg0) {  }

    /**
     * The code require a value of the monitored field. If providers are
     * available, the method return service object(s) immediately. Else, the
     * thread is blocked until an arrival. If no provider arrives during the
     * among of time specified, the method throws a Runtime Exception.
     * @param arg0 POJO instance asking for the service
     * @param arg1 field name
     * @param arg2 previous value
     * @return the object to inject.
     * @see org.apache.felix.ipojo.FieldInterceptor#onGet(java.lang.Object, java.lang.String, java.lang.Object)
     */
    public synchronized Object onGet(Object arg0, String arg1, Object arg2) {
        // Check if the Thread local as a value
        if (! m_proxy) {
            Usage usage = (Usage) m_usage.get();
            if (usage.m_stack > 0) {
                return usage.m_object;
            }
        }
       
        ServiceReference[] refs = getServiceReferences();
        if (refs != null) {
            // Immediate return.
            return getServiceObjects(refs);
        } else {
            // Begin to wait ...           
            long enter = System.currentTimeMillis();
            boolean exhausted = false;
            synchronized (this) {
                while (getServiceReference() == null && !exhausted) {
                    try {
                        wait(1);
                    } catch (InterruptedException e) {
                        // We was interrupted ....
                    } finally {
                        long end = System.currentTimeMillis();
                        exhausted = (end - enter) > m_timeout;
                    }
                }
            }
            // Check
            if (exhausted) {
                return onTimeout();
            } else {
                refs = getServiceReferences();
                return getServiceObjects(refs);
            }
        }
    }
   
    /**
     * A POJO method will be invoked.
     * @param pojo : Pojo object
     * @param method : called method
     * @param args : arguments
     * @see org.apache.felix.ipojo.MethodInterceptor#onEntry(java.lang.Object, java.lang.reflect.Method, java.lang.Object[])
     */
    public void onEntry(Object pojo, Method method, Object[] args) {
        if (m_usage != null) {
            Usage usage = (Usage) m_usage.get();
            if (usage.m_stack > 0) {
                usage.inc();
                m_usage.set(usage); // Set the Thread local as value has been modified
            }
        }
    }

    /**
     * A POJO method has thrown an error.
     * This method does nothing and wait for the finally.
     * @param pojo : POJO object.
     * @param method : Method object.
     * @param throwable : thrown error
     * @see org.apache.felix.ipojo.MethodInterceptor#onError(java.lang.Object, java.lang.reflect.Method, java.lang.Throwable)
     */
    public void onError(Object pojo, Method method, Throwable throwable) {
        // Nothing to do  : wait onFinally
    }

    /**
     * A POJO method has returned.
     * @param pojo : POJO object.
     * @param method : Method object.
     * @param returnedObj : returned object (null for void method)
     * @see org.apache.felix.ipojo.MethodInterceptor#onExit(java.lang.Object, java.lang.reflect.Method, java.lang.Object)
     */
    public void onExit(Object pojo, Method method, Object returnedObj) {
        // Nothing to do  : wait onFinally       
    }
   
    /**
     * A POJO method is finished.
     * @param pojo : POJO object.
     * @param method : Method object.
     * @see org.apache.felix.ipojo.MethodInterceptor#onFinally(java.lang.Object, java.lang.reflect.Method)
     */
    public void onFinally(Object pojo, Method method) {
        if (m_usage != null) {
            Usage usage = (Usage) m_usage.get();
            if (usage.m_stack > 0) {
                if (usage.dec()) {
                    // Exit the method flow => Release all objects
                    usage.clear();
                    m_usage.set(usage); // Set the Thread local as value has been modified
                }
            }
        }
    }
   
    /**
     * Creates and returns object to inject in the dependency.
     * This method handles aggregate, collection and proxy cases.
     * @param refs the available service references
     * @return the object to inject. Can be a 'simple' object, a proxy,
     * a collection or an array.
     */
    private Object getServiceObjects(ServiceReference [] refs) {
        if (m_proxy) {
            if (m_proxyObject == null) { // Not aggregate.
                return new ServiceCollection(this);
            } else {
                return m_proxyObject;
            }
        } else {
            // Initialize the thread local object is not already touched.
            Usage usage = (Usage) m_usage.get();
            if (usage.m_stack == 0) { // uninitialized usage.
                if (isAggregate()) {
                    if (m_collection) {
                        Collection svc = new ArrayList(refs.length)// Use an array list as collection implementation.
                        for (int i = 0; i < refs.length; i++) {
                            svc.add(getService(refs[i]));
                        }
                        usage.m_object = svc;
                    } else {
                        Object[] svc = (Object[]) Array.newInstance(getSpecification(),
                                refs.length);
                        for (int i = 0; i < svc.length; i++) {
                            svc[i] = getService(refs[i]);
                        }
                        usage.m_object = svc;
                    }
                } else {
                    usage.m_object = getService(refs[0]);
                }
                usage.inc(); // Start the caching, so set the stack level to 1
                m_usage.set(usage);
            }
            return usage.m_object;
        }
       
    }
   
    /**
     * Called by the proxy to get a service object to delegate a method.
     * This methods manages the waited time and on timeout policies.
     * @return a service object or a nullable/default-implmentation object.
     */
    public Object getService() {
        ServiceReference ref = getServiceReference();
        if (ref != null) {
            return getService(ref); // Return immediately the service object.
        } else {
            // Begin to wait ...
            long enter = System.currentTimeMillis();
            boolean exhausted = false;
            synchronized (this) {
                while (ref == null && !exhausted) {
                    try {
                        wait(1);
                    } catch (InterruptedException e) {
                        // We was interrupted ....
                    } finally {
                        long end = System.currentTimeMillis();
                        exhausted = (end - enter) > m_timeout;
                        ref = getServiceReference();
                    }
                }
            }
            // Check
            if (exhausted) {
                Object obj =  onTimeout(); // Throw the Runtime Exception
                if (obj == null) {
                    throw new RuntimeException("No service available"); // Runtime Exception to be consistent with iPOJO Core.
                } else {
                    return obj; // Return a nullable or DI
                }
            } else {
               // If not exhausted, ref is not null.
                return getService(ref);
            }
        }
    }

    /**
     * Start method. Initializes the nullable object.
     * @see org.apache.felix.ipojo.util.DependencyModel#start()
     */
    public void start() {
        super.start();
        switch (m_policy) {
            case TemporalHandler.NULL:
                m_nullableObject = null;
                break;
            case TemporalHandler.NULLABLE:
                // To load the proxy we use the POJO class loader. Indeed, this
                // classloader imports iPOJO (so can access to Nullable) and has
                // access to the service specification.
                try {
                    m_nullableObject = Proxy.newProxyInstance(m_handler
                            .getInstanceManager().getClazz().getClassLoader(),
                            new Class[] { getSpecification(), Nullable.class },
                            new NullableObject()); // NOPMD
                    if (isAggregate()) {
                        if (m_collection) {
                            List list = new ArrayList(1);
                            list.add(m_nullableObject);
                            m_nullableObject = list;
                        } else {
                            Object[] array = (Object[]) Array.newInstance(
                                getSpecification(), 1);
                            array[0] = m_nullableObject;
                            m_nullableObject = array;
                        }
                    }
                } catch (NoClassDefFoundError e) {
                    // A NoClassDefFoundError is thrown if the specification
                    // uses a
                    // class not accessible by the actual instance.
                    // It generally comes from a missing import.
                    throw new IllegalStateException(
                            "Cannot create the Nullable object, a referenced class cannot be loaded: "
                                    + e.getMessage());
                }

                break;
            case TemporalHandler.DEFAULT_IMPLEMENTATION:
                // Create the default-implementation object.
                try {
                    Class clazz = m_handler.getInstanceManager().getContext()
                            .getBundle().loadClass(m_di);
                    m_nullableObject = clazz.newInstance();
                } catch (IllegalAccessException e) {
                    throw new IllegalStateException(
                            "Cannot load the default-implementation " + m_di
                                    + " : " + e.getMessage());
                } catch (InstantiationException e) {
                    throw new IllegalStateException(
                            "Cannot load the default-implementation " + m_di
                                    + " : " + e.getMessage());
                } catch (ClassNotFoundException e) {
                    throw new IllegalStateException(
                            "Cannot load the default-implementation " + m_di
                                    + " : " + e.getMessage());
                }
                if (isAggregate()) {
                    if (m_collection) {
                        List list = new ArrayList(1);
                        list.add(m_nullableObject);
                        m_nullableObject = list;
                    } else {
                        Object[] array = (Object[]) Array.newInstance(
                            getSpecification(), 1);
                        array[0] = m_nullableObject;
                        m_nullableObject = array;
                    }
                }
                break;
            case TemporalHandler.EMPTY:
                if (! m_collection) {
                    m_nullableObject = Array.newInstance(getSpecification(), 0);
                } else { // Empty collection
                    m_nullableObject = new ArrayList(0);
                }
                break;
            default: // Cannot occurs
                break;
        }
    }

    /**
     * Stop method. Just releases the reference on the nullable object.
     * @see org.apache.felix.ipojo.util.DependencyModel#stop()
     */
    public void stop() {
        super.stop();
        m_nullableObject = null;
        m_proxyObject = null;
    }

    /**
     * The monitored field receives a value. Nothing to do.
     * @param arg0 POJO setting the value.
     * @param arg1 field name
     * @param arg2 received value
     * @see org.apache.felix.ipojo.FieldInterceptor#onSet(java.lang.Object, java.lang.String, java.lang.Object)
     */
    public void onSet(Object arg0, String arg1, Object arg2) { }

    /**
     * Implements the timeout policy according to the specified configuration.
     * @return the object to return when the timeout occurs.
     */
    Object onTimeout() {
        switch (m_policy) {
            case TemporalHandler.NULL:
            case TemporalHandler.NULLABLE:
            case TemporalHandler.DEFAULT_IMPLEMENTATION:
            case TemporalHandler.EMPTY:
                return m_nullableObject;
            default:
                // Throws a runtime exception
                throw new RuntimeException("Service "
                        + getSpecification().getName()
                        + " unavailable : timeout");
        }
    }
   
    long getTimeout() {
        return m_timeout;
    }
   
    /**
     * Creates proxy object for proxied scalar dependencies.
     */
    private class ProxyFactory extends ClassLoader {
       
        /**
         * Handler classloader, used to load the temporal dependency class.
         */
        private ClassLoader m_handlerCL;
       
        /**
         * Creates the proxy classloader.
         * @param parent the handler classloader.
         */
        public ProxyFactory(ClassLoader parent) {
            this.m_handlerCL = parent;
        }
       
        /**
         * Loads a proxy class generated for the given (interface) class.
         * @param clazz the service specification to proxy
         * @return the Class object of the proxy.
         */
        protected Class getProxyClass(Class clazz) {
            byte[] clz = ProxyGenerator.dumpProxy(clazz); // Generate the proxy.
            return defineClass(clazz.getName() + "$$Proxy", clz, 0, clz.length);
        }
       
        /**
         * Create a proxy object for the given specification. The proxy
         * uses the given temporal dependency to get the service object. 
         * @param spec the service specification (interface)
         * @param dep the temporal dependency used to get the service
         * @return the proxy object.
         */
        public Object getProxy(Class spec, TemporalDependency dep) {
            try {
                Class clazz = getProxyClass(getSpecification());
                Constructor constructor = clazz.getConstructor(new Class[] {dep.getClass()}); // The proxy constructor
                return constructor.newInstance(new Object[] {dep});
            } catch (Throwable e) {
                m_handler.error("Cannot create the proxy object", e);
                m_handler.getInstanceManager().stop();
                return null;
            }
        }
       
        /**
         * Loads the given class.
         * This class use the classloader of the specification class
         * or the handler class loader.
         * @param name the class name
         * @return the class object
         * @throws ClassNotFoundException if the class is not found by the two classloaders.
         * @see java.lang.ClassLoader#loadClass(java.lang.String)
         */
        public Class loadClass(String name) throws ClassNotFoundException {
            try {
                return m_handler.getInstanceManager().getContext().getBundle().loadClass(name);
            } catch (ClassNotFoundException e) {
                return m_handlerCL.loadClass(name);
            }
        }
    } 
   

}
TOP

Related Classes of org.apache.felix.ipojo.handler.temporal.TemporalDependency$ProxyFactory

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.