All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.sun.ejb.containers.interceptors.InterceptorManager Maven / Gradle / Ivy

The newest version!
/*
 * Copyright (c) 2022, 2023 Contributors to the Eclipse Foundation.
 * Copyright (c) 1997, 2020 Oracle and/or its affiliates. All rights reserved.
 *
 * This program and the accompanying materials are made available under the
 * terms of the Eclipse Public License v. 2.0, which is available at
 * http://www.eclipse.org/legal/epl-2.0.
 *
 * This Source Code may also be made available under the following Secondary
 * Licenses when the conditions for such availability set forth in the
 * Eclipse Public License v. 2.0 are satisfied: GNU General Public License,
 * version 2 with the GNU Classpath Exception, which is available at
 * https://www.gnu.org/software/classpath/license.html.
 *
 * SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
 */

package com.sun.ejb.containers.interceptors;

import static com.sun.ejb.EJBUtils.loadGeneratedSerializableClass;
import static com.sun.enterprise.deployment.LifecycleCallbackDescriptor.CallbackType.POST_ACTIVATE;
import static com.sun.enterprise.deployment.LifecycleCallbackDescriptor.CallbackType.PRE_PASSIVATE;
import static java.util.logging.Level.CONFIG;
import static java.util.logging.Level.FINE;

import com.sun.ejb.containers.BaseContainer;
import com.sun.ejb.containers.EJBContextImpl;
import com.sun.enterprise.container.common.spi.util.InterceptorInfo;
import com.sun.enterprise.deployment.EjbInterceptor;
import com.sun.enterprise.deployment.InterceptorDescriptor;
import com.sun.enterprise.deployment.LifecycleCallbackDescriptor;
import com.sun.enterprise.deployment.LifecycleCallbackDescriptor.CallbackType;
import com.sun.enterprise.deployment.MethodDescriptor;
import jakarta.annotation.PostConstruct;
import jakarta.ejb.EJBException;
import jakarta.interceptor.InvocationContext;
import java.io.Serializable;
import java.lang.reflect.InaccessibleObjectException;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.security.PrivilegedActionException;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.glassfish.ejb.deployment.descriptor.EjbDescriptor;
import org.glassfish.ejb.deployment.descriptor.EjbSessionDescriptor;

/**
 * UserInterceptorsManager manages UserInterceptors. There is one instance of InterceptorManager per container.
 *
 * @author Mahesh Kannan
 */
public class InterceptorManager {

    // The following are set when initializing EJB interceptors
    private BaseContainer container;
    private EjbDescriptor ejbDesc;

    // Set when initializing interceptors for a non-ejb
    private InterceptorInfo interceptorInfo;

    private final ClassLoader loader;

    private final Class beanClass;

    private final String beanClassName;

    private final Logger _logger;

    private Class[] interceptorClasses;

    private Class[] serializableInterceptorClasses;

    private final Map instanceIndexMap = new HashMap<>();

    private boolean interceptorsExists;

    private String[] pre30LCMethodNames;

    private final Class[] lcAnnotationClasses;

    private CallbackChainImpl[] callbackChain;

    // Optionally specified delegate to be set on SystemInterceptorProxy
    private Object runtimeInterceptor;

    List frameworkInterceptors = new LinkedList<>();

    public InterceptorManager(Logger _logger, BaseContainer container, Class[] lcAnnotationClasses, String[] pre30LCMethodNames) throws Exception {
        this._logger = _logger;
        this.container = container;
        this.lcAnnotationClasses = lcAnnotationClasses;
        this.pre30LCMethodNames = pre30LCMethodNames;

        ejbDesc = container.getEjbDescriptor();
        loader = container.getClassLoader();
        beanClassName = ejbDesc.getEjbImplClassName();

        this.beanClass = loader.loadClass(beanClassName);
        frameworkInterceptors = ejbDesc.getFrameworkInterceptors();

        buildEjbInterceptorChain();

        _logger.log(FINE, () -> "InterceptorManager: " + toString());
    }

    public InterceptorManager(Logger _logger, ClassLoader classLoader, String className, InterceptorInfo interceptorInfo) throws Exception {
        this._logger = _logger;
        this.loader = classLoader;
        this.beanClassName = className;
        this.beanClass = loader.loadClass(beanClassName);
        this.interceptorInfo = interceptorInfo;
        this.lcAnnotationClasses = null;

        if (interceptorInfo.getSupportRuntimeDelegate()) {
            frameworkInterceptors.add(SystemInterceptorProxy.createInterceptorDesc());
        }

        buildInterceptorChain(interceptorInfo);

    }

