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

com.sun.ejb.EjbInvocation Maven / Gradle / Ivy

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 *
 * Copyright (c) 1997-2013 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 [2019-2021] Payara Foundation and/or affiliates

package com.sun.ejb;

import com.sun.ejb.containers.BaseContainer;
import com.sun.ejb.containers.EJBContextImpl;
import com.sun.ejb.containers.EJBLocalRemoteObject;
import com.sun.ejb.containers.EjbContainerUtilImpl;
import com.sun.ejb.containers.EjbFutureTask;
import com.sun.ejb.containers.SimpleEjbResourceHandlerImpl;
import com.sun.ejb.containers.interceptors.InterceptorManager;
import com.sun.ejb.containers.interceptors.InterceptorManager.AroundInvokeContext;
import com.sun.ejb.containers.interceptors.InterceptorUtil;
import com.sun.enterprise.deployment.EjbBundleDescriptor;
import com.sun.enterprise.deployment.MethodDescriptor;
import com.sun.enterprise.transaction.spi.TransactionOperationsManager;

import java.lang.reflect.Constructor;
import java.lang.reflect.Method;
import java.rmi.UnmarshalException;
import java.util.Arrays;
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.function.Predicate;

import jakarta.ejb.EJBContext;
import jakarta.ejb.Timer;
import jakarta.interceptor.InvocationContext;
import javax.naming.NameNotFoundException;
import jakarta.transaction.Transaction;
import jakarta.xml.soap.SOAPMessage;
import jakarta.xml.ws.WebServiceContext;

import org.glassfish.api.invocation.ComponentInvocation;
import org.glassfish.api.invocation.ResourceHandler;
import org.glassfish.ejb.api.EJBInvocation;

/**
 * The EjbInvocation object contains state associated with an invocation
 * on an EJB or EJBHome (local/remote). It is usually created by generated code
 * in *ObjectImpl and *HomeImpl classes. It is passed as a parameter to
 * Container.preInvoke() * and postInvoke(), which are called by the
 * EJB(Local)Object/EJB(Local)Home before and after an invocation.
 */

