/**
 * JOnAS: Java(TM) Open Application Server
 * Copyright (C) 2008-2009 Bull S.A.S.
 * Contact: jonas-team@ow2.org
 *
 * This library 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 any later version.
 *
 * This library 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 library; if not, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307
 * USA
 *
 * --------------------------------------------------------------------------
 * $Id$
 * --------------------------------------------------------------------------
 */
package org.ow2.jonas.ws.axis2.jaxws;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileFilter;
import java.io.FileNotFoundException;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.UnsupportedEncodingException;
import java.lang.annotation.Annotation;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.net.HttpURLConnection;
import java.net.URI;
import java.net.URL;
import java.net.URLClassLoader;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Properties;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;
import javax.ejb.EJB;
import javax.naming.Context;
import javax.naming.InitialContext;
import javax.servlet.ServletContext;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.xml.namespace.QName;
import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Result;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerFactory;
import javax.xml.transform.dom.DOMSource;
import javax.xml.transform.stream.StreamResult;
import javax.xml.ws.Binding;
import javax.xml.ws.Holder;
import javax.xml.ws.handler.Handler;

import org.apache.axis2.Constants;
import org.apache.axis2.addressing.AddressingHelper;
import org.apache.axis2.addressing.EndpointReference;
import org.apache.axis2.context.ConfigurationContext;
import org.apache.axis2.context.ConfigurationContextFactory;
import org.apache.axis2.context.MessageContext;
import org.apache.axis2.context.OperationContext;
import org.apache.axis2.context.ServiceContext;
import org.apache.axis2.deployment.DeploymentException;
import org.apache.axis2.description.AxisService;
import org.apache.axis2.description.TransportInDescription;
import org.apache.axis2.description.TransportOutDescription;
import org.apache.axis2.description.java2wsdl.Java2WSDLConstants;
import org.apache.axis2.engine.AxisEngine;
import org.apache.axis2.engine.Handler.InvocationResponse;
import org.apache.axis2.jaxws.binding.BindingUtils;
import org.apache.axis2.jaxws.context.WebServiceContextImpl;
import org.apache.axis2.jaxws.description.EndpointDescription;
import org.apache.axis2.jaxws.description.xml.handler.HandlerChainsType;
import org.apache.axis2.jaxws.registry.FactoryRegistry;
import org.apache.axis2.jaxws.server.JAXWSMessageReceiver;
import org.apache.axis2.jaxws.server.endpoint.lifecycle.factory.EndpointLifecycleManagerFactory;
import org.apache.axis2.transport.RequestResponseTransport;
import org.apache.axis2.transport.http.HTTPConstants;
import org.apache.axis2.transport.http.HTTPTransportReceiver;
import org.apache.axis2.transport.http.HTTPTransportUtils;
import org.apache.axis2.transport.http.TransportHeaders;
import org.apache.axis2.transport.http.util.RESTUtil;
import org.apache.axis2.util.MessageContextBuilder;
import org.apache.axis2.util.UUIDGenerator;
import org.apache.http.HttpStatus;
import org.apache.xbean.recipe.ObjectRecipe;
import org.ow2.easybeans.container.session.stateless.StatelessSessionFactory;
import org.ow2.easybeans.resolver.api.EZBJNDIBeanData;
import org.ow2.easybeans.resolver.api.EZBRemoteJNDIResolver;
import org.ow2.jonas.ws.axis2.easybeans.Axis2EjbMessageReceiver;
import org.ow2.jonas.ws.axis2.easybeans.Axis2EndpointLifecycleManagerFactory;
import org.ow2.jonas.ws.axis2.http.Axis2RequestResponseTransport;
import org.ow2.jonas.ws.axis2.http.Axis2TransportInfo;
import org.ow2.jonas.ws.axis2.http.AxisServiceGenerator;
import org.ow2.jonas.ws.axis2.http.WSDLQueryHandler;
import org.ow2.jonas.ws.axis2.jaxws.description.impl.DescriptionUtils;
import org.ow2.jonas.ws.axis2.util.JAXWSUtils;
import org.ow2.jonas.ws.jaxws.IWSRequest;
import org.ow2.jonas.ws.jaxws.IWSResponse;
import org.ow2.jonas.ws.jaxws.IWebServiceEndpoint;
import org.ow2.jonas.ws.jaxws.PortIdentifier;
import org.ow2.jonas.ws.jaxws.PortMetaData;
import org.ow2.jonas.ws.jaxws.WSException;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;

import com.sun.xml.ws.api.BindingID;
import com.sun.xml.ws.api.wsdl.writer.WSDLGeneratorExtension;
import com.sun.xml.ws.binding.WebServiceFeatureList;
import com.sun.xml.ws.model.AbstractSEIModelImpl;
import com.sun.xml.ws.model.RuntimeModeler;
import com.sun.xml.ws.util.ServiceFinder;
import com.sun.xml.ws.wsdl.writer.WSDLGenerator;
import com.sun.xml.ws.wsdl.writer.WSDLResolver;
/**
 * This class represents the JOnAS' view on a web service endpoint (server side).
 * @author xiaoda
 */
public class Axis2WSEndpoint implements IWebServiceEndpoint {

    private static final Log logger = LogFactory.getLog(Axis2WSEndpoint.class);

    private String style = Java2WSDLConstants.DOCUMENT;
    private String use = Java2WSDLConstants.LITERAL;

    protected ConfigurationContext configurationContext;

    protected AxisService service;

    protected Binding binding;

    protected PortMetaData portMetaData;

    protected Class endpointClass;

    protected WSDLQueryHandler wsdlQueryHandler;

    protected StatelessSessionFactory factory;

    protected URL moduleURL;

    private final static String JAVA_HOME = "JAVA_HOME";

    private final static String TOOLS = "tools.jar";

    private int forkTimeout;

    private final static long FORK_POLL_FREQUENCY = 1000 * 2; // 2 seconds

    private URL wsdlFile;

    private URL wsdlFileURL;
    /**
     * Identifier.
     */
    private PortIdentifier identifier;
    /**
     * Describe the WS type (EJB/POJO).
     */
    private IWebServiceEndpoint.EndpointType type;

    public static final String END_POINT_TYPE = "EndPointType";

    public Axis2WSEndpoint(final Class endpointClass, final IWebServiceEndpoint.EndpointType type, final PortMetaData portMetaData,
            final StatelessSessionFactory factory,final URL moduleURL) {
        this.endpointClass = endpointClass;
        this.portMetaData = portMetaData;
        this.type = type;
        this.factory = factory;
        this.moduleURL = moduleURL;
        forkTimeout = 1000*60;

        String name = endpointClass.getSimpleName();
        String namespace = JAXWSUtils.getDefaultNamespace(endpointClass);

        // Create the identifier
        this.identifier = new PortIdentifier(new QName(namespace,name),
                                             portMetaData.getContextRoot());
    }

    public void init() throws Exception {


        configurationContext = ConfigurationContextFactory.createBasicConfigurationContext("META-INF/axis2.xml");
        AxisServiceGenerator serviceGen = createServiceGenerator();

        // TODO: Get webservice from wsdl
        service = serviceGen.getServiceFromClass(this.endpointClass,this.configurationContext,portMetaData);
        service.setScope(Constants.SCOPE_APPLICATION);
        //service.setEndpointName();???
        configurationContext.getAxisConfiguration().addService(service);
        this.wsdlQueryHandler = new WSDLQueryHandler(this.service);
        if (this.type == IWebServiceEndpoint.EndpointType.EJB) {
            this.configurationContext.setContextRoot(this.portMetaData.getContextRoot());
            this.configurationContext.setServicePath(this.portMetaData.getUrlPattern());
        }

        this.configureHandlers();

        FactoryRegistry.setFactory(EndpointLifecycleManagerFactory.class, new Axis2EndpointLifecycleManagerFactory());

        ClassLoader old = Thread.currentThread().getContextClassLoader();
        try {
            //generate the wsdl file
            this.wsdlFile = this.generateWSDL();
        } finally {
            Thread.currentThread().setContextClassLoader(old);
        }
    }

