/* * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER. * * Copyright (c) 1997-2012 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.appclient.client.acc; import com.sun.enterprise.deployment.ApplicationClientDescriptor; import com.sun.enterprise.deployment.BundleDescriptor; import com.sun.enterprise.deployment.ServiceReferenceDescriptor; import com.sun.enterprise.deployment.annotation.introspection.AppClientPersistenceDependencyAnnotationScanner; import com.sun.enterprise.deployment.archivist.AppClientArchivist; import com.sun.enterprise.deployment.archivist.Archivist; import com.sun.enterprise.deployment.archivist.ArchivistFactory; import com.sun.enterprise.deployment.util.AnnotationDetector; import com.sun.enterprise.loader.ASURLClassLoader; import org.glassfish.apf.AnnotationProcessorException; import org.glassfish.api.deployment.archive.ReadableArchive; import org.glassfish.deployment.common.RootDeploymentDescriptor; import org.jvnet.hk2.annotations.Service; import org.glassfish.hk2.api.PostConstruct; import org.xml.sax.SAXParseException; import javax.enterprise.deploy.shared.ModuleType; import javax.inject.Inject; import java.io.File; import java.io.FileNotFoundException; import java.io.IOException; import java.net.MalformedURLException; import java.net.URI; import java.net.URISyntaxException; import java.util.Iterator; import java.util.logging.Logger; /** * Represents an app client that is in a stand-alone archive, not inside an * enterprise app archive and not a .class file. * @author tjquinn */ @Service public class StandAloneAppClientInfo extends AppClientInfo implements PostConstruct { @Inject protected ArchivistFactory archivistFactory; private ReadableArchive appClientArchive; private AppClientArchivist appClientArchivist = null; public StandAloneAppClientInfo( boolean isJWS, Logger logger, ReadableArchive archive, String mainClassFromCommandLine) throws IOException, ClassNotFoundException, URISyntaxException, SAXParseException { super(isJWS, logger, mainClassFromCommandLine); appClientArchive = archive; } public void postConstruct() { Archivist archivist = archivistFactory.getArchivist(ModuleType.CAR.toString(), getClassLoader()); if (!(archivist instanceof AppClientArchivist)) { throw new IllegalArgumentException("expected an app client module but " + appClientArchive.getURI().toASCIIString() + " was recognized by " + archivist.getClass().getName()); } appClientArchivist = (AppClientArchivist) archivist; setDescriptor(appClientArchivist.getDescriptor()); } /** *Finishes initialization work. *<p> *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 * */ @Override protected void completeInit(/*URL[] persistenceURLs*/) throws Exception { //expand if needed. initialize the appClientArchive // appClientArchive = expand(appClientArchive); //Create the class loader to be used for persistence unit checking, //validation, and running the app client. // XXX The system class loader should have everything we need // 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, appClientArchivist, getClassLoader()); //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. appClientArchivist.validate(getClassLoader()); fixupWSDLEntries(); // XXX restore or move elsewhere // if (isJWS) { // grantRequestedPermissionsToUserCode(); // } } /** *Adjusts the web services WSDL entries corresponding to where they *actually reside. */ protected void fixupWSDLEntries() throws URISyntaxException, MalformedURLException, IOException, AnnotationProcessorException { 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(getEntryAsUrl( moduleFile, serviceRef.getWsdlFileUri())); } } } } /** *Reports whether the selected app client depends on a persistence unit *or not. *@returns true if the app client depends on a persistence unit */ protected boolean appClientDependsOnPersistenceUnit( ApplicationClientDescriptor acDescr) throws MalformedURLException, ClassNotFoundException, IOException, URISyntaxException { /* *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 boolean mainClassContainsPURefcAnnotations( ApplicationClientDescriptor acDescr) throws MalformedURLException, ClassNotFoundException, IOException, URISyntaxException { AnnotationDetector annoDetector = new AnnotationDetector(new AppClientPersistenceDependencyAnnotationScanner()); //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); } private RootDeploymentDescriptor populateDescriptor( ReadableArchive archive, Archivist theArchivist, ClassLoader loader) throws IOException, SAXParseException, Exception { //@@@ 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. massageDescriptor(); theArchivist.setDescriptor((BundleDescriptor)d); return d; } // @Override // protected ReadableArchive expand(File file) // throws IOException, Exception { // return archiveFactory.openArchive(file); // } // // @Override // protected boolean deleteAppClientDir() { // return false; // } @Override protected void massageDescriptor() throws IOException, AnnotationProcessorException { getDescriptor().getModuleDescriptor().setStandalone(true); } /** *Closes the instance of AppClientInfo, deleting any temporary directory *created and closing the archive. *@throws IOException in case of error closing the archive */ @Override protected void close() throws IOException { try { // XXX Mitesh helping to update this // if (puAppInfo != null) { // new PersistenceUnitLoaderImpl().unload(puAppInfo); // puAppInfo = null; // } if (appClientArchive != null) { appClientArchive.close(); } ClassLoader classLoader = getClassLoader(); if (classLoader != null && classLoader instanceof ASURLClassLoader) { ((ASURLClassLoader) classLoader).done(); } } finally { if (deleteAppClientDir()) { if (appClientArchive != null) { appClientArchive.delete(); } } appClientArchive = null; } } @Override protected boolean classContainsAnnotation( String entry, AnnotationDetector detector, ReadableArchive archive, ApplicationClientDescriptor descriptor) throws FileNotFoundException, IOException { // JarFile jar = null; try { return detector.containsAnnotation(archive, entry); } catch (Throwable thr) { throw new RuntimeException(getLocalString( "appclient.errorCheckingAnnos", "Error checking for persistence unit annotations in the main class"), thr); } finally { // if (jar != null) { // try { // jar.close(); // } catch (IOException ioe) { // throw new RuntimeException(getLocalString( // "appclient.errorClosingJar", // "Error closing archive {0} used in checking for persistence unit annotations", // archive.getURI().toASCIIString()), ioe); // } // } } } @Override public String toString() { String lineSep = System.getProperty("line.separator"); StringBuilder result = new StringBuilder(); result.append(this.getClass().getName() + ": " + lineSep); result.append(" isJWS: " + isJWS); result.append(" archive file: " + appClientArchive.getURI().toASCIIString() + lineSep); result.append(" archive type: " + appClientArchive.getClass().getName() + lineSep); result.append(" archivist type: " + appClientArchivist.getClass().getName() + lineSep); result.append(" main class to be run: " + mainClassNameToRun + lineSep); result.append(" temporary archive directory: " + appClientArchive.getURI() + lineSep); result.append(" class loader type: " + getClassLoader().getClass().getName() + lineSep); return result.toString(); }}