public class EjbInvocation //
    extends ComponentInvocation //
    implements InvocationContext, TransactionOperationsManager, EJBInvocation, AroundInvokeContext {

    public ComponentContext context;

    private TransactionOperationsManager transactionOperationsManager;

    /**
     * The EJBObject/EJBLocalObject which created this EjbInvocation object.
     * This identifies the target bean.
     */
    public EJBLocalRemoteObject ejbObject;

    /**
     * Local flag: true if this invocation was through the 2.x (or earlier)
     * Local client view, the 3.x local client view or a no-interface client view.
     */
    public boolean isLocal = false;

    /**
     * True if this invocation was made through the 2.x (or earlier) Remote
     * client view or the 3.x remote client view.
     */
    public boolean isRemote = false;

    /**
     * InvocationInfo object caches information about the current method
     */
    public InvocationInfo invocationInfo;

    /**
     * True if this invocation was made through a local business interface or
     * bean local view or a remote business interface.
     */
    public boolean isBusinessInterface;

    /**
     * true if this is a web service invocation
     */
    public boolean isWebService = false;

    /**
     * true if this is an ejb timeout method invocation
     */
    public boolean isTimerCallback = false;

    /**
     * true if this is a message-driven bean invocation
     */
    public boolean isMessageDriven = false;

    /**
     * true if this is an invocation on the home object
     * this is required for jacc.
     */
    public boolean isHome = false;

    /**
     * Home, Remote, LocalHome, Local, WebService, or business interface
     * through which a synchronous ejb invocation was made.
     */
    public Class clientInterface;

    /**
     * Method to be invoked. This is a method of the EJB's local/remote
     * component interface for invocations on EJB(Local)Objects,
     * or of the local/remote Home interface
     * for invocations on the EJBHome.
     * Set by the EJB(Local)Object/EJB(Local)Home before calling
     * Container.preInvoke().
     */
    public java.lang.reflect.Method method;

    /**
     * The EJB instance to be invoked.
     * Set by Container and used by EJBObject/EJBHome.
     */
    public Object ejb;

    /**
     * This reflects any exception that has occurred during this invocation,
     * including preInvoke, bean method execution, and postInvoke.
     */
    public Throwable exception;

    /**
     * Set to any exception directly thrown from bean method invocation,
     * which could be either an application exception or a runtime exception.
     * This is set *in addition to* the this.exception field.  Some container
     * processing logic, e.g. @Remove, depends specifically on whether a
     * bean method threw an exception.
     */
    public Throwable exceptionFromBeanMethod;

    /**
     * The client's transaction if any.
     * Set by the Container during preInvoke() and used by the Container
     * during postInvoke().
     */
    public Transaction clientTx;

    /**
     * The transaction attribute of the bean method. Set in generated
     * EJBObject/Home/LocalObject/LocalHome class.
     */
    public int transactionAttribute;

    /**
     * Used by MessageBeanContainer.  true if container started
     * a transaction for this invocation.
     */
    private boolean containerStartsTx;

    /**
     * Used by MessageBeanContainer to keep track of the context class
     * loader that was active before message delivery began.
     */
    private ClassLoader originalContextClassLoader;

    /**
     * Used for JACC PolicyContextHandlers. The handler can query the container
     * back for parameters on the ejb. This is set during the method invocation
     * and is not available for preInvoke calls.
     */
    public Object[] methodParams;

    public Timer timer;

    /**
     * Result of txManager.getStatus() performed at the beginning of
     * BaseContainer.preInvoke() and valid up until preinvokeTx().
     * txManager.getStatus() accesses a thread-local which is an
     * expensive operation.  Storing status in the invocation makes it
     * easier for some of the other early pre-invoke operations to
     * re-use it.
     */
    private Integer preInvokeTxStatus;

    /**
     * Tells if a CMP2.x bean was found in the Tx cache. Applicable
     * only for CMP2.x beans
     */
    public boolean foundInTxCache = false;

    /**
     * Tells if a fast path can be taken for a business method
     * invocation.
     */
    public boolean useFastPath = false;

    private java.util.concurrent.locks.Lock cmcLock;

    private boolean doTxProcessingInPostInvoke;

    private long invId;

    private boolean yetToSubmitStatus = true;

    private EjbFutureTask asyncFuture;

    private boolean wasCancelCalled = false;

    private Method webServiceMethod;

    // True if lock is currently held for this invocation
    private boolean holdingSFSBSerializedLock = false;

    private int interceptorIndex;

    public Method beanMethod;

    /** Only set for web service invocations. */
    private WebServiceContext webServiceContext;

    /** Only set for EJB JAXWS */
    // FIXME: private Message message = null;
    private Object message;

    private SOAPMessage soapMessage = null;

    private Map contextData;


    EjbInvocation(String compEnvId, Container container) {
        super.componentId = compEnvId;
        super.container = container;
        super.setComponentInvocationType(ComponentInvocation.ComponentInvocationType.EJB_INVOCATION);

        EjbBundleDescriptor ejbBundleDesc = container.getEjbDescriptor().getEjbBundleDescriptor();
        moduleName = ejbBundleDesc.getModuleName();
        appName = ejbBundleDesc.getApplication().getAppName();
        registrationName = ejbBundleDesc.getApplication().getRegistrationName();

        //By default we enable TransactionOperationsManager checks. But EjbInvocation.clone()
        //  clears transactionOperationsManager so that, be default, cloned invocations
        //  doesn't enforce Transaction Operations checks.
        transactionOperationsManager = this;
    }

    public ClassLoader getOriginalContextClassLoader() {
        return originalContextClassLoader;
    }

    public void setOriginalContextClassLoader(ClassLoader originalContextClassLoader) {
        this.originalContextClassLoader = originalContextClassLoader;
    }

    public EjbFutureTask getEjbFutureTask() {
        return asyncFuture;
    }

    public void setEjbFutureTask(EjbFutureTask future) {
        asyncFuture = future;
    }

    public void setWasCancelCalled(boolean flag) {
        wasCancelCalled = flag;
    }

    public boolean getWasCancelCalled() {
        return wasCancelCalled;
    }

    public long getInvId() {
        return invId;
    }

    public void setInvId(long invId) {
        this.invId = invId;
    }

    public boolean mustInvokeAsynchronously() {
        return (invocationInfo != null) && invocationInfo.isAsynchronous() && yetToSubmitStatus;
    }

    public void clearYetToSubmitStatus() {
        yetToSubmitStatus = false;
    }

    public boolean getDoTxProcessingInPostInvoke() {
        return doTxProcessingInPostInvoke;
    }

    public void setDoTxProcessingInPostInvoke(boolean doTxProcessingInPostInvoke) {
        this.doTxProcessingInPostInvoke = doTxProcessingInPostInvoke;
    }

    @Override
    public EjbInvocation clone() {
        EjbInvocation newInv = (EjbInvocation) super.clone();

        newInv.ejb = null;
        newInv.exception = null;
        newInv.exceptionFromBeanMethod = null;
        newInv.clientTx = null;
        newInv.preInvokeTxStatus = null;
        newInv.originalContextClassLoader = null;

        //The cloned invocation contains a ResourceHandler that points to the same
        //  resource list as the original invocation. If any one of these resource lists
        //  are modified, then we may get a ConcurrentModification exception.
        //
        //To avoid this, we will create a new ResourceHandler for the cloned invocation. I
        //  have simply reused SimpleEjbResourceHandlerImpl that was used in async Ejb invocation.
        newInv.setResourceHandler(SimpleEjbResourceHandlerImpl.createResourceHandler(EjbContainerUtilImpl.getInstance().getTransactionManager()));

        //The cloned invocation is most likely to be used for running a batch task.
        //  In this case, we don't want TransactionOperationsManager restricting the Batch runtime
        //  from performing a java:comp/UserTransaction lookup. So, we explicitly set a null
        //  TransactionOperationsManager in this case.
        newInv.setTransactionOperationsManager(null);

        //We also don't want any JPA EMs registry entries from being shared.
        newInv.clearRegistry();

        return newInv;
    }

    /**
     * Used by JACC implementation to get an enterprise bean
     * instance for the EnterpriseBean policy handler.  The jacc
     * implementation should use this method rather than directly
     * accessing the ejb field.
     */
    @Override
    public Object getJaccEjb() {
        Object bean = null;
        if( container != null ) {
            bean = ((Container) container).getJaccEjb(this);
        }
        return bean;
    }

    /**
     * This method returns the method interface constant for this EjbInvocation.
     */
    public String getMethodInterface() {
        if (isWebService) {
            return MethodDescriptor.EJB_WEB_SERVICE;
        } else if (isMessageDriven) {
            return MethodDescriptor.EJB_BEAN;
        } else if (isLocal) {
            return (isHome) ? MethodDescriptor.EJB_LOCALHOME :
                    MethodDescriptor.EJB_LOCAL;
        } else {
            return (isHome) ? MethodDescriptor.EJB_HOME :
                    MethodDescriptor.EJB_REMOTE;
        }
    }

    /**
     * @return CachedPermission associated with this invocation, or
     * null if not available.
     */
    public Object getCachedPermission() {
        return invocationInfo == null ? null : invocationInfo.cachedPermission;
    }

    /**
     * @return Returns the ejbCtx.
     */
    @Override
    public EJBContext getEJBContext() {
        return (EJBContext) this.context;
    }

    public Integer getPreInvokeTxStatus() {
        return preInvokeTxStatus;
    }

    public void setPreInvokeTxStatus(Integer txStatus) {
        // Can be null, which means preInvokeTxStatus is no longer applicable.
        preInvokeTxStatus = txStatus;
    }

    public java.util.concurrent.locks.Lock getCMCLock() {
        return cmcLock;
    }

    public void setCMCLock(java.util.concurrent.locks.Lock l) {
        cmcLock = l;
    }

    public boolean holdingSFSBSerializedLock() {
        return this.holdingSFSBSerializedLock;
    }

    public void setHoldingSFSBSerializedLock(boolean flag) {
        holdingSFSBSerializedLock = flag;
    }

    @Override
    public Object getTransactionOperationsManager() {
        return transactionOperationsManager;
    }

    public void setTransactionOperationsManager(TransactionOperationsManager transactionOperationsManager) {
        // Note: clone() clears transactionOperationsManager so that, be default, cloned invocations
        // doesn't enforce Transaction Operations checks.
        this.transactionOperationsManager = transactionOperationsManager;
    }

    /**
     * Called by the UserTransaction implementation to verify
     * access to the UserTransaction methods.
     */
    @Override
    public boolean userTransactionMethodsAllowed() {
        return ((Container) container).userTransactionMethodsAllowed(this);
    }

    /**
     * Called by the UserTransaction lookup to verify
     * access to the UserTransaction itself.
     */
    @Override
    public void userTransactionLookupAllowed() throws NameNotFoundException {
        ((BaseContainer) container).checkUserTransactionLookup(this);
    }

    /**
     * Called by the UserTransaction when transaction is started.
     */
    @Override
    public void doAfterUtxBegin() {
        ((Container) container).doAfterBegin(this);
    }

    public InterceptorManager.InterceptorChain getInterceptorChain() {
        return (invocationInfo == null) ? null : invocationInfo.interceptorChain;
    }

    /**
     * @return Returns the bean instance.
     */
    @Override
    public Object getTarget() {
        return this.ejb;
    }

    /**
     * @return Returns the timer instance.
     */
    @Override
    public Object getTimer() {
        return timer;
    }


    /**
     * @return For AroundInvoke/AroundTimeout methods, returns the bean class
     *         method being invoked.  For lifecycle callback methods,
     *         returns null.
     */
    @Override
    public Method getMethod() {
        return getBeanMethod();
    }

    public Method getBeanMethod() {
        return this.beanMethod;
    }

    @Override
    public Constructor getConstructor() {
        return null;
    }

    /**
     * @return Returns the parameters that will be used to invoke
     * the business method.  If setParameters has been called,
     * getParameters() returns the values to which the parameters
     * have been set.
     */
    @Override
    public Object[] getParameters() {
        return this.methodParams;
    }

    /**
     * Set the parameters that will be used to invoke the business method.
     *
     */
    @Override
    public void setParameters(Object[] params) {
        InterceptorUtil.checkSetParameters(params, getMethod());
        this.methodParams = params;
    }

    /**
     * Method takes Object to decouple {@link EJBInvocation} interface
     * from jaxws (which isn't available in all profiles).
     */
    @Override
    public void setWebServiceContext(Object webServiceContext) {
        // shouldn't be necessary, but to be safe
        if (webServiceContext instanceof WebServiceContext) {
            this.webServiceContext = (WebServiceContext) webServiceContext;
        }
    }

    /**
     * @return Returns the contextMetaData.
     */
    @Override
    public Map getContextData() {
        if (this.contextData == null) {
            if (webServiceContext != null) {
                this.contextData = webServiceContext.getMessageContext();
            } else {
                this.contextData = new HashMap<>();
            }
        }
        return contextData;
    }

    /**
     * This is for EJB JAXWS only.
     * @param message  an unconsumed message
     */
    @Override
    public  void setMessage(T message) {
        this.message = message;
    }

    /**
     * This is for EJB JAXWS only.
     * @return the JAXWS message
     */
    @Override
    public Object getMessage() {
        return this.message;
    }

    /**
     * This is for EJB JAXWS only.
     */
    public SOAPMessage getSOAPMessage() {
        if (message != null && soapMessage == null) {
            try {
                //FIXME: soapMessage = message.readAsSOAPMessage();
                soapMessage = (SOAPMessage) message;
            } catch(Exception e) {
                throw new RuntimeException(e);
            }
            //message consumed, set it to null
            message = null;
        }
        return soapMessage;
    }

    @Override
    public Object proceed() throws Exception {
        try {
            //TODO: Internal error if getInterceptorChain() is null
            interceptorIndex++;
            return getInterceptorChain().invokeNext(interceptorIndex, this);
        } catch (Exception ex) {
            throw ex;
        } catch (Error error) {
            throw error;
        } catch (Throwable t) {
            // This shouldn't be possible since we specifically catch
            // Exception and Error before this, but ...
            throw new RuntimeException(t);
        } finally {
            interceptorIndex--;
        }
    }

    /**
     * Print most useful fields.  Don't do all of them (yet) since there
     * are a large number.
     */
    @Override
    public String toString() {

        StringBuilder sbuf = new StringBuilder();
        sbuf.append("EjbInvocation  ");
        sbuf.append("componentId="+getComponentId());
        sbuf.append(",isLocal="+isLocal);
        sbuf.append(",isRemote="+isRemote);
        sbuf.append(",isBusinessInterface="+isBusinessInterface);
        sbuf.append(",isWebService="+isWebService);
        sbuf.append(",isMessageDriven="+isMessageDriven);
        sbuf.append(",isHome="+isHome);
        sbuf.append(",clientInterface="+clientInterface);
        sbuf.append(",method="+method);
        sbuf.append(",ejb="+ejb);
        sbuf.append(",exception="+exception);
        sbuf.append(",exceptionFromBeanMethod="+exceptionFromBeanMethod);
        sbuf.append(",invId="+invId);
        sbuf.append(",wasCancelCalled="+wasCancelCalled);
        sbuf.append(",yetToSubmitStatus="+yetToSubmitStatus);

        return sbuf.toString();
    }

    // Implementation of AroundInvokeContext
    @Override
    public Object[] getInterceptorInstances() {
        return  ((EJBContextImpl)context).getInterceptorInstances();
    }

    @Override
    public  Object invokeBeanMethod() throws Throwable {
        return ((BaseContainer) container).invokeBeanMethod(this);
    }

    public com.sun.enterprise.security.SecurityManager getEjbSecurityManager() {
        return ((BaseContainer)container).getSecurityManager();
    }

    @Override
    public boolean isAWebService() {
        return this.isWebService;
    }

    @Override
    public Object[] getMethodParams() {
        return this.methodParams;
    }

    @Override
    public boolean authorizeWebService(Method m) throws Exception {
        if (!isAWebService()) {
            setWebServiceMethod(null);
            return true;
        }
        final Exception ie = authorizeWebServiceAndSetMethod(m);
        if (ie == null) {
            return true;
        }
        exception = ie;
        throw ie;
	}

    private Exception authorizeWebServiceAndSetMethod(Method m) {
        try {
            this.method = m;
            if (((com.sun.ejb.Container) container).authorize(this)) {
                // Record the method on which the successful
                // authorization check was performed.
                setWebServiceMethod(m);
                return null;
            }
            return new Exception("Client not authorized for invocation of method {" + method + "}");
        } catch (Exception e) {
            String errorMsg = "Error unmarshalling method {" + method + "} for ejb ";
            // note: this exception is undeclared, but catched!
            return new UnmarshalException(errorMsg, e);
        }
    }

    /**
     * @return true if the SecurityManager reports that the caller is in role
     */
    @Override
    public boolean isCallerInRole(String role) {
        return getEjbSecurityManager().isCallerInRole(role);
    }

    public void setWebServiceMethod(Method method) {
        webServiceMethod = method;
    }

    @Override
    public Method getWebServiceMethod() {
        return webServiceMethod;
    }

    @Override
    public ResourceHandler getResourceHandler() {
        ResourceHandler rh = super.getResourceHandler();
        if (rh == null) {
            rh = context;
        }
        return rh;
    }

    public boolean isContainerStartsTx() {
        return containerStartsTx;
    }

    public void setContainerStartsTx(boolean containerStartsTx) {
        this.containerStartsTx = containerStartsTx;
    }

    /**
     * Checks if the client interface is not null and is assignable to at least one of classes
     * in parameter.
     *
     * @param classes must not be null
     * @return true if the client interface is assignable to at least one of classes in parameter
     */
    public boolean isClientInterfaceAssignableToOneOf(final Class... classes) {
        Objects.requireNonNull(classes, "classes");
        if (this.clientInterface == null) {
            return false;
        }
        final Predicate> predicate = c -> c.isAssignableFrom(this.clientInterface);
        return Arrays.stream(classes).anyMatch(predicate);
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy