package org.quartz.jobs.ee.ejb;

import java.lang.reflect.InvocationTargetException;

import javax.naming.InitialContext;
import javax.naming.NamingException;

import org.quartz.JobDataMap;
import org.quartz.JobExecutionContext;
import org.quartz.JobExecutionException;

/**
 * <p>
 * A <code>Job</code> that invokes a method on an EJB3.
 * </p>
 * 
 * <p>
 * Expects the properties corresponding to the following keys to be in the
 * <code>JobDataMap</code> when it executes:
 * <ul>
 * <li><code>EJB_JNDI_NAME_KEY</code>- the JNDI name (location) of the EJB's
 * home interface.</li>
 * <li><code>EJB_METHOD_KEY</code>- the name of the method to invoke on the EJB.
 * </li>
 * <li><code>EJB_ARGS_KEY</code>- an Object[] of the args to pass to the method
 * (optional, if left out, there are no arguments).</li>
 * <li><code>EJB_ARG_TYPES_KEY</code>- an Class[] of the types of the args to
 * pass to the method (optional, if left out, the types will be derived by
 * calling getClass() on each of the arguments).</li>
 * </ul>
 * <br/>
 * The following keys can also be used at need:
 * <ul>
 * <li><code>INITIAL_CONTEXT_FACTORY</code> - the context factory used to build
 * the context.</li>
 * <li><code>PROVIDER_URL</code> - the name of the environment property for
 * specifying configuration information for the service provider to use.</li>
 * </ul>
 * </p>
 * 
 * <p>
 * The result of the EJB method invocation will be available to
 * <code>Job/TriggerListener</code>s via
 * <code>{@link org.quartz.JobExecutionContext#getResult()}</code>.
 * </p>
 * 
 * @author hhuynh
 * @see {@link org.quartz.jobs.ee.ejb.EJBInvokerJob}
 */
public class EJB3InvokerJob extends EJBInvokerJob {

    @Override
    public void execute(JobExecutionContext context)
            throws JobExecutionException {
        JobDataMap dataMap = context.getMergedJobDataMap();

        String ejb = dataMap.getString(EJB_JNDI_NAME_KEY);
        String method = dataMap.getString(EJB_METHOD_KEY);

        Object[] arguments = (Object[]) dataMap.get(EJB_ARGS_KEY);
        if (arguments == null) {
            arguments = new Object[0];
        }
        if (ejb == null) {
            throw new JobExecutionException("must specify EJB_JNDI_NAME_KEY");
        }
        if (method == null) {
            throw new JobExecutionException("must specify EJB_METHOD_KEY");
        }

        InitialContext jndiContext = null;
        Object value = null;
        try {
            try {
                jndiContext = getInitialContext(dataMap);
                value = jndiContext.lookup(ejb);
            } catch (NamingException ne) {
                throw new JobExecutionException(ne);
            }
            
            Class<?>[] argTypes = (Class[]) dataMap.get(EJB_ARG_TYPES_KEY);
            if (argTypes == null) {
                argTypes = new Class[arguments.length];
                for (int i = 0; i < arguments.length; i++) {
                    argTypes[i] = arguments[i].getClass();
                }
            }

            try {
                Object returnValue = value.getClass()
                        .getMethod(method, argTypes).invoke(value, arguments);
                context.setResult(returnValue);
            } catch (IllegalAccessException iae) {
                throw new JobExecutionException(iae);
            } catch (InvocationTargetException ite) {
                throw new JobExecutionException(ite.getTargetException());
            } catch (NoSuchMethodException nsme) {
                throw new JobExecutionException(nsme);
            }
        } finally {
            if (jndiContext != null) {
                try {
                    jndiContext.close();
                } catch (Exception e) {
                    // ignored
                }
            }
        }
    }
}
