FileDocCategorySizeDatePackage
NamingConventions.javaAPI DocGlassfish v2 API27004Fri May 04 22:34:12 BST 2007com.sun.enterprise.appclient.jws

NamingConventions.java

/*
 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
 * 
 * Copyright 1997-2007 Sun Microsystems, Inc. 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.html
 * or glassfish/bootstrap/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 glassfish/bootstrap/legal/LICENSE.txt.
 * Sun designates this particular file as subject to the "Classpath" exception
 * as provided by Sun in the GPL Version 2 section of the License file that
 * accompanied this code.  If applicable, add the following below the License
 * Header, with the fields enclosed by brackets [] replaced by your own
 * identifying information: "Portions Copyrighted [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 com.sun.enterprise.appclient.jws;
import com.sun.enterprise.config.ConfigException;
import com.sun.enterprise.deployment.Application;
import com.sun.enterprise.deployment.ApplicationClientDescriptor;
import com.sun.enterprise.deployment.BundleDescriptor;
import com.sun.enterprise.deployment.interfaces.DeploymentImplConstants;
import com.sun.enterprise.deployment.runtime.JavaWebStartAccessDescriptor;
import com.sun.enterprise.deployment.util.ModuleDescriptor;
import com.sun.enterprise.instance.AppclientModulesManager;
import com.sun.enterprise.instance.ApplicationEnvironment;
import com.sun.enterprise.instance.AppsManager;
import com.sun.enterprise.instance.BaseManager;
import com.sun.enterprise.instance.InstanceEnvironment;
import com.sun.enterprise.server.ApplicationServer;
import com.sun.enterprise.server.ServerContext;
import com.sun.enterprise.util.i18n.StringManager;
import com.sun.enterprise.web.WebContainer;
import com.sun.logging.LogDomains;
import java.io.File;
import java.io.FileFilter;
import java.io.FilenameFilter;
import java.net.URI;
import java.net.URISyntaxException;
import java.util.Iterator;
import java.util.Vector;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.enterprise.deploy.shared.ModuleType;
import javax.servlet.ServletConfig;
import javax.servlet.http.HttpServletRequest;

/**
 *Defines strings and algorithms used in constructing names, URLs, etc. related to supporting Java
 *Web Start access to app clients.
 *<p>
 *The logic for building names, URLs, etc. is collected into this one class
 *so it is easier to understand go to make changes.
 *
 * @author tjquinn
 */
public class NamingConventions {
    
    /** fixed prefix for URLs handled by the Java Web Start support web app */
    
    /*
     *The system EAR for Java Web Start specifies the URL for the embedded
     *web app.  That same value must be assigned here.
     */
    public static final String SYSTEM_WEBAPP_URL = "/__JWSappclients";
    
    /**
     *Performs any conversion needed from the HTTP request path information to 
     *the corresponding content key
     *@param the pathInfo from the HTTP request
     *@return the content key for the given request pathInfo
     */
    public static String pathToContentKey(String requestPath) {
        return requestPath;
    }
    
    /**
     *Converts the content key into the corresponding path for URLs.
     *@param content key to be converted
     *@return the path to be used in URLs
     */
    public static String contentKeyToPath(String contentKey) {
        return contentKey;
    }
    
    /**
     *URL paths used to request content have this form:
     *
     *${category}/${subcategory}/${path-to-content}
     *
     *The subcategory has different meanings depending on the category.
     *
     *Categories:
     *
     *appclient - Content related to a particular app client.  The subcategory
     *is the appclient's context-root (for Java Web Start access) assigned either
     *by the user in the runtime deployment descriptor or by default - the module ID
     *
     *application - Content related to the enterprise app in which one or more
     *app clients were bundled.  The subcategory is the enterprise app's module
     *ID. 
     *
     *appserver - Content related to the app server itself.  The subcategory 
     *is used to gather files and content into roughly related groups.  Partly it
     *exists to let the Java Web Start support logic treat all requests pretty
     *uniformly.
     *
     *appserverS - Content related to the app server itself, but signed (jars).
     *
     */
    
    /** Category definitions */
    public static final String APPCLIENT_CATEGORY = "__appclient";
    public static final String APPLICATION_CATEGORY = "__application";
    public static final String APPSERVER_CATEGORY = "__appserver";
    public static final String APPSERVER_SIGNED_CATEGORY = "__appserverS";
    
    /** Subcategories of appserver content. */

    public static final String APPSERVER_LIB_FILES = "aslib";   // typically in ${installRoot}/lib 
    public static final String APPSERVER_MQLIB_FILES = "mqlib"; // MQ files packaged with the app server - typically in ${installRoot}/imq/lib 
    public static final String APPSERVER_JMSRALIB_FILES = "jmsralib"; // JMS files packaged with the app server - in ${installRoot}/lib/install/applications/jmsra
    public static final String APPSERVER_EXTJAR_FILES = "extjar"; // extension jars
    public static final String APPSERVER_DERBY_FILES = "derby"; // derby library jar files
    /**
     *Templates for dynamic documents are packaged in the jar file with this class.
     *These names are used in getResource method invocations to retrieve the templates.
     */
    public static final String APPCLIENT_MAIN_JNLP_TEMPLATE_NAME = "appclientMainDocumentTemplate.jnlp";
    public static final String APPCLIENT_CLIENT_JNLP_TEMPLATE_NAME = "appclientClientDocumentTemplate.jnlp";
    public static final String APPCLIENT_MAIN_HTML_TEMPLATE_NAME = "appclientMainDocumentTemplate.html";
    public static final String APPCLIENT_CLIENT_HTML_TEMPLATE_NAME = "appclientClientDocumentTemplate.html";
    public static final String APPCLIENT_MAIN_JNLP_EXT_TEMPLATE_NAME = "appclientMainExtDocumentTemplate.jnlp";

    /**
     *Several generated virtual file names use the context root as part of the
     *name with fixed suffix appended.  
     */
    private static final String MAIN_JNLP_SUFFIX = ".jnlp";
    private static final String MAIN_HTML_SUFFIX = "-jnlp.html";
    private static final String CLIENT_JNLP_SUFFIX = "-client.jnlp";
    private static final String CLIENT_HTML_SUFFIX = "-client-jnlp.html";
    private static final String MAIN_EXT_JNLP_SUFFIX = "-ext.jnlp";
    
    /** the logger to use for Java Web Start-related code */
    public static final String JWS_LOGGER = LogDomains.DPL_LOGGER;
    
    /**
     *The admin GUI (or any other client) can create an instance of NamingConventions
     *for use in retrieving the actual URL path for a stand-alone app client
     *or a nested app client, including using user-specified paths if they 
     *are present.
     *
     *The next instance variables support that use.
     */
    
    /** the instance's apps manager */
    private AppsManager appsManager = null;
    
    /** the instance's app client modules manager */
    private AppclientModulesManager appclientModulesManager = null;
    
    /**
     *Creates a new instance of NamingConventions, locating the required
     *manager objects automatically.
     *@throws ConfigException in case of any problem locating the app server
     *objects that provide the AppsManager and AppclientModulesManager objects.
     */
    public NamingConventions() throws ConfigException {
        ServerContext appServerContext;
        InstanceEnvironment instEnv;
        
        if ((appServerContext = ApplicationServer.getServerContext()) == null) {
            throw new ConfigException("Error getting current app server context; ApplicationServer.getServerContext() returned null");
        }
        if ((instEnv = appServerContext.getInstanceEnvironment()) == null) {
            throw new ConfigException("Error getting current instance environment; appServercontext.getInstanceEnvironment() returned null");
        }
        appsManager = new AppsManager(instEnv, false);
        appclientModulesManager = new AppclientModulesManager(instEnv, false);
    }
    
    /** Creates a new instance of NamingConventions, using previously-created
     *references to the two manager objects.
     *@param appsManager the AppsManager instance to use for looking up applications
     *@param appclientsManager the AppclientModulesManager instance to use for looking up app clients
     */
    public NamingConventions(AppsManager appsManager, AppclientModulesManager appclientModulesManager) {
        this.appsManager = appsManager;
        this.appclientModulesManager = appclientModulesManager;
    }
    
    public static String webAppURI() {
        return SYSTEM_WEBAPP_URL;
    }
    
    public static String appServerCodebasePath() {
        return SYSTEM_WEBAPP_URL + "/" + APPSERVER_CATEGORY;
    }

    public static String fullJarPath(String contentKey) {
        return SYSTEM_WEBAPP_URL + contentKeyToPath(contentKey);
    }
    
    public static String relativeFilePath(URI instanceRootDirURI, File targetFile) {
        URI targetURI = targetFile.toURI();
        URI relativeURI = instanceRootDirURI.relativize(targetURI);
        return relativeURI.toString();
    }

    public static String extJarFilePath(int extDirNumber, File extJarFile) {
        String path = "/" + extDirNumber + "/" + extJarFile.getName();
        return path;
    }

