Package org.rioproject.impl.bean

Source Code of org.rioproject.impl.bean.BeanAdapter

/*
* 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.bean;

import net.jini.config.Configuration;
import net.jini.config.ConfigurationException;
import net.jini.export.Exporter;
import net.jini.jeri.BasicJeriExporter;
import net.jini.jeri.tcp.TcpServerEndpoint;
import org.rioproject.annotation.*;
import org.rioproject.config.Constants;
import org.rioproject.deploy.ServiceBeanInstantiationException;
import org.rioproject.impl.servicebean.ServiceBeanAdapter;
import org.rioproject.net.HostUtil;
import org.rioproject.proxy.bean.BeanDelegator;
import org.rioproject.servicebean.ServiceBean;
import org.rioproject.servicebean.ServiceBeanContext;
import org.rioproject.servicecore.Service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.management.*;
import java.io.IOException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.rmi.Remote;
import java.util.Arrays;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;

/**
* The BeanAdapter provides a basic concrete implementation of a ServiceBean,
* and provides the support to delegate to a component, a <i>bean</i>
* (a Plain Old Java Object : POJO), making the bean remotable as Jini
* technology service using the dynamic service architecture provided by Rio.
*
* <p><b><u>Lifecycle Support</u></b><br>
* The BeanAdapter will invoke lifecycle methods on the bean if
* the bean has the following methods defined:
*
* <pre>
* public void preAdvertise();
* public void postUnAdvertise();
* public void preDestroy();
* </pre>
*
* <p>Alternatively, the bean can use the {@link org.rioproject.annotation.Initialized}, {@link org.rioproject.annotation.Started},
* {@link javax.annotation.PostConstruct}{@link org.rioproject.annotation.PreAdvertise},
* {@link org.rioproject.annotation.PostUnAdvertise}, and {@link PreDestroy} annotations to be
* notified of each respective lifecycle event.
*
* <p>Note: ServiceBean initialization is invoked by the start method to
* initialize the ServiceBean. This method is called only once during the
* lifecycle of a ServiceBean
*
* <p><b><u>Properties and Context</u></b><br>
* Properties, the {@link net.jini.config.Configuration}, the
* {@link org.rioproject.servicebean.ServiceBeanContext} and the service bean itself
* can be injected into the bean as well. The following methods must be
* declared to have the respective properties injected into the bean:
* <pre>
* public void setParameters(Map<String, Object> parameters);
* public void setConfiguration(Configuration config);
* public void setServiceBeanContext(ServiceBeanContext context);
* public void setServiceBean(ServiceBean serviceBean);
* </pre>
*
* <p>Alternatively, the bean can use the {@link org.rioproject.annotation.SetConfiguration},
* {@link org.rioproject.annotation.SetParameters}, and {@link org.rioproject.annotation.SetServiceBeanContext}
* annotations as well.
*
* This property injection will be completed during the initialization of the
* ServiceBean.
*
* <p><b><u>Proxy support</u></b><br>
* The bean may also define a smart proxy. In order for the BeanAdapter to obtain
* the smart proxy the following method signatures must be defined:
*
* <pre>
* public Object createProxy(<proxy interface type>);
* </pre>
*
* Using the <tt>createProxy</tt> method, the method passes a remote reference
* to the exported back end implementation. This parameter can be declared to be
* the interface type your proxy implements, or a Object, which can then be
* narrowed to the interface type the bean implements. The method must return
* the bean's smart proxy, using the reference provided.
*
* <pre>
* public void setProxy(Object);
* </pre>
*
* If the bean has the <tt>setProxy</tt> method declared, this method will be
* invoked with the proxy that has been created for the bean.
*
* <p>Alternatively, the bean can use the {@link org.rioproject.annotation.CreateProxy}
* and {@link org.rioproject.annotation.SetProxy} annotations
*
* @see org.rioproject.servicebean.ServiceBean
* @see org.rioproject.impl.servicebean.ServiceBeanAdapter
*
* @author Dennis Reedy
*/
@SuppressWarnings("PMD.AvoidThrowingRawExceptionTypes")
public class BeanAdapter extends ServiceBeanAdapter {
    private static final String COMPONENT = "org.rioproject.bean";
    private static final Logger logger = LoggerFactory.getLogger(COMPONENT);
    private Object bean;
    private Remote delegatingProxy;

    /**
     * Create an instance of the BeanAdapter
     *
     * @param bean The bean, must not be null
     */
    public BeanAdapter(Object bean) {
        if(bean == null)
            throw new IllegalArgumentException("bean is null");
        this.bean = bean;
    }
  
    @Override
    protected void registerMBean(ObjectName oName, MBeanServer mbeanServer)
    throws NotCompliantMBeanException, MBeanRegistrationException, InstanceAlreadyExistsException {
        String implClass = bean.getClass().getName();
        Class[] ifaces = bean.getClass().getInterfaces();
        Class mbean = null;
        for (Class iface : ifaces) {
            if (iface.getName().equals(implClass + "MBean")) {
                mbean = iface;
                break;
            }
        }
        if(mbean!=null) {
            String comment = context.getServiceBeanConfig().getComment();
            mbeanServer.registerMBean(new AggregatingMBean(this, bean, mbean, comment), oName);
        } else {
            mbeanServer.registerMBean(this, oName);
        }
    }
   
    /**
     * Override the start method to create a delegating proxy required
     * to navigate between the ServiceBean and the bean.
     *
     * <p>Once the bean has been started, the wrapped bean will be checked
     * for {@code @Initialized()} or a {@code postStart()} method declaration. If the wrapped bean
     * does have an accessible {@code @Initialized()} or a {@code postStart()} declared, it will be
     * called following the parent's start method.
     *
     * @param context The ServiceBeanContext
     * @return A remoted proxy used to communicate to the bean
     * @throws ServiceBeanInstantiationException if starting the bean fails
     */
    @Override
    public Object start(final ServiceBeanContext context) throws ServiceBeanInstantiationException {
        delegatingProxy = createDelegatingProxy();
        Object o = super.start(context);
        /* If defined, invoke postStart lifecycle method, Check if we are
         * being started up first. If so, then the Cybernode will invoke
         * the lifecycle method (RIO-141) */
        Boolean rioStarting = (Boolean)context.getServiceBeanConfig().getConfigurationParameters().get(Constants.STARTING);
        logger.trace("The bean [{}], is in the process of being instantiated: {}",
                     bean.getClass().getName(), (rioStarting==null?"false":rioStarting));
        if(!(rioStarting!=null && rioStarting)) {
            Class<? extends Annotation> annotation = BeanHelper.hasAnnotation(bean, PostConstruct.class)?
                                                     PostConstruct.class:Started.class;
            BeanHelper.invokeLifeCycle(annotation, "postStart", bean);
        }
        return(o);
    }

    /**
     * If the provided bean has the following method signatures defined,
     * the corresponding properties will be injected into the bean:
     * <pre>
     * public void setParameters(Map parameters);
     * public void setConfiguration(Configuration config);
     * public void setServiceBeanContext(ServiceBeanContext context);
     * </pre>
     * <br>
     * <p>Alternatively, the bean can use the {@link org.rioproject.annotation.SetConfiguration},
     * {@link org.rioproject.annotation.SetParameters}, and
     * {@link org.rioproject.annotation.SetServiceBeanContext} annotations as
     * well.
     *
     * @param bean The bean to check for property injection
     * @param context The {@link ServiceBeanContext}
     *
     * @throws IllegalArgumentException if either of the parameters are null
     */
    public static void invokeLifecycleInjectors(Object bean,
                                                ServiceBeanContext context) throws ServiceBeanInstantiationException {
        if(bean==null)
            throw new IllegalArgumentException("bean cannot be null");
        if(context==null)
            throw new IllegalArgumentException("ServiceBeanContext cannot be null");
        /* Invoke the method with @SetParameters annotation or the setParameters method */
        Map<String, ?> parameters = context.getServiceBeanConfig().getInitParameters();
        BeanHelper.invokeBeanMethod(bean,
                                    SetParameters.class,
                                    "setParameters",
                                    new Class[]{Map.class},
                                    new Object[]{parameters});
        /* Invoke the method with @SetConfiguration annotation or the setConfiguration method */
        Configuration config ;
        try {
            config = context.getConfiguration();
        } catch (ConfigurationException e) {
            throw new ServiceBeanInstantiationException(e.getLocalizedMessage());
        }
        BeanHelper.invokeBeanMethod(bean,
                                    SetConfiguration.class,
                                    "setConfiguration",
                                    new Class[]{Configuration.class},
                                    new Object[]{config});

        /* Invoke the method with @SetServiceBeanContext annotation or the setServiceBeanContext method */
        BeanHelper.invokeBeanMethod(bean,
                                    SetServiceBeanContext.class,
                                    "setServiceBeanContext",
                                    new Class[]{ServiceBeanContext.class},
                                    new Object[]{context});
    }

    /**
     * Override the initialize method to check if the wrapped bean has a
     * {@code preInitialize()} method declared. If the wrapped bean
     * does have an accessible {@code preInitialize()} declared, it
     * will be called prior to initializing the bean.
     *
     * <p>Once the parent's initialize method has been invoked, if the wrapped
     * bean has the following method signatures defined, the corresponding
     * properties will be injected into the bean:
     * <pre>
     * public void setParameters(Map parameters);
     * public void setConfiguration(Configuration config);
     * public void setServiceBeanContext(ServiceBeanContext context);
     * public void setServiceBean(ServiceBean serviceBean);
     * </pre>
     * <br>
     *
     * <p>Once bean initialization has been processed,
     * the wrapped bean will be checked for {@code postInitialize()}
     * method declaration. If the wrapped bean does have an accessible
     * {@code postInitialize()} declared, it will be called following
     * the parent's initialize method.
     *
     * @throws Exception if the initialization process fails
     */
    @Override
    public void initialize(final ServiceBeanContext context) throws Exception {
        /* If defined, invoke preInitialize lifecycle method */
        BeanHelper.invokeLifeCycle(null, "preInitialize", bean);

        invokeLifecycleInjectors(bean, context);

        super.initialize(context);

        /* Invoke the setServiceBean method */
        BeanHelper.invokeBeanMethod(bean,
                                    SetServiceBean.class,
                                    "setServiceBean",
                                    new Class[]{ServiceBean.class},
                                    new Object[]{this});
    }

    /**
     * Override the advertise method to check if the wrapped bean has a
     * {@code preAdvertise()} method declared. If the wrapped bean does have an accessible
     * {@code preAdvertise()} declared, it will be called prior to
     * advertising the bean.
     */
    @Override
    public void advertise() throws IOException {
        try {
            BeanHelper.invokeLifeCycle(PreAdvertise.class, "preAdvertise", bean);
        } catch(Throwable t) {
            String message = String.format("Invoking Bean [%s] preAdvertise lifecycle", bean.getClass().getName());
            throw new IOException(message, t);
        }
        super.advertise();
    }

    /**
     * Override the unadvertise method to check if the wrapped bean has a
     * {@code postUnAdvertise()} method declared. If the wrapped bean does have an accessible
     * {@code postUnAdvertise()} declared, it will be called following
     * the parent's unadvertise method.
     */
    @Override
    public void unadvertise() throws IOException {
        super.unadvertise();
        /* If defined, invoke unadvertised lifecycle method */
        try {
            BeanHelper.invokeLifeCycle(PostUnAdvertise.class, "postUnAdvertise", bean);
        } catch(Throwable t) {
            String message = String.format("Invoking Bean [%s] postUnAdvertise lifecycle", bean.getClass().getName());
            throw new IOException(message, t);
        }
    }

    /**
     * Override the destroy method to check if the wrapped bean has a
     * {@code preDestroy()} method declared. If the wrapped bean does
     * have an accessible {@code preDestroy()} declared, it will be
     * called prior to destroying the bean.
     */
    @Override
    public void destroy() {
        try {
            BeanHelper.invokeLifeCycle(PreDestroy.class, "preDestroy", bean);
        } catch(Exception e) {
            String s = bean==null?"<unknown:null>":bean.getClass().getName();
            logger.warn("Invoking Bean [{}] preDestroy()", s, e);
        }
        try {
            super.destroy();
        } finally {
            bean = null;
            if(delegatingProxy!=null) {
                delegatingProxy = null;
            }
        }
    }

    /**
     * Create the delegating proxy
     *
     * @return The proxy that will handle the delegation between the ServiceBean
     * and the Bean (POJO)
     *
     * @throws RuntimeException
     */
    protected Remote createDelegatingProxy() {
        try {
            Class[] interfaces = getInterfaceClasses(bean.getClass());
            Remote proxy = (Remote) BeanDelegator.getInstance(this, bean, interfaces);
            return(proxy);
        } catch (Exception e) {
            logger.info("exporting a standard proxy");
            throw new RuntimeException("could not create proxy", e);
        }
    }

    /*
     * Get an array of classes the bean & service bean implement
     */
    private static Class[] getInterfaceClasses(Class c)
        throws ClassNotFoundException {
        Set<Class> remotes = new HashSet<Class>();
        Class[] interfaces = c.getInterfaces();
        remotes.addAll(Arrays.asList(interfaces));
        remotes.add(Service.class);
        return(remotes.toArray(new Class[remotes.size()]));
    }

    /**
     * Override exportDo, using our delegating proxy to handle
     * invocations
     */
    @Override
    protected Remote exportDo(Exporter exporter) throws Exception {
        if(exporter == null)
            throw new IllegalArgumentException("exporter is null");
        return (exporter.export(delegatingProxy));
    }

    /**
     * Override getExporter, creating a BeanInvocationLayerFactory
     * for the Exporter instead of of the BasicILFactory
     */
    @Override
    protected Exporter getExporter(Configuration config) throws Exception {
        Exporter exporter = (Exporter)config.getEntry(COMPONENT,
                                                      "serverExporter",
                                                      Exporter.class,
                                                      null);
        if(exporter==null) {
            String host = HostUtil.getHostAddressFromProperty(Constants.RMI_HOST_ADDRESS);
            exporter = new BasicJeriExporter(TcpServerEndpoint.getInstance(host, 0),
                                             new BeanInvocationLayerFactory(),
                                             false,
                                             true);
        }
        logger.debug("[{}] using exporter {}", bean.getClass().getName(), exporter.toString());
        return exporter;
    }

    /*
     * Override createProxy to check if the bean has it's own proxy defined
     */
    @Override
    protected Object createProxy() {
        Remote remoteRef = getExportedProxy();
        Class proxyType;

        Object proxy = getCustomProxy(bean, remoteRef);
        if(proxy==null)
            proxy = super.createProxy();

        String setProxyMethodName="setProxy";
        /* First check if the @SetProxy annotation is declared */
        Method setProxy = BeanHelper.getAnnotatedMethod(bean, SetProxy.class);
        if(setProxy!=null) {
            proxyType = BeanHelper.getMethodFirstParamType(setProxy);
            setProxyMethodName = setProxy.getName();
        } else {
            /* Else, if the setProxy method is declared, invoke it */
            proxyType = BeanHelper.getMethodFirstParamType("setProxy", bean);
        }

        try {
            if(proxyType!=null) {
                BeanHelper.invokeBeanMethod(bean,
                                            null,
                                            setProxyMethodName,
                                            new Class[]{proxyType},
                                            new Object[]{proxy});
            }
        } catch (Exception e) {
            logger.warn("Count not set bean proxy");
            throw new RuntimeException("Could not set bean proxy", e);
        }
        return (proxy);
    }

    public static Object getCustomProxy(Object bean, Remote remoteRef) {
        Object proxy = null;
        Class proxyType;
        String createProxyMethodName="createProxy";

        /* First check if the @CreateProxy annotation is declared */
        Method createProxy = BeanHelper.getAnnotatedMethod(bean,
                                                           CreateProxy.class);
        if(createProxy!=null) {
            proxyType = BeanHelper.getMethodFirstParamType(createProxy);
            createProxyMethodName = createProxy.getName();
        } else {
            /* Else, if the createProxy method is declared, invoke it */
            proxyType = BeanHelper.getMethodFirstParamType("createProxy", bean);
        }

        /* If declared, invoke the createProxy method */
        try {
            Object beanProxy = null;
            if(proxyType!=null)
                beanProxy = BeanHelper.invokeBeanMethod(bean,
                                                        null,
                                                        createProxyMethodName,
                                                        new Class[]{proxyType},
                                                        new Object[]{remoteRef});
            if(beanProxy!=null) {
                /* RIO-201 */
                if(beanProxy instanceof Service) {
                     proxy = beanProxy;
                } else {
                    Class[] interfaces = getInterfaceClasses(beanProxy.getClass());
                    proxy = BeanDelegator.getInstance(remoteRef,
                                                      beanProxy,
                                                      interfaces);
                }
            } /*else {
                proxy = super.createProxy();
            }*/
        } catch (Exception e) {
            logger.warn("Could not create bean proxy");
            throw new RuntimeException("could not create bean proxy", e);
        }
        return proxy;
    }

}
TOP

Related Classes of org.rioproject.impl.bean.BeanAdapter

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.