/*
 * JBoss, Home of Professional Open Source
 * Copyright 2007, Red Hat Middleware LLC, and individual contributors
 * as indicated by the @author tags.
 * 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 General Public License, v. 2.0.
 * 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 General Public License for more details.
 * You should have received a copy of the GNU General Public License,
 * v. 2.0 along with this distribution; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston,
 * MA  02110-1301, USA.
 *
 * (C) 2007,
 * @author JBoss Inc.
 */
package org.jboss.txbridge.ba.helper;

import org.apache.log4j.Logger;
import org.jboss.txbridge.ba.annotation.*;
import org.jboss.txbridge.ba.exception.MethodIncorrectlyAnnotatedException;
import org.jboss.txbridge.ba.exception.MethodNotAccessibleException;
import org.jboss.txbridge.ba.service.ServiceDescription;
import org.jboss.txbridge.ba.service.ServiceDescriptionImpl;
import org.jboss.txbridge.ba.service.MethodDescription;
import org.jboss.txbridge.ba.service.MethodDescriptionImpl;

import javax.jws.WebMethod;
import java.lang.reflect.Method;
import java.lang.annotation.Annotation;

/**
 * This component visits the service and describes it in terms of its requirements related to
 * participating in Business Activities.
 *
 * TODO: Redesign this class - it would be better to have multiple small and exchangeable classes 
 * than a big one.
 *
 * @author Maciej P. Machulak (mmachulak@redhat.com)
 * @version 0.1
 */
public class BAServiceVisitor
{
    // Logger
    private static Logger log = Logger.getLogger(BAServiceVisitor.class);

    /**
     * This method processes a single method that
     *
     * @param method is the method to be processed.
     * @return the service description.
     * @throws org.jboss.txbridge.ba.exception.MethodIncorrectlyAnnotatedException if the method is not annotated correctly.
     * @throws org.jboss.txbridge.ba.exception.MethodNotAccessibleException if the specified compensatiom method is not accessible.
     */
    public static synchronized ServiceDescription processMethod(Method method)
            throws MethodIncorrectlyAnnotatedException, MethodNotAccessibleException
    {
        log.info("processMethod()");

        // Create new ServiceDescription
        ServiceDescription serviceDescription = new ServiceDescriptionImpl();

        // Create new MethodDescription for the original class
        MethodDescription originalMethod = new MethodDescriptionImpl();

        // Check what is the name of the method...
        log.info("Method name: " + method.getName());
        String methodName = method.getName();
        // ...by default the web method name is the same as the method's name.
        String webMethodName = methodName;

        // Set the class of the method
        Class methodClass = method.getDeclaringClass();
        originalMethod.setClazz(methodClass);

        // Check if there is a business activity annotation
        Class[] baClass = null;
        Class[] baInterface = null;
        String baJndiName = null;
        String baProviderURL = null;
        String endpointAddress = null;
        String targetNamespace = null;
        String serviceName = null;
        BAService BAServiceAnnotation = (BAService) methodClass.getAnnotation(BAService.class);
        if (BAServiceAnnotation != null)
        {
            log.info("BAService annotation found.");
            baClass = BAServiceAnnotation.serviceClass();
            log.info("Compensation class: " + baClass);
            baInterface = BAServiceAnnotation.ejbInterface();
            log.info("EJB Interface: " + baInterface);
            baJndiName = BAServiceAnnotation.jndiName();
            log.info("JNDI name: " + baJndiName);
            baProviderURL = BAServiceAnnotation.providerURL();
            log.info("Provider URL: " + baProviderURL);
            endpointAddress = BAServiceAnnotation.wsdl();
            targetNamespace = BAServiceAnnotation.namespace();
            serviceName = BAServiceAnnotation.serviceName();
        }

        // Check if there is a WebMethod annotation
        WebMethod webMethodAnnotation = method.getAnnotation(WebMethod.class);
        if (webMethodAnnotation != null)
        {
            log.info("WebMethod annotation present!");

            // Check if the operation name has been specified
            String name = webMethodAnnotation.operationName();
            if (name != null && !name.equals(""))
            {
                log.info("WebMethod operationName: " + name);
                // If yes... change the name of the web method
                webMethodName = name;
            }
        }
        else
        {
            throw new MethodIncorrectlyAnnotatedException("Method does not have @WebMethod annotation.");
        }

        // Remember names of the method
        originalMethod.setMethodName(methodName);
        originalMethod.setWebMethodName(webMethodName);

        // Set the parameter types of the original method
        originalMethod.setParameterTypes(method.getParameterTypes());

        // Check if this method is a BA service
        BAMethod baMethod = method.getAnnotation(BAMethod.class);
        MethodType methodType;
        AgreementType agreementType;
        if (baMethod != null)
        {
            log.info("Method " + methodName + " is a BA service.");

            // Get the type of the service
            methodType = baMethod.type();
            log.info("MethodType: " + methodType);
            serviceDescription.setMethodType(methodType);

            // Get the agreement type
            agreementType = baMethod.agreement();
            log.info("AgreementType: " + agreementType);
            serviceDescription.setAgreementType(agreementType);
        }
        else
        {
            throw new MethodIncorrectlyAnnotatedException("Method does not have @BAMethod annotation.");
        }

        // Check the compensation method
        MethodDescription md = new MethodDescriptionImpl();
        BACompensatedBy baComp = method.getAnnotation(BACompensatedBy.class);
        if (baComp != null)
        {
            log.info("BACompensatedBy annotation present!");

            String compensationMethodName = baComp.value();
            log.info("Compensation method: " + compensationMethodName);
            md.setMethodName(compensationMethodName);

            // Check the datamgmt type
            log.info("Compensation type: " + baComp.type());
            md.setDataMatch(baComp.type());

            // Set parameter match
            log.info("Parameters match: " + baComp.match());
            md.setParameterMatch(baComp.match());

            // Check if this is a single datamgmt
            log.info("Compensation single: " + baComp.single());
            md.setSingle(baComp.single());

            // Get datamgmt mode
            ExecutionMode compensationMode = baComp.mode();
            md.setExecutionMode(compensationMode);
            log.info("Compensation mode: " + compensationMode);

            // Get information according to the required datamgmt
            if (compensationMode == ExecutionMode.POJO)
            {
                // We need following information
                // 1) Method name (already obtained)
                // 2) Method's class

                // Getting method's class
                Class compensationMethodClass = baComp.serviceClass()[0];
                if (compensationMethodClass == null)
                {
                    // It can be either local class or a class specified in BAService annotation
                    if (baClass != null)
                    {
                        log.info("Compensation method class specified in BA");
                        compensationMethodClass = baClass[0];
                    }
                    else
                    {
                        log.info("Compensation method is in the same class: ");
                        compensationMethodClass = method.getDeclaringClass();
                    }
                }
                else
                {
                    log.info("Compensation class: " + compensationMethodClass);
                }
                md.setClazz(compensationMethodClass);

                // Checking if the method exists
                log.info("Checking if method exists");
                Method[] methodList = compensationMethodClass.getMethods();
                boolean methodFound = false;
                for (Method singleMethod : methodList)
                {
                    log.info("Comparing " + compensationMethodName + " against " + singleMethod.getName() + ".");
                    if (singleMethod.getName().equals(compensationMethodName))
                    {
                        // Remember types of parameters
                        log.info("Getting parameter types");
                        Class[] parameterTypes = singleMethod.getParameterTypes();

                        if (parameterTypes != null)
                        {
                            log.info("Compensation method has parameters");

                            log.info("Setting parameter match type");
                            md.setParameterTypes(singleMethod.getParameterTypes());

                            // Remember annotations of the parameters of the datamgmt method
                            log.info("Checking annotations");
                            Object[] parameterAnnotation = new Object[singleMethod.getParameterTypes().length];
                            Annotation[][] parameterAnnotations = singleMethod.getParameterAnnotations();
                            int i = 0;
                            for (Annotation[] annotationArray : parameterAnnotations)
                            {
                                for (Annotation annotation : annotationArray)
                                {
                                    if (annotation instanceof BAParam)
                                    {
                                        log.info("BAParam annotation present for parameter: " + i);
                                        String valueS = ((BAParam)annotation).value();
                                        try
                                        {
                                            Integer valueI = new Integer(valueS);
                                            parameterAnnotation[i] = valueI;
                                        }
                                        catch (NumberFormatException nfe)
                                        {
                                            parameterAnnotation[i] = valueS;
                                        }
                                    }
                                    else
                                    {
                                        log.info("No BAParam annotation for parameter: " + i);
                                    }
                                }
                                i++;
                            }
                            md.setParameterAnnotations(parameterAnnotation);
                        }
                        else
                        {
                            log.info("Compensation method does not take any parameters");
                        }
                        methodFound = true;
                        break;
                    }
                }
                if (!methodFound)
                {
                    throw new MethodNotAccessibleException("Compensation method not found.");
                }

            }
            else if (compensationMode == ExecutionMode.DII)
            {
                md.setEndpointAddress(baComp.wsdl());
                md.setTargetNamespace(baComp.namespace());
                md.setServiceName(baComp.serviceName());
            }
            else if (compensationMode == ExecutionMode.EJB)
            {

                // We need following information
                // 1) Method name (already obtained)
                // 2) Method's class (optional)
                // 3) Interface of the EJB
                // 4) JNDI name of the EJB
                // 5) Provider URL (optional)

                // Method's class
                log.info("Getting method's class");
                Class[] compensationMethodClass = baComp.serviceClass();
                boolean baParamAnnotationFound = true;
                if (compensationMethodClass.length == 0)
                {
                    log.info("Method's class not specified");

                    // Class can be either specified in the Business Activity annotation or datamgmt is remote
                    log.info("Checking if method's class has been specified in BAService");
                    if (baClass != null && baClass.length > 0 && baClass[0] != null)
                    {
                        log.info("Compensation class specified in the BAService annotation");
                        compensationMethodClass = new Class[1];
                        compensationMethodClass[0] = baClass[0];
                    }
                    else
                    {
                        log.info("Compensation class not specified - remote datamgmt.");
                    }
                }
                else
                {
                    log.info("Method's class specified");
                }

                if (compensationMethodClass.length > 0)
                {
                    // Compensation class specified
                    log.info("Compensation class: " + compensationMethodClass[0]);

                    // Checking if the datamgmt method exists
                    log.info("Checking if datamgmt method exists");
                    Method[] methodList = compensationMethodClass[0].getMethods();
                    boolean methodFound = false;
                    for (Method singleMethod : methodList)
                    {
                        log.info("Comparing " + compensationMethodName + " against " + singleMethod.getName() + ".");
                        if (singleMethod.getName().equals(compensationMethodName))
                        {
                            log.info("Method found!");

                            // Remember types of parameters
                            log.info("Getting parameter types");
                            Class[] parameterTypes = singleMethod.getParameterTypes();

                            if (parameterTypes != null)
                            {
                                log.info("Compensation method has parameters");

                                log.info("Setting datamgmt type");
                                md.setParameterTypes(singleMethod.getParameterTypes());

                                // Remember annotations of the parameters of the datamgmt method
                                log.info("Checking annotations");
                                Object[] parameterAnnotation = new Object[singleMethod.getParameterTypes().length];
                                Annotation[][] parameterAnnotations = singleMethod.getParameterAnnotations();
                                int i = 0;
                                for (Annotation[] annotationArray : parameterAnnotations)
                                {
                                    for (Annotation annotation : annotationArray)
                                    {
                                        if (annotation instanceof BAParam)
                                        {
                                            baParamAnnotationFound = true;
                                            log.info("BAParam annotation present for parameter: " + i);
                                            String valueS = ((BAParam)annotation).value();
                                            try
                                            {
                                                Integer valueI = new Integer(valueS);
                                                parameterAnnotation[i] = valueI;
                                            }
                                            catch (NumberFormatException nfe)
                                            {
                                                parameterAnnotation[i] = valueS;
                                            }
                                        }
                                        else
                                        {
                                            log.info("No BAParam annotation for parameter: " + i);
                                        }
                                    }
                                    i++;
                                }
                                md.setParameterAnnotations(parameterAnnotation);
                            }
                            else
                            {
                                log.info("Compensation method does not take any parameters");
                            }
                            methodFound = true;
                            break;
                        }
                    }
                    if (!methodFound)
                    {
                        throw new MethodNotAccessibleException("Compensation method not found.");
                    }

                }
                md.setClazz(compensationMethodClass[0]);

                // Get the interface
                Class[] ejbInterface = baComp.ejbInterface();
                if (ejbInterface.length == 0)
                {
                    log.info("EJB interface not specified");
                    if (baInterface != null && baInterface.length > 0 && !baInterface[0].equals(""))
                    {
                        log.info("EJB interface specified in BAService - storing value");
                        ejbInterface = new Class[1];
                        ejbInterface[0] = baInterface[0];
                    }
                    else
                    {
                        throw new MethodIncorrectlyAnnotatedException("Interface to EJB not specified.");
                    }
                }
                else
                {
                    log.info("EJB Interface specified.");
                }
                if (ejbInterface.length > 0 )
                {
                    // Interface specified
                    log.info("EJB Interface: " + ejbInterface[0]);

                    // Checking if the datamgmt method exists
                    log.info("Checking if datamgmt method exists");
                    Method[] methodList = ejbInterface[0].getMethods();
                    boolean methodFound = false;
                    for (Method singleMethod : methodList)
                    {
                        log.info("Comparing " + compensationMethodName + " against " + singleMethod.getName() + ".");
                        if (singleMethod.getName().equals(compensationMethodName))
                        {
                            log.info("Method found!");

                            // Remember types of parameters
                            log.info("Getting parameter types");
                            Class[] parameterTypes = singleMethod.getParameterTypes();

                            if (parameterTypes != null)
                            {
                                log.info("Compensation method has parameters");

                                log.info("Setting datamgmt type");
                                md.setParameterTypes(singleMethod.getParameterTypes());

                                // Remember annotations of the parameters of the datamgmt method
                                if (!baParamAnnotationFound)
                                {

                                    log.info("Checking annotations");
                                    Object[] parameterAnnotation = new Object[singleMethod.getParameterTypes().length];
                                    Annotation[][] parameterAnnotations = singleMethod.getParameterAnnotations();
                                    int i = 0;
                                    for (Annotation[] annotationArray : parameterAnnotations)
                                    {
                                        for (Annotation annotation : annotationArray)
                                        {
                                            if (annotation instanceof BAParam)
                                            {
                                                log.info("BAParam annotation present for parameter: " + i);
                                                String valueS = ((BAParam)annotation).value();
                                                try
                                                {
                                                    Integer valueI = new Integer(valueS);
                                                    parameterAnnotation[i] = valueI;
                                                }
                                                catch (NumberFormatException nfe)
                                                {
                                                    parameterAnnotation[i] = valueS;
                                                }
                                            }
                                            else
                                            {
                                                log.info("No BAParam annotation for parameter: " + i);
                                            }
                                        }
                                        i++;
                                    }
                                    md.setParameterAnnotations(parameterAnnotation);
                                }
                            }
                            else
                            {
                                log.info("Compensation method does not take any parameters");
                            }
                            methodFound = true;
                            break;
                        }
                    }
                    if (!methodFound)
                    {
                        throw new MethodNotAccessibleException("Compensation method not found.");
                    }

                }
                md.setEjbInterface(ejbInterface[0]);

                // Get the JNDI name of the EJB
                log.info("Getting JNDI name of the EJB");
                String jndiName = baComp.jndiName();
                if (!jndiName.equals(""))
                {
                    log.info("JNDI name: " + jndiName);
                }
                else
                {
                    // Check if the JNDI name has been specified
                    if (baJndiName != null && !baJndiName.equals(""))
                    {
                        jndiName = baJndiName;
                    }
                    else
                    {
                        throw new MethodIncorrectlyAnnotatedException("JNDI name not specified");
                    }
                }
                md.setJndiName(jndiName);

                // Check if the URL provider has been specified
                String providerURL = baComp.providerURL();
                if (!providerURL.equals(""))
                {
                    log.info("Provider URL: " + providerURL);
                }
                else
                {
                    // Check if the provider URL has been specified
                    if (baProviderURL != null && !baProviderURL.equals(""))
                    {
                        providerURL = baProviderURL;
                    }
                    else
                    {
                        providerURL = null;
                    }
                }
                md.setProviderURL(providerURL);
            }

        }
        else
        {
            log.info("BACompensatedBy annotation missing.");
            log.info("Checking the type of the service.");
            if (methodType == MethodType.READONLY)
            {
                log.info("Service is read only. BACompensatedBy is not necessary.");
            }
            else
            {
                throw new MethodIncorrectlyAnnotatedException("BACompensatedBy annotation missing.");
            }
        }
        serviceDescription.putMethod("compensate",md);

        // Check the Completion method
        md = new MethodDescriptionImpl();
        BACompletedBy baCompleted = method.getAnnotation(BACompletedBy.class);
        if (baCompleted != null)
        {
            log.info("BACompletedBy annotation present!");

            String CompletionMethodName = baCompleted.value();
            log.info("Completion method: " + CompletionMethodName);
            md.setMethodName(CompletionMethodName);

            // Check the Completion type
            log.info("Data match type: " + baCompleted.type());
            md.setDataMatch(baCompleted.type());

            // Set parameter match
            log.info("Parameters match: " + baCompleted.match());
            md.setParameterMatch(baCompleted.match());

            // Check if this is a single Completion
            log.info("Completion single: " + baCompleted.single());
            md.setSingle(baCompleted.single());

            // Get Completion mode
            ExecutionMode completionMode = baCompleted.mode();
            md.setExecutionMode(baCompleted.mode());
            log.info("Completion mode: " + completionMode);

            // Get information according to the required Completion
            if (completionMode == ExecutionMode.POJO)
            {
                // We need following information
                // 1) Method name (already obtained)
                // 2) Method's class

                // Getting method's class
                Class CompletionMethodClass = baCompleted.serviceClass()[0];
                if (CompletionMethodClass == null)
                {
                    // It can be either local class or a class specified in BAService annotation
                    if (baClass != null)
                    {
                        log.info("Completion method class specified in BA");
                        CompletionMethodClass = baClass[0];
                    }
                    else
                    {
                        log.info("Completion method is in the same class: ");
                        CompletionMethodClass = method.getDeclaringClass();
                    }
                }
                else
                {
                    log.info("Completion class: " + CompletionMethodClass);
                }
                md.setClazz(CompletionMethodClass);

                // Checking if the Completion method exists
                log.info("Checking if Completion method exists");
                Method[] methodList = CompletionMethodClass.getMethods();
                boolean methodFound = false;
                for (Method singleMethod : methodList)
                {
                    log.info("Comparing " + CompletionMethodName + " against " + singleMethod.getName() + ".");
                    if (singleMethod.getName().equals(CompletionMethodName))
                    {
                        // Remember types of parameters
                        log.info("Getting parameter types");
                        Class[] parameterTypes = singleMethod.getParameterTypes();

                        if (parameterTypes != null)
                        {
                            log.info("Completion method has parameters");

                            log.info("Setting Completion type");
                            md.setParameterTypes(singleMethod.getParameterTypes());

                            // Remember annotations of the parameters of the Completion method
                            log.info("Checking annotations");
                            Object[] parameterAnnotation = new Object[singleMethod.getParameterTypes().length];
                            Annotation[][] parameterAnnotations = singleMethod.getParameterAnnotations();
                            int i = 0;
                            for (Annotation[] annotationArray : parameterAnnotations)
                            {
                                for (Annotation annotation : annotationArray)
                                {
                                    if (annotation instanceof BAParam)
                                    {
                                        log.info("BAParam annotation present for parameter: " + i);
                                        String valueS = ((BAParam)annotation).value();
                                        try
                                        {
                                            Integer valueI = new Integer(valueS);
                                            parameterAnnotation[i] = valueI;
                                        }
                                        catch (NumberFormatException nfe)
                                        {
                                            parameterAnnotation[i] = valueS;
                                        }
                                    }
                                    else
                                    {
                                        log.info("No BAParam annotation for parameter: " + i);
                                    }
                                }
                                i++;
                            }
                            md.setParameterAnnotations(parameterAnnotation);
                        }
                        else
                        {
                            log.info("Completion method does not take any parameters");
                        }
                        methodFound = true;
                        break;
                    }
                }
                if (!methodFound)
                {
                    throw new MethodNotAccessibleException("Completion method not found.");
                }

            }
            else if (completionMode == ExecutionMode.DII)
            {
                md.setEndpointAddress(baComp.wsdl());
                md.setTargetNamespace(baComp.namespace());
                md.setServiceName(baComp.serviceName());
            }
            else if (completionMode == ExecutionMode.EJB)
            {

                // We need following information
                // 1) Method name (already obtained)
                // 2) Method's class (optional)
                // 3) Interface of the EJB
                // 4) JNDI name of the EJB
                // 5) Provider URL (optional)

                // Method's class
                log.info("Getting method's class");
                Class[] CompletionMethodClass = baCompleted.serviceClass();
                boolean baParamAnnotationFound = true;
                if (CompletionMethodClass.length == 0)
                {
                    log.info("Method's class not specified");

                    // Class can be either specified in the Business Activity annotation or Completion is remote
                    log.info("Checking if method's class has been specified in BAService");
                    if (baClass != null && baClass.length > 0 && baClass[0] != null)
                    {
                        log.info("Completion class specified in the BAService annotation");
                        CompletionMethodClass = new Class[1];
                        CompletionMethodClass[0] = baClass[0];
                    }
                    else
                    {
                        log.info("Completion class not specified - remote Completion.");
                    }
                }
                else
                {
                    log.info("Method's class specified");
                }

                if (CompletionMethodClass.length > 0)
                {
                    // Completion class specified
                    log.info("Completion class: " + CompletionMethodClass[0]);

                    // Checking if the Completion method exists
                    log.info("Checking if Completion method exists");
                    Method[] methodList = CompletionMethodClass[0].getMethods();
                    boolean methodFound = false;
                    for (Method singleMethod : methodList)
                    {
                        log.info("Comparing " + CompletionMethodName + " against " + singleMethod.getName() + ".");
                        if (singleMethod.getName().equals(CompletionMethodName))
                        {
                            log.info("Method found!");

                            // Remember types of parameters
                            log.info("Getting parameter types");
                            Class[] parameterTypes = singleMethod.getParameterTypes();

                            if (parameterTypes != null)
                            {
                                log.info("Completion method has parameters");

                                log.info("Setting Completion type");
                                md.setParameterTypes(singleMethod.getParameterTypes());

                                // Remember annotations of the parameters of the Completion method
                                log.info("Checking annotations");
                                Object[] parameterAnnotation = new Object[singleMethod.getParameterTypes().length];
                                Annotation[][] parameterAnnotations = singleMethod.getParameterAnnotations();
                                int i = 0;
                                for (Annotation[] annotationArray : parameterAnnotations)
                                {
                                    for (Annotation annotation : annotationArray)
                                    {
                                        if (annotation instanceof BAParam)
                                        {
                                            baParamAnnotationFound = true;
                                            log.info("BAParam annotation present for parameter: " + i);
                                            String valueS = ((BAParam)annotation).value();
                                            try
                                            {
                                                Integer valueI = new Integer(valueS);
                                                parameterAnnotation[i] = valueI;
                                            }
                                            catch (NumberFormatException nfe)
                                            {
                                                parameterAnnotation[i] = valueS;
                                            }
                                        }
                                        else
                                        {
                                            log.info("No BAParam annotation for parameter: " + i);
                                        }
                                    }
                                    i++;
                                }
                                md.setParameterAnnotations(parameterAnnotation);
                            }
                            else
                            {
                                log.info("Completion method does not take any parameters");
                            }
                            methodFound = true;
                            break;
                        }
                    }
                    if (!methodFound)
                    {
                        throw new MethodNotAccessibleException("Completion method not found.");
                    }

                }
                md.setClazz(CompletionMethodClass[0]);

                // Get the interface
                Class[] ejbInterface = baCompleted.ejbInterface();
                if (ejbInterface.length == 0)
                {
                    log.info("EJB interface not specified");
                    if (baInterface != null && baInterface.length > 0 && !baInterface[0].equals(""))
                    {
                        log.info("EJB interface specified in BAService - storing value");
                        ejbInterface = new Class[1];
                        ejbInterface[0] = baInterface[0];
                    }
                    else
                    {
                        throw new MethodIncorrectlyAnnotatedException("Interface to EJB not specified.");
                    }
                }
                else
                {
                    log.info("EJB Interface specified.");
                }
                if (ejbInterface.length > 0 )
                {
                    // Interface specified
                    log.info("EJB Interface: " + ejbInterface[0]);

                    // Checking if the Completion method exists
                    log.info("Checking if Completion method exists");
                    Method[] methodList = ejbInterface[0].getMethods();
                    boolean methodFound = false;
                    for (Method singleMethod : methodList)
                    {
                        log.info("Comparing " + CompletionMethodName + " against " + singleMethod.getName() + ".");
                        if (singleMethod.getName().equals(CompletionMethodName))
                        {
                            log.info("Method found!");

                            // Remember types of parameters
                            log.info("Getting parameter types");
                            Class[] parameterTypes = singleMethod.getParameterTypes();

                            if (parameterTypes != null)
                            {
                                log.info("Completion method has parameters");

                                log.info("Setting Completion type");
                                md.setParameterTypes(singleMethod.getParameterTypes());

                                // Remember annotations of the parameters of the Completion method
                                if (!baParamAnnotationFound)
                                {

                                    log.info("Checking annotations");
                                    Object[] parameterAnnotation = new Object[singleMethod.getParameterTypes().length];
                                    Annotation[][] parameterAnnotations = singleMethod.getParameterAnnotations();
                                    int i = 0;
                                    for (Annotation[] annotationArray : parameterAnnotations)
                                    {
                                        for (Annotation annotation : annotationArray)
                                        {
                                            if (annotation instanceof BAParam)
                                            {
                                                log.info("BAParam annotation present for parameter: " + i);
                                                String valueS = ((BAParam)annotation).value();
                                                try
                                                {
                                                    Integer valueI = new Integer(valueS);
                                                    parameterAnnotation[i] = valueI;
                                                }
                                                catch (NumberFormatException nfe)
                                                {
                                                    parameterAnnotation[i] = valueS;
                                                }
                                            }
                                            else
                                            {
                                                log.info("No BAParam annotation for parameter: " + i);
                                            }
                                        }
                                        i++;
                                    }
                                    md.setParameterAnnotations(parameterAnnotation);
                                }
                            }
                            else
                            {
                                log.info("Completion method does not take any parameters");
                            }
                            methodFound = true;
                            break;
                        }
                    }
                    if (!methodFound)
                    {
                        throw new MethodNotAccessibleException("Completion method not found.");
                    }

                }
                md.setEjbInterface(ejbInterface[0]);

                // Get the JNDI name of the EJB
                log.info("Getting JNDI name of the EJB");
                String jndiName = baCompleted.jndiName();
                if (!jndiName.equals(""))
                {
                    log.info("JNDI name: " + jndiName);
                }
                else
                {
                    // Check if the JNDI name has been specified
                    if (baJndiName != null && !baJndiName.equals(""))
                    {
                        jndiName = baJndiName;
                    }
                    else
                    {
                        throw new MethodIncorrectlyAnnotatedException("JNDI name not specified");
                    }
                }
                md.setJndiName(jndiName);

                // Check if the URL provider has been specified
                String providerURL = baCompleted.providerURL();
                if (!providerURL.equals(""))
                {
                    log.info("Provider URL: " + providerURL);
                }
                else
                {
                    // Check if the provider URL has been specified
                    if (baProviderURL != null && !baProviderURL.equals(""))
                    {
                        providerURL = baProviderURL;
                    }
                    else
                    {
                        providerURL = null;
                    }
                }
                md.setProviderURL(providerURL);
            }

        }
        else
        {
            log.info("BACompletedBy annotation missing.");
            log.info("Checking the agreement protocol type.");
            if (agreementType == AgreementType.PARTICIPANT_COMPLETION)
            {
                log.info("Agreement protocol is participant completion. BACompletedBy is not necessary.");
            }
            else
            {
                throw new MethodIncorrectlyAnnotatedException("BACompletedBy annotation missing.");
            }
        }
        serviceDescription.putMethod("complete",md);


        // Remember @BAParam annotation values
        Object[] parameterAnnotation = new Object[method.getParameterTypes().length];
        Annotation[][] parameterAnnotations = method.getParameterAnnotations();
        int i = 0;
        for (Annotation[] annotationArray : parameterAnnotations)
        {
            for (Annotation annotation : annotationArray)
            {
                if (annotation instanceof BAParam)
                {
                    log.info("BAParam annotation present for parameter: " + i);
                    String valueS = ((BAParam)annotation).value();
                    try
                    {
                        Integer valueI = new Integer(valueS);
                        parameterAnnotation[i] = valueI;
                    }
                    catch (NumberFormatException nfe)
                    {
                        parameterAnnotation[i] = valueS;
                    }
                }
            }
            i++;
        }
        originalMethod.setParameterAnnotations(parameterAnnotation);

        // Remember @BAResult annotation value
        BAResult baResult = method.getAnnotation(BAResult.class);
        Integer returnId;
        if (baResult != null)
        {
            log.info("BAResult annotation present!");

            // Check if the service really returns a value
            Class returnType = method.getReturnType();
            if (returnType == null)
            {
                throw new MethodIncorrectlyAnnotatedException("BAResult used on method with no return value!");
            }
            String returnName = baResult.value();
            if (!returnName.equals(""))
            {
                // Check if this is a numerical identifier or not
                try
                {
                    returnId = new Integer(returnName);
                    log.info("Return identifier: " + returnId);
                    originalMethod.setReturnId(returnId);
                }
                catch (NumberFormatException nfe)
                {
                    log.info("Return identifier: " + returnName);
                    originalMethod.setReturnId(returnName);
                }
            }
            else
            {
                throw new MethodIncorrectlyAnnotatedException("BAResult annotation has a wrong identifier.");
            }

        }

        // Remember the original method description
        serviceDescription.putMethod("original",originalMethod);

        // Return the service description
        return serviceDescription;
    }

}