    /**
     *Returns the developer-specified context root from the app client's
     *runtime descriptor.
     *@return the developer-specified context root; null if none was specified
     */
    private static String getExplicitContextRoot(ModuleDescriptor moduleDescr) {
        /*
         *Return the developer-specified context root, if there is one.
         */
        String result = null;

        BundleDescriptor bd = moduleDescr.getDescriptor();
        if (bd instanceof ApplicationClientDescriptor) {
            ApplicationClientDescriptor acd = (ApplicationClientDescriptor) bd;
            JavaWebStartAccessDescriptor jwsAD = acd.getJavaWebStartAccessDescriptor();
            if (jwsAD != null) {
                result = jwsAD.getContextRoot();
            }
        }
        return result;
    }
    
    /**
     *Returns the explicit context root, if non-null, or the default value otherwise.
     *@return the correct context root value to be used
     */
    private static String chooseContextRoot(String explicitValue, String defaultValue) {
        return (explicitValue == null) ? defaultValue : explicitValue;
    }
    
    public static String appclientCodebasePath(AppclientContentOrigin origin) {
        String result = SYSTEM_WEBAPP_URL + "/" + APPCLIENT_CATEGORY + /* "/" + */ origin.getContextRoot();
        return result;
    }

    private static String appclientJarFilename(String regName) {
        return regName + DeploymentImplConstants.ClientJarSuffix;
    }

  
    /**
     *Defines naming conventions specific to top-level app clients.
     */
    public static class TopLevelAppclient {
        
        public static String virtualContextRoot(Application application, ModuleDescriptor moduleDescr) {
            return chooseContextRoot(getExplicitContextRoot(moduleDescr), defaultVirtualContextRoot(application));
            }
        
        public static String actualContextRoot(Application application) {
            String regName = application.getRegistrationName();
            return "/" + APPCLIENT_CATEGORY + "/" + regName + NamingConventions.Main.JNLPPath(regName);
        }
        
        public static String defaultVirtualContextRoot(Application application) {
            return "/" + application.getRegistrationName();
        }
        
        public static String contentKeyPrefix(AppclientContentOrigin origin) {
            return "/" + APPCLIENT_CATEGORY + "/" + origin.getTopLevelRegistrationName();
        }
        
        public static String appclientJarPath(AppclientContentOrigin origin) {
            return "/" + appclientJarFilename(origin.getTopLevelRegistrationName());
        }
    }

    /**
     *Defines naming conventions specific to nested app clients.
     */
    public static class NestedAppclient {
        
        public static String virtualContextRoot(Application parentApplication, ModuleDescriptor moduleDescr) {
            return chooseContextRoot(getExplicitContextRoot(moduleDescr), defaultVirtualContextRoot(parentApplication, moduleDescr));
        }
        
        public static String actualContextRoot(NestedAppclientContentOrigin origin) {
            String regName = origin.getTopLevelRegistrationName();
            String clientName = origin.getName();
            return "/" + APPCLIENT_CATEGORY + "/" + regName + "/" + clientName + NamingConventions.Main.JNLPPath(clientName);
        }
        
        public static String defaultVirtualContextRoot(Application parentApplication, ModuleDescriptor moduleDescr) {
            return "/" + parentApplication.getRegistrationName() +  "/" + trimJarFileType(moduleDescr.getArchiveUri(),"");
        }
 
        public static String contentKeyPrefix(NestedAppclientContentOrigin origin) {
            return "/" + APPCLIENT_CATEGORY + "/" + origin.getTopLevelRegistrationName() + "/" + origin.getName();
        }
        
        /*
         *Nested app clients do not have their own appclient jar files, so there
         *is no implementation of that method for this inner class.
         */
        
        /**
         *Returns the string to use as the app client identifier within the
         *parent application.
         *<p>
         *Nested app clients are uniquely identified within the containing parent
         *only by the URI to the archive.  Convert the URI to the archive into
         *a unique name by removing the trailing ".jar" from the URI.
         *@param URI to the archive in string form
         *@return the unique name for this nested app client within its parent
         */
        public static String archiveURIToName(String archiveURI) {
            return trimJarFileType(archiveURI, "");
        }
    }
    
    public static class TopLevelApplication {
        
        public static String contextRoot(Application application) {
            return "/" + application.getRegistrationName();
        }
        
        public static String contentKeyPrefix(ApplicationContentOrigin origin) {
            return "/" + APPLICATION_CATEGORY + "/" + origin.getTopLevelRegistrationName();
        }
        
        public static String appclientJarPath(ApplicationContentOrigin origin) {
            return "/" + appclientJarFilename(origin.getTopLevelRegistrationName());
        }
    }
    
    public static class Appserver {
        public static String contentKeyPrefix(AppserverContentOrigin origin) {
            return "/" + APPSERVER_CATEGORY + "/" + origin.getSubcategory();
        }
    }
    
