/**
 * 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.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.util.Collection;
import java.util.HashSet;
import java.util.List;
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.parsers.ParserConfigurationException;
import javax.xml.transform.OutputKeys;
import javax.xml.transform.Transformer;
import javax.xml.transform.TransformerException;
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.handler.Handler;

import org.apache.axiom.om.OMElement;
import org.apache.axiom.om.util.UUIDGenerator;
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.description.AxisService;
import org.apache.axis2.description.AxisService2WSDL11;
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.impl.DescriptionUtils;
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.XMLPrettyPrinter;
import org.apache.http.HttpStatus;
import org.apache.ws.java2wsdl.Java2WSDLBuilder;
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.util.Axis2Java2WSDLBuilder;
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 org.xml.sax.SAXException;

/**
 * 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;
    /**
     * 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) {
        this.endpointClass = endpointClass;
        this.portMetaData = portMetaData;
        this.type = type;
        this.factory = factory;

        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(service.getName());
        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());

    }

    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, this.service, msgContext);
                logger.debug("received a GET request");

                logger.debug("Query String:" + httpRequest.getQueryString());

                String query = httpRequest.getQueryString();
                if (query != null) {
                    if (query.toLowerCase().indexOf("wsdl") >= 0) {
                        try {
                            processListWSDL(request, response);
                        } catch (Exception e) {
                            e.printStackTrace();
                            throw new WSException("Get WSDL for " + this.service.getName() + "  failed");
                        }
                    }else {
                        // REST request
                        processURLRequest(request, response, service, 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);
                }
                engine.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);
        }
    }

    // TODO: Get wsdl
    // protected void processGETRequest(final IWSRequest request, final
    // IWSResponse response, AxisService service,
    // MessageContext msgContext) throws Exception {
    // if (request.getRequestURI().getQuery() != null
    // && (request.getRequestURI().getQuery().startsWith("wsdl") ||
    // request.getURI().getQuery().startsWith("xsd"))) {
    // // wsdl or xsd request
    //
    // if (portInfo.getWsdlFile() != null && !portInfo.getWsdlFile().equals(""))
    // {
    // URL wsdlURL = AxisServiceGenerator.getWsdlURL(portInfo.getWsdlFile(),
    // configurationBaseUrl, classLoader);
    // this.wsdlQueryHandler
    // .writeResponse(request.getRequestURI().toString(), wsdlURL.toString(),
    // response.getOutputStream());
    // } else {
    // service.printWSDL(response.getOutputStream());
    // }
    // } else if (AxisServiceGenerator.isSOAP11(service)) {
    // response.setContentType("text/html");
    // PrintWriter pw = new PrintWriter(response.getOutputStream());
    // pw.write("<html><title>Web Service</title><body>");
    // pw.write("Hi, this is '" + service.getName() + "' web service.");
    // pw.write("</body></html>");
    // pw.flush();
    // } else {
    // // REST request
    // setMsgContextProperties(request, response, service, msgContext);
    //
    // String contentType =
    // request.getHeader(HTTPConstants.HEADER_CONTENT_TYPE);
    //
    // msgContext.setTo(new EndpointReference(request.getURI().toString()));
    //
    // msgContext.setProperty(MessageContext.TRANSPORT_OUT,
    // response.getOutputStream());
    // msgContext.setProperty(Constants.OUT_TRANSPORT_INFO, new
    // Axis2TransportInfo(response));
    //
    // InvocationResponse processed = RESTUtil.processURLRequest(msgContext,
    // response.getOutputStream(), contentType);
    //
    // if (!processed.equals(InvocationResponse.CONTINUE)) {
    // response.setStatus(HttpURLConnection.HTTP_OK);
    // String s = HTTPTransportReceiver.getServicesHTML(configurationContext);
    // PrintWriter pw = new PrintWriter(response.getOutputStream());
    // pw.write(s);
    // pw.flush();
    // }
    // }
    // }

    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);
                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);
                }

           }

        }
    }

    private void processListWSDL(final IWSRequest request, final IWSResponse response) 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.portMetaData.getWSDLLocation() != null) {

            inStream = this.endpointClass.getClassLoader().getResourceAsStream(this.portMetaData.getWSDLLocation());

            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, "GB2312");
            properties.setProperty(OutputKeys.METHOD, "xml");
            transformer.setOutputProperties(properties);

            DOMSource source = new DOMSource(doc);

            StreamResult result = new StreamResult(outStream);

            transformer.transform(source, result);
        } else {
            
            OutputStream outStream = response.getOutputStream();

            Axis2Java2WSDLBuilder builder = new Axis2Java2WSDLBuilder(epr, outStream, this.endpointClass.getName(), this.endpointClass.getClassLoader());

            builder.generateWSDL();

        }
    }

    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);
            }
        }
    }
}
