Package org.glassfish.webservices

Source Code of org.glassfish.webservices.WebServicesDeployer

/*
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
*
* Copyright (c) 2006-2013 Oracle and/or its affiliates. All rights reserved.
*
* The contents of this file are subject to the terms of either the GNU
* General Public License Version 2 only ("GPL") or the Common Development
* and Distribution License("CDDL") (collectively, the "License").  You
* may not use this file except in compliance with the License.  You can
* obtain a copy of the License at
* https://glassfish.dev.java.net/public/CDDL+GPL_1_1.html
* or packager/legal/LICENSE.txt.  See the License for the specific
* language governing permissions and limitations under the License.
*
* When distributing the software, include this License Header Notice in each
* file and include the License file at packager/legal/LICENSE.txt.
*
* GPL Classpath Exception:
* Oracle designates this particular file as subject to the "Classpath"
* exception as provided by Oracle in the GPL Version 2 section of the License
* file that accompanied this code.
*
* Modifications:
* If applicable, add the following below the License Header, with the fields
* enclosed by brackets [] replaced by your own identifying information:
* "Portions Copyright [year] [name of copyright owner]"
*
* Contributor(s):
* If you wish your version of this file to be governed by only the CDDL or
* only the GPL Version 2, indicate your decision by adding "[Contributor]
* elects to include this software in this distribution under the [CDDL or GPL
* Version 2] license."  If you don't indicate a single choice of license, a
* recipient has the option to distribute your version of this file under
* either the CDDL, the GPL Version 2 or to extend the choice of license to
* its licensees as provided above.  However, if you add GPL Version 2 code
* and therefore, elected the GPL Version 2 license, then the option applies
* only if the new code is made subject to such option by the copyright
* holder.
*/

package org.glassfish.webservices;


import com.sun.enterprise.deployment.*;
import com.sun.enterprise.deployment.util.DOLUtils;
import com.sun.enterprise.deployment.archivist.Archivist;
import org.glassfish.api.deployment.archive.ArchiveType;
import com.sun.enterprise.deployment.web.AppListenerDescriptor;
import com.sun.enterprise.util.io.FileUtils;
import com.sun.enterprise.deploy.shared.FileArchive;
import com.sun.enterprise.deploy.shared.ArchiveFactory;
import com.sun.tools.ws.util.xml.XmlUtil;
import org.glassfish.api.deployment.UndeployCommandParameters;
import org.glassfish.loader.util.ASClassLoaderUtil;
import org.glassfish.api.deployment.DeploymentContext;
import org.glassfish.api.deployment.MetaData;
import org.glassfish.api.deployment.archive.ReadableArchive;
import org.glassfish.api.deployment.archive.WritableArchive;
import org.glassfish.api.container.RequestDispatcher;
import org.glassfish.deployment.common.DeploymentException;
import org.glassfish.web.deployment.descriptor.AppListenerDescriptorImpl;
import org.glassfish.web.deployment.util.WebServerInfo;
import org.glassfish.webservices.deployment.WebServicesDeploymentMBean;
import org.glassfish.javaee.core.deployment.JavaEEDeployer;
import org.glassfish.internal.api.JAXRPCCodeGenFacade;
import org.jvnet.hk2.annotations.Service;
import javax.inject.Inject;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import org.w3c.dom.NodeList;
import org.xml.sax.SAXException;
import org.xml.sax.SAXParseException;

import javax.xml.parsers.DocumentBuilder;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import javax.servlet.SingleThreadModel;
import java.io.*;
import java.net.*;
import java.nio.channels.FileChannel;
import java.security.AccessController;
import java.security.PrivilegedAction;
import java.text.MessageFormat;
import java.util.*;
import java.util.logging.Level;
import java.util.logging.Logger;


/**
* Webservices module deployer. This is loaded from WebservicesContainer
*
* @author Bhakti Mehta
* @author Rama Pulavarthi
*/
@Service
public class WebServicesDeployer extends JavaEEDeployer<WebServicesContainer,WebServicesApplication> {
    public static final WebServiceDeploymentNotifier deploymentNotifier =
            new WebServiceDeploymentNotifierImpl();

    public static WebServiceDeploymentNotifier getDeploymentNotifier() {
        return deploymentNotifier;
    }

    private static final Logger logger = LogUtils.getLogger();

    private ResourceBundle rb = logger.getResourceBundle();

    @Inject
    private RequestDispatcher dispatcher;

    @Inject
    private ArchiveFactory archiveFactory;

    /**
     * Constructor
     */
    public WebServicesDeployer() {
    }


    protected void cleanArtifacts(DeploymentContext deploymentContext) throws DeploymentException {

    }

  

    /**
     * Prepares the application bits for running in the application server.
     * For certain cases, this is exploding the jar file to a format the
     * ContractProvider instance is expecting, generating non portable
     * artifacts and other application specific tasks.
     * Failure to prepare should throw an exception which will cause the overall
     * deployment to fail.
     *
     * @param dc deployment context
     * @return true if the prepare phase was successful
     *
     */
    @Override
    public boolean prepare(DeploymentContext dc) {
        try {

            Application app = dc.getModuleMetaData(Application.class);
            if (app==null) {
                // hopefully the DOL gave a good message of the failure...
                logger.log(Level.SEVERE, LogUtils.FAILED_LOADING_DD);
                return false;
            }
            BundleDescriptor bundle = DOLUtils.getCurrentBundleForContext(dc);

            String moduleCP = getModuleClassPath(dc);
            final List<URL> moduleCPUrls = ASClassLoaderUtil.getURLsFromClasspath(moduleCP, File.pathSeparator, null);
            final ClassLoader oldCl = Thread.currentThread().getContextClassLoader();
            URLClassLoader newCl = AccessController.doPrivileged(new PrivilegedAction<URLClassLoader>() {
                @Override
                public URLClassLoader run() {
                    return new URLClassLoader(ASClassLoaderUtil.convertURLListToArray(moduleCPUrls), oldCl);
                }
            });


            Thread.currentThread().setContextClassLoader(newCl);
            WebServicesDescriptor wsDesc = bundle.getWebServices();
            for (WebService ws : wsDesc.getWebServices()) {
                if ((new WsUtil()).isJAXWSbasedService(ws)){
                    setupJaxWSServiceForDeployment(dc, ws);
                } else {
                    JAXRPCCodeGenFacade facade= habitat.getService(JAXRPCCodeGenFacade.class);
                    if (facade != null) {
                        facade.run(habitat, dc, moduleCP, false);
                    else {
                        throw new DeploymentException(rb.getString(LogUtils.JAXRPC_CODEGEN_FAIL)) ;
                    }
                }
            }
            doWebServicesDeployment(app,dc);
            Thread.currentThread().setContextClassLoader(oldCl);
            WebServicesContainer container = habitat.getService(WebServicesContainer.class);
            WebServicesDeploymentMBean bean = container.getDeploymentBean();
            WebServiceDeploymentNotifier notifier = getDeploymentNotifier();
            bean.deploy(wsDesc,notifier);
            return true;
        } catch (Exception ex) {
            RuntimeException re = new RuntimeException(ex.getMessage());
            re.initCause(ex);
            throw re;
        }
    }


    protected void setupJaxWSServiceForDeployment(DeploymentContext dc, WebService ws) throws DeploymentException {

        BundleDescriptor bundle = dc.getModuleMetaData(BundleDescriptor.class);

        // for modules this is domains/<domain-name>/j2ee-modules/<module-name>
        // for apps this is domains/<domain-name>/j2ee-apps/<app-name>/<foo_war> (in case of embedded wars)
        //  or domains/<domain-name>/j2ee-apps/<app-name>/<foo_jar> (in case of embedded jars)
        File moduleDir = dc.getSourceDir();

        //For modules this is domains/<domain-name>/generated/xml
        //Check with Hong about j2ee-modules
        File wsdlDir = dc.getScratchDir("xml");
        mkDirs(wsdlDir);


        //For modules this is domains/<domain-name>/generated/xml
        //Check with Hong about j2ee-modules
        File stubsDir = dc.getScratchDir("ejb");
        mkDirs(stubsDir);


        if (!DOLUtils.warType().equals(bundle.getModuleType()) &&
                !DOLUtils.ejbType().equals(bundle.getModuleType())) {
            // unknown module type with @WebService, just ignore...
            return;
        }


        wsdlDir = new File(wsdlDir, bundle.getWsdlDir().replaceAll("/", "\\"+File.separator));

        // Check if catalog file is present, if so get mapped WSDLs
        String wsdlFileUri;
        File wsdlFile;
        try {
            checkCatalog(bundle, ws, moduleDir);
        } catch (DeploymentException e) {
            logger.log(Level.SEVERE, LogUtils.ERROR_RESOLVING_CATALOG);
        }
        if (ws.hasWsdlFile()) {
            // If wsdl file is an http URL, download that WSDL and all embedded relative wsdls, schemas
            if (ws.getWsdlFileUri().startsWith("http")) {
                try {
                    wsdlFileUri = downloadWsdlsAndSchemas( new URL(ws.getWsdlFileUri()), wsdlDir);
                } catch(Exception e) {
                    throw new DeploymentException(e.toString(), e);
                }
                wsdlFile = new File(wsdlDir, wsdlFileUri);
            } else {
                wsdlFileUri = ws.getWsdlFileUri();
                File wsdlFileAbs = new File(wsdlFileUri);
                wsdlFile = wsdlFileAbs.isAbsolute()? wsdlFileAbs : new File(moduleDir, wsdlFileUri);
            }

            if (!wsdlFile.exists()) {
                String errorMessage = format(logger.getResourceBundle().getString(LogUtils.WSDL_NOT_FOUND),
                        ws.getWsdlFileUri(), bundle.getModuleDescriptor().getArchiveUri());
                logger.log(Level.SEVERE, errorMessage);
                throw new DeploymentException(errorMessage);
            }
        }
    }

    /**
     * Loads the meta date associated with the application.
     *
     * @parameters type type of metadata that this deployer has declared providing.
     */
    @Override
    public Object loadMetaData(Class type, DeploymentContext dc) {
         //Moved the doWebServicesDeployment back to prepare after discussing with
         //Jerome
         //see this bug  https://glassfish.dev.java.net/issues/show_bug.cgi?id=8080
         return true;
    }

    /**
     * Returns the meta data assocated with this Deployer
     *
     * @return the meta data for this Deployer
     */
    @Override
    public MetaData getMetaData() {
        return new MetaData(false, null, new Class[] {Application.class});
    }

    /**
     *  This method downloads the main wsdl/schema and its imports in to the directory specified and returns the name of downloaded root
     * document.
     * @param httpUrl
     * @param wsdlDir
     * @return Returns the name of the root file downloaded with the invocation.
     * @throws Exception
     */
    private String downloadWsdlsAndSchemas( URL httpUrl, File wsdlDir) throws Exception {
        // First make required directories and download this wsdl file
        mkDirs(wsdlDir);
        String fileName = httpUrl.toString().substring(httpUrl.toString().lastIndexOf("/")+1);
        File toFile = new File(wsdlDir.getAbsolutePath()+File.separator+fileName);
        downloadFile(httpUrl, toFile);

        // Get a list of wsdl and schema relative imports in this wsdl
        HashSet<Import> wsdlRelativeImports = new HashSet<Import>();
        HashSet<Import> schemaRelativeImports = new HashSet<Import>();
        HashSet<Import> wsdlIncludes = new HashSet<Import>();
        HashSet<Import> schemaIncludes = new HashSet<Import>();
        parseRelativeImports(httpUrl, wsdlRelativeImports, wsdlIncludes,
                schemaRelativeImports, schemaIncludes);
        wsdlRelativeImports.addAll(wsdlIncludes);
        schemaRelativeImports.addAll(schemaIncludes);

        // Download all schema relative imports
        String urlWithoutFileName = httpUrl.toString().substring(0, httpUrl.toString().lastIndexOf("/"));
        for(Import next : schemaRelativeImports) {
            String location = next.getLocation();
            location = location.replaceAll("/", "\\"+File.separator);
            if(location.lastIndexOf(File.separator) != -1) {
                File newDir = new File(wsdlDir.getAbsolutePath()+File.separator+
                location.substring(0, location.lastIndexOf(File.separator)));
                mkDirs(newDir);
            }
            downloadFile(new URL(urlWithoutFileName+"/"+next.getLocation()),
                        new File(wsdlDir.getAbsolutePath()+File.separator+location));
        }

        // Download all wsdl relative imports
        for(Import next : wsdlRelativeImports) {
            String newWsdlLocation = next.getLocation();
            newWsdlLocation = newWsdlLocation.replaceAll("/""\\"+File.separator);
            File newWsdlDir;
            if(newWsdlLocation.lastIndexOf(File.separator) != -1) {
                newWsdlDir = new File(wsdlDir.getAbsolutePath() + File.separator +
                newWsdlLocation.substring(0, newWsdlLocation.lastIndexOf(File.separator)));
            } else {
                newWsdlDir = wsdlDir;
            }
            downloadWsdlsAndSchemas( new URL(urlWithoutFileName+"/"+next.getLocation()), newWsdlDir);
        }
        return fileName;
    }
    // If catalog file is present, get the mapped WSDL for given WSDL and replace the value in
    // the given WebService object
    private void checkCatalog(BundleDescriptor bundle, WebService ws, File moduleDir)
                            throws DeploymentException {
        // If no catalog file is present, return
        File catalogFile = new File(moduleDir,
                bundle.getDeploymentDescriptorDir() +
                File.separator + "jax-ws-catalog.xml");
        if(!catalogFile.exists()) {
            return;
        }
        resolveCatalog(catalogFile, ws.getWsdlFileUri(), ws);
    }

    public URL resolveCatalog(File catalogFile, String wsdlFile, WebService ws) throws DeploymentException {

        try {


           URL retVal = null;
            // Get an entity resolver
            org.xml.sax.EntityResolver resolver =
                    XmlUtil.createEntityResolver(catalogFile.toURL());
            org.xml.sax.InputSource source = resolver.resolveEntity(null, wsdlFile);
            if(source != null) {
                String mappedEntry = source.getSystemId();
                // For entries with relative paths, Entity resolver always
                // return file://<absolute path
                if(mappedEntry.startsWith("file:")) {
                    File f = new File(mappedEntry.substring(mappedEntry.indexOf(":")+1));
                    if(!f.exists()) {
                        throw new DeploymentException(format(rb.getString(LogUtils.CATALOG_RESOLVER_ERROR), mappedEntry));
                    }
                    retVal = f.toURI().toURL();
                    if(ws != null) {
                        ws.setWsdlFileUri(f.getAbsolutePath());
                        ws.setWsdlFileUrl(retVal);
                    }
                } else if(mappedEntry.startsWith("http")) {
                    retVal = new URL(mappedEntry);
                    if(ws != null) {
                        ws.setWsdlFileUrl(retVal);
                    }
                }
            }
            return retVal;

        } catch (Throwable t) {
            throw new DeploymentException(format(rb.getString(LogUtils.CATALOG_ERROR),
                     catalogFile.getAbsolutePath(), t.getMessage()));
        }
     
    }

    /**
     * Copies file from source to destination
     *
     * @param src
     * @param dest
     * @throws IOException
     */
    private static void copyFile(File src, File dest) throws IOException {
        if (!dest.exists()) {
            mkDirs(dest.getParentFile());
            mkFile(dest);
        }

        FileChannel srcChannel = null;
        FileChannel destChannel = null;
        try {
            srcChannel = new FileInputStream(src).getChannel();
            destChannel = new FileOutputStream(dest).getChannel();
            destChannel.transferFrom(srcChannel, 0, srcChannel.size());
        }
        finally {
            if (srcChannel != null) {
                srcChannel.close();
            }
            if (destChannel != null) {
                destChannel.close();
            }
        }
    }

    public void downloadFile(URL httpUrl, File toFile) throws Exception {
        InputStream is = null;
        FileOutputStream os = null;
        try {
            if(!toFile.createNewFile()) {
                throw new Exception(format(rb.getString(LogUtils.FILECREATION_ERROR), toFile.getAbsolutePath()));
            }
            is = httpUrl.openStream();

            os = new FileOutputStream(toFile, true);
            int readCount;
            byte[] buffer = new byte[10240]; // Read 10KB at a time
            while(true) {
                readCount = is.read(buffer, 0, 10240);
                if(readCount != -1) {
                    os.write(buffer, 0, readCount);
                } else {
                    break;
                }
            }
            os.flush();
        } finally {
            try {
                if (is != null) {
                    is.close();
                }
            } finally {
                if (os != null) {
                    os.close();
                }
            }
        }
    }

    /**
     * Collect all relative imports from a web service's main wsdl document.
     *
     * @param wsdlFileUrl
     * @param wsdlRelativeImports output param in which wsdl relative imports
     * will be added
     *
     * @param schemaRelativeImports output param in which schema relative
     * imports will be added
     * @param schemaIncludes output param in which schema includes will be added
     */
    private void parseRelativeImports(URL wsdlFileUrl,
                                      Collection wsdlRelativeImports,
                                      Collection wsdlIncludes,
                                      Collection schemaRelativeImports,
                                       Collection schemaIncludes)
        throws Exception {

        // We will use our little parser rather than using JAXRPC's heavy weight WSDL parser
        DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
        factory.setNamespaceAware(true);
        //Validation is not needed as we want to be too strict in processing wsdls that are generated by buggy tools.
        factory.setExpandEntityReferences(false);
        factory.setFeature("http://apache.org/xml/features/disallow-doctype-decl", true);
        InputStream is = null;
        try {
            DocumentBuilder builder = factory.newDocumentBuilder();
            is = wsdlFileUrl.openStream();
            Document document = builder.parse(is);
            procesSchemaImports(document, schemaRelativeImports);
            procesWsdlImports(document, wsdlRelativeImports);
            procesSchemaIncludes(document, schemaIncludes);
            procesWsdlIncludes(document, wsdlIncludes);
        } catch (SAXParseException spe) {
            // Error generated by the parser
            logger.log(Level.SEVERE, LogUtils.PARSING_ERROR,
                    new Object[] {spe.getLineNumber() ,spe.getSystemId()});
            // Use the contained exception, if any
            Exception x = spe;
            if (spe.getException() != null) {
                x = spe.getException();
            }
            logger.log(Level.SEVERE, LogUtils.ERROR_OCCURED, x);
        } catch (Exception sxe) {
            logger.log(Level.SEVERE, LogUtils.WSDL_PARSING_ERROR, sxe.getMessage());
        } finally {
            try {
                if(is != null) {
                    is.close();
                }
            } catch (IOException io) {
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, LogUtils.EXCEPTION_THROWN, io.getMessage());
                }
            }
        }
    }

    private void procesSchemaImports(Document document, Collection schemaImportCollection) throws SAXException,
            ParserConfigurationException, IOException {
        NodeList schemaImports =
                document.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "import");
        addImportsAndIncludes(schemaImports, schemaImportCollection, "namespace", "schemaLocation");
    }

    private void procesWsdlImports(Document document, Collection wsdlImportCollection) throws SAXException,
            ParserConfigurationException, IOException {
        NodeList wsdlImports =
                document.getElementsByTagNameNS("http://schemas.xmlsoap.org/wsdl/", "import");
        addImportsAndIncludes(wsdlImports, wsdlImportCollection, "namespace", "location");
    }

    private void procesSchemaIncludes(Document document, Collection schemaIncludeCollection) throws SAXException,
            ParserConfigurationException, IOException {
        NodeList schemaIncludes =
                document.getElementsByTagNameNS("http://www.w3.org/2001/XMLSchema", "include");
        addImportsAndIncludes(schemaIncludes, schemaIncludeCollection, null, "schemaLocation");
    }

    private void procesWsdlIncludes(Document document, Collection wsdlIncludesCollection) throws SAXException,
            ParserConfigurationException, IOException {
        NodeList wsdlIncludes =
                document.getElementsByTagNameNS("http://schemas.xmlsoap.org/wsdl/", "include");
        addImportsAndIncludes(wsdlIncludes, wsdlIncludesCollection, null, "location");
    }

    private void addImportsAndIncludes(NodeList list, Collection result,
                        String namespace, String location) throws SAXException,
                        ParserConfigurationException, IOException {
        for(int i=0; i<list.getLength(); i++) {
            String givenLocation = null;
            Node element = list.item(i);
            NamedNodeMap attrs = element.getAttributes();
            Node n= attrs.getNamedItem(location);
            if(n != null) {
                givenLocation = n.getNodeValue();
            }
            if(givenLocation == null || givenLocation.startsWith("http")) {
                continue;
            }
            Import imp = new Import();
            imp.setLocation(givenLocation);
            if(namespace != null) {
                n = attrs.getNamedItem(namespace);
                if(n != null) {
                    imp.setNamespace(n.getNodeValue());
                }
            }
            result.add(imp);
        }

    }

    /**
     * Processes all the web services in the module and prepares for deployment.
     * The tasks include composing the endpoint publish url and generating WSDL in to the application repository
     * directory.
     *
     * In JAX-WS, WSDL files are generated dynamically, hence skips the wsdl generation step unless explicitly requested
     * for WSDL file publishing via DD.
     *
     * @param app
     * @param dc
     * @throws Exception
     */
    private void doWebServicesDeployment(Application app, DeploymentContext dc)
        throws Exception{

        Collection<WebService> webServices = new HashSet<WebService>();

        // when there are multiple sub modules in ear, we only want to handle the ones local to this deployment context

        //First get the web services associated with module bundle descriptor.
        WebServicesDescriptor wsDesc = dc.getModuleMetaData(WebServicesDescriptor.class);
        if (wsDesc != null && wsDesc.getWebServices().size() > 0) {
            if (logger.isLoggable(Level.FINE)) {
                logger.log(Level.FINE, LogUtils.WS_LOCAL,
                        new Object[] {wsDesc.getWebServices().size(), getWebServiceDescriptors(app).size()});
            }
            webServices.addAll(wsDesc.getWebServices());
        }
        //Now get the web services associated with extension descriptors,ex: EJB WS in war.
        WebBundleDescriptor webBundleDesc = dc.getModuleMetaData(WebBundleDescriptor.class);
        if (webBundleDesc != null) {
            Collection<EjbBundleDescriptor> ejbBundleDescriptors = webBundleDesc.getExtensionsDescriptors(EjbBundleDescriptor.class);
            for (EjbBundleDescriptor ejbBundleDescriptor : ejbBundleDescriptors) {
                Collection wsInExtnDesc = ejbBundleDescriptor.getWebServices().getWebServices();
                webServices.addAll(wsInExtnDesc);
                if (logger.isLoggable(Level.FINE)) {
                    logger.log(Level.FINE, LogUtils.WS_VIA_EXT, wsInExtnDesc);
                }
            }
        }

        // swap the deployment descriptors context-root with the one
        // provided in the deployment request.
        //do not do for ejb in war case
        if (webBundleDesc!= null && webBundleDesc.getExtensionsDescriptors(EjbBundleDescriptor.class).size()==0) {
            if (dc.getAppProps().get("context-root") != null &&
                    app.isVirtual() ) {

                String contextRoot = ((String)dc.getAppProps().get("context-root"));
                webBundleDesc.setContextRoot(contextRoot);
            }
        }

        // Generate final wsdls for all web services and store them in
        // the application repository directory.
        for(WebService next : webServices) {
            WsUtil wsUtil = new WsUtil();

            // For JAXWS services, we rely on JAXWS RI to do WSL gen and publishing
            // For JAXRPC we do it here in 109
            //however it is needed for file publishing for jaxws
            if(wsUtil.isJAXWSbasedService(next) && (!next.hasFilePublishing())) {
                for(WebServiceEndpoint wsep : next.getEndpoints()) {
                    URL finalWsdlURL = wsep.composeFinalWsdlUrl(wsUtil.getWebServerInfoForDAS().getWebServerRootURL(wsep.isSecure()));
                    Set<ServiceReferenceDescriptor> serviceRefs = new HashSet<ServiceReferenceDescriptor>();
                    if (webBundleDesc != null) {
                        serviceRefs = webBundleDesc.getServiceReferenceDescriptors();
                    } else {
                        EjbBundleDescriptor ejbBundleDesc = dc.getModuleMetaData(EjbBundleDescriptor.class);
                        if (ejbBundleDesc != null) {
                            serviceRefs = ejbBundleDesc.getEjbServiceReferenceDescriptors();
                        } else {
                            logger.log(Level.SEVERE, LogUtils.UNSUPPORTED_MODULE_TYPE,
                                    DOLUtils.getCurrentBundleForContext(dc).getModuleType());
                        }
                    }
                    //if there's service-ref to this service update its
                    // wsdl-file value to point to the just exposed URL
                    for (ServiceReferenceDescriptor srd : serviceRefs) {
                        if (srd.getServiceName().equals(wsep.getServiceName())
                                && srd.getServiceNamespaceUri().equals(wsep.getWsdlService().getNamespaceURI())) {
                            srd.setWsdlFileUri(finalWsdlURL.toExternalForm() + "?WSDL");
                        }
                    }
                }
            } else {

                // Even if deployer specified a wsdl file
                // publish location, server can't assume it can access that
                // file system.  Plus, it's cleaner to depend on a file stored
                // within the application repository rather than one directly
                // exposed to the deployer. Name of final wsdl is derived based
                // on the location of its associated module.  This prevents us
                // from having write the module to disk in order to store the
                // modified runtime info.
                URL url = next.getWsdlFileUrl();
                if (url == null ) {
                    File f = new File(dc.getSourceDir(),next.getWsdlFileUri()) ;
                    url = f.toURL();
                }

                // Create the generated WSDL in the generated directory; for that create the directories first
                File genXmlDir =  dc.getScratchDir("xml");

                String wsdlFileDir = next.getWsdlFileUri().substring(0, next.getWsdlFileUri().lastIndexOf('/'));
                mkDirs(new File(genXmlDir, wsdlFileDir));
                File genWsdlFile = new File(genXmlDir, next.getWsdlFileUri());
                wsUtil.generateFinalWsdl(url, next, wsUtil.getWebServerInfoForDAS(), genWsdlFile);
            }
        }
        //Swap the servlet class name with a real servlet processing the SOAP requests.
        if (webBundleDesc != null) {
            doWebServiceDeployment(webBundleDesc);
        }
    }

    /**
     * Prepares the servlet based web services specified in web.xml for deployment.
     *
     * Swap the application written servlet implementation class for
     * one provided by the container.  The original class is stored
     * as runtime information since it will be used as the servant at
     * dispatch time.
     */
    private void doWebServiceDeployment(WebBundleDescriptor webBunDesc)
        throws DeploymentException, MalformedURLException {

        /**
         * Combining code from <code>com.sun.enterprise.deployment.backend.WebServiceDeployer</code>
         * in v2
         */

        Collection<WebServiceEndpoint> endpoints =
            webBunDesc.getWebServices().getEndpoints();
        ClassLoader cl = webBunDesc.getClassLoader();
        WsUtil wsutil = new WsUtil();

        for(WebServiceEndpoint nextEndpoint : endpoints) {
            WebComponentDescriptor webComp = nextEndpoint.getWebComponentImpl();

            if( !nextEndpoint.hasServletImplClass() ) {
                throw new DeploymentException( format(rb.getString(
                        LogUtils.DEPLOYMENT_BACKEND_CANNOT_FIND_SERVLET),
                        nextEndpoint.getEndpointName()));
            }

            if (nextEndpoint.hasEndpointAddressUri()) {
                webComp.getUrlPatternsSet().clear();
                webComp.addUrlPattern(nextEndpoint.getEndpointAddressUri());
            }

            if( !nextEndpoint.getWebService().hasFilePublishing() ) {
                String publishingUri = nextEndpoint.getPublishingUri();
                String publishingUrlPattern =
                        (publishingUri.charAt(0) == '/') ?publishingUri : "/" + publishingUri + "/*";
                webComp.addUrlPattern(publishingUrlPattern);

            }

            String servletImplClass = nextEndpoint.getServletImplClass();
            try {
                Class servletImplClazz  = cl.loadClass(servletImplClass);
                String containerServlet;
                if(wsutil.isJAXWSbasedService(nextEndpoint.getWebService())) {
                    containerServlet = "org.glassfish.webservices.JAXWSServlet";
                    addWSServletContextListener(webBunDesc);
                } else {
                    containerServlet =
                    SingleThreadModel.class.isAssignableFrom(servletImplClazz) ?
                    "org.glassfish.webservices.SingleThreadJAXRPCServlet" :
                        "org.glassfish.webservices.JAXRPCServlet";
                }
                webComp.setWebComponentImplementation(containerServlet);

            } catch(ClassNotFoundException cex) {
                throw new DeploymentException( format(rb.getString(
                        LogUtils.DEPLOYMENT_BACKEND_CANNOT_FIND_SERVLET),
                        nextEndpoint.getEndpointName()));
            }

            /**
             * Now trying to figure the address from <code>com.sun.enterprise.webservice.WsUtil.java</code>
             */
            // Get a URL for the root of the webserver, where the host portion
            // is a canonical host name.  Since this will be used to compose the
            // endpoint address that is written into WSDL, it's better to use
            // hostname as opposed to IP address.
            // The protocol and port will be based on whether the endpoint
            // has a transport guarantee of INTEGRAL or CONFIDENTIAL.
            // If yes, https will be used.  Otherwise, http will be used.
            WebServerInfo wsi = new WsUtil().getWebServerInfoForDAS();
            URL rootURL = wsi.getWebServerRootURL(nextEndpoint.isSecure());
            String contextRoot = webBunDesc.getContextRoot();
            URL actualAddress = nextEndpoint.composeEndpointAddress(rootURL, contextRoot);
            if (wsi.getHttpVS() != null && wsi.getHttpVS().getPort()!=0) {
               logger.log(Level.INFO, LogUtils.ENDPOINT_REGISTRATION,
                       new Object[] {nextEndpoint.getEndpointName(), actualAddress});
            }
        }
    }

    private void addWSServletContextListener(WebBundleDescriptor webBunDesc) {
        for(AppListenerDescriptor appListner: webBunDesc.getAppListenerDescriptors()) {
            if(appListner.getListener().equals(WSServletContextListener.class.getName())) {
                //already registered
                return;
            }
        }
        webBunDesc.addAppListenerDescriptor(new AppListenerDescriptorImpl(WSServletContextListener.class.getName()));
    }
   
    private String format(String key, String ... values){
        return MessageFormat.format(key, (Object [])values);
    }

    public static void moveFile(String sourceFile, String destFile)
    throws IOException {
        FileUtils.copy(sourceFile, destFile);
        FileUtils.deleteFile(new File(sourceFile));
    }

    @Override
    public void unload(WebServicesApplication container, DeploymentContext context) {
        final WebServiceDeploymentNotifier notifier = getDeploymentNotifier();
        deletePublishedFiles(container.getPublishedFiles());
        Application app = container.getApplication();
        if ( app == null ) {
            // load uses context.getModuleMetaData(Application.class) to get the Application.  If there's a deployment
            // failure then "container" may not have initialized the application and container.getApplication() returns
            // null and we get NPE.  So use context.getModuleMetaData(Application.class) always.
            app = context.getModuleMetaData(Application.class);
        }
        if ( app != null ) {
            for(WebService svc : getWebServiceDescriptors(app)) {
                for(WebServiceEndpoint endpoint : svc.getEndpoints()) {
                    if (notifier != null) {
                        notifier.notifyUndeployed(endpoint);
                    }
                }
            }
        }
    }

    @Override
    public void clean(DeploymentContext dc) {
        super.clean(dc);

        WebServicesContainer container = habitat.getService(WebServicesContainer.class);
        WebServicesDeploymentMBean bean = container.getDeploymentBean();
        UndeployCommandParameters params = dc.getCommandParameters(UndeployCommandParameters.class);
        if (params != null)  {
            bean.undeploy(params.name);
        }
    }

    @Override
    public WebServicesApplication load(WebServicesContainer container, DeploymentContext context) {
        Set<String> publishedFiles = null;
        Application app = context.getModuleMetaData(Application.class);
        try {
            publishedFiles = populateWsdlFilesForPublish(context, getWebServiceDescriptors(app));
        } catch(Exception e) {
            throw new RuntimeException(e);
        }


        return new WebServicesApplication(context,  dispatcher, publishedFiles);
    }

    /**
     * Populate the wsdl files entries to download (if any) (Only for webservices which
     * use file publishing).
     *
     * TODO File publishing currently works only for wsdls packaged in the application for jax-ws.
     * Need to publish the dynamically generated wsdls as well. Lazy creation of WSEndpoint objects prohibits it now.
     */
    private Set<String> populateWsdlFilesForPublish(
            DeploymentContext dc, Set<WebService> webservices) throws IOException {

        Set<String> publishedFiles = new HashSet<String>();
        for (WebService webService : webservices) {
            if (!webService.hasFilePublishing()) {
                continue;
            }

            copyExtraFilesToGeneratedFolder(dc);
            BundleDescriptor bundle = webService.getBundleDescriptor();

            ArchiveType moduleType = bundle.getModuleType();
            //only EAR, WAR and EJB archives could contain wsdl files for publish
            if (moduleType==null || !(moduleType.equals(DOLUtils.earType()) ||
                    moduleType.equals(DOLUtils.warType()) ||
                    moduleType.equals(DOLUtils.ejbType()))) {
                return publishedFiles;
            }


            File sourceDir = dc.getScratchDir("xml");

            File parent;
            try {
                URI clientPublishURI = webService.getClientPublishUrl().toURI();
                if(!clientPublishURI.isOpaque()) {
                    parent = new File(clientPublishURI);
                } else {
                    parent =  new File(webService.getClientPublishUrl().getPath());
                }
            } catch (URISyntaxException e) {
                logger.log(Level.WARNING, LogUtils.EXCEPTION_THROWN, e);
                parent = new File(webService.getClientPublishUrl().getPath());
            }

            // Collect the names of all entries in or below the
            // dedicated wsdl directory.
            FileArchive archive = new FileArchive();
            archive.open(sourceDir.toURI());


            Enumeration entries = archive.entries(bundle.getWsdlDir());


            while (entries.hasMoreElements()) {
                String name = (String) entries.nextElement();
                String wsdlName = stripWsdlDir(name, bundle);
                File clientwsdl = new File(parent, wsdlName);
                File fulluriFile = new File(sourceDir, name);
                if (!fulluriFile.isDirectory()) {
                    publishFile(fulluriFile, clientwsdl);
                    publishedFiles.add(clientwsdl.getAbsolutePath());
                }
            }

        }
        return publishedFiles;
    }

    private void publishFile(File file, File publishLocation) throws IOException {
        copyFile(file, publishLocation);
    }

    private void deletePublishedFiles(Set<String> publishedFiles) {
        if (publishedFiles != null) {
            for (String path: publishedFiles) {
                File f = new File(path);
                if (f.exists()) {
                    FileUtils.deleteFile(f);
                }
            }
        }
    }
    /**
     * This is to be used for file publishing only. In case of wsdlImports and wsdlIncludes
     * we need to copy the nested wsdls from applications folder to the generated/xml folder
     *
     */
    private void copyExtraFilesToGeneratedFolder( DeploymentContext context) throws IOException{
        Archivist archivist = habitat.getService(Archivist.class);

        ReadableArchive archive = archiveFactory.openArchive(
                context.getSourceDir());

        WritableArchive archive2 = archiveFactory.createArchive(
                context.getScratchDir("xml"));

        // copy the additional webservice elements etc
        archivist.copyExtraElements(archive, archive2);
    }

    /**
     * Return the entry name without "WEB-INF/wsdl" or "META-INF/wsdl".
     */
    private String stripWsdlDir(String entry, BundleDescriptor bundle) {
        String wsdlDir = bundle.getWsdlDir();
        return entry.substring(wsdlDir.length()+1);
    }

    /**
     * Return a set of all com.sun.enterprise.deployment.WebService
     * descriptors in the application.
     */
    private Set<WebService> getWebServiceDescriptors(Application app) {
        Set<WebService> webServiceDescriptors = new HashSet<WebService>();
        for (BundleDescriptor next : app.getBundleDescriptors()) {
            WebServicesDescriptor webServicesDesc =
                    next.getWebServices();
            webServiceDescriptors.addAll(webServicesDesc.getWebServices());
        }
        return webServiceDescriptors;
    }

    private static void mkDirs(File f) {
        if (!f.mkdirs() && logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, LogUtils.DIR_EXISTS, f);
        }
    }

    private static void mkFile(File f) throws IOException {
        if (!f.createNewFile() && logger.isLoggable(Level.FINE)) {
            logger.log(Level.FINE, LogUtils.FILE_EXISTS, f);
        }
    }
}


TOP

Related Classes of org.glassfish.webservices.WebServicesDeployer

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.