/**
 * 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: JOnASWSDLQueryHandler.java 17104 2009-04-10 15:54:11Z xiaoda $
 * --------------------------------------------------------------------------
 */

package org.ow2.jonas.ws.cxf.http;

import java.util.Collection;
import java.util.List;
import java.util.Map;

import javax.wsdl.Definition;
import javax.wsdl.Port;
import javax.wsdl.extensions.ExtensibilityElement;
import javax.wsdl.extensions.schema.SchemaReference;
import javax.xml.namespace.QName;

import org.apache.cxf.Bus;
import org.apache.cxf.service.model.EndpointInfo;
import org.apache.cxf.tools.common.extensions.soap.SoapAddress;
import org.apache.cxf.tools.util.SOAPBindingUtil;
import org.apache.cxf.transport.http.WSDLQueryHandler;
import org.ow2.jonas.ws.cxf.jaxws.WebservicesContainer;
import org.ow2.jonas.ws.jaxws.PortIdentifier;
import org.ow2.util.log.Log;
import org.ow2.util.log.LogFactory;

/**
 * This WSDLQuery handler will update the SOAP address of the WSDL by replacing
 * the existing address by the given URL.
 * @author Florent Benoit
 */
public class JOnASWSDLQueryHandler extends WSDLQueryHandler {

    /**
     * Logger.
     */
    private static Log logger = LogFactory.getLog(JOnASWSDLQueryHandler.class);

    /**
     * URI of the WSDL port binding.
     */
    private String portBindingURI = null;

    /**
     * Principal target.
     */
    private EndpointInfo endpointInfo;

    /**
     * Contains URL info about web services in the same group.
     */
    private WebservicesContainer container;

    /**
     * Build a new WSDL Query Handler that will update the port binding with the
     * given URL.
     * @param bus the bus used by this handler.
     * @param portBindingURI the URI of the port binding
     */
    public JOnASWSDLQueryHandler(final Bus bus, final String portBindingURI) {
        super(bus);
        this.portBindingURI = portBindingURI;
    }

    public JOnASWSDLQueryHandler(final Bus bus, final String url, final EndpointInfo ei, final WebservicesContainer container) {
        this(bus, url);
        this.endpointInfo = ei;
        this.container = container;
    }

    /**
     * Overrides the super method by using the definition object and updating
     * the SOAP address.
     * @param definition the definition used to get the WSDL service
     * @param done the map between WSDL and definition objects.
     * @param doneSchemas the map between XSD and SchemaReference objects
     * @param base the base URI
     * @param endpointInfo the endpoint info
     */
    @Override
    @SuppressWarnings("unchecked")
    protected void updateDefinition(final Definition definition,
                                    final Map<String, Definition> done,
                                    final Map<String, SchemaReference> doneSchemas,
                                    final String base,
                                    final EndpointInfo endpointInfo) {

        // This code is from OW Celtix ServletServerTransport class (that was
        // updating the address of the Port binding)
        // And this is not done anymore in CXF
        QName serviceName = endpointInfo.getService().getName();
        javax.wsdl.Service serv = definition.getService(serviceName);
        if (serv != null) {
            Collection<Port> ports = serv.getPorts().values();
            for (Port port : ports) {
                List<?> exts = port.getExtensibilityElements();
                if (exts != null && exts.size() > 0) {
                    ExtensibilityElement el = (ExtensibilityElement) exts.get(0);
                    if (SOAPBindingUtil.isSOAPAddress(el)) {

                        // Get the soap:address element
                        SoapAddress add = SOAPBindingUtil.getSoapAddress(el);

                        // If this port is the main target
                        if (isPortMatching(endpointInfo, serviceName, port.getName())) {
                            add.setLocationURI(this.portBindingURI);
                            logger.debug("Updated ''{0}'' location to ''{1}''", port, portBindingURI);
                        } else {
                            // If this is a port of the group
                            String url = container.findUpdatedURL(new PortIdentifier(serviceName, port.getName()));
                            if (url != null) {
                                add.setLocationURI(url);
                                logger.debug("Updated ''{0}'' location to ''{1}''", port, url);
                            } else {
                                // Override the value
                                add.setLocationURI(this.portBindingURI);
                                logger.debug("Updated ''{0}'' location to ''{1}''", port, portBindingURI);
                            }
                        }


                    }
                }
            }
        }

        super.updateDefinition(definition, done, doneSchemas, base, endpointInfo);

    }

    /**
     * Check if the given service and port name are matching the given endpoint info.
     * @param info CXF endpoint info
     * @param serviceName wsdl:service name
     * @param name wsdl:port name
     * @return true if the endpoint info match the names
     */
    private boolean isPortMatching(final EndpointInfo info, final QName serviceName, final String name) {

        // Return true if service name and port names are both matching
        return (serviceName.equals(info.getService().getName())
                && name.equals(info.getName().getLocalPart()));
    }

}
