FileDocCategorySizeDatePackage
AppClientInfo.javaAPI DocGlassfish v2 API26845Fri May 04 22:34:12 BST 2007com.sun.enterprise.appclient

AppClientInfo

public abstract class AppClientInfo extends Object
Represents information about the app client, regardless of what type of archive (jar or directory) it is stored in or what type of module (app client or nested within an ear) that archive holds.
author
tjquinn

Fields Summary
public static final String
USER_CODE_IS_SIGNED_PROPERTYNAME
private static final String
SIGNED_USER_CODE_PERMISSION_TEMPLATE_NAME
private static final String
UNSIGNED_USER_CODE_PERMISSION_TEMPLATE_NAME
private static final String
CODE_BASE_PLACEHOLDER_NAME
protected Logger
_logger
logger
private com.sun.enterprise.deployment.deploy.shared.AbstractArchive
appClientArchive
abstract representation of the storage location
private File
appClientFile
original appclient (file or directory)
private com.sun.enterprise.deployment.archivist.Archivist
archivist
abstract archivist able to operate on the module in the location (specified by archive
protected String
mainClassFromCommandLine
main class name as the user specified it on the command line
protected String
mainClassNameToRun
main class to be used - could come from the command line or from the manifest of the selected app client archive
private ClassLoader
classLoader
class loader - cached
protected boolean
isJWS
indicates if the app client has been launched using Java Web Start
protected com.sun.enterprise.util.i18n.StringManager
localStrings
access to the localizable strings
private PersistenceUnitLoader.ApplicationInfo
puAppInfo
protected static final boolean
_keepExplodedDir
Constructors Summary
public AppClientInfo(boolean isJWS, Logger logger, File appClientFile, com.sun.enterprise.deployment.archivist.Archivist archivist, String mainClassFromCommandLine)
Creates a new instance of AppClientInfo. Always invoked from subclasse because AppClientInfo is abstract.

Note that any code instantiationg one of the concrete subclasses MUST invoke completeInit after this super.constructor has returned.

param
isJWS indicates if ACC has been launched using Java Web Start
param
logger the logger to use for messages
param
archive the AbstractArchive for the app client module or directory being launched
param
archivist the Archivist corresponding to the type of module in the archive
param
mainClassFromCommandLine the main class name specified on the command line (null if not specified)


                                                                                                                                     
           
                             
        this.isJWS = isJWS;
        _logger = logger;
        this.appClientFile = appClientFile;
        this.archivist = archivist;
        this.mainClassFromCommandLine = mainClassFromCommandLine;
    
Methods Summary
private booleanappClientDependsOnPersistenceUnit(ApplicationClientDescriptor acDescr)
Reports whether the selected app client depends on a persistence unit or not.

returns
true if the app client depends on a persistence unit

            /*
             *If the descriptor contains at least one reference to an entity
             *manager then it definitely depends on a persistence unit.  
             */
            return descriptorContainsPURefcs(acDescr) 
                    || mainClassContainsPURefcAnnotations(acDescr);
    
protected booleanclassContainsAnnotation(java.lang.String entry, com.sun.enterprise.deployment.util.AnnotationDetector detector, com.sun.enterprise.deployment.deploy.shared.AbstractArchive archive, ApplicationClientDescriptor descriptor)

        String acRoot = getAppClientRoot(archive, descriptor);
        String entryLocation = acRoot + File.separator + entry;
        File entryFile = new File(entryLocation);
        return detector.containsAnnotation(entryFile);
    
protected voidclose()
Closes the instance of AppClientInfo, deleting any temporary directory created and closing the archive.

throws
IOException in case of error closing the archive

        try {
            if (puAppInfo != null) {
                new PersistenceUnitLoaderImpl().unload(puAppInfo);
                puAppInfo = null;
            }
            if (appClientArchive != null) {
                appClientArchive.close();
            }
            if (classLoader != null && 
                    classLoader instanceof EJBClassLoader) {
                ((EJBClassLoader) classLoader).done();
            }
        } finally {
            if (deleteAppClientDir()) {
                if (appClientArchive != null) {
                    deleteFile(new File(appClientArchive.getArchiveUri()));
                }
            }
            appClientArchive = null;
        }
    
protected voidcompleteInit(java.net.URL[] persistenceURLs)
Finishes initialization work.

The calling logic that instantiates this object must invoke completeInit after instantiation but before using the object.

throws
IOException for errors opening the expanded archive
throws
SAXParseException for errors parsing the descriptors in a newly-opened archive
throws
ClassNotFoundException if the main class requested cannot be located in the archive
throws
URISyntaxException if preparing URIs for the class loader fails


        //expand if needed. initialize the appClientArchive
        appClientArchive = expand(appClientFile);

        //Create the class loader to be used for persistence unit checking, 
        //validation, and running the app client.
        classLoader = createClassLoader(appClientArchive, persistenceURLs);

        //Populate the deployment descriptor without validation.
        //Note that validation is done only after the persistence handling
        //has instructed the classloader created above.
        populateDescriptor(appClientArchive, archivist, classLoader);
        
         //If the selected app client depends on at least one persistence unit
         //then handle the P.U. before proceeding.
        if (appClientDependsOnPersistenceUnit(getAppClient())) {
            //@@@check to see if the descriptor is metadata-complet=true
            //if not, we would have loaded classes into the classloader
            //during annotation processing.  we need to hault and ask
            //the user to deploy the application.
            //if (!getAppClient().isFullFlag()) {
            //    throw new RuntimeException("Please deploy your application");
            //}
            handlePersistenceUnitDependency();
        }
        
         //Now that the persistence handling has run and instrumented the class
         //loader - if it had to - it's ok to validate.
        archivist.validate(classLoader);
        
        fixupWSDLEntries();
        
        if (isJWS) {
            grantRequestedPermissionsToUserCode();
        }
    
private java.lang.ClassLoadercreateClassLoader(com.sun.enterprise.deployment.deploy.shared.AbstractArchive archive, java.net.URL[] persistenceURLs)

        List<String> paths = getClassPaths(archive);
        ClassLoader parent = Thread.currentThread().getContextClassLoader();
        EJBClassLoader loader = new EJBClassLoader(parent);

        final int LIST_SZ = paths.size();
        for (int i=0; i<LIST_SZ; i++) {
            String path = paths.get(i);
            loader.appendURL(new File(path));
        }
        
        if (_logger.isLoggable(Level.FINE)) {
            for (int i = 0; i < paths.size(); i++) {
                _logger.fine("Added path to classloader ==> " + paths.get(i));
            }
        }
        
        for (URL url : persistenceURLs) {
            loader.appendURL(url);
            _logger.fine("Added path to classloader ==> " + url);
        }

        return loader;
    
protected java.io.FilecreateTmpArchiveDir(java.io.File forArchive)

        /*
         *Create a temporary file (cannot create temp directories directly),
         *use the unique file name as a directory name.
         */
        String name = forArchive.getName();
        
        File tmpDir = File.createTempFile("acc-" + name, "");
        tmpDir.delete();

        tmpDir.mkdirs();

        /*
         *We will try to delete the directory when we finish with it, but this
         *is a back-up.
         */
        if (!_keepExplodedDir) {
            tmpDir.deleteOnExit();
        }

        return tmpDir;
    
protected booleandeleteAppClientDir()

        return !_keepExplodedDir;
    
private voiddeleteFile(java.io.File f)

        if (f.isDirectory()) {
            for (File subFile : f.listFiles()) {
                deleteFile(subFile);
            }
        }
        f.delete();
    
private booleandescriptorContainsPURefcs(ApplicationClientDescriptor descr)
Reports whether the app client's descriptor shows a dependence on a persistence unit.

param
descr the descriptor for the app client in question
returns
true if the descriptor shows such a dependency

        return ! descr.getEntityManagerFactoryReferenceDescriptors().isEmpty();
    
protected abstract com.sun.enterprise.deployment.deploy.shared.AbstractArchiveexpand(java.io.File file)
Expands the contents of the source archive into a temporary directory, using the same format as backend server expansions.

param
file an archive file to be expanded
return
an opened archive for the expanded directory archive
exception
IOException in case of errors during the expansion

private voidfixupWSDLEntries()
Adjusts the web services WSDL entries corresponding to where they actually reside.

        ApplicationClientDescriptor ac = getAppClient();
        URI uri = (new File(getAppClientRoot(appClientArchive, ac))).toURI();
        File moduleFile = new File(uri);
        for (Iterator itr = ac.getServiceReferenceDescriptors().iterator();
                    itr.hasNext();) {
            ServiceReferenceDescriptor serviceRef = 
                    (ServiceReferenceDescriptor) itr.next();
            if (serviceRef.getWsdlFileUri()!=null) {
                // In case WebServiceRef does not specify wsdlLocation, we get
                // wsdlLocation from @WebClient in wsimport generated source; 
                // If wsimport was given a local WSDL file, then WsdlURI will
                // be an absolute path - in that case it should not be prefixed
                // with modileFileDir
                String wsdlURI = serviceRef.getWsdlFileUri();
                File wsdlFile = new File(wsdlURI);
                if(wsdlFile.isAbsolute()) {
                    serviceRef.setWsdlFileUrl(wsdlFile.toURI().toURL());
                } else {
                    // This is the case where WsdlFileUri is a relative path
                    // (hence relative to the root of this module or wsimport
                    // was executed with WSDL in HTTP URL form
                    serviceRef.setWsdlFileUrl(FileUtil.getEntryAsUrl(
                        moduleFile, serviceRef.getWsdlFileUri()));
                }
            }
        }
    
protected ApplicationClientDescriptorgetAppClient(com.sun.enterprise.deployment.archivist.Archivist archivist)

        return ApplicationClientDescriptor.class.cast(
                    archivist.getDescriptor());
    
protected ApplicationClientDescriptorgetAppClient()
Returns the app client descriptor to be run.

return
the descriptor for the selected app client

        return getAppClient(archivist);
    
protected java.lang.StringgetAppClientRoot(com.sun.enterprise.deployment.deploy.shared.AbstractArchive archive, ApplicationClientDescriptor descriptor)

        return archive.getArchiveUri();
    
protected java.lang.ClassLoadergetClassLoader()

        return classLoader;
    
protected java.util.ListgetClassPaths(com.sun.enterprise.deployment.deploy.shared.AbstractArchive archive)

        List<String> paths = new ArrayList();
        paths.add(archive.getArchiveUri());
        return paths;
    
protected java.lang.StringgetMainClassNameToRun(ApplicationClientDescriptor acDescr)
Returns the main class that should be executed.

return
the name of the main class to execute when the client starts

         if (mainClassNameToRun == null) {
             if (mainClassFromCommandLine != null) {
                 mainClassNameToRun = mainClassFromCommandLine;
                 _logger.fine("Main class is " + mainClassNameToRun + " from command line");
             } else {
                 /*
                  *Find out which class to execute from the descriptor.
                  */
                 mainClassNameToRun = getAppClient().getMainClassName();
                 _logger.fine("Main class is " + mainClassNameToRun + " from descriptor");
             }
         }
         return mainClassNameToRun;
    
private voidgrantRequestedPermissionsToUserCode()
Granting the appropriate level of permissions to user code, emulating the Java Web Start behavior as required in the JNLP spec.

Classes from the user's app client jar are loaded using the EJBClassLoader rather than the Java Web Start class loader. As a result, Java Web Start cannot grant the appropriate level of permissions to the user code since it is the JWS class loader that does that. So we need to grant the user code the appropriate level of permissions, based on whether the user's app client jar was signed or not.

param
retainTempFiles indicates whether to keep the generated policy file

        /*
         *Create a temporary file containing permissions grants.  We will use
         *this temp file to refresh the Policy objects's settings.  The temp
         *file will contain one segment for each element in the class path
         *for the user code (from the expanded generated app client jar). 
         *The permissions granted will be the same for all class path elements,
         *and the specific settings will either be the sandbox permissions
         *as described in the JNLP spec or full permissions.
         */
        boolean userJarIsSigned = Boolean.getBoolean(USER_CODE_IS_SIGNED_PROPERTYNAME);
        
        boolean retainTempFiles = Boolean.getBoolean(MainWithModuleSupport.APPCLIENT_RETAIN_TEMP_FILES_PROPERTYNAME);
        
        /*
         *Use a template to construct each part of the policy file, choosing the
         *template based on whether the user code is signed or not.
         */
        String templateName = (userJarIsSigned ? SIGNED_USER_CODE_PERMISSION_TEMPLATE_NAME : UNSIGNED_USER_CODE_PERMISSION_TEMPLATE_NAME);
        String template = Util.loadResource(JWSACCMain.class, templateName);
        
        /*
         *Create the temporary policy file to write to.
         */
        File policyFile = File.createTempFile("accjws-user", ".policy");
        if ( ! retainTempFiles) {
            policyFile.deleteOnExit();
        }
        
        PrintStream ps = null;
        
        try {
            ps = new PrintStream(policyFile);
        
            Properties p = new Properties();

            /*
             *Use the list of class paths already set up on the EJBClassLoader.
             *Then for each element in the class path, write a part of the policy 
             *file granting the privs specified in the selected template to the code path.
             */
            EJBClassLoader loader = (EJBClassLoader) getClassLoader();
            for (URL classPathElement : loader.getURLs()) {
                /*
                 *Convert the URL into a proper codebase expression suitable for
                 *a grant clause in the policy file.
                 */
                String codeBase = Util.URLtoCodeBase(classPathElement);
                if (codeBase != null) {
                    p.setProperty(CODE_BASE_PLACEHOLDER_NAME, codeBase);
                    String policyPart = Util.replaceTokens(template, p);

                    ps.println(policyPart);
                }
            }

            /*
             *Close the temp file and use it to refresh the current Policy object.
             */
            ps.close();

            JWSACCMain.refreshPolicy(policyFile);

            if ( ! retainTempFiles) {
                policyFile.delete();
            }
        } finally {
            if (ps != null) {
                ps.close();
            }
        }
    
private voidhandlePersistenceUnitDependency()
Processes persistence unit handling for the ACC.

        this.puAppInfo = new ApplicationInfoImpl(this);
        new PersistenceUnitLoaderImpl().load(puAppInfo);
    
private booleanmainClassContainsPURefcAnnotations(ApplicationClientDescriptor acDescr)
Reports whether the main class in the archive contains annotations that refer to persistence units.

return
boolean if the main class contains annotations that refer to a pers. unit

        AppClientPersistenceDependencyAnnotationDetector annoDetector = 
                    new AppClientPersistenceDependencyAnnotationDetector();

        //e.g. FROM a.b.Foo or Foo TO a/b/Foo.class or Foo.class
        String mainClassEntryName = 
                acDescr.getMainClassName().replace('.", '/") + ".class";

        return classContainsAnnotation
                (mainClassEntryName, annoDetector, appClientArchive, acDescr);
    
protected voidmessageDescriptor(RootDeploymentDescriptor d, com.sun.enterprise.deployment.archivist.Archivist archivist, com.sun.enterprise.deployment.deploy.shared.AbstractArchive archive)

        //default behavor: no op
    
private RootDeploymentDescriptorpopulateDescriptor(com.sun.enterprise.deployment.deploy.shared.AbstractArchive archive, com.sun.enterprise.deployment.archivist.Archivist theArchivist, java.lang.ClassLoader loader)


        //@@@ Optimize it later.
        //Here the application.xml is read twice for NestedAppClientInfo.
        //Once already in expand() method.

        theArchivist.setAnnotationProcessingRequested(true);

        //@@@ setting of the classloader would trigger annotation processing 
        //for appclients that have only partial deployment descriptors or no
        //descriptors at all. 
        //Note that the annotation processing is bypassed if the descriptors 
        //are meta-complete=true", which will be the case for anything that is 
        //generated by the backend, i.e. if the appclient being executed here 
        //is a generated jar produced by the appserver, obtained by deploying
        //the original application client and retrieve.
        theArchivist.setClassLoader(loader);

        //open with Archivist./pen(AbstractArchive) to also ensure the
        //validation is not called
        //return archivist.open(archive);
        RootDeploymentDescriptor d = null;
        try {
            d = theArchivist.open(archive);
        } catch (Exception ex) { 
            close(); //make sure there is no junk tmp director left
            throw ex;
        }
        
        //depend on the type of the appclient, additional work needs
        //to be done.
        messageDescriptor(d, theArchivist, archive);

        theArchivist.setDescriptor(d);
        return d;
    
public java.lang.StringtoString()

        String lineSep = System.getProperty("line.separator");
        StringBuilder result = new StringBuilder();
        result.append(this.getClass().getName() + ": " + lineSep);
        result.append("  isJWS: " + isJWS);
        result.append("  archive file: " + appClientFile.getAbsolutePath() + lineSep);
        result.append("  archive type: " + appClientArchive.getClass().getName() + lineSep);
        result.append("  archivist type: " + archivist.getClass().getName() + lineSep);
        result.append("  main class to be run: " + mainClassNameToRun + lineSep);
        result.append("  temporary archive directory: " + appClientArchive.getArchiveUri() + lineSep);
        result.append("  class loader type: " + classLoader.getClass().getName() + lineSep);
       
        return result.toString();