Package org.jboss.soa.esb.actions

Source Code of org.jboss.soa.esb.actions.EJBProcessor$Argument

/*
* JBoss, Home of Professional Open Source
* Copyright 2006, JBoss Inc., and others contributors as indicated
* by the @authors tag. All rights reserved.
* See the copyright.txt in the distribution for a
* full listing of individual contributors.
* This copyrighted material is made available to anyone wishing to use,
* modify, copy, or redistribute it subject to the terms and conditions
* of the GNU Lesser General Public License, v. 2.1.
* This program is distributed in the hope that it will be useful, but WITHOUT A
* 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,
* v.2.1 along with this distribution; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
* MA  02110-1301, USA.
*
* (C) 2008, JBoss Inc.
*/

/*
* Stateless Session Bean Action provided by Dominik Kunz.
*
* http://jira.jboss.com/jira/browse/JBESB-1350
*/

package org.jboss.soa.esb.actions;

import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import java.util.Set;
import java.util.Map.Entry;

import javax.ejb.EJBHome;
import javax.ejb.EJBMetaData;
import javax.ejb.EJBObject;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.naming.NamingException;
import javax.rmi.PortableRemoteObject;
import javax.security.auth.callback.CallbackHandler;
import javax.security.auth.login.LoginContext;
import javax.security.auth.login.LoginException;

import org.apache.log4j.Logger;
import org.jboss.security.auth.callback.AppCallbackHandler;
import org.jboss.soa.esb.ConfigurationException;
import org.jboss.soa.esb.helpers.ConfigTree;
import org.jboss.soa.esb.message.Message;
import org.jboss.soa.esb.util.ClassUtil;

/**
* EJBProcessor is an action that can call stateless session beans
* deployed in an application server.
* <p/>
* This implementation currently supports EJB2.x and EJB3.x session beans.
*
* Example EJB 2.x configuration:
* <pre>{@code
* <action name="EJBTest" class="org.jboss.soa.esb.actions.EJBProcessor">
*    <property name="ejb-name" value="MyBean" />
*    <property name="jndi-name" value="ejb/MyBean" />
*    <property name="initial-context-factory" value="org.jnp.interfaces.NamingContextFactory" />
*    <property name="security-principal" value="username" />
*    <property name="security-credentials" value="password" />
*    <!-- Optional property for JAAS login -->"
*    <property name="security-login-module" value="securityDomain" />
*    <property name="provider-url" value="localhost:1099" />
*    <property name="method" value="login" />
*
*    <!-- Optional output location, defaults to "DEFAULT_EJB_OUT"
*    <property name="esb-out-var" value="MY_OUT_LOCATION"/> -->
*    <property name="ejb-params">
*    <!-- arguments of the operation and where to find them in the message -->
*        <arg0 type="java.lang.String">username</arg0>
*        <arg1 type="java.lang.String">password</arg1>
*    </property>
* </action>
* }</pre>
*
* Example EJB 3.x configuration:
* <pre>{@code
* <action name="EJBTest" class="org.jboss.soa.esb.actions.EJBProcessor">
*    <property name="ejb3" value="true" />
*    <property name="jndi-name" value="ejb/MyBean" />
*    <property name="initial-context-factory" value="org.jnp.interfaces.NamingContextFactory" />
*    <property name="security-principal" value="username" />
*    <property name="security-credentials" value="password" />
*    <!-- Optional property for JAAS login -->"
*    <property name="security-login-module" value="securityDomain" />
*    <property name="provider-url" value="localhost:1099" />
*    <property name="method" value="login" />
*
*    <!-- Optional output location, defaults to "DEFAULT_EJB_OUT"
*    <property name="esb-out-var" value="MY_OUT_LOCATION"/> -->
*    <property name="ejb-params">
*    <!-- arguments of the operation and where to find them in the message -->
*        <arg0 type="java.lang.String">username</arg0>
*        <arg1 type="java.lang.String">password</arg1>
*    </property>
* </action>
* }</pre>
*
*/
public class EJBProcessor extends AbstractActionLifecycle
{
    private static final Logger log = Logger.getLogger(EJBProcessor.class);

    public static final String EJB_NAME = "ejb-name";

    public static final String JNDI_NAME = "jndi-name";

    public static final String EJB_METHOD = "method";

    public static final String JAVA_TYPE = "type";

    public static final String INICTXFACTORY = "initial-context-factory";

    public static final String PROVIDERURL = "provider-url";

    public static final String OUT_VAR = "esb-out-var";

    public static final String DEFAULT_OUT = "DEFAULT_EJB_OUT";

    public static final String EJB3_ATTRIBUTE = "ejb3";
   
  public static final String SECURITY_PRINCIPAL = "security-principal";
  public static final String SECURITY_CREDENTIALS = "security-credentials";
  public static final String SECURITY_LOGIN_MODULE = "security-login-module";

    public static final int ARG_PREFIX_LENGTH = 3;

    protected ConfigTree configTree;

    private Map<String, String> ejbRef;

    private Map<Integer, Argument> ejbParams;

    private List<String> ejbParamTypeNames;

    private EJBHome ejbHome;
    private EJBObject ejbObject;

  private Object ejb3Interface;

  private boolean ejb3;

    private LoginContext loginContext;


    public EJBProcessor(ConfigTree config)
    {
        configTree = config;
    }

    public Message process (Message msg) throws ActionProcessingException, ConfigurationException
    {
        try
        {
            // Assemble parameter array
            Object[] param = new Object[ejbParams.size()];
            for (int i = 0; i < ejbParams.size(); i++)
            {
                // get the parameter from the esb message and
                // cast it to the in the jboss-esb.xml specified type
                param[i] = ClassUtil.forName(ejbParams.get(i).getType(), getClass()).cast(
                        msg.getBody().get(ejbParams.get(i).getLoc()));
            }
            Object ret;
            if ( ejb3 )
            {
              // invoke EJB3.x
              ret = invoke(ejb3Interface.getClass(), ejb3Interface, ejbRef.get(EJB_METHOD), param);
            }
            else
            {
              //  invoke EJB2.x
                ret = invoke( ejbHome.getEJBMetaData().getRemoteInterfaceClass(), ejbObject, ejbRef.get(EJB_METHOD), param);
            }

            //  add return object to messages output location
            if ( ret != null )
            {
                msg.getBody().add( ejbRef.get(OUT_VAR), ret);
            }

            log.debug("###########################################");
            log.debug(msg);
            log.debug("###########################################");

        }
        catch (Exception e)
        {
            throw new ActionProcessingException( "Got an error while processing EJB method [" + ejbRef.get(EJB_METHOD) + "]", e);
        }

        return msg;
    }

    @Override
    public void initialise () throws ActionLifecycleException
    {
        /*
         * Only do the lookup once. We can do this because
         * all of this data is statically defined and not
         * modified by the incoming Message during process execution.
         */

        ejbRef = new HashMap<String, String>();
        ejbParams = new HashMap<Integer, Argument>();
        ejbParamTypeNames = new ArrayList<String>();

        ejb3 = Boolean.parseBoolean(configTree.getAttribute(EJB3_ATTRIBUTE, "false"));

        //  get properties common to both EJB2.x and EJB3.x
        ejbRef.put(JNDI_NAME, configTree.getAttribute(JNDI_NAME));
        ejbRef.put(EJB_METHOD, configTree.getAttribute(EJB_METHOD));
        ejbRef.put(INICTXFACTORY, configTree.getAttribute(INICTXFACTORY));
        ejbRef.put(PROVIDERURL, configTree.getAttribute(PROVIDERURL));

        if ( !ejb3 )
            ejbRef.put(EJB_NAME, configTree.getAttribute(EJB_NAME));

        if (configTree.getAttribute(OUT_VAR) != null)
        {
            ejbRef.put(OUT_VAR, configTree.getAttribute(OUT_VAR));
        }
        else
        {
            ejbRef.put(OUT_VAR, DEFAULT_OUT);
        }
       
        // Get all parameters for the EJB method, defined in jboss-esb.xml
        ConfigTree[] subElements = configTree.getAllChildren();

        for (ConfigTree child : subElements)
        {
            Integer argNum;
            String jType;
            String esbLocation;

            argNum = Integer.parseInt(child.getName().substring(ARG_PREFIX_LENGTH));
            jType = child.getAttribute(JAVA_TYPE);
            esbLocation = child.getWholeText();
            ejbParams.put(argNum, new Argument(jType, esbLocation));
            ejbParamTypeNames.add(jType);
        }

        // Check for missing configuration values
        Set<Entry<String, String>> entrySet = ejbRef.entrySet();
        for (Entry<String, String> entry : entrySet)
    {
          if ( entry.getValue() == null )
          {
                throw new ActionLifecycleException( "Error configuring EJBProcessor.[" + entry.getKey() + "] must not be null");
          }
    }

        // Build Properties for InitialContext lookup
        Properties props = new Properties();

        props.put(Context.INITIAL_CONTEXT_FACTORY, ejbRef.get(INICTXFACTORY));
        props.put(Context.PROVIDER_URL, ejbRef.get(PROVIDERURL));
       
        // extract security principal from config
        final String username = configTree.getAttribute(SECURITY_PRINCIPAL);
        if (username != null)
        {
          // extract security credential from config
            final String password = configTree.getAttribute(SECURITY_CREDENTIALS);
            if (password == null)
            {
                throw new ActionLifecycleException("'" + SECURITY_CREDENTIALS + "' configuration property is missing from esb configuration. It is required when '" + SECURITY_PRINCIPAL + "' is specified");
            }
            // Check if a jaas login module was specified. If so use a jaas login.
            final String loginModuleName = configTree.getAttribute(SECURITY_LOGIN_MODULE);
            if (loginModuleName != null)
            {
                login(loginModuleName, createCallbackHandler(configTree));
            }
            // assume that the principal and credential are to be used by the initial context factory.
            else
            {
                props.setProperty(Context.SECURITY_CREDENTIALS, password);
                props.setProperty(Context.SECURITY_PRINCIPAL, username);
            }
        }

        InitialContext initCtx = getInitialContext(props);
       

    if ( ejb3 )
        {
      ejb3Interface = getEjb3FromJndi(initCtx);
        }
    else
    {
            try
            {
                // Lookup and narrow
                ejbHome = (EJBHome) PortableRemoteObject.narrow( (EJBHome) initCtx.lookup(ejbRef.get(JNDI_NAME)), EJBHome.class);

                // Get the EJB metadata
                EJBMetaData metaData = ejbHome.getEJBMetaData();
                Class<?> homeClass = metaData.getHomeInterfaceClass();

                // convert handle to real home type
                ejbHome = (EJBHome) javax.rmi.PortableRemoteObject.narrow(ejbHome, homeClass);

                if (!(metaData.isSession() && metaData.isStatelessSession()))
                {
                    throw new ActionLifecycleException("Only SLSBs are supported!");
                }

                ejbObject = (EJBObject) EJBProcessor.create(homeClass, ejbHome);
            }
            catch (Exception e)
            {
                throw new ActionLifecycleException( "Got an error while processing EJB " + ejbRef.get(EJB_METHOD), e);
            }
    }
    }

    InitialContext getInitialContext(final Properties props) throws ActionLifecycleException
    {
      InitialContext context;
    try
    {
      context = new InitialContext(props);
    } catch (final NamingException e)
    {
        throw new ActionLifecycleException("Could not create a new InitialContext with properties : " + props, e);
    }
    return context;
    }

    Object getEjb3FromJndi(final Context context) throws ActionLifecycleException
    {
      Object ejb3Interface;
      try
    {
      ejb3Interface = context.lookup(ejbRef.get(JNDI_NAME));
    } catch (final NamingException e)
    {
        throw new ActionLifecycleException("Could not lookup " + ejbRef.get(JNDI_NAME),  e);
    }
    return ejb3Interface;
    }
   
    /**
     * Creates an {@link AppCallbackHandler} that takes a username and password.
     * This method is protected to let subclasses override it to implement other
     * security authentication mechanisms.
     *
     * @param config The configuration for this action. Gives access to all config parameters.
     * @return CallbackHandler A callback handler that suitable for the login module configured.
     */
    protected CallbackHandler createCallbackHandler(final ConfigTree config)
    {
        final String username = configTree.getAttribute(SECURITY_PRINCIPAL);
        return new AppCallbackHandler(username, ((String)configTree.getAttribute(SECURITY_CREDENTIALS)).toCharArray());
    }
   
    void login(final String loginModuleName, final CallbackHandler callbackHandler) throws ActionLifecycleException
    {
        try
        {
            loginContext = new LoginContext (loginModuleName, callbackHandler);
            loginContext.login();
        }
        catch (final Exception e)
        {
            throw new ActionLifecycleException(e.getMessage(), e);
        }
    }
   
    @Override
    public void destroy()
    {
        if (loginContext != null)
        {
            try
            {
                loginContext.logout();
            }
            catch (LoginException e)
            {
                log.warn(e.getMessage(), e);
            }
        }
    }

    private static Object create (Class<?> c, Object obj) throws Exception
    {
        Object ret = null;

        Method create = c.getMethod("create");
        ret = create.invoke(obj);

        return ret;
    }

    private Object invoke (Class<?> c, Object obj, String mname, Object[] params) throws ClassNotFoundException, SecurityException, NoSuchMethodException, IllegalArgumentException, IllegalAccessException, InvocationTargetException
    {

        // The return Object
        Object r = null;

        // Assemble method signature array
        Class<?>[] sigArray = new Class[ejbParams.size()];
        for (int i = 0; i < ejbParams.size(); i++)
        {
            sigArray[i] = ClassUtil.forName(ejbParams.get(i).getType(), getClass());
        }

        // Get the specified method
        Method method = c.getMethod(mname, sigArray);

        // finally invoke it...
        r = method.invoke(obj, params);

        return r;
    }

    // Helper inner class for method arguments and where to find it in the esb
    // message
    private static class Argument
    {

        private String type;

        private String loc;

        public Argument(String javaType, String esbLoc)
        {
            type = javaType;
            loc = esbLoc;
        }

        public String getType ()
        {
            return type;
        }

        public void setType (String type)
        {
            this.type = type;
        }

        public String getLoc ()
        {
            return loc;
        }

        public void setLoc (String loc)
        {
            this.loc = loc;
        }

    }

}
TOP

Related Classes of org.jboss.soa.esb.actions.EJBProcessor$Argument

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.