    // Used when InterceptorManager should instantiate interceptor instances
    // Alternatively, if the caller needs to instantiate the instances, call
    // getInterceptorClasses() and then initializeInterceptorInstances(Object[])
    public Object[] createInterceptorInstances() {
        Object[] interceptors = new Object[serializableInterceptorClasses.length];

        for (int index = 0; index <  serializableInterceptorClasses.length; index++) {
            Class clazz = serializableInterceptorClasses[index];
            try {
                interceptors[index] = clazz.getDeclaredConstructor().newInstance();
            } catch (ReflectiveOperationException | IllegalArgumentException | SecurityException e) {
                throw new RuntimeException(e);
            }

        }

        initializeInterceptorInstances(interceptors);

        return interceptors;
    }

    public Class[] getInterceptorClasses() {
        return serializableInterceptorClasses;
    }

    public void initializeInterceptorInstances(Object[] interceptorInstances) {
        for (int index = 0; index < interceptorInstances.length; index++) {
            Class clazz = serializableInterceptorClasses[index];

            if (SystemInterceptorProxy.class.isAssignableFrom(clazz) && runtimeInterceptor != null) {
                ((SystemInterceptorProxy) interceptorInstances[index]).setDelegate(runtimeInterceptor);
            }
        }
    }

    /**
     * Can be called after original interceptor initialization. Install the given interceptor class instance before any
     * application level interceptors.
     *
     * @param interceptor optionally specified delegate to be set on SystemInterceptorProxy
     */
    public void registerRuntimeInterceptor(Object interceptor) {
        _logger.log(CONFIG, "registerRuntimeInterceptor({0})", interceptor);
        runtimeInterceptor = interceptor;
    }

    public InterceptorManager.InterceptorChain getAroundInvokeChain(MethodDescriptor mDesc, Method beanMethod) {
        List interceptors = new ArrayList<>();

        for (InterceptorDescriptor interceptor : frameworkInterceptors) {
            Set aroundInvokeDescriptors = interceptor.getAroundInvokeDescriptors();
            if (aroundInvokeDescriptors.isEmpty()) {
                continue;
            }

            List orderedAIInterceptors = null;
            Class interceptorClass = interceptor.getInterceptorClass();
            ClassLoader classLoaderToUse = (interceptorClass != null) ? interceptorClass.getClassLoader() : loader;

            try {
                orderedAIInterceptors = interceptor.getOrderedAroundInvokeDescriptors(classLoaderToUse);
            } catch (Exception e) {
                throw new IllegalStateException("No AroundInvokeIntercetpors found " + " on class " + interceptor.getInterceptorClassName(), e);
            }

            addAroundInvokeInterceptors(
                interceptors, interceptor, orderedAIInterceptors, interceptor.getInterceptorClassName(), classLoaderToUse);
        }

        List interceptorDescriptors = ejbDesc != null ?
                ejbDesc.getAroundInvokeInterceptors(mDesc) :
                interceptorInfo.getAroundInvokeInterceptors(beanMethod);

        for (InterceptorDescriptor interceptorDescriptor : interceptorDescriptors) {
            String className = interceptorDescriptor.getInterceptorClassName();
            Set aroundInvokeDescs = interceptorDescriptor.getAroundInvokeDescriptors();
            if (aroundInvokeDescs.isEmpty()) {
                continue;
            }

            List orderedAIInterceptors = null;
            try {
                orderedAIInterceptors = interceptorDescriptor.getOrderedAroundInvokeDescriptors(loader);
            } catch (Exception e) {
                throw new IllegalStateException("No AroundInvokeIntercetpors found " + " on class " + className, e);
            }

            addAroundInvokeInterceptors(
                interceptors, interceptorDescriptor,
                orderedAIInterceptors, className,
                interceptorDescriptor.getInterceptorClass() != null ? interceptorDescriptor.getInterceptorClass().getClassLoader() : loader);
        }

        return new AroundInvokeChainImpl(interceptors.toArray(new AroundInvokeInterceptor[interceptors.size()]));
    }

    public InterceptorManager.InterceptorChain getAroundTimeoutChain(MethodDescriptor methodDescriptor, Method beanMethod) {
        List interceptors = new ArrayList<>();

        for (InterceptorDescriptor frameworkInterceptor : frameworkInterceptors) {
            Set aroundTimeoutDescriptors = frameworkInterceptor.getAroundTimeoutDescriptors();
            if (aroundTimeoutDescriptors.isEmpty()) {
                continue;
            }

            List orderedAIInterceptors = null;
            Class interceptorClass = frameworkInterceptor.getInterceptorClass();
            ClassLoader classLoaderToUse = interceptorClass != null ? interceptorClass.getClassLoader() : loader;

            try {
                orderedAIInterceptors = frameworkInterceptor.getOrderedAroundTimeoutDescriptors(classLoaderToUse);
            } catch (Exception e) {
                throw new IllegalStateException(
                    "No AroundTimeoutIntercetpors found " + " on class " + frameworkInterceptor.getInterceptorClassName(), e);
            }

            addAroundInvokeInterceptors(
                interceptors,
                frameworkInterceptor,
                orderedAIInterceptors,
                frameworkInterceptor.getInterceptorClassName(),
                classLoaderToUse);
        }

        List aroundTimeoutInterceptors = ejbDesc != null ? ejbDesc.getAroundTimeoutInterceptors(methodDescriptor) : new LinkedList<>();

        for (EjbInterceptor aroundTimeoutInterceptor : aroundTimeoutInterceptors) {
            String className = aroundTimeoutInterceptor.getInterceptorClassName();
            Set aroundTimeoutDescs = aroundTimeoutInterceptor.getAroundTimeoutDescriptors();
            if (aroundTimeoutDescs.isEmpty()) {
                continue;
            }

            List orderedATInterceptors;
            try {
                orderedATInterceptors = aroundTimeoutInterceptor.getOrderedAroundTimeoutDescriptors(loader);
            } catch (Exception e) {
                throw new IllegalStateException("No AroundTimeoutIntercetpors found " + " on class " + className, e);
            }
            addAroundInvokeInterceptors(interceptors, aroundTimeoutInterceptor, orderedATInterceptors, className, loader);
        }

        return new AroundInvokeChainImpl(interceptors.toArray(new AroundInvokeInterceptor[interceptors.size()]));
    }

    private void addAroundInvokeInterceptors(List interceptors, InterceptorDescriptor interceptor,
            List orderedInterceptors, String className, ClassLoader classLoaderToUse) {

        for (LifecycleCallbackDescriptor desc : orderedInterceptors) {
            Method method = null;
            try {
                method = desc.getLifecycleCallbackMethodObject(classLoaderToUse);
            } catch (Exception e) {
                throw new IllegalStateException(
                    "No callback method of name " + desc.getLifecycleCallbackMethod() + " found on class " + className, e);
            }

            if (interceptor.getFromBeanClass()) {
                interceptors.add(new BeanAroundInvokeInterceptor(method));
            } else {
                Integer bigInt = instanceIndexMap.get(className);
                int index = (bigInt == null) ? -1 : bigInt;
                if (index == -1) {
                    throw new IllegalStateException(getInternalErrorString(className));
                }
                Class clazz = interceptorClasses[index];
                _logger.log(FINE, "*[md.getDeclaredMethod() => " + method + " FOR CLAZZ: " + clazz);
                interceptors.add(new AroundInvokeInterceptor(index, method));
            }
        }
    }

    public boolean hasInterceptors() {
        return this.interceptorsExists;
    }

    public Object intercept(InterceptorManager.InterceptorChain chain, AroundInvokeContext ctx) throws Throwable {
        return chain.invokeNext(0, ctx);
    }

    public boolean intercept(CallbackType eventType, EJBContextImpl ctx) throws Throwable {
        return intercept(eventType, ctx.getEJB(), ctx.getInterceptorInstances(), ctx);
    }

    public boolean intercept(CallbackType eventType, Object targetObject, Object[] interceptorInstances) throws Throwable {
        return intercept(eventType, targetObject, interceptorInstances, null);
    }

    public boolean intercept(CallbackType eventType, Object targetObject, Object[] interceptorInstances, EJBContextImpl ctx) throws Throwable {
        CallbackChainImpl chain = null;
        CallbackInvocationContext invContext = null;

        switch (eventType) {
            case AROUND_CONSTRUCT:
                chain = callbackChain[eventType.ordinal()];
                if (container != null) {
                    invContext = new CallbackInvocationContext(beanClass, interceptorInstances, chain, eventType, container, ctx);
                } else {
                    invContext = new CallbackInvocationContext(beanClass, interceptorInstances, chain, eventType, interceptorInfo);
                }
                if (chain != null) {
                    chain.invokeNext(0, invContext);
                }
                break;
            case POST_CONSTRUCT:
            case PRE_PASSIVATE:
            case POST_ACTIVATE:
            case PRE_DESTROY:
                chain = callbackChain[eventType.ordinal()];
                invContext = new CallbackInvocationContext(targetObject, interceptorInstances, chain, eventType);
                if (chain != null) {
                    chain.invokeNext(0, invContext);
                }
                break;
            default:
                throw new IllegalStateException("Invalid event type");
        }

        return true;
    }

    Object getTargetInstance() {
        return interceptorInfo.getTargetObjectInstance();
    }

    private void buildEjbInterceptorChain() throws Exception {
        Set> listOfClasses = new HashSet<>();

        for (EjbInterceptor ejbInterceptor : ejbDesc.getInterceptorClasses()) {
            if (ejbInterceptor.getInterceptorClass() == null) {
                listOfClasses.add(loader.loadClass(ejbInterceptor.getInterceptorClassName()));
            } else {
                listOfClasses.add(ejbInterceptor.getInterceptorClass());
            }
        }

        // Add framework interceptors to list, but check for existence of
        // class before attempting to load it via application class loader
        for (InterceptorDescriptor frameworkInterceptor : frameworkInterceptors) {
            Class clazz = frameworkInterceptor.getInterceptorClass();
            if (clazz == null) {
                clazz = loader.loadClass(frameworkInterceptor.getInterceptorClassName());
            }
            listOfClasses.add(clazz);

        }

        initInterceptorClasses(listOfClasses);

        interceptorsExists = (!listOfClasses.isEmpty()) || ejbDesc.hasAroundInvokeMethod() || ejbDesc.hasAroundTimeoutMethod();

        initEjbCallbackIndices();
    }

    private void buildInterceptorChain(InterceptorInfo interceptorInfo) throws Exception {
        Set> listOfClasses = new HashSet<>();
        for (String name : interceptorInfo.getInterceptorClassNames()) {
            listOfClasses.add(loader.loadClass(name));
        }

        // Add framework interceptors to list, but check for existence of
        // class before attempting to load it via application class loader
        for (InterceptorDescriptor frameworkInterceptor : frameworkInterceptors) {
            Class clazz = frameworkInterceptor.getInterceptorClass();
            if (clazz == null) {
                clazz = loader.loadClass(frameworkInterceptor.getInterceptorClassName());
            }
            listOfClasses.add(clazz);
        }

        initInterceptorClasses(listOfClasses);

        interceptorsExists = (listOfClasses.size() > 0) || interceptorInfo.getHasTargetClassAroundInvoke();

        int size = CallbackType.values().length;
        callbackChain = new CallbackChainImpl[size];

        initCallbackIndices(interceptorInfo.getAroundConstructInterceptors(), CallbackType.AROUND_CONSTRUCT);
        initCallbackIndices(interceptorInfo.getPostConstructInterceptors(), CallbackType.POST_CONSTRUCT);
        initCallbackIndices(interceptorInfo.getPreDestroyInterceptors(), CallbackType.PRE_DESTROY);

    }

    private void initInterceptorClasses(Set> classes) throws Exception {
        int size = classes.size();
        interceptorClasses = new Class[size];
        serializableInterceptorClasses = new Class[size];
        int index = 0;

        for (Class interClass : classes) {
            interceptorClasses[index] = interClass;
            serializableInterceptorClasses[index] = interClass;
            instanceIndexMap.put(interClass.getName(), index);
            if (!Serializable.class.isAssignableFrom(interClass)) {
                serializableInterceptorClasses[index] = loadGeneratedSerializableClass(interClass.getClassLoader(), interClass);
            }
            index++;
        }

        // bean class is never accessed from instanceIndexMap so it's
        // never added.
    }

    private void initEjbCallbackIndices() throws ClassNotFoundException, Exception {
        int size = CallbackType.values().length;
        ArrayList[] callbacks = new ArrayList[size];
        boolean scanFor2xLifecycleMethods = true;
        int numPostConstructFrameworkCallbacks = 0;
        for (CallbackType eventType : CallbackType.values()) {
            int index = eventType.ordinal();
            callbacks[index] = new ArrayList();
            boolean scanForCallbacks = true;
            if (!(ejbDesc instanceof EjbSessionDescriptor)) {
                if (eventType == PRE_PASSIVATE || eventType == POST_ACTIVATE) {
                    scanForCallbacks = false;
                }
            }

            if (scanForCallbacks) {

                // Make sure any framework interceptors are first
                for (InterceptorDescriptor callback : frameworkInterceptors) {
                    if (callback.hasCallbackDescriptor(eventType)) {
                        Class interceptorClass = callback.getInterceptorClass();
                        ClassLoader classLoaderToUse = (interceptorClass != null) ? interceptorClass.getClassLoader() : loader;
                        List inters = createCallbackInterceptors(eventType, callback, classLoaderToUse);
                        for (CallbackInterceptor inter : inters) {
                            callbacks[index].add(inter);
                            if (eventType == CallbackType.POST_CONSTRUCT) {
                                numPostConstructFrameworkCallbacks++;
                            }
                        }
                    }
                }

                List callbackList = ejbDesc.getCallbackInterceptors(eventType);

                // If there are any application-specified callbacks in metadata, there's no need
                // to scan for the old 2.x style lifecycle methods
                if (callbackList.size() > 0) {
                    scanFor2xLifecycleMethods = false;
                }

                for (EjbInterceptor callback : callbackList) {
                    List inters = createCallbackInterceptors(eventType, callback);
                    for (CallbackInterceptor inter : inters) {
                        callbacks[index].add(inter);
                    }
                }
            }

        }

        if (scanFor2xLifecycleMethods) {
            load2xLifecycleMethods(callbacks);
        }

        // The next set of lines are to handle the case where
        // the app doesn't have a @PostConstruct or it
        // doesn't implement the EntrerpriseBean interface
        // In this case we scan for ejbCreate() for MDBs and SLSBs
        boolean lookForEjbCreateMethod = container.scanForEjbCreateMethod();

        if (lookForEjbCreateMethod) {
            loadOnlyEjbCreateMethod(callbacks, numPostConstructFrameworkCallbacks);
        }

        callbackChain = new CallbackChainImpl[size];
        for (CallbackType eventType : CallbackType.values()) {
            int index = eventType.ordinal();
            CallbackInterceptor[] interceptors = (CallbackInterceptor[])
                callbacks[index].toArray(new CallbackInterceptor[callbacks[index].size()]);
            callbackChain[index] = new CallbackChainImpl(interceptors);
        }

    }

    private void initCallbackIndices(List callbackList, CallbackType callbackType) throws Exception {
        List callbacks = new ArrayList<>();

        int index = callbackType.ordinal();

        for (InterceptorDescriptor callback : frameworkInterceptors) {
            if (callback.hasCallbackDescriptor(callbackType)) {
                Class interceptorClass = callback.getInterceptorClass();
                ClassLoader classLoaderToUse = (interceptorClass != null) ? interceptorClass.getClassLoader() : loader;
                List inters = createCallbackInterceptors(callbackType, callback, classLoaderToUse);
                for (CallbackInterceptor inter : inters) {
                    callbacks.add(inter);
                }
            }
        }

        for (InterceptorDescriptor callback : callbackList) {
            List inters = createCallbackInterceptors(callbackType, callback);
            for (CallbackInterceptor inter : inters) {
                callbacks.add(inter);
            }
        }

        // move above callbackChain = new CallbackChainImpl[size];

        callbackChain[index] = new CallbackChainImpl(callbacks.toArray(new CallbackInterceptor[callbacks.size()]));
    }

    private List createCallbackInterceptors(CallbackType eventType, InterceptorDescriptor inter) throws Exception {
        // Create callback interceptors using loader provided at initialization time
        return createCallbackInterceptors(eventType, inter, loader);

    }

    private List createCallbackInterceptors(CallbackType eventType, InterceptorDescriptor inter, ClassLoader classLoaderToUse) throws Exception {
        List callbackList = new ArrayList<>();

        List orderedCallbackMethods = inter.getOrderedCallbackDescriptors(eventType, classLoaderToUse);

        String className = inter.getInterceptorClassName();

        for (LifecycleCallbackDescriptor callbackDesc : orderedCallbackMethods) {
            Method method = null;
            try {
                method = callbackDesc.getLifecycleCallbackMethodObject(classLoaderToUse);
            } catch (Exception e) {
                throw new IllegalStateException(
                        "No callback method of name " + callbackDesc.getLifecycleCallbackMethod() + " found on class " + className, e);
            }

            CallbackInterceptor interceptor = null;
            if (inter.getFromBeanClass()) {
                interceptor = new BeanCallbackInterceptor(method);
            } else {
                Integer bigInt = instanceIndexMap.get(className);
                int index = (bigInt == null) ? -1 : bigInt;
                if (index == -1) {
                    throw new IllegalStateException(getInternalErrorString(className));
                }
                interceptor = new CallbackInterceptor(index, method);
            }

            callbackList.add(interceptor);
        }

        return callbackList;
    }

    private void load2xLifecycleMethods(ArrayList[] metaArray) {

        if (jakarta.ejb.EnterpriseBean.class.isAssignableFrom(beanClass)) {
            int sz = lcAnnotationClasses.length;
            for (int i = 0; i < sz; i++) {
                if (pre30LCMethodNames[i] == null) {
                    continue;
                }
                try {
                    Method method = beanClass.getMethod(pre30LCMethodNames[i], (Class[]) null);
                    if (method != null) {
                        CallbackInterceptor meta = new BeanCallbackInterceptor(method);
                        metaArray[i].add(meta);
                        _logger.log(FINE, "**## bean has 2.x LM: " + meta);
                    }
                } catch (NoSuchMethodException nsmEx) {
                    // TODO: Log exception
                    // Error for a 2.x bean????
                }
            }
        }
    }

    // TODO: load2xLifecycleMethods and loadOnlyEjbCreateMethod can be
    // refactored to use a common method.
    private void loadOnlyEjbCreateMethod(ArrayList[] metaArray, int numPostConstructFrameworkCallbacks) {
        int sz = lcAnnotationClasses.length;
        for (int i = 0; i < sz; i++) {
            if (lcAnnotationClasses[i] != PostConstruct.class) {
                continue;
            }

            boolean needToScan = true;
            if (metaArray[i] != null) {
                ArrayList al = metaArray[i];
                needToScan = (al.size() == numPostConstructFrameworkCallbacks);
            }

            if (!needToScan) {
                // We already have found a @PostConstruct method
                // So just ignore any ejbCreate() method
                break;
            } else {
                try {
                    Method method = beanClass.getMethod(pre30LCMethodNames[i], (Class[]) null);
                    if (method != null) {
                        CallbackInterceptor meta = new BeanCallbackInterceptor(method);
                        metaArray[i].add(meta);
                        _logger.log(FINE, "**##[ejbCreate] bean has 2.x style ejbCreate: " + meta);
                    }
                } catch (NoSuchMethodException nsmEx) {
                    // TODO: Log exception
                    // Error for a 2.x bean????
                }
            }
        }
    }

    @Override
    public String toString() {
        StringBuilder sbldr = new StringBuilder();
        sbldr.append("##########################################################\n");
        sbldr.append("InterceptorManager<").append(beanClassName).append("> has ").append(interceptorClasses.length)
                .append(" interceptors");
        sbldr.append("\n\tbeanClassName: ").append(beanClassName);
        sbldr.append("\n\tInterceptors: ");
        for (Class clazz : interceptorClasses) {
            sbldr.append("\n\t\t").append(clazz.getName());
        }
        if (lcAnnotationClasses != null) {
            // lcAnnotationClasses are available for EJBs only
            sbldr.append("\n\tCallback Interceptors: ");
            for (int i = 0; i < lcAnnotationClasses.length; i++) {
                CallbackChainImpl chain = callbackChain[i];
                sbldr.append("\n\t").append(i).append(": ").append(lcAnnotationClasses[i]);
                sbldr.append("\n\t\t").append(chain.toString());
            }
        }
        sbldr.append("\n");
        sbldr.append("##########################################################\n");
        return sbldr.toString();
    }

    private String getInternalErrorString(String className) {
        StringBuilder sbldr = new StringBuilder("Internal error: ");
        sbldr.append(" className: ").append(className).append(" is neither a bean class (").append(beanClassName).append(") nor an ")
                .append("interceptor class (");
        for (Class cn : interceptorClasses) {
            sbldr.append(cn.getName()).append("; ");
        }
        sbldr.append(")");
        _logger.log(Level.INFO, "++ : " + sbldr.toString());
        return sbldr.toString();
    }

    public interface AroundInvokeContext extends InvocationContext {

        Object[] getInterceptorInstances();

        /**
         * Called from Interceptor Chain to invoke the actual bean method. This method must throw any exception from the bean
         * method *as is*, without being wrapped in an InvocationTargetException. The exception thrown from this method will be
         * propagated through the application's interceptor code, so it must not be changed in order for any exception handling
         * logic in that code to function properly.
         */
        Object invokeBeanMethod() throws Throwable;

    }

    public interface InterceptorChain {
        Object invokeNext(int index, AroundInvokeContext invCtx) throws Throwable;
    }

}

class AroundInvokeChainImpl implements InterceptorManager.InterceptorChain {
    enum ChainType {
        METHOD, CALLBACK
    }

    protected AroundInvokeInterceptor[] interceptors;
    protected int size;

    protected AroundInvokeChainImpl(AroundInvokeInterceptor[] interceptors) {

        this.interceptors = interceptors;
        this.size = (interceptors == null) ? 0 : interceptors.length;
    }

    @Override
    public Object invokeNext(int index, InterceptorManager.AroundInvokeContext inv) throws Throwable {
        return (index < size) ? interceptors[index].intercept(inv) : inv.invokeBeanMethod();
    }

    @Override
    public String toString() {
        StringBuilder bldr = new StringBuilder();
        for (AroundInvokeInterceptor inter : interceptors) {
            bldr.append("\n\t").append(inter);
        }

        return bldr.toString();
    }
}

class AroundInvokeInterceptor {
    protected int index;
    protected Method method;

    AroundInvokeInterceptor(int index, Method method) {
        this.index = index;
        this.method = method;

        try {
            final Method finalM = method;
            if (!finalM.trySetAccessible()) {
                throw new InaccessibleObjectException("Unable to make accessible: "+ finalM);
            }
        } catch (Exception e) {
            throw new EJBException(e);
        }

    }

    Object intercept(final InterceptorManager.AroundInvokeContext invCtx) throws Throwable {
        try {
            return method.invoke(invCtx.getInterceptorInstances()[index], invCtx);
        } catch (InvocationTargetException invEx) {
            throw invEx.getCause();
        }
    }

    @Override
    public String toString() {
        return "[" + index + "]: " + method;
    }

}

class BeanAroundInvokeInterceptor extends AroundInvokeInterceptor {

    BeanAroundInvokeInterceptor(Method method) {
        super(-1, method);
    }

    @Override
    Object intercept(final InterceptorManager.AroundInvokeContext invCtx) throws Throwable {
        try {
            return method.invoke(invCtx.getTarget(), invCtx);
        } catch (InvocationTargetException invEx) {
            throw invEx.getCause();
        }
    }
}

class CallbackInterceptor {
    protected int index;
    protected Method method;

    CallbackInterceptor(int index, Method method) {
        this.index = index;
        this.method = method;

        try {
            if (!method.trySetAccessible()) {
                throw new InaccessibleObjectException("Unable to make accessible: " + method);
            }
        } catch (Exception e) {
            throw new EJBException(e);
        }

    }

    Object intercept(final CallbackInvocationContext invContext) throws Throwable {
        try {
            return method.invoke(invContext.getInterceptorInstances()[index], invContext);
        } catch (java.lang.reflect.InvocationTargetException invEx) {
            throw invEx.getCause();
        }
    }

    boolean isBeanCallback() {
        return false;
    }

    @Override
    public String toString() {
        return "callback[" + index + "]: " + method;
    }
}

class BeanCallbackInterceptor extends CallbackInterceptor {
    private static final Object[] NULL_ARGS = null;

    BeanCallbackInterceptor(Method method) {
        super(-1, method);
    }

    @Override
    Object intercept(final CallbackInvocationContext invContext) throws Throwable {
        try {
            method.invoke(invContext.getTarget(), NULL_ARGS);
            return invContext.proceed();
        } catch (InvocationTargetException invEx) {
            throw invEx.getCause();
        } catch (PrivilegedActionException paEx) {
            Throwable th = paEx.getCause();
            if (th.getCause() != null) {
                throw th.getCause();
            }
            throw th;
        }
    }

    @Override
    boolean isBeanCallback() {
        return true;
    }

    @Override
    public String toString() {
        return "beancallback[" + index + "]: " + method;
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy