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

org.jboss.ejb.client.EJBClient Maven / Gradle / Ivy

/*
 * JBoss, Home of Professional Open Source.
 * Copyright 2011, Red Hat, Inc., and individual contributors
 * as indicated by the @author tags. See the copyright.txt file in the
 * distribution for a full listing of individual contributors.
 *
 * This is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Lesser General Public License as
 * published by the Free Software Foundation; either version 2.1 of
 * the License, or (at your option) any later version.
 *
 * This software is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * License along with this software; if not, write to the Free
 * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
 * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
 */

package org.jboss.ejb.client;

import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Proxy;
import java.util.Collection;
import java.util.HashSet;
import java.util.concurrent.Future;

import javax.transaction.UserTransaction;

/**
 * The main EJB client API class.  This class contains helper methods which may be used to create proxies, open sessions,
 * and associate the current invocation context.
 *
 * @author jpai
 * @author David M. Lloyd
 */
public final class EJBClient {

    private static final Logs log = Logs.MAIN;

    static {
        log.greeting(Version.getVersionString());
    }

    private EJBClient() {
    }

    private static final ThreadLocal> FUTURE_RESULT = new ThreadLocal>();

    /**
     * Get an asynchronous view of a proxy.  Any {@code void} method on the proxy will be invoked fully asynchronously
     * without a server round-trip delay.  Any method which returns a {@link java.util.concurrent.Future Future} will
     * continue to be asynchronous.  Any other method invoked on the returned proxy will return {@code null} (the future
     * result can be acquired by wrapping the remote call with {@link #getFutureResult(Object)} or by using {@link #getFutureResult()}).
     * If an asynchronous view is passed in, the same view is returned.
     *
     * @param proxy the proxy interface instance
     * @param    the proxy type
     * @return the asynchronous view
     * @throws IllegalArgumentException if the given object is not a valid proxy
     */
    @SuppressWarnings("unchecked")
    public static  T asynchronous(final T proxy) throws IllegalArgumentException {
        final InvocationHandler invocationHandler = Proxy.getInvocationHandler(proxy);
        if (invocationHandler instanceof EJBInvocationHandler) {
            final EJBInvocationHandler remoteInvocationHandler = (EJBInvocationHandler) invocationHandler;
            // determine proxy "type", return existing instance if it's already async
            if (remoteInvocationHandler.isAsyncHandler()) {
                return proxy;
            } else {
                return (T) Proxy.newProxyInstance(proxy.getClass().getClassLoader(), proxy.getClass().getInterfaces(), remoteInvocationHandler.getAsyncHandler());
            }
        } else {
            throw log.unknownProxy(proxy);
        }
    }

    /**
     * Get the future result of an operation.  Should be called in conjunction with {@link #asynchronous(Object)}.
     *
     * @param operation the operation
     * @param        the result type
     * @return the future result
     * @throws IllegalStateException if the operation is not appropriately given
     */
    @SuppressWarnings("unchecked")
    public static  Future getFutureResult(final T operation) throws IllegalStateException {
        if (operation != null) {
            return new FinishedFuture(operation);
        }
        final ThreadLocal> futureResult = FUTURE_RESULT;
        try {
            final Future future = futureResult.get();
            if (future == null) throw log.noAsyncInProgress();
            return (Future) future;
        } finally {
            futureResult.remove();
        }
    }

    /**
     * Get the future result of an operation.  Should be called in conjunction with {@link #asynchronous(Object)}.
     *
     * @return the future result
     * @throws IllegalStateException if the operation is not appropriately given
     */
    public static Future getFutureResult() throws IllegalStateException {
        final ThreadLocal> futureResult = FUTURE_RESULT;
        try {
            final Future future = futureResult.get();
            if (future == null) throw log.noAsyncInProgress();
            return future;
        } finally {
            futureResult.remove();
        }
    }

    static void setFutureResult(final Future future) {
        FUTURE_RESULT.set(future);
    }

    /**
     * Create a new proxy for the remote object identified by the given locator.
     *
     * @param locator the locator
     * @param      the proxy type
     * @return the new proxy
     * @throws IllegalArgumentException if the locator parameter is {@code null} or is invalid
     */
    public static  T createProxy(final EJBLocator locator) throws IllegalArgumentException {
        return createProxy(locator, null);
    }

    /**
     * Creates a new proxy for the remote object identified by the given locator and
     * associates that proxy with the passed {@link EJBClientContextIdentifier identifier}
     *
     * @param locator    The locator
     * @param identifier The EJB client context identifier to associate this proxy with. Can be null.
     * @param         The proxy type
     * @return IllegalArgumentException if the locator {@code null}
     */
    public static  T createProxy(final EJBLocator locator, final EJBClientContextIdentifier identifier) {
        if (locator == null) {
            throw Logs.MAIN.paramCannotBeNull("EJB locator");
        }
        return locator.createProxyInstance(new EJBInvocationHandler(identifier, locator));
    }

    /**
     * Determine whether an object is indeed a valid EJB proxy object created by this API.
     *
     * @param object the object to test
     * @return {@code true} if it is an EJB proxy, {@code false} otherwise
     */
    public static boolean isEJBProxy(final Object object) {
        return object != null && Proxy.isProxyClass(object.getClass()) && Proxy.getInvocationHandler(object) instanceof EJBInvocationHandler;
    }

    /**
     * Create a new EJB session.
     *
     * @param viewType     the view type
     * @param appName      the application name
     * @param moduleName   the module name
     * @param beanName     the EJB name
     * @param distinctName the module distinct name
     * @return the new session ID
     * @throws Exception if an error occurs
     */
    // TODO: narrow exception type(s)
    public static  StatefulEJBLocator createSession(final Class viewType, final String appName, final String moduleName, final String beanName, final String distinctName) throws Exception {
        return createSession(null, viewType, appName, moduleName, beanName, distinctName);
    }

    /**
     * Create a new EJB session.
     *
     * @param ejbClientContextIdentifier The EJB client context identifier. Can be null in which case the session will
     *                                   be created using the {@link org.jboss.ejb.client.EJBClientContext#requireCurrent() current active EJB client context}
     * @param viewType                   the view type
     * @param appName                    the application name
     * @param moduleName                 the module name
     * @param beanName                   the EJB name
     * @param distinctName               the module distinct name
     * @return the new session ID
     * @throws Exception if an error occurs
     */
    // TODO: narrow exception type(s)
    public static  StatefulEJBLocator createSession(final EJBClientContextIdentifier ejbClientContextIdentifier, final Class viewType, final String appName, final String moduleName, final String beanName, final String distinctName) throws Exception {
        final EJBClientContext clientContext;
        if (ejbClientContextIdentifier != null) {
            // find the appropriate EJB client context
            clientContext = EJBClientContext.require(ejbClientContextIdentifier);
        } else {
            // use the "current" EJB client context
            clientContext = EJBClientContext.requireCurrent();
        }
        return createSessionWithPossibleRetries(clientContext, new HashSet(), viewType, appName, moduleName, beanName, distinctName);
    }

    /**
     * Create a new EJB session with possible retries to different eligible node(s) if the session creation failed on some node(s)
     *
     * @param clientContext The EJB client context
     * @param excludedNodeNames The node names of EJB receivers which have to be ignored while selecting a EJB receiver for handling the session creation
     * @param viewType The view type
     * @param appName The app name
     * @param moduleName Module name
     * @param beanName bean name
     * @param distinctName Distinct name
     * @param 
     * @return
     * @throws Exception
     */
    private static  StatefulEJBLocator createSessionWithPossibleRetries(final EJBClientContext clientContext, final Collection excludedNodeNames, final Class viewType, final String appName,
                                                                              final String moduleName, final String beanName, final String distinctName) throws Exception {
        // find a receiver
        final EJBReceiver ejbReceiver = clientContext.requireEJBReceiver(excludedNodeNames, appName, moduleName, distinctName);
        final EJBReceiverContext receiverContext = clientContext.requireEJBReceiverContext(ejbReceiver);
        try {
            return ejbReceiver.openSession(receiverContext, viewType, appName, moduleName, distinctName, beanName);
        } catch (Exception e) {
            Logs.MAIN.debugf(e, "Retrying session creation which failed on node %s due to:", ejbReceiver.getNodeName());
            // retry ignoring the current failed node
            excludedNodeNames.add(ejbReceiver.getNodeName());
            return createSessionWithPossibleRetries(clientContext, excludedNodeNames, viewType, appName, moduleName, beanName, distinctName);
        }
    }

    /**
     * Get the locator for a proxy, if it has one.
     *
     * @param proxy the proxy
     * @return the locator
     * @throws IllegalArgumentException if the given proxy is not a valid client proxy instance
     */
    public static  EJBLocator getLocatorFor(T proxy) throws IllegalArgumentException {
        return EJBInvocationHandler.forProxy(proxy).getLocator();
    }

    /**
     * Get the {@link EJBClientContextIdentifier} associated with the passed EJB proxy. If no {@link EJBClientContextIdentifier}
     * is associated with the proxy then this method returns null.
     *
     * @param proxy The EJB proxy
     * @return
     * @throws IllegalArgumentException If the passed proxy is not a valid EJB proxy
     */
    public static EJBClientContextIdentifier getEJBClientContextIdentifierFor(Object proxy) throws IllegalArgumentException {
        return EJBInvocationHandler.forProxy(proxy).getEjbClientContextIdentifier();
    }

    /**
     * Get a {@code UserTransaction} object instance which can be used to control transactions on a specific node.
     *
     * @param targetNodeName the node name
     * @return the {@code UserTransaction} instance
     * @throws IllegalStateException if the transaction context isn't set or cannot provide a {@code UserTransaction} instance
     */
    public static UserTransaction getUserTransaction(String targetNodeName) {
        return EJBClientTransactionContext.requireCurrent().getUserTransaction(targetNodeName);
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy