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

org.glassfish.weld.services.EjbServicesImpl Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 2009-2015 Oracle and/or its affiliates. All rights reserved.
 *
 * The contents of this file are subject to the terms of either the GNU
 * General Public License Version 2 only ("GPL") or the Common Development
 * and Distribution License("CDDL") (collectively, the "License").  You
 * may not use this file except in compliance with the License.  You can
 * obtain a copy of the License at
 * https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
 * or packager/legal/LICENSE.txt.  See the License for the specific
 * language governing permissions and limitations under the License.
 *
 * When distributing the software, include this License Header Notice in each
 * file and include the License file at packager/legal/LICENSE.txt.
 *
 * GPL Classpath Exception:
 * Oracle designates this particular file as subject to the "Classpath"
 * exception as provided by Oracle in the GPL Version 2 section of the License
 * file that accompanied this code.
 *
 * Modifications:
 * If applicable, add the following below the License Header, with the fields
 * enclosed by brackets [] replaced by your own identifying information:
 * "Portions Copyright [year] [name of copyright owner]"
 *
 * Contributor(s):
 * If you wish your version of this file to be governed by only the CDDL or
 * only the GPL Version 2, indicate your decision by adding "[Contributor]
 * elects to include this software in this distribution under the [CDDL or GPL
 * Version 2] license."  If you don't indicate a single choice of license, a
 * recipient has the option to distribute your version of this file under
 * either the CDDL, the GPL Version 2 or to extend the choice of license to
 * its licensees as provided above.  However, if you add GPL Version 2 code
 * and therefore, elected the GPL Version 2 license, then the option applies
 * only if the new code is made subject to such option by the copyright
 * holder.
 */
// Portions Copyright [2016-2022] [Payara Foundation and/or its affiliates]

package org.glassfish.weld.services;

import static java.util.logging.Level.FINE;
import static jakarta.enterprise.inject.spi.InterceptionType.AROUND_CONSTRUCT;
import static jakarta.enterprise.inject.spi.InterceptionType.POST_ACTIVATE;
import static jakarta.enterprise.inject.spi.InterceptionType.POST_CONSTRUCT;
import static jakarta.enterprise.inject.spi.InterceptionType.PRE_DESTROY;
import static jakarta.enterprise.inject.spi.InterceptionType.PRE_PASSIVATE;
import static org.glassfish.cdi.CDILoggerInfo.ADDING_INTERCEPTOR_FOR_EJB;
import static org.glassfish.cdi.CDILoggerInfo.TRYING_TO_REGISTER_INTERCEPTOR;

import java.lang.reflect.Method;
import java.util.LinkedList;
import java.util.List;
import java.util.logging.Logger;

import jakarta.annotation.PostConstruct;
import jakarta.annotation.PreDestroy;
import jakarta.ejb.PostActivate;
import jakarta.ejb.PrePassivate;
import jakarta.enterprise.inject.spi.InterceptionType;
import jakarta.enterprise.inject.spi.Interceptor;
import jakarta.interceptor.AroundConstruct;
import jakarta.interceptor.AroundInvoke;
import jakarta.interceptor.AroundTimeout;
import javax.naming.InitialContext;
import javax.naming.NamingException;

import jakarta.interceptor.InvocationContext;
import org.glassfish.ejb.api.EjbContainerServices;
import org.glassfish.hk2.api.ServiceLocator;
import org.glassfish.weld.ejb.EjbDescriptorImpl;
import org.glassfish.weld.ejb.SessionObjectReferenceImpl;
import org.jboss.weld.ejb.api.SessionObjectReference;
import org.jboss.weld.ejb.spi.EjbDescriptor;
import org.jboss.weld.ejb.spi.EjbServices;
import org.jboss.weld.ejb.spi.InterceptorBindings;

import com.sun.enterprise.deployment.EjbBundleDescriptor;
import com.sun.enterprise.deployment.EjbInterceptor;
import com.sun.enterprise.deployment.EjbSessionDescriptor;
import com.sun.enterprise.deployment.LifecycleCallbackDescriptor;