    /**
     *Defines naming conventions related to the main JNLP document created for 
     *either top-level or nested app clients.
     */
    public static class Main {

        private static final String name = "main";
        
        public static String JNLPFilename(String regName) {
            return name + MAIN_JNLP_SUFFIX;
        }
        
        public static String JNLPPath(String regName) {
            return "/" + JNLPFilename(regName);
        }

        public static String JNLPExtFilename(String regName) {
            return name + MAIN_EXT_JNLP_SUFFIX;
        }

        public static String JNLPExtPath(String regName) {
            return "/" + JNLPExtFilename(regName);
        }

        public static String HTMLPath(String regName) {
            return "/" + HTMLFilename(regName);
        }

        public static String HTMLFilename(String regName) {
            return name + MAIN_HTML_SUFFIX;
        }
    }

    /**
     *Defines naming conventions related to the client JNLP document created for
     *either top-level or nested app clients.
     */
    public static class Client {

        private static final String name = "client";
        
        public static String JNLPFilename(String regName) {
            return name + CLIENT_JNLP_SUFFIX;
        }

        public static String JNLPPath(String regName) {
            return "/" + JNLPFilename(regName);
        }

        public static String HTMLFilename(String regName) {
            return name + CLIENT_HTML_SUFFIX;
        }

        public static String HTMLPath(String regName) {
            return "/" + HTMLFilename(regName);
        }
    }


    /**
     *Replaces the final appearance of ".jar" in the input string with the
     *replacement string.
     *@param the input string (typically the URI of the app client jar file)
     *@param the string to replace the final ".jar" in the original
     */
    private static String trimJarFileType(String jarURI, String replacement) {
        int startOfType = jarURI.lastIndexOf(".jar");
        if (startOfType == -1) {
            startOfType = jarURI.length();
        }
        String result = jarURI.substring(0, startOfType) + replacement;
        return result;
    }
    
    /**
     *Returns the URL path for use in launching the stand-alone app client with the
     *specified module ID.
     *@param moduleID the module ID of the stand-alone app client 
     *@return String containing the URL path (excluding scheme, host, and port) for launching the app client; null if the
     *app client is not eligible for Java Web Start access or cannot be queried
     */
    public String getLaunchURLPath(String appclientModuleID) {
        String result = null;
        try {
            Application app = appclientModulesManager.getDescriptor(appclientModuleID, Thread.currentThread().getContextClassLoader(), false);
            /*
             *There should be exactly one app client ModuleDescriptor in this wrapper app.
             */
            ModuleDescriptor[] moduleDescrs = getEligibleAppclientModuleDescriptors(app);
            if (moduleDescrs.length == 1) {
                /*
                 *With the app client's module descr, find the path using either
                 *the developer-provided path or the default one.
                 */
                result = TopLevelAppclient.virtualContextRoot(app, moduleDescrs[0]);
            }
        } catch (ConfigException ce) {
            /*
             *Allow the return to be null if we cannot locate the app client.
             */
        }
        return result;
    }
    
    /**
     *Returns the URL path for use in launching the app client with the specified
     *archive URI embedded in the application with the specified module ID.
     *@param appModuleID the module ID of the application containing the app client 
     *@param appclientArchiveURI the URI of the app client within the ear
     *@return String containing the URL path (excluding scheme, host, and port) for launching the app client; null if the
     *app client is not eligible for Java Web Start access, or it or the containing
     *app cannot be found
     */
    public String getLaunchURLPath(String appModuleID, String appclientArchiveURI) {
        String result = null;
        try {
            Application app = appsManager.getDescriptor(appModuleID, Thread.currentThread().getContextClassLoader(), false);
            ModuleDescriptor[] moduleDescrs = getEligibleAppclientModuleDescriptors(app);
            /*
             *Search the eligible module descriptors for one with an archive
             *URI that matches the desired one.
             */
            for (ModuleDescriptor m : moduleDescrs) {
                String archiveURI = m.getArchiveUri();
                if (archiveURI != null && archiveURI.equals(appclientArchiveURI)) {
                    result = NestedAppclient.virtualContextRoot(app, m);
                    break;
                }
            }
        } catch (ConfigException ce) {
            /*
             *Allow the return to be null if we cannot locate the app.
             */
        }
        return result;
    }

    /**
     *Returns an array of ModuleDescriptors corresponding to the app clients
     *in this application that are eligible for Java Web Start access.  
     *@param app Application containing (potentially) the nested app clients
     *@return array of ModuleDescriptor objects for eligible app clients
     */
    public static ModuleDescriptor[] getEligibleAppclientModuleDescriptors(Application app) {
        return getEligibleAppclientModuleDescriptors(app, null);
    }
    
    
    /**
     *Returns an array of ModuleDescriptors corresponding to the app clients
     *in this application that are eligible for Java Web Start access. Provides
     *logging if the expected nested ModuleDescriptor(s) are not found.
     *@param Application containing (potentially) the nested app clients
     *@param logger the Logger to use for writing warnings
     *@return array of ModuleDescriptor objects for eligible app clients
     */
    public static ModuleDescriptor[] getEligibleAppclientModuleDescriptors(Application app, Logger logger) {
        Vector<ModuleDescriptor> mds = new Vector<ModuleDescriptor>();

        /*
         *Iterate through the app's app client modules.  For each, get the bundle
         *descriptor and make sure it's an app client descriptor.  If so, get
         *that descriptor's Java Web Start access descriptor.  If that is null, or
         *if it's non-null and the JWS access descriptor says the app client is 
         *eligible for JWS access, add the module descriptor to the collection
         *to be returned.
         */
        for (Iterator it = app.getModulesByType(ModuleType.CAR); it.hasNext();) {
            Object o = it.next();
            if (o instanceof ModuleDescriptor) {
                ModuleDescriptor moduleDescriptor = (ModuleDescriptor) o;
                BundleDescriptor bd = moduleDescriptor.getDescriptor();
                if (bd instanceof ApplicationClientDescriptor) {
                    ApplicationClientDescriptor appclientDescriptor = (ApplicationClientDescriptor) bd;
                    JavaWebStartAccessDescriptor jwsAD = appclientDescriptor.getJavaWebStartAccessDescriptor();
                    if (jwsAD == null || jwsAD.isEligible()) {
                        mds.add(moduleDescriptor);
                    }
                } else {
                    if (logger != null) {
                        logger.warning("During app/app client loading, expected bundleDescriptor for app client module to be of type ApplicationClientDescriptor but it is " + bd.getClass().getName() + "; ignoring it and continuing");
                    }
                }
            } else {
                if (logger != null) {
                    logger.warning("During app/app client loading, expected descriptor of type ModuleDescriptor but found " + o.getClass().getName() + " instead; ignoring it and continuing");
                }
            }
        }
        return mds.toArray(new ModuleDescriptor[mds.size()]);
    }
    
    /**
     *Encapsulates logic related to detecing and handling signed copies of the
     *generated app client jar.
     */
    public static class SignedJar {

        private static String SIGNED_JAR_FILENAME_SUFFIX = "-signed";

        /**
         *Returns a File for the signed version of the generated app client jar.
         *@param origin the UserContentOrigin (the app or app client) that owns the jar
         *@param instEnv the environment for this instance
         *@param generatedAppclientFile the generated app client jar file
         *@return File for where the signed generated app client jar should be
         */
        public static File signedGeneratedAppclientJarFile(
                UserContentOrigin origin,
                InstanceEnvironment instEnv,
                File generatedAppclientFile) {
            String regName = origin.getTopLevelRegistrationName();
            ApplicationEnvironment appEnv = instEnv.getApplicationEnvironment(regName);
            File result = new File(appEnv.getJavaWebStartPath(), signedJarPath(generatedAppclientFile.getName()));
            return result;
        }

        /**
         *Returns the path for the signed jar based on the path for the unsigned jar.
         *@param unsignedJarPath path (partial) for the unsigned jar
         *@return the path for the corresponding signed jar
         */
        public static String signedJarPath(String unsignedJarPath) {
//            int lastDot = unsignedJarPath.lastIndexOf('.');
//            if (lastDot == -1) {
//                throw new IllegalArgumentException(unsignedJarPath);
//            }
//            String signedJarPath = unsignedJarPath.substring(0, lastDot) + SIGNED_JAR_FILENAME_SUFFIX + unsignedJarPath.substring(lastDot);
//            return signedJarPath;
            return unsignedJarPath;
        }

        /**
         *Return the java-web-start directory for the given application.
         *@param instEnv InstanceEnvironment for the current instance
         *@param regName the module name of the app for which the j-w-s directory is needed
         *@return the directory file for the app's java-web-start subdirectory
         */
        private static File getAppJWSDir(InstanceEnvironment instEnv, String regName) {
            ApplicationEnvironment appEnv = instEnv.getApplicationEnvironment(regName);
            String appJWSDirPath = appEnv.getJavaWebStartPath();
            File appJWSDir = new File(appJWSDirPath);
            return appJWSDir;
        }
    }
}