KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > com > sun > enterprise > appclient > AppClientInfo


1 /*
2  * The contents of this file are subject to the terms
3  * of the Common Development and Distribution License
4  * (the License). You may not use this file except in
5  * compliance with the License.
6  *
7  * You can obtain a copy of the license at
8  * https://glassfish.dev.java.net/public/CDDLv1.0.html or
9  * glassfish/bootstrap/legal/CDDLv1.0.txt.
10  * See the License for the specific language governing
11  * permissions and limitations under the License.
12  *
13  * When distributing Covered Code, include this CDDL
14  * Header Notice in each file and include the License file
15  * at glassfish/bootstrap/legal/CDDLv1.0.txt.
16  * If applicable, add the following below the CDDL Header,
17  * with the fields enclosed by brackets [] replaced by
18  * you own identifying information:
19  * "Portions Copyrighted [year] [name of copyright owner]"
20  *
21  * Copyright 2006 Sun Microsystems, Inc. All rights reserved.
22  */

23
24 package com.sun.enterprise.appclient;
25
26 import com.sun.enterprise.appclient.jws.Util;
27 import com.sun.enterprise.appclient.jws.boot.JWSACCMain;
28 import com.sun.enterprise.deployment.annotation.AnnotationProcessorException;
29 import com.sun.enterprise.deployment.ApplicationClientDescriptor;
30 import com.sun.enterprise.deployment.archivist.Archivist;
31 import com.sun.enterprise.deployment.deploy.shared.AbstractArchive;
32 import com.sun.enterprise.deployment.RootDeploymentDescriptor;
33 import com.sun.enterprise.deployment.ServiceReferenceDescriptor;
34 import com.sun.enterprise.deployment.util.AnnotationDetector;
35 import com.sun.enterprise.deployment.util.AppClientPersistenceDependencyAnnotationDetector;
36 import com.sun.enterprise.loader.EJBClassLoader;
37 import com.sun.enterprise.loader.InstrumentableClassLoader;
38 import com.sun.enterprise.util.FileUtil;
39 import com.sun.enterprise.util.i18n.StringManager;
40 import java.io.File JavaDoc;
41 import java.io.FileNotFoundException JavaDoc;
42 import java.io.IOException JavaDoc;
43 import java.io.PrintStream JavaDoc;
44 import java.net.MalformedURLException JavaDoc;
45 import java.net.URI JavaDoc;
46 import java.net.URISyntaxException JavaDoc;
47 import java.net.URL JavaDoc;
48 import java.util.ArrayList JavaDoc;
49 import java.util.Iterator JavaDoc;
50 import java.util.List JavaDoc;
51 import java.util.Properties JavaDoc;
52 import java.util.logging.Level JavaDoc;
53 import java.util.logging.Logger JavaDoc;
54 import org.xml.sax.SAXParseException JavaDoc;
55
56 /**
57  *Represents information about the app client, regardless of what type of
58  *archive (jar or directory) it is stored in or what type of module
59  *(app client or nested within an ear) that archive holds.
60  *
61  *@author tjquinn
62  */

63
64 public abstract class AppClientInfo {
65
66     public static final String JavaDoc USER_CODE_IS_SIGNED_PROPERTYNAME = "com.sun.aas.user.code.signed";
67     
68     private static final String JavaDoc SIGNED_USER_CODE_PERMISSION_TEMPLATE_NAME = "jwsclientSigned.policy";
69     
70     private static final String JavaDoc UNSIGNED_USER_CODE_PERMISSION_TEMPLATE_NAME = "jwsclientUnsigned.policy";
71     
72     private static final String JavaDoc CODE_BASE_PLACEHOLDER_NAME = "com.sun.aas.jws.client.codeBase";
73     
74     /** logger */
75     protected Logger JavaDoc _logger;
76
77     /** abstract representation of the storage location */
78     private AbstractArchive appClientArchive = null;
79
80     /** original appclient (file or directory) */
81     private File JavaDoc appClientFile = null;
82
83     /** abstract archivist able to operate on the module in the location
84       * (specified by archive */

85     private Archivist archivist = null;
86     
87     /** main class name as the user specified it on the command line */
88     protected String JavaDoc mainClassFromCommandLine;
89     
90     /**
91      *main class to be used - could come from the command line or from the
92      *manifest of the selected app client archive
93      */

94     protected String JavaDoc mainClassNameToRun = null;
95
96     /** class loader - cached */
97     private ClassLoader JavaDoc classLoader = null;
98     
99     /** indicates if the app client has been launched using Java Web Start */
100     protected boolean isJWS = false;
101     
102     /** loader for handling persistence units */
103     protected PersistenceUnitLoaderImpl puLoader = null;
104     
105     /** access to the localizable strings */
106     protected StringManager localStrings =
107                         StringManager.getManager(MainWithModuleSupport.class);
108
109     /**
110      *Creates a new instance of AppClientInfo. Always invoked from subclasse
111      *because AppClientInfo is abstract.
112      *<p>
113      *Note that any code instantiationg one of the concrete subclasses MUST
114      *invoke completeInit after this super.constructor has returned.
115      *
116      *@param isJWS indicates if ACC has been launched using Java Web Start
117      *@param logger the logger to use for messages
118      *@param archive the AbstractArchive for the app client module or
119      * directory being launched
120      *@param archivist the Archivist corresponding to the type of module
121      * in the archive
122      *@param mainClassFromCommandLine the main class name specified on the
123      * command line (null if not specified)
124      */

125     public AppClientInfo(boolean isJWS, Logger JavaDoc logger, File JavaDoc appClientFile,
126                          Archivist archivist, String JavaDoc mainClassFromCommandLine) {
127         this.isJWS = isJWS;
128         _logger = logger;
129         this.appClientFile = appClientFile;
130         this.archivist = archivist;
131         this.mainClassFromCommandLine = mainClassFromCommandLine;
132     }
133
134     /**
135      *Finishes initialization work.
136      *<p>
137      *The calling logic that instantiates this object must invoke completeInit
138      *after instantiation but before using the object.
139      *@throws IOException for errors opening the expanded archive
140      *@throws SAXParseException for errors parsing the descriptors in a newly-opened archive
141      *@throws ClassNotFoundException if the main class requested cannot be located in the archive
142      *@throws URISyntaxException if preparing URIs for the class loader fails
143      *
144      */

145     protected void completeInit()
146         throws IOException JavaDoc, SAXParseException JavaDoc, ClassNotFoundException JavaDoc,
147                URISyntaxException JavaDoc, AnnotationProcessorException, Exception JavaDoc {
148
149         //expand if needed. initialize the appClientArchive
150
appClientArchive = expand(appClientFile);
151
152         //Create the class loader to be used for persistence unit checking,
153
//validation, and running the app client.
154
classLoader = createClassLoader(appClientArchive);
155
156         //Populate the deployment descriptor without validation.
157
//Note that validation is done only after the persistence handling
158
//has instructed the classloader created above.
159
RootDeploymentDescriptor descriptor =
160             populateDescriptor(appClientArchive, archivist, classLoader);
161         
162          //If the selected app client depends on at least one persistence unit
163
//then handle the P.U. before proceeding.
164
if (appClientDependsOnPersistenceUnit(getAppClient())) {
165             //@@@check to see if the descriptor is metadata-complet=true
166
//if not, we would have loaded classes into the classloader
167
//during annotation processing. we need to hault and ask
168
//the user to deploy the application.
169
//if (!getAppClient().isFullFlag()) {
170
// throw new RuntimeException("Please deploy your application");
171
//}
172
handlePersistenceUnitDependency();
173         }
174         
175          //Now that the persistence handling has run and instrumented the class
176
//loader - if it had to - it's ok to validate.
177
archivist.validate(classLoader);
178         
179         fixupWSDLEntries();
180         
181         if (isJWS) {
182             grantRequestedPermissionsToUserCode();
183         }
184     }
185     
186     /**
187      *Returns the app client descriptor to be run.
188      *@return the descriptor for the selected app client
189      */

190     protected ApplicationClientDescriptor getAppClient() {
191         return getAppClient(archivist);
192     }
193
194     protected ClassLoader JavaDoc getClassLoader() {
195         return classLoader;
196     }
197
198     protected File JavaDoc createTmpArchiveDir(File JavaDoc forArchive)
199         throws IOException JavaDoc {
200         /*
201          *Create a temporary file (cannot create temp directories directly),
202          *use the unique file name as a directory name.
203          */

204         String JavaDoc name = forArchive.getName();
205         
206         File JavaDoc tmpDir = File.createTempFile("acc-" + name, "");
207         tmpDir.delete();
208
209         tmpDir.mkdirs();
210
211         /*
212          *We will try to delete the directory when we finish with it, but this
213          *is a back-up.
214          */

215         if (!_keepExplodedDir) {
216             tmpDir.deleteOnExit();
217         }
218
219         return tmpDir;
220     }
221
222     /**
223      *Closes the instance of AppClientInfo, deleting any temporary directory
224      *created and closing the archive.
225      *@throws IOException in case of error closing the archive
226      */

227     protected void close() throws IOException JavaDoc {
228         try {
229             if (puLoader != null) {
230                 puLoader.unload();
231                 puLoader = null;
232             }
233             if (appClientArchive != null) {
234                 appClientArchive.close();
235             }
236             if (classLoader != null &&
237                     classLoader instanceof EJBClassLoader) {
238                 ((EJBClassLoader) classLoader).done();
239             }
240         } finally {
241             if (deleteAppClientDir()) {
242                 if (appClientArchive != null) {
243                     deleteFile(new File JavaDoc(appClientArchive.getArchiveUri()));
244                 }
245             }
246             appClientArchive = null;
247         }
248     }
249
250     protected boolean deleteAppClientDir() {
251         return !_keepExplodedDir;
252     }
253     
254     
255     /**
256      *Processes persistence unit handling for the ACC.
257      */

258     private void handlePersistenceUnitDependency()
259             throws URISyntaxException JavaDoc, MalformedURLException JavaDoc,
260                    ClassNotFoundException JavaDoc, IOException JavaDoc {
261         InstrumentableClassLoader classLoader =
262                 (InstrumentableClassLoader) getClassLoader();
263         puLoader = new PersistenceUnitLoaderImpl(
264             appClientArchive.getArchiveUri(), classLoader, getAppClient());
265         puLoader.load();
266     }
267     
268     private ClassLoader JavaDoc createClassLoader(AbstractArchive archive)
269         throws IOException JavaDoc {
270         List JavaDoc<String JavaDoc> paths = getClassPaths(archive);
271         ClassLoader JavaDoc parent = Thread.currentThread().getContextClassLoader();
272         EJBClassLoader loader = new EJBClassLoader(parent);
273
274         final int LIST_SZ = paths.size();
275         for (int i=0; i<LIST_SZ; i++) {
276             String JavaDoc path = paths.get(i);
277             loader.appendURL(new File JavaDoc(path));
278         }
279         
280         if (_logger.isLoggable(Level.FINE)) {
281             for (int i = 0; i < paths.size(); i++) {
282                 _logger.fine("Added path to classloader ==> " + paths.get(i));
283             }
284         }
285
286         return loader;
287     }
288
289     private void deleteFile(File JavaDoc f) {
290         if (f.isDirectory()) {
291             for (File JavaDoc subFile : f.listFiles()) {
292                 deleteFile(subFile);
293             }
294         }
295         f.delete();
296     }
297
298     /**
299      *Reports whether the selected app client depends on a persistence unit
300      *or not.
301      *@returns true if the app client depends on a persistence unit
302      */

303     private boolean appClientDependsOnPersistenceUnit(
304         ApplicationClientDescriptor acDescr)
305             throws MalformedURLException JavaDoc, ClassNotFoundException JavaDoc,
306                    IOException JavaDoc, URISyntaxException JavaDoc {
307             /*
308              *If the descriptor contains at least one reference to an entity
309              *manager then it definitely depends on a persistence unit.
310              */

311             return descriptorContainsPURefcs(acDescr)
312                     || mainClassContainsPURefcAnnotations(acDescr);
313     }
314     
315     /**
316      *Reports whether the app client's descriptor shows a dependence on a
317      *persistence unit.
318      *@param descr the descriptor for the app client in question
319      *@returns true if the descriptor shows such a dependency
320      */

321     private boolean descriptorContainsPURefcs(
322         ApplicationClientDescriptor descr) {
323         return ! descr.getEntityManagerFactoryReferenceDescriptors().isEmpty();
324     }
325     
326     /**
327      *Reports whether the main class in the archive contains annotations that
328      *refer to persistence units.
329      *@return boolean if the main class contains annotations that refer to a pers. unit
330      */

331     private boolean mainClassContainsPURefcAnnotations(
332         ApplicationClientDescriptor acDescr)
333             throws MalformedURLException JavaDoc, ClassNotFoundException JavaDoc,
334                    IOException JavaDoc, URISyntaxException JavaDoc {
335         AppClientPersistenceDependencyAnnotationDetector annoDetector =
336                     new AppClientPersistenceDependencyAnnotationDetector();
337
338         //e.g. FROM a.b.Foo or Foo TO a/b/Foo.class or Foo.class
339
String JavaDoc mainClassEntryName =
340                 acDescr.getMainClassName().replace('.', '/') + ".class";
341
342         return classContainsAnnotation
343                 (mainClassEntryName, annoDetector, appClientArchive, acDescr);
344     }
345
346     /**
347      *Adjusts the web services WSDL entries corresponding to where they
348      *actually reside.
349      */

350     private void fixupWSDLEntries()
351         throws URISyntaxException JavaDoc, MalformedURLException JavaDoc, IOException JavaDoc,
352                AnnotationProcessorException {
353         ApplicationClientDescriptor ac = getAppClient();
354         URI JavaDoc uri = (new File JavaDoc(getAppClientRoot(appClientArchive, ac))).toURI();
355         File JavaDoc moduleFile = new File JavaDoc(uri);
356         for (Iterator JavaDoc itr = ac.getServiceReferenceDescriptors().iterator();
357                     itr.hasNext();) {
358             ServiceReferenceDescriptor serviceRef =
359                     (ServiceReferenceDescriptor) itr.next();
360             if (serviceRef.getWsdlFileUri()!=null) {
361                 // In case WebServiceRef does not specify wsdlLocation, we get
362
// wsdlLocation from @WebClient in wsimport generated source;
363
// If wsimport was given a local WSDL file, then WsdlURI will
364
// be an absolute path - in that case it should not be prefixed
365
// with modileFileDir
366
URI JavaDoc wsdlURI = new URI JavaDoc(serviceRef.getWsdlFileUri());
367                 File JavaDoc wsdlFile = new File JavaDoc(serviceRef.getWsdlFileUri());
368                 if(wsdlFile.isAbsolute()) {
369                     serviceRef.setWsdlFileUrl(wsdlFile.toURI().toURL());
370                 } else {
371                     // This is the case where WsdlFileUri is a relative path
372
// (hence relative to the root of this module or wsimport
373
// was executed with WSDL in HTTP URL form
374
serviceRef.setWsdlFileUrl(FileUtil.getEntryAsUrl(
375                         moduleFile, serviceRef.getWsdlFileUri()));
376                 }
377             }
378         }
379     }
380
381     private RootDeploymentDescriptor populateDescriptor(
382             AbstractArchive archive, Archivist theArchivist, ClassLoader JavaDoc loader)
383         throws IOException JavaDoc, SAXParseException JavaDoc, Exception JavaDoc {
384
385         //@@@ Optimize it later.
386
//Here the application.xml is read twice for NestedAppClientInfo.
387
//Once already in expand() method.
388

389         theArchivist.setAnnotationProcessingRequested(true);
390
391         //@@@ setting of the classloader would trigger annotation processing
392
//for appclients that have only partial deployment descriptors or no
393
//descriptors at all.
394
//Note that the annotation processing is bypassed if the descriptors
395
//are meta-complete=true", which will be the case for anything that is
396
//generated by the backend, i.e. if the appclient being executed here
397
//is a generated jar produced by the appserver, obtained by deploying
398
//the original application client and retrieve.
399
theArchivist.setClassLoader(loader);
400
401         //open with Archivist./pen(AbstractArchive) to also ensure the
402
//validation is not called
403
//return archivist.open(archive);
404
RootDeploymentDescriptor d = null;
405         try {
406             d = theArchivist.open(archive);
407         } catch (Exception JavaDoc ex) {
408             close(); //make sure there is no junk tmp director left
409
throw ex;
410         }
411         
412         //depend on the type of the appclient, additional work needs
413
//to be done.
414
messageDescriptor(d, theArchivist, archive);
415
416         theArchivist.setDescriptor(d);
417         return d;
418     }
419
420     /**
421      *Granting the appropriate level of permissions to user code, emulating
422      *the Java Web Start behavior as required in the JNLP spec.
423      *<p>
424      *Classes from the user's app client jar are loaded using the EJBClassLoader
425      *rather than the Java Web Start class loader. As a result, Java Web Start
426      *cannot grant the appropriate level of permissions to the user code since
427      *it is the JWS class loader that does that. So we need to grant the user
428      *code the appropriate level of permissions, based on whether the user's
429      *app client jar was signed or not.
430      *@param retainTempFiles indicates whether to keep the generated policy file
431      */

432     private void grantRequestedPermissionsToUserCode() throws IOException JavaDoc, URISyntaxException JavaDoc {
433         /*
434          *Create a temporary file containing permissions grants. We will use
435          *this temp file to refresh the Policy objects's settings. The temp
436          *file will contain one segment for each element in the class path
437          *for the user code (from the expanded generated app client jar).
438          *The permissions granted will be the same for all class path elements,
439          *and the specific settings will either be the sandbox permissions
440          *as described in the JNLP spec or full permissions.
441          */

442         boolean userJarIsSigned = Boolean.getBoolean(USER_CODE_IS_SIGNED_PROPERTYNAME);
443         
444         boolean retainTempFiles = Boolean.getBoolean(MainWithModuleSupport.APPCLIENT_RETAIN_TEMP_FILES_PROPERTYNAME);
445         
446         /*
447          *Use a template to construct each part of the policy file, choosing the
448          *template based on whether the user code is signed or not.
449          */

450         String JavaDoc templateName = (userJarIsSigned ? SIGNED_USER_CODE_PERMISSION_TEMPLATE_NAME : UNSIGNED_USER_CODE_PERMISSION_TEMPLATE_NAME);
451         String JavaDoc template = Util.loadResource(JWSACCMain.class, templateName);
452         
453         /*
454          *Create the temporary policy file to write to.
455          */

456         File JavaDoc policyFile = File.createTempFile("accjws-user", ".policy");
457         if ( ! retainTempFiles) {
458             policyFile.deleteOnExit();
459         }
460         
461         PrintStream JavaDoc ps = new PrintStream JavaDoc(policyFile);
462         
463         Properties JavaDoc p = new Properties JavaDoc();
464         
465         /*
466          *Use the list of class paths already set up on the EJBClassLoader.
467          *Then for each element in the class path, write a part of the policy
468          *file granting the privs specified in the selected template to the code path.
469          */

470         EJBClassLoader loader = (EJBClassLoader) getClassLoader();
471         for (URL JavaDoc classPathElement : loader.getURLs()) {
472             /*
473              *Convert the URL into a proper codebase expression suitable for
474              *a grant clause in the policy file.
475              */

476             String JavaDoc codeBase = Util.URLtoCodeBase(classPathElement);
477             if (codeBase != null) {
478                 p.setProperty(CODE_BASE_PLACEHOLDER_NAME, codeBase);
479                 String JavaDoc policyPart = Util.replaceTokens(template, p);
480
481                 ps.println(policyPart);
482             }
483         }
484         
485         /*
486          *Close the temp file and use it to refresh the current Policy object.
487          */

488         ps.close();
489         
490         JWSACCMain.refreshPolicy(policyFile);
491         
492         if ( ! retainTempFiles) {
493             policyFile.delete();
494         }
495     }
496     
497     /////////////////////////////////////////////////////////////////
498
// The following protected methods are overridden by at least //
499
// one of the sub classes. //
500
/////////////////////////////////////////////////////////////////
501

502     /**
503      *Expands the contents of the source archive into a temporary
504      *directory, using the same format as backend server expansions.
505      *@param file an archive file to be expanded
506      *@return an opened archive for the expanded directory archive
507      *@exception IOException in case of errors during the expansion
508      */

509     protected abstract AbstractArchive expand(File JavaDoc file)
510         throws IOException JavaDoc, Exception JavaDoc;
511
512     protected ApplicationClientDescriptor getAppClient(
513         Archivist archivist) {
514         return ApplicationClientDescriptor.class.cast(
515                     archivist.getDescriptor());
516     }
517
518     protected String JavaDoc getAppClientRoot(
519         AbstractArchive archive, ApplicationClientDescriptor descriptor) {
520         return archive.getArchiveUri();
521     }
522
523     protected void messageDescriptor(RootDeploymentDescriptor d,
524         Archivist archivist, AbstractArchive archive)
525             throws IOException JavaDoc, AnnotationProcessorException {
526         //default behavor: no op
527
}
528
529     protected List JavaDoc<String JavaDoc> getClassPaths(AbstractArchive archive) {
530         List JavaDoc<String JavaDoc> paths = new ArrayList JavaDoc();
531         paths.add(archive.getArchiveUri());
532         return paths;
533     }
534
535     /**
536      *Returns the main class that should be executed.
537      *@return the name of the main class to execute when the client starts
538      */

539     protected String JavaDoc getMainClassNameToRun(ApplicationClientDescriptor acDescr) {
540          if (mainClassNameToRun == null) {
541              if (mainClassFromCommandLine != null) {
542                  mainClassNameToRun = mainClassFromCommandLine;
543                  _logger.fine("Main class is " + mainClassNameToRun + " from command line");
544              } else {
545                  /*
546                   *Find out which class to execute from the descriptor.
547                   */

548                  mainClassNameToRun = getAppClient().getMainClassName();
549                  _logger.fine("Main class is " + mainClassNameToRun + " from descriptor");
550              }
551          }
552          return mainClassNameToRun;
553     }
554
555     protected boolean classContainsAnnotation(
556             String JavaDoc entry, AnnotationDetector detector,
557             AbstractArchive archive, ApplicationClientDescriptor descriptor)
558             throws FileNotFoundException JavaDoc, IOException JavaDoc {
559         String JavaDoc acRoot = getAppClientRoot(archive, descriptor);
560         String JavaDoc entryLocation = acRoot + File.separator + entry;
561         File JavaDoc entryFile = new File JavaDoc(entryLocation);
562         return detector.containsAnnotation(entryFile);
563     }
564
565     public String JavaDoc toString() {
566         String JavaDoc lineSep = System.getProperty("line.separator");
567         StringBuilder JavaDoc result = new StringBuilder JavaDoc();
568         result.append(this.getClass().getName() + ": " + lineSep);
569         result.append(" isJWS: " + isJWS);
570         result.append(" archive file: " + appClientFile.getAbsolutePath() + lineSep);
571         result.append(" archive type: " + appClientArchive.getClass().getName() + lineSep);
572         result.append(" archivist type: " + archivist.getClass().getName() + lineSep);
573         result.append(" main class to be run: " + mainClassNameToRun + lineSep);
574         result.append(" temporary archive directory: " + appClientArchive.getArchiveUri() + lineSep);
575         result.append(" class loader type: " + classLoader.getClass().getName() + lineSep);
576        
577         return result.toString();
578     }
579
580     //for debug purpose
581
protected static final boolean _keepExplodedDir =
582             Boolean.getBoolean("appclient.keep.exploded.dir");
583 }
584
Popular Tags