    private URL generateWSDL() throws Exception {

        BindingID bindingID = BindingID.parse(endpointClass);
        WebServiceFeatureList wsfeatures = new WebServiceFeatureList(endpointClass);
        QName qName = new QName(this.service.getTargetNamespace(),this.service.getName());
        RuntimeModeler rtModeler = new RuntimeModeler(endpointClass, qName, bindingID);

        URL mURL = null;
        if(this.type==EndpointType.POJO) {
            mURL = new URL(this.moduleURL.toString() + "WEB-INF/classes/");
        } else {
            mURL = new URL(this.moduleURL.toString());
        }
        ClassLoader classLoader = null;
        if(this.type==EndpointType.POJO) {
            classLoader= new URLClassLoader(new URL[]{mURL},this.getClass().getClassLoader());
        } else {
            classLoader= Thread.currentThread().getContextClassLoader();
        }
        rtModeler.setClassLoader(classLoader);
        if(this.service.getPortTypeName()!=null) {
            rtModeler.setPortName(qName);
        }
        AbstractSEIModelImpl rtModel = null;
        try{
         rtModel = rtModeler.buildRuntimeModel();
        }
        catch(Exception e) {
            e.printStackTrace();
        }

     final File[] wsdlFileName = new File[1]; // used to capture the generated WSDL file.
        final Map<String,File> schemaFiles = new HashMap<String,File>();

        WSDLGenerator wsdlGenerator = new WSDLGenerator(rtModel,
                new WSDLResolver() {
            private File toFile(final String suggestedFilename) {
                return new File(moduleURL.getPath(), suggestedFilename);
            }
            private Result toResult(final File file) {
                Result result;
                try {
                    result = new StreamResult(new FileOutputStream(file));
                    result.setSystemId(file.getPath().replace('\\', '/'));
                } catch (FileNotFoundException e) {
                    //errReceiver.error(e);
                    e.printStackTrace();
                    return null;
                }
                return result;
            }
            public Result getWSDL(final String suggestedFilename) {
                File f = toFile(suggestedFilename);
                wsdlFileName[0] = f;
                return toResult(f);
            }
            public Result getSchemaOutput(final String namespace, final String suggestedFilename) {
                if (namespace.equals("")) {
                    return null;
                }
                File f = toFile(suggestedFilename);
                schemaFiles.put(namespace,f);
                return toResult(f);
            }
            public Result getAbstractWSDL(final Holder<String> filename) {
                return toResult(toFile(filename.value));
            }
            public Result getSchemaOutput(final String namespace, final Holder<String> filename) {
                return getSchemaOutput(namespace, filename.value);
            }
        }, bindingID.createBinding(wsfeatures.toArray()), null, endpointClass, ServiceFinder.find(WSDLGeneratorExtension.class).toArray());
        wsdlGenerator.doGeneration();

        return wsdlFileName[0].toURL();

        /*
        File [] jarFiles = JAXWSTools.getClasspath();
        StringBuilder classPath = new StringBuilder();

        for(int i = 0 ; i < jarFiles.length ; i++ ) {
            classPath.append((jarFiles[i].getAbsolutePath())).append(File.pathSeparator);
        }
        classPath.append(this.moduleURL.getPath() + "WEB-INF/classes").append(File.pathSeparator);

        logger.info(classPath);

        //create arguments;
        String[] arguments = buildArguments(this.endpointClass.getName(), classPath.toString(), new File(this.moduleURL.getPath()));



        boolean result = forkWsgen(classPath, arguments);


        if (result) {
            File wsdlFile = findWsdlFile(new File(this.moduleURL.getPath() + "/WEB-INF"));
            this.wsdlFileURL = wsdlFile.toURL();
            if (wsdlFile == null) {
                throw new DeploymentException("Unable to find the service wsdl file");
            }
            return getRelativeNameOrURL(new File(this.moduleURL.getPath()),wsdlFile);
        } else {
            throw new DeploymentException("WSDL generation failed");
        }
        */
    }


    public static String getRelativeNameOrURL(final File baseDir, final File file) {
        String basePath = baseDir.getAbsolutePath();
        String path = file.getAbsolutePath();

        if (path.startsWith(basePath)) {
            if (File.separatorChar == path.charAt(basePath.length())) {
                return path.substring(basePath.length() + 1);
            } else {
                return path.substring(basePath.length());
            }
        } else {
            return file.toURI().toString();
        }
    }

    private File findWsdlFile(final File baseDir) {
        QName serviceQName = JAXWSUtils.getServiceQName(this.endpointClass);
        String serviceName = (serviceQName == null) ? null : serviceQName.getLocalPart();
        return findWsdlFile(baseDir, serviceName);
    }

    private static File[] getWsdlFiles(final File baseDir) {
        File[] files = baseDir.listFiles(new FileFilter() {
            public boolean accept(final File file) {
                return (file.isFile() && file.getName().endsWith(".wsdl"));
            }
        });
        return files;
    }

    public static File findWsdlFile(final File baseDir, final String serviceName) {
        File[] files = getWsdlFiles(baseDir);
        if (files == null || files.length == 0) {
            // no wsdl files found
            return null;
        } else {
            if (files.length == 1) {
                // found one wsdl file, must be it
                return files[0];
            } else if (serviceName != null) {
                // found multiple wsdl files, check filenames to match serviceName
                String wsdlFileName = serviceName + ".wsdl";
                for (File file : files) {
                    if (wsdlFileName.equalsIgnoreCase(file.getName())) {
                        return file;
                    }
                }
                return null;
            } else {
                // found multiple wsdl files and serviceName is not specified
                // so we don't know which wsdl file is the right one
                return null;
            }
        }
    }
    private boolean forkWsgen(final StringBuilder classPath, final String[] arguments) throws Exception {
        List<String> cmd = new ArrayList<String>();
        cmd.add("-classpath");
        cmd.add(classPath.toString());
        cmd.add("com.sun.tools.ws.WsGen");
        cmd.addAll(Arrays.asList(arguments));

        try {
            return execJava(cmd, 1000 * 60);
        } catch (Exception e) {
            throw new DeploymentException("WSDL generation failed", e);
        }
    }

    public static boolean execJava(final List<String> arguments, final long timeout) throws Exception {

        List<String> cmd = new ArrayList<String>();
        String javaHome = System.getProperty("java.home");
        String java = javaHome + File.separator + "bin" + File.separator + "java";

        cmd.add(java);
        cmd.addAll(arguments);
        return exec(cmd, timeout);
    }

    private static boolean waitFor(final Process process, final long timeout) throws Exception {
        CaptureOutputThread outputThread = new CaptureOutputThread(process.getInputStream());
        outputThread.start();

        long sleepTime = 0;
        while(sleepTime < timeout) {
            try {
                int errorCode = process.exitValue();
                if (errorCode == 0) {
                    logger.debug("Process output: {}", outputThread.getOutput());
                    return true;
                } else {
                    logger.error("Process failed: {}", outputThread.getOutput());
                    return false;
                }
            } catch (IllegalThreadStateException e) {
                // still running
                try {
                    Thread.sleep(FORK_POLL_FREQUENCY);
                } catch (InterruptedException ee) {
                    // interrupted
                    process.destroy();
                    throw new Exception("Process was interrupted");
                }
                sleepTime += FORK_POLL_FREQUENCY;
            }
        }

        // timeout;
        process.destroy();

        logger.error("Process timed out: {}", outputThread.getOutput());

        throw new Exception("Process timed out");
    }

    public static boolean exec(final List<String> cmd, final long timeout) throws Exception {
        logger.debug("Executing process: {}", cmd);

        ProcessBuilder builder = new ProcessBuilder(cmd);
        builder.redirectErrorStream(true);

        Process process = builder.start();
        return waitFor(process, timeout);
    }
    private String[] buildArguments(final String sei, final String classPath, final File moduleBaseDir) {
        List<String> arguments = new ArrayList<String>();

        arguments.add("-cp");
        arguments.add(classPath);
        arguments.add("-keep");
        arguments.add("-wsdl");
        arguments.add("-d");
        arguments.add(moduleBaseDir.getAbsolutePath() + "/WEB-INF");

        arguments.add(sei);

        return arguments.toArray(new String[]{});
    }

    private static class CaptureOutputThread extends Thread {

        private InputStream in;
        private ByteArrayOutputStream out;

        public CaptureOutputThread(final InputStream in) {
            this.in = in;
            this.out = new ByteArrayOutputStream();
        }

        public String getOutput() {
            // make sure the thread is done
            try {
                join(10 * 1000);

                // if it's still not done, interrupt it
                if (isAlive()) {
                    interrupt();
                }
            } catch (InterruptedException e) {
                // that's ok
            }

            // get the output
            byte [] arr = this.out.toByteArray();
            String output = new String(arr, 0, arr.length);
            return output;
        }

        @Override
        public void run() {
            try {
                copyAll(this.in, this.out);
            } catch (IOException e) {
                // ignore
            } finally {
                try { this.out.close(); } catch (IOException ee) {}
                try { this.in.close(); } catch (IOException ee) {}
            }
        }

        private static void copyAll(final InputStream in, final OutputStream out) throws IOException {
            byte[] buffer = new byte[4096];
            int count;
            while ((count = in.read(buffer)) > 0) {
                out.write(buffer, 0, count);
            }
            out.flush();
        }
    }
    private boolean waitFor(final Process process) throws DeploymentException {
        CaptureOutputThread outputThread = new CaptureOutputThread(process.getInputStream());
        outputThread.start();

        long sleepTime = 0;
        while(sleepTime < this.forkTimeout) {
            try {
                int errorCode = process.exitValue();
                if (errorCode == 0) {
                    if (logger.isDebugEnabled()) {
                        logger.debug("wsgen output: " + outputThread.getOutput());
                    }
                    return true;
                } else {
                    logger.error("WSDL generation process failed");
                    logger.error(outputThread.getOutput());
                    return false;
                }
            } catch (IllegalThreadStateException e) {
                // still running
                try {
                    Thread.sleep(FORK_POLL_FREQUENCY);
                } catch (InterruptedException ee) {
                    // interrupted
                    process.destroy();
                    throw new DeploymentException("WSDL generation process was interrupted");
                }
                sleepTime += FORK_POLL_FREQUENCY;
            }
        }

        // timeout;
        process.destroy();

        logger.error("WSDL generation process timed out");
        logger.error(outputThread.getOutput());

        throw new DeploymentException("WSDL generation process timed out");
    }

    protected AxisServiceGenerator createServiceGenerator() {
        AxisServiceGenerator serviceGenerator = null;
        if (this.type == IWebServiceEndpoint.EndpointType.POJO) {
            serviceGenerator = new AxisServiceGenerator();
            //Axis2PojoMessageReceiver messageReceiver = new Axis2PojoMessageReceiver();
            //serviceGenerator.setMessageReceiver(messageReceiver);
        } else if (this.type == IWebServiceEndpoint.EndpointType.EJB) {
            serviceGenerator = new AxisServiceGenerator();
            Axis2EjbMessageReceiver messageReceiver = new Axis2EjbMessageReceiver(this, this.endpointClass, this.factory);
            serviceGenerator.setMessageReceiver(messageReceiver);
        }
        return serviceGenerator;
    }

    public EndpointType getType() {
        return this.type;
    }

    /**
     * @return the port identifier of this endpoint.
     */
    public PortIdentifier getIdentifier() {
        return this.identifier;
    }

    public void invoke(final IWSRequest request, final IWSResponse response) throws WSException {


        HttpServletRequest httpRequest = request.getAttribute(HttpServletRequest.class);
        //HttpServletResponse httpResponse = request.getAttribute(HttpServletResponse.class);

        MessageContext msgContext = new MessageContext();
        msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTP);
        msgContext.setProperty(MessageContext.REMOTE_ADDR, request.getRemoteAddr());
        msgContext.setProperty(END_POINT_TYPE, this.getType());


        ClassLoader old = Thread.currentThread().getContextClassLoader();

        try {
            Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader());

            //inject resource for all Handlers
            this.injectHandlers();


            TransportOutDescription transportOut = this.configurationContext.getAxisConfiguration().getTransportOut(
                    Constants.TRANSPORT_HTTP);
            TransportInDescription transportIn = this.configurationContext.getAxisConfiguration().getTransportIn(
                    Constants.TRANSPORT_HTTP);

            msgContext.setConfigurationContext(this.configurationContext);
            msgContext.setTransportIn(transportIn);
            msgContext.setTransportOut(transportOut);
            msgContext.setServiceGroupContextId(UUIDGenerator.getUUID());
            msgContext.setServerSide(true);
            msgContext.setAxisService(this.service);


            if ("GET".equals(httpRequest.getMethod())) {
                processGETRequest(request, response,msgContext);
            }else if ("POST".equals(httpRequest.getMethod())) {
                doService(request, response, msgContext);
            } else {
                throw new UnsupportedOperationException("[" + httpRequest.getMethod() + " ] method not supported");
            }

        } catch (Throwable e) {
            String msg = "Exception occurred while trying to invoke service method doService()";
            logger.error(msg, e);
            try {
                //AxisEngine engine = new AxisEngine(this.configurationContext);

                msgContext.setProperty(MessageContext.TRANSPORT_OUT, response.getOutputStream());
                msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, new Axis2TransportInfo(response));

                MessageContext faultContext = MessageContextBuilder.createFaultMessageContext(msgContext, e);
                // If the fault is not going along the back channel we should be
                // 202ing
                if (AddressingHelper.isFaultRedirected(msgContext)) {
                    response.setStatus(HttpURLConnection.HTTP_ACCEPTED);
                } else {
                    response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
                }
                AxisEngine.sendFault(faultContext);
            } catch (Exception ex) {
                if (AddressingHelper.isFaultRedirected(msgContext)) {
                    response.setStatus(HttpURLConnection.HTTP_ACCEPTED);
                } else {
                    response.setStatus(HttpURLConnection.HTTP_INTERNAL_ERROR);
                    response.setHeader(HTTPConstants.HEADER_CONTENT_TYPE, "text/plain");
                    PrintWriter pw = null;
                    try {
                        pw = new PrintWriter(response.getOutputStream());
                    } catch (IOException e1) {
                        // TODO Auto-generated catch block
                        throw new WSException("Exception occured while getting output print writer",e);
                    }
                    ex.printStackTrace(pw);
                    pw.flush();
                    logger.error(msg, ex);
                }
            } finally {
                Thread.currentThread().setContextClassLoader(old);
            }

        }
    }

    protected void doService(final IWSRequest request, final IWSResponse response, final MessageContext msgContext) throws Exception {

        processPOSTRequest(request, response, this.service, msgContext);
        // Finalize response
        OperationContext operationContext = msgContext.getOperationContext();
        Object contextWritten = null;
        Object isTwoChannel = null;
        if (operationContext != null) {
            contextWritten = operationContext.getProperty(Constants.RESPONSE_WRITTEN);
            isTwoChannel = operationContext.getProperty(Constants.DIFFERENT_EPR);
        }

        if ((contextWritten != null) && Constants.VALUE_TRUE.equals(contextWritten)) {
            if ((isTwoChannel != null) && Constants.VALUE_TRUE.equals(isTwoChannel)) {
                response.setStatus(HttpURLConnection.HTTP_ACCEPTED);
                return;
            }
            response.setStatus(HttpURLConnection.HTTP_OK);
        } else {
            response.setStatus(HttpURLConnection.HTTP_ACCEPTED);
        }
    }

    protected void setMsgContextProperties(final IWSRequest request, final IWSResponse response, final AxisService service,
            final MessageContext msgContext) throws IOException {
        msgContext.setProperty(MessageContext.TRANSPORT_OUT, response.getOutputStream());
        msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, new Axis2TransportInfo(response));
        msgContext.setProperty(RequestResponseTransport.TRANSPORT_CONTROL, new Axis2RequestResponseTransport(response));
        msgContext.setIncomingTransportName(Constants.TRANSPORT_HTTP);

        HttpServletRequest servletRequest = request.getAttribute(HttpServletRequest.class);
        msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETREQUEST, servletRequest);
        msgContext.setProperty(Constants.Configuration.TRANSPORT_IN_URL, servletRequest.getRequestURI().toString());


        HttpServletResponse servletResponse = request.getAttribute(HttpServletResponse.class);
        msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETRESPONSE, servletResponse);

        ServletContext servletContext = request.getAttribute(ServletContext.class);
        msgContext.setProperty(HTTPConstants.MC_HTTP_SERVLETCONTEXT, servletContext);

        if (servletRequest != null) {
            msgContext.setProperty(MessageContext.TRANSPORT_HEADERS, new TransportHeaders(servletRequest));
        }

        if (this.binding != null) {
            msgContext.setProperty(JAXWSMessageReceiver.PARAM_BINDING, this.binding);
        }
        msgContext.setTo(new EndpointReference(servletRequest.getRequestURL().toString()));

    }


    protected void processURLRequest(final IWSRequest request, final IWSResponse response, final AxisService service,
            final MessageContext msgContext) throws Exception {
        String contentType = request.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);

        ConfigurationContext configurationContext = msgContext.getConfigurationContext();
        configurationContext.fillServiceContextAndServiceGroupContext(msgContext);

        setMsgContextProperties(request, response, service, msgContext);

        ServiceContext serviceContext = msgContext.getServiceContext();


        ObjectRecipe objectRecipe = new ObjectRecipe(this.endpointClass.getName());
        Object obj = objectRecipe.create(this.endpointClass.getClassLoader());

        WebServiceContextImpl wci = new WebServiceContextImpl();

        serviceContext.setProperty(ServiceContext.SERVICE_OBJECT, obj);

        InvocationResponse processed = null;
        try {
            processed = RESTUtil.processURLRequest(msgContext, response.getOutputStream(), contentType);
        } finally {
            // de-associate JAX-WS MessageContext with the thread
            // (association happens in
            // POJOEndpointLifecycleManager.createService() call)
            // POJOWebServiceContext.clear();
        }

        if (!processed.equals(InvocationResponse.CONTINUE)) {
            // response.setStatusCode(HttpURLConnection.HTTP_OK);
            String s = HTTPTransportReceiver.getServicesHTML(configurationContext);
            PrintWriter pw = new PrintWriter(response.getOutputStream());
            pw.write(s);
            pw.flush();
        }
    }

    protected void processPOSTRequest(final IWSRequest request, final IWSResponse response, final AxisService service,
            final MessageContext msgContext) throws Exception {
        HttpServletRequest httpRequest = request.getAttribute(HttpServletRequest.class);
        HttpServletResponse httpResponse = request.getAttribute(HttpServletResponse.class);

        String contentType = request.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
        String soapAction = request.getHeader(HTTPConstants.HEADER_SOAP_ACTION);
        if (soapAction == null) {
            soapAction = "\"\"";
        }

        ConfigurationContext configurationContext = msgContext.getConfigurationContext();
        configurationContext.fillServiceContextAndServiceGroupContext(msgContext);

        setMsgContextProperties(request, response, service, msgContext);
        ClassLoader old = Thread.currentThread().getContextClassLoader();
        try {
                if (!HTTPTransportUtils.isRESTRequest(contentType)) {

                    Thread.currentThread().setContextClassLoader(this.endpointClass.getClassLoader());

                HTTPTransportUtils.processHTTPPostRequest(msgContext, httpRequest.getInputStream(), httpResponse
                        .getOutputStream(), contentType, soapAction, new URI(httpRequest.getRequestURI()).getPath());
            } else {
                RESTUtil.processXMLRequest(msgContext,
                        request.getInputStream(),
                        response.getOutputStream(),
                        contentType);
            }

        } finally {
            Thread.currentThread().currentThread().setContextClassLoader(old);
        }
    }

    public void start() {
        // TODO Auto-generated method stub

    }

    public void stop() {
        // TODO Auto-generated method stub
        // call handlers preDestroy
        List<Handler> handlers = this.binding.getHandlerChain();
        for (Handler handler : handlers) {
            invokePreDestroy(handler);
        }
    }

    public PortMetaData getPortMetaData() {
        // TODO Auto-generated method stub
        // return this.portMetaData;
        return this.portMetaData;
    }

    /*
     * Gets the right handlers for the port/service/bindings and performs injection.
     */
    protected void configureHandlers() throws UnsupportedEncodingException, WSException {
        EndpointDescription desc = AxisServiceGenerator.getEndpointDescription(this.service);
        if (desc == null) {
            //this.binding = new BindingImpl("");
            throw new WSException("There is no endpoint description for this service: " + this.service.getName());
        } else {
            String xml = this.portMetaData.getHandlerXML();
            HandlerChainsType handlerChains = null;
            if (xml != null) {
                ByteArrayInputStream in = new ByteArrayInputStream(xml.getBytes("UTF-8"));
                handlerChains = DescriptionUtils.loadHandlerChains(in,null);
                desc.setHandlerChain(handlerChains);
            }

            this.binding = BindingUtils.createBinding(desc);

            ClassLoader old = Thread.currentThread().getContextClassLoader();

            Thread.currentThread().setContextClassLoader(this.endpointClass.getClassLoader());
            try{
                DescriptionUtils.registerHandlerHeaders(desc.getAxisService(), this.binding.getHandlerChain());
            } finally {
                Thread.currentThread().setContextClassLoader(old);
            }

        }
    }


    //here we inject @resource for handlers
    // we also need to inject @resource for services
    protected void injectHandlers() throws WSException {
        List<Handler> handlers = this.binding.getHandlerChain();
        try {
            for (Handler<?> handler : handlers) {
                // axis2 has finished WebServiceContext injection
                // so we just need to do injection of @EJB resource
                 injectEJBResources(handler);
                 invokePostConstruct(handler);
            }
        } catch (Exception e) {
            throw new WSException("Injection of @EJB resource for " + this.service.getName() + " failed!", e);
        }
    }

    protected void injectEJBResources(final Object instance) throws WSException {

        Field[] fieldArray = instance.getClass().getDeclaredFields();

        for(Field field : fieldArray){
            if(field.isAnnotationPresent(EJB.class)) {
                //LOG.info("@EJB exists!");

                EJB ejbResource = field.getAnnotation(EJB.class);

                String dependentEJBName = ejbResource.name();

               try
                {

                    Context ictx = new InitialContext();

                    //Remote JNDI Resolver
                    EZBRemoteJNDIResolver jndiResolver = (EZBRemoteJNDIResolver)ictx.lookup("EZB_Remote_JNDIResolver");

                    String ejbResourceName = ((EZBJNDIBeanData)(jndiResolver.getEJBJNDINames(dependentEJBName)).toArray()[0]).getName();


                    ClassLoader old = Thread.currentThread().getContextClassLoader();

                    try {
                          Thread.currentThread().setContextClassLoader(this.endpointClass.getClassLoader());

                          Object ejbObject = ictx.lookup(ejbResourceName);/*"org.ow2.easybeans.examples.statelessbean.StatelessBean"
                                  + "_" + dependentEJBName + "@Remote");*/

                          boolean accessibility = field.isAccessible();

                          try {
                                field.setAccessible(true);
                                field.set(instance, ejbObject);
                              } catch (Exception e) {
                                  throw new WSException("Exception occurs while injecting field for " + this.service.getName() + "!",e);
                              } finally {
                                field.setAccessible(accessibility);
                              }
                         } finally {
                              Thread.currentThread().setContextClassLoader(old);
                         }
                }
                catch(Exception e) {
                    // TODO Auto-generated catch block
                    throw new WSException("Unable to inject the field for " + this.service.getName() + "!", e);
                }

           }

        }
    }

    protected void processGETRequest(final IWSRequest request, final IWSResponse response, final MessageContext msgContext) throws Exception {
        HttpServletRequest httpRequest = request.getAttribute(HttpServletRequest.class);
        HttpServletResponse httpResponse = request.getAttribute(HttpServletResponse.class);

        response.setStatus(HttpStatus.SC_OK);

        response.setContentType("text/xml");

        InputStream inStream = null;
        String epr = httpRequest.getRequestURL().toString();

        if(this.wsdlFile!=null) {
            inStream = this.endpointClass.getClassLoader().getResourceAsStream(this.wsdlFile.toString());

            OutputStream outStream = response.getOutputStream();

            DocumentBuilderFactory dbf = DocumentBuilderFactory.newInstance();
            DocumentBuilder db = dbf.newDocumentBuilder();

            Document doc = null;
            doc = db.parse(inStream);
            doc.normalize();

            Element root = doc.getDocumentElement();

            NodeList nodeList = root.getChildNodes();

            replaceURL(nodeList, epr);

            TransformerFactory factory = TransformerFactory.newInstance();
            Transformer transformer = factory.newTransformer();
            Properties properties = transformer.getOutputProperties();
            properties.setProperty(OutputKeys.ENCODING, "UTF-8");
            properties.setProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperties(properties);

            DOMSource source = new DOMSource(doc);

            StreamResult result = new StreamResult(outStream);

            transformer.transform(source, result);
        }

    }

    public void replaceURL(final NodeList nodeList,final String url) {

        for(int i=0;i<nodeList.getLength();i++) {
            Node node = nodeList.item(i);
            if(node.getNodeType() == Node.ELEMENT_NODE &&
                    ( "service".equals(node.getNodeName()) || "wsdl:service".equals(node.getNodeName()) ) ) {

                NodeList nl1 = node.getChildNodes();
                for(int j=0;j<nl1.getLength();j++) {

                    Node nd1 = nl1.item(j);
                    if(nd1.getNodeType() == Node.ELEMENT_NODE &&
                       ("port".equals(nd1.getNodeName()) || "wsdl:port".equals(nd1.getNodeName()) ) ) {

                        logger.debug(nd1.getNodeName());

                        NodeList nl2 = nd1.getChildNodes();
                        for(int k=0;k<nl2.getLength();k++) {
                            Node nd2 = nl2.item(k);
                            if(nd2.getNodeType() == Node.ELEMENT_NODE &&
                               ( "soap:address".equals(nd2.getNodeName())
                               || "soap12:address".equals(nd2.getNodeName())
                               || "http:address".equals(nd2.getNodeName())) ) {

                                logger.debug(nd2.getNodeName());
                                nd2.getAttributes().getNamedItem("location").setNodeValue(url);
                                logger.debug(nd2.getAttributes().getNamedItem("location"));

                            }
                        }
                    }
                }
            }
        }
    }

    /**
     * Prints info about the endPoint.
     */
    public void displayInfos() {
        String endpointURL = "not yet available";
        if (portMetaData != null && portMetaData.getEndpointURL() != null) {
            endpointURL = portMetaData.getEndpointURL();
        }
        logger.info("{0} endpoint URL at ''{1}''", type, endpointURL);
    }

    private void invokePostConstruct(final Object instance) {
        for (Method method : getMethods(instance.getClass(),
                PostConstruct.class)) {
            PostConstruct pc = method.getAnnotation(PostConstruct.class);
            if (pc != null) {
                boolean accessible = method.isAccessible();
                try {
                    method.setAccessible(true);
                    method.invoke(instance);
                } catch (IllegalAccessException e) {
                    logger.warn("@PostConstruct method is not visible: " + method);
                } catch (InvocationTargetException e) {
                    logger.warn("@PostConstruct method threw exception", e);
                } finally {
                    method.setAccessible(accessible);
                }
            }
        }
    }

    private void invokePreDestroy(final Object instance) {

        for (Method method : getMethods(instance.getClass(), PreDestroy.class)) {
            PreDestroy pc = method.getAnnotation(PreDestroy.class);
            if (pc != null) {
                boolean accessible = method.isAccessible();
                try {
                    method.setAccessible(true);
                    method.invoke(instance);
                } catch (IllegalAccessException e) {
                    logger.warn("@PreDestroy method is not visible: " + method);
                } catch (InvocationTargetException e) {
                    logger.warn("@PreDestroy method threw exception", e);
                } finally {
                    method.setAccessible(accessible);
                }
            }
        }
    }

    private Collection<Method> getMethods(final Class target, final Class<? extends Annotation> annotationType) {
        Collection<Method> methods = new HashSet<Method>();
        addMethods(target.getMethods(), annotationType, methods);
        addMethods(target.getDeclaredMethods(), annotationType, methods);
        return methods;
    }

    private void addMethods(final Method[] methods, final Class<? extends Annotation> annotationType, final Collection<Method> methodsCol) {
        for (Method method : methods) {
            if (method.isAnnotationPresent(annotationType)) {
                methodsCol.add(method);
            }
        }
    }
}