/**
 * An implementation of th EJBServices Weld SPI. The Weld
 * implementation uses this SPI to resolve EJB and register CDI Interceptors for
 * EJBs.
 */
public class EjbServicesImpl implements EjbServices {

    private ServiceLocator services;
    private Logger logger = Logger.getLogger(EjbServicesImpl.class.getName());

    public EjbServicesImpl(ServiceLocator h) {
        services = h;
    }

    /**
     * Request a reference to an EJB session object from the container. If the
     * EJB being resolved is a stateful session bean, the container should
     * ensure the session bean is created before this method returns.
     *
     * @param ejbDescriptor the ejb to resolve
     * @return a reference to the session object
     */
    @Override
    public SessionObjectReference resolveEjb(EjbDescriptor ejbDescriptor) {

        SessionObjectReference sessionObj = null;

        // All we need to do is create a reference based on one of the beans'
        // client views, so just choose one and get its corresponding portable
        // JNDI name.
        String globalJndiName = getDefaultGlobalJndiName(ejbDescriptor);
        if (globalJndiName != null) {
            try {

                InitialContext ic = new InitialContext();

                Object ejbRef = ic.lookup(globalJndiName);

                EjbContainerServices containerServices = services.getService(EjbContainerServices.class);

                sessionObj = new SessionObjectReferenceImpl(containerServices, ejbRef);

            } catch (NamingException ne) {
                throw new IllegalStateException("Error resolving session object reference for ejb name "
                        + ejbDescriptor.getBeanClass() + " and jndi name " + globalJndiName, ne);
            }
        } else {
            throw new IllegalArgumentException("Not enough type information to resolve ejb for "
                    + " ejb name " + ejbDescriptor.getBeanClass());
        }

        return sessionObj;

    }

    private String getDefaultGlobalJndiName(EjbDescriptor ejbDesc) {

        EjbSessionDescriptor sessionDesc = (EjbSessionDescriptor) ((EjbDescriptorImpl) ejbDesc).getEjbDescriptor();

        String clientView = null;

        if (sessionDesc.isLocalBean()) {
            clientView = sessionDesc.getEjbClassName();
        } else if (sessionDesc.getLocalBusinessClassNames().size() >= 1) {
            clientView = sessionDesc.getLocalBusinessClassNames().iterator().next();
        } else if (sessionDesc.getRemoteBusinessClassNames().size() >= 1) {
            clientView = sessionDesc.getRemoteBusinessClassNames().iterator().next();
        }

        return clientView != null ? sessionDesc.getPortableJndiName(clientView) : null;
    }

    @Override
    public void registerInterceptors(EjbDescriptor ejbDesc, InterceptorBindings interceptorBindings) {

        com.sun.enterprise.deployment.EjbDescriptor glassfishEjbDesc = (com.sun.enterprise.deployment.EjbDescriptor) ((EjbDescriptorImpl) ejbDesc).getEjbDescriptor();

        // Convert to EjbInterceptor
        // First create master list of EjbInterceptor descriptors
        for (Interceptor next : interceptorBindings.getAllInterceptors()) {
            if (logger.isLoggable(FINE)) {
                logger.log(FINE,
                    TRYING_TO_REGISTER_INTERCEPTOR,
                    new Object[]{next});
            }
            
            // Add interceptor to list all interceptors in ejb descriptor
            if (!(glassfishEjbDesc.hasInterceptorClass(next.getBeanClass().getName()))) {
                if (logger.isLoggable(FINE)) {
                    logger.log(FINE,
                        ADDING_INTERCEPTOR_FOR_EJB,
                        new Object[]{next.getBeanClass().getName(), glassfishEjbDesc.getEjbClassName()});
                }
                
                EjbInterceptor ejbInt = makeEjbInterceptor(next, glassfishEjbDesc.getEjbBundleDescriptor());
                ejbInt.setInterceptorClass(next.getBeanClass());
                glassfishEjbDesc.addInterceptorClass(ejbInt);
            }
        }

        // Create ordered list of EjbInterceptor for each lifecycle interception type and append to
        // EjbDescriptor.   
        // JSR 299 (CDI) interceptors are always added after any interceptors defined via
        // EJB-defined metadata, so the ordering will be correct since all the ejb interceptors
        // have already been processed.
        
        glassfishEjbDesc.appendToInterceptorChain(makeInterceptorChain(AROUND_CONSTRUCT,
                interceptorBindings.getLifecycleInterceptors(AROUND_CONSTRUCT),
                glassfishEjbDesc));
        
        glassfishEjbDesc.appendToInterceptorChain(makeInterceptorChain(POST_CONSTRUCT,
                interceptorBindings.getLifecycleInterceptors(POST_CONSTRUCT),
                glassfishEjbDesc));

        glassfishEjbDesc.appendToInterceptorChain(makeInterceptorChain(PRE_DESTROY,
                interceptorBindings.getLifecycleInterceptors(PRE_DESTROY),
                glassfishEjbDesc));

        glassfishEjbDesc.appendToInterceptorChain(makeInterceptorChain(PRE_PASSIVATE,
                interceptorBindings.getLifecycleInterceptors(PRE_PASSIVATE),
                glassfishEjbDesc));

        glassfishEjbDesc.appendToInterceptorChain(makeInterceptorChain(POST_ACTIVATE,
                interceptorBindings.getLifecycleInterceptors(POST_ACTIVATE),
                glassfishEjbDesc));

        // JSR 299-provided list is organized as per-method.  Append each method chain to EjbDescriptor.
        Class ejbBeanClass = null;

        try {
            ClassLoader cl = glassfishEjbDesc.getEjbBundleDescriptor().getClassLoader();
            ejbBeanClass = cl.loadClass(glassfishEjbDesc.getEjbClassName());
        } catch (ClassNotFoundException cnfe) {
            throw new IllegalStateException("Cannot load bean class " + glassfishEjbDesc.getEjbClassName(),
                    cnfe);
        }

        Class ejbBeanSuperClass = ejbBeanClass;
        while (!ejbBeanSuperClass.equals(java.lang.Object.class)) {
            for (Method m : ejbBeanSuperClass.getDeclaredMethods()) {
                if (!methodOverridden(ejbBeanClass, m)) {
                    List aroundInvokeChain
                            = makeInterceptorChain(InterceptionType.AROUND_INVOKE,
                                    interceptorBindings.getMethodInterceptors(InterceptionType.AROUND_INVOKE, m),
                                    glassfishEjbDesc);
                    glassfishEjbDesc.addMethodLevelChain(aroundInvokeChain, m, true);

                    List aroundTimeoutChain
                            = makeInterceptorChain(InterceptionType.AROUND_TIMEOUT,
                                    interceptorBindings.getMethodInterceptors(InterceptionType.AROUND_TIMEOUT, m),
                                    glassfishEjbDesc);
                    glassfishEjbDesc.addMethodLevelChain(aroundTimeoutChain, m, false);
                }
            }
            ejbBeanSuperClass = ejbBeanSuperClass.getSuperclass();
        }

    }

    // Section 4.2. Inheritance of member-level metadata of CDI spec states:
    //   If X declares a non-static method x() annotated with an interceptor binding type Z then Y inherits
    //   the binding if and only if neither Y nor any intermediate class that is a subclass of X and a
    //   superclass of Y overrides the method x(). (This behavior is defined by the Common Annotations
    //   for the Java Platform specification.)
    //
    // So look at a method on a class.  If the method is overridden (not if it over rides) then we skip it
    // because it will have already been processed to see if it should be intercepted.
    @SuppressWarnings("unchecked")
    private boolean methodOverridden(Class beanClass, Method methodOfCurrentClass) {
        String methodName = methodOfCurrentClass.getName();
        Class[] methodParams = methodOfCurrentClass.getParameterTypes();
        Class declaringClass = methodOfCurrentClass.getDeclaringClass();

        try {
            return !beanClass.getMethod(methodName, methodParams)
                             .getDeclaringClass()
                             .equals(declaringClass);
            
        } catch (NoSuchMethodException ignored) {
            // ignore
        }
        
        return false;
    }

    private List makeInterceptorChain(InterceptionType interceptionType, List> lifecycleList,
            com.sun.enterprise.deployment.EjbDescriptor ejbDesc) {

        List ejbInterceptorList = new LinkedList();

        if (lifecycleList == null) {
            return ejbInterceptorList;
        }

        for (Interceptor next : lifecycleList) {
            EjbInterceptor ejbInt = makeEjbInterceptor(next, ejbDesc.getEjbBundleDescriptor());
            Class interceptorClass = next.getBeanClass();

            while (interceptorClass != null && !interceptorClass.equals(Object.class)) {
                String methodName = getInterceptorMethod(interceptorClass, getInterceptorAnnotationType(interceptionType));
                if (methodName != null) {
                    LifecycleCallbackDescriptor lifecycleDesc = new LifecycleCallbackDescriptor();

                    lifecycleDesc.setLifecycleCallbackClass(interceptorClass.getName());
                    lifecycleDesc.setLifecycleCallbackMethod(methodName);
                    
                    switch (interceptionType) {
                        case POST_CONSTRUCT :
                            ejbInt.addPostConstructDescriptor(lifecycleDesc);
                            break;
                        case PRE_DESTROY :
                            ejbInt.addPreDestroyDescriptor(lifecycleDesc);
                            break;
                        case PRE_PASSIVATE :
                            ejbInt.addPrePassivateDescriptor(lifecycleDesc);
                            break;
                        case POST_ACTIVATE :
                            ejbInt.addPostActivateDescriptor(lifecycleDesc);
                            break;
                        case AROUND_INVOKE :
                            lifecycleDesc.setRequiresInvocationContextArgument(true);
                            ejbInt.addAroundInvokeDescriptor(lifecycleDesc);
                            break;
                        case AROUND_TIMEOUT :
                            lifecycleDesc.setRequiresInvocationContextArgument(true);
                            ejbInt.addAroundTimeoutDescriptor(lifecycleDesc);
                            break;
                        case AROUND_CONSTRUCT :
                             ejbInt.addAroundConstructDescriptor(lifecycleDesc);
                             break;
                        default :
                            throw new IllegalArgumentException("Invalid lifecycle interception type " + interceptionType);
                    }
                }

                interceptorClass = interceptorClass.getSuperclass();
            }

            ejbInterceptorList.add(ejbInt);
        }

        return ejbInterceptorList;
    }

    private Class getInterceptorAnnotationType(InterceptionType interceptionType) {

        switch (interceptionType) {
            case AROUND_CONSTRUCT :
                return AroundConstruct.class;
            case POST_CONSTRUCT:
                return PostConstruct.class;
            case PRE_DESTROY:
                return PreDestroy.class;
            case PRE_PASSIVATE:
                return PrePassivate.class;
            case POST_ACTIVATE:
                return PostActivate.class;
            case AROUND_INVOKE:
                return AroundInvoke.class;
            case AROUND_TIMEOUT:
                return AroundTimeout.class;
        }

        throw new IllegalArgumentException("Invalid interception type " + interceptionType);
    }

    @SuppressWarnings({"unchecked", "rawtypes"})
    private String getInterceptorMethod(Class interceptorClass, Class annotation) {

        for (Method next : interceptorClass.getDeclaredMethods()) {
            if (next.getAnnotation(annotation) != null) {
                return next.getName();
            }
        }

        return null;
    }

    private EjbInterceptor makeEjbInterceptor(Interceptor interceptor, EjbBundleDescriptor bundle) {

        EjbInterceptor ejbInt = new EjbInterceptor();
        ejbInt.setBundleDescriptor(bundle);
        ejbInt.setInterceptorClass(interceptor.getBeanClass());
        ejbInt.setInterceptorClassName(interceptor.getBeanClass().getName());
        ejbInt.setCDIInterceptor(true);
        ejbInt.setInterceptor(interceptor);

        return ejbInt;
    }

    @Override
    public void cleanup() {
        //Nothing to do here.
    }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy