Package org.rioproject.impl.associations

Source Code of org.rioproject.impl.associations.AssociationProxySupport

/*
* Copyright 2008 the original author or authors.
*
* 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.
*/
package org.rioproject.impl.associations;

import net.jini.core.discovery.LookupLocator;
import org.rioproject.associations.Association;
import org.rioproject.associations.AssociationDescriptor;
import org.rioproject.associations.AssociationProxy;
import org.rioproject.associations.ServiceSelectionStrategy;
import org.rioproject.impl.associations.strategy.FailOver;
import org.rioproject.impl.util.ThrowableUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.rmi.RemoteException;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import java.util.concurrent.atomic.AtomicLong;

/**
* Provides support for an {@link org.rioproject.associations.AssociationProxy}
*
* @author Dennis Reedy
*/
public class AssociationProxySupport<T> implements AssociationProxy<T> {
    private ServiceSelectionStrategy<T> strategy;
    private final List<String> proxyMethods = new ArrayList<String>();
    Logger logger = LoggerFactory.getLogger(AssociationProxySupport.class);
    private AtomicLong invocationCount = new AtomicLong();
    private boolean terminated;

    /**
     * Create an InvocationHandler
     *
     * @param association The Association to use
     *
     * @return An InvocationHandler for use with a dynamic JDK proxy. This
     * method returns a
     * {@link AssociationProxySupport.AssociationInvocationHandler}
     * instance
     */
    public InvocationHandler getInvocationHandler(final Association<T> association) {
        return new AssociationInvocationHandler(association, this);
    }


    public Association<T> getAssociation() {
        return strategy.getAssociation();
    }

    /**
     * Get the {@link ServiceSelectionStrategy}
     *
     * @return The <tt>ServiceSelectionStrategy</tt>
     */
    public ServiceSelectionStrategy<T> getServiceSelectionStrategy() {
        return strategy;
    }

    /**
     * Set the strategy for selecting services
     *
     * @param strategy The {@link ServiceSelectionStrategy}. Must not be null.
     */
    public void setServiceSelectionStrategy(ServiceSelectionStrategy<T> strategy) {
        this.strategy = strategy;
    }

    /**
     * Notification that an Association has been discovered
     */
    public void discovered(Association<T> association, T service) {
        logger.trace("Adding service for {}", association.getName());
        strategy.serviceAdded(service);
    }

    /**
     * Notification that an Association has changed
     */
    public void changed(Association<T> association, T service) {
        strategy.serviceRemoved(service);
    }

    /**
     * Notification that an Association is broken
     */
    public void broken(Association<T> association, T service) {
        strategy.serviceRemoved(service);
    }

    /**
     * Clean up any resources allocated
     */
    public void terminate() {
        terminated = true;
        strategy.terminate();
    }

    /*
    * Invokes the method on on the first available service in
    * the collection of associated services. If an invocation to the service
    * fails as a result of remote communication failure, the next available
    * service will be used.
    *
    * <p>Attempts to invoke an available service will continue until either the
    * invocation succeeds, or there are no more services available
    *
    * @param a The Association referencing a collection of associated services
    * @param method The method to invoke
    * @param args Method arguments
    *
    * @return The result of the method invocation
    *
    * @throws Throwable the exception to throw from the method invocation on
    * the associated service instance.
    */
    public Object doInvokeService(Association<T> a, Method method, Object[] args) throws Throwable {
        if(terminated)
            throw new IllegalStateException("The association proxy for "+formatAssociationService(a)+" "+
                                            "has been terminated, invocations to the service through this " +
                                            "generated proxy are not possible in it's current state. Make sure " +
                                            "all invoking threads are terminated to resolve this issue");
        Object result = null;
        long stopTime = 0;
        while (!terminated) {
            T service = getServiceSelectionStrategy().getService();
            if(service==null) {
                AssociationDescriptor aDesc = a.getAssociationDescriptor();
                if(aDesc.getServiceDiscoveryTimeout()>0) {
                    stopTime = (stopTime==0?
                                System.currentTimeMillis()+
                                aDesc.getServiceDiscoveryTimeUnits().toMillis(aDesc.getServiceDiscoveryTimeout()): stopTime);
                    if(System.currentTimeMillis()<stopTime) {
                        if(logger.isTraceEnabled()) {
                            logger.trace("The association proxy for {} is not available. A service discovery timeout of " +
                                         "[{} {}], has been configured, and the computed stop time is: {}, sleep for one " +
                                         "second and re-evaluate",
                                         formatAssociationService(a),
                                         aDesc.getServiceDiscoveryTimeout(),
                                         aDesc.getServiceDiscoveryTimeUnits().name(),
                                         new Date(stopTime));
                        }
                        Thread.sleep(1000);
                        continue;
                    } else {
                        String s = formatAssociationService(a);
                        throw new RemoteException("No services available for associated service " +
                                                  s+", "+formatDiscoveryAttributes(a)+". "+
                                                  "A timeout of "+aDesc.getServiceDiscoveryTimeout()+" "+
                                                  aDesc.getServiceDiscoveryTimeUnits()+" expired. Check network " +
                                                  "connections and ensure that the "+s+" service is deployed");
                    }
                } else {
                    String s = formatAssociationService(a);
                    throw new RemoteException("No services available for service association " +
                                              s+", "+formatDiscoveryAttributes(a)+". Check network " +
                                              "connections and ensure that the ["+s+"] service is deployed. " +
                                              "You may also want to check the service discovery timeout property, " +
                                              "it is set to ["+aDesc.getServiceDiscoveryTimeout()+"]. Changing this " +
                                              "value will allow Rio to wait the specified amount of time for a service " +
                                              "to become available.");
                }
            }
            try {
                result = method.invoke(service, args);
                invocationCount.incrementAndGet();
                break;
            } catch (Throwable t) {
                if(!ThrowableUtil.isRetryable(t)) {
                    logger.warn("Failed to invoke method [{}], remove  service [{}]", method.getName(), service.toString(), t);
                    getServiceSelectionStrategy().serviceRemoved(service);
                    if(a.removeService(service)!=null) {
                        logger.warn("Service [{}] removed, have [{}] services", service.toString(), a.getServiceCount());
                    } else {
                        logger.warn("Unable to remove service [{}], have [{}] services", service.toString(), a.getServiceCount());
                        //terminated = true;
                    }
                } else {
                    if(t instanceof InvocationTargetException) {
                        t = t.getCause()==null? ((InvocationTargetException)t).getTargetException(): t.getCause();
                    }
                    throw t;
                }
            }
        }
        return result;
    }

    private String formatAssociationService(Association<T> a) {
        AssociationDescriptor aDesc = a.getAssociationDescriptor();
        StringBuilder sb = new StringBuilder();
        String[] names = aDesc.getInterfaceNames();
        sb.append("[");
        for(int i=0; i<names.length; i++) {
            if(i>0)
                sb.append(", ");
            sb.append(names[i]);
        }
        sb.append("]");
        if(!aDesc.getName().equals(AssociationDescriptor.NO_NAME)) {
            sb.append(", name [");
            sb.append(aDesc.getName());
            sb.append("]");
        }
        sb.append(", ");
        sb.append("strategy=[");
        String strategy = a.getAssociationDescriptor().getServiceSelectionStrategy();
        strategy = (strategy==null? FailOver.class.getName() : strategy);
        sb.append(strategy);
        sb.append("]");
        return sb.toString();
    }

    private String formatDiscoveryAttributes(Association<T> a) {
        StringBuilder sb = new StringBuilder();
        sb.append("Using discovery attributes: ");
        String[] groups = a.getAssociationDescriptor().getGroups();
        sb.append("groups=[");
        int i=0;
        for(String s : groups) {
            if(i>0)
                sb.append(", ");
            sb.append(s);
            i++;
        }
        sb.append("]");
        LookupLocator[] locators = a.getAssociationDescriptor().getLocators();
        if(locators!=null) {
            sb.append(" ");
            sb.append("locators=[");
            i=0;
            for(LookupLocator l : locators) {
                if(i>0)
                    sb.append(", ");
                sb.append(l.toString());
                i++;
            }
            sb.append("] ");
        }
        return sb.toString();
    }

    /*
     * An InvocationHandler that operates on the service returned by the
     * {@link ServiceSelectionStrategy}. If an invocation to the service
     * fails as a result of remote communication failure, the next service
     * returned by the {@link ServiceSelectionStrategy}will be used.
     *
     * Attempts to invoke an available service will continue until either the
     * invocation succeeds, or there are no more services available
     */
    class AssociationInvocationHandler implements InvocationHandler {
        Association<T> association;
        Object localRef;

        AssociationInvocationHandler(Association<T> association, Object localRef) {
            this.association = association;
            this.localRef = localRef;
        }

        public Object invoke(Object object, Method method, Object[] args) throws Throwable {
            Object result;

            if (!isProxyMethod(method)) {
                if(logger.isTraceEnabled())
                    logger.trace("Invoking local method [{}]", method.toString());
                result = method.invoke(localRef, args);
            } else {
                result = doInvokeService(association, method, args);
            }
            return result;
        }
    }

    /**
     * Check if the method is found in the generated proxy
     *
     * @param method The Method to check
     * @return true if the method is local to the proxy
     */
    public boolean isProxyMethod(Method method) {
        return proxyMethods.contains(method.toString());
    }

    public void setProxyInterfaces(Class[] classes) {
        for (Class clazz : classes) {
            Method[] methods = clazz.getMethods();
            for (Method m : methods) {
                proxyMethods.add(m.toString());
            }
        }
    }

    public long getInvocationCount() {
        return invocationCount.get();
    }
}
TOP

Related Classes of org.rioproject.impl.associations.AssociationProxySupport

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.
ts)},i[r].l=1*new Date();a=s.createElement(o), m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m) })(window,document,'script','//www.google-analytics.com/analytics.js','ga'); ga('create', 'UA-20639858-1', 'auto'); ga('send', 'pageview');