001: /**
002: * JOnAS : Java(TM) OpenSource Application Server
003: * Copyright (C) 1999-2007 Bull S.A.S.
004: * Contact: jonas-team@objectweb.org
005: *
006: * This library is free software; you can redistribute it and/or
007: * modify it under the terms of the GNU Lesser General Public
008: * License as published by the Free Software Foundation; either
009: * version 2.1 of the License, or any later version.
010: *
011: * This library is distributed in the hope that it will be useful,
012: * but WITHOUT ANY WARRANTY; without even the implied warranty of
013: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
014: * Lesser General Public License for more details.
015: *
016: * You should have received a copy of the GNU Lesser General Public
017: * License along with this library; if not, write to the Free Software
018: * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307
019: * USA
020: *
021: * --------------------------------------------------------------------------
022: * $Id: Application.java 10094 2007-03-23 16:15:39Z sauthieg $
023: * --------------------------------------------------------------------------
024: */package org.objectweb.jonas_lib.genbase.archive;
025:
026: import java.io.File;
027: import java.io.IOException;
028: import java.io.InputStream;
029: import java.net.URL;
030: import java.net.URLClassLoader;
031: import java.util.Hashtable;
032: import java.util.Iterator;
033: import java.util.List;
034: import java.util.Map;
035: import java.util.Vector;
036: import java.util.jar.Attributes;
037: import java.util.jar.JarFile;
038:
039: import javax.xml.parsers.ParserConfigurationException;
040:
041: import org.w3c.dom.Document;
042: import org.xml.sax.SAXException;
043:
044: import org.objectweb.jonas_ear.deployment.api.EarDeploymentDesc;
045: import org.objectweb.jonas_ear.deployment.lib.EarDeploymentDescManager;
046: import org.objectweb.jonas_ear.deployment.xml.Web;
047:
048: import org.objectweb.jonas_lib.deployment.api.DeploymentDescException;
049: import org.objectweb.jonas_lib.files.FileUtils;
050: import org.objectweb.jonas_lib.files.FileUtilsException;
051: import org.objectweb.jonas_lib.genbase.GenBaseException;
052: import org.objectweb.jonas_lib.genbase.utils.TempRepository;
053: import org.objectweb.jonas_lib.genbase.utils.XMLUtils;
054: import org.objectweb.jonas_lib.loader.EjbJarClassLoader;
055:
056: import org.objectweb.util.monolog.api.BasicLevel;
057:
058: /**
059: * Application is a wrapper around an ear (packaged as a jar or as an unpacked
060: * jar).
061: *
062: * @author Guillaume Sauthier
063: */
064: public class Application extends J2EEArchive {
065:
066: /** list of embded clients archive. */
067: private List clients;
068:
069: /** list of embded webapps archive. */
070: private List webapps;
071:
072: /** list of embded ejbjars archive. */
073: private List ejbjars;
074:
075: /** Application DeploymentDesc */
076: private EarDeploymentDesc earDD = null;
077:
078: /** name to document map */
079: private Map descriptors;
080:
081: /** application Descriptor */
082: private Document app;
083:
084: /** EJB Jar ClassLoader */
085: private URLClassLoader ejbCL = null;
086:
087: /** Common Libs ClassLoader */
088: private URLClassLoader commonCL = null;
089:
090: /** libs path File */
091: private List pathFiles;
092:
093: /** application archive filename */
094: private String appFilename;
095:
096: /**
097: * Creates a new Application archive.
098: *
099: * @param archive the file containing the application archive.
100: *
101: * @throws GenBaseException When Init fails
102: */
103: public Application(Archive archive) throws GenBaseException {
104: super (archive);
105: appFilename = archive.getRootFile().getName();
106: if (getLogger().isLoggable(BasicLevel.DEBUG)) {
107: getLogger().log(
108: BasicLevel.DEBUG,
109: "Wrapping '" + archive.getName()
110: + "' in Application");
111: }
112: init();
113: }
114:
115: /**
116: * Initialize the Application. Creates modules lists, unpack if not
117: * unpacked.
118: *
119: * @throws GenBaseException When application unpack fails or when Decriptors
120: * cannot be parsed or found.
121: */
122: protected void init() throws GenBaseException {
123:
124: pathFiles = new Vector();
125:
126: ejbjars = new Vector();
127: webapps = new Vector();
128: clients = new Vector();
129:
130: if (isPacked()) {
131:
132: try {
133: // unpack data
134: JarFile jf = new JarFile(getArchive().getRootFile());
135: TempRepository tr = TempRepository.getInstance();
136: File unpacked = tr.createDir();
137: FileUtils.unpack(jf, unpacked);
138: jf.close();
139: setArchive(new FileArchive(unpacked));
140: } catch (FileUtilsException fue) {
141: String err = getI18n().getMessage(
142: "Application.init.unpackException",
143: getArchive().getRootFile());
144: throw new GenBaseException(err, fue);
145: } catch (IOException ioe) {
146: String err = getI18n().getMessage(
147: "Application.init.unpackException",
148: getArchive().getRootFile());
149: throw new GenBaseException(err, ioe);
150: }
151: }
152:
153: // load META-INF/application.xml
154: try {
155: earDD = EarDeploymentDescManager.getDeploymentDesc(
156: getRootFile().getAbsolutePath(), Thread
157: .currentThread().getContextClassLoader());
158: } catch (DeploymentDescException dde) {
159: String err = getI18n().getMessage(
160: "Application.init.earDDExc",
161: getArchive().getRootFile());
162: throw new GenBaseException(err, dde);
163: }
164:
165: // add EjbJars
166: String[] ejbs = earDD.getEjbTags();
167:
168: for (int i = 0; i < ejbs.length; i++) {
169: File ejbFile = new File(getRootFile(), ejbs[i]);
170: Archive ejbArch = null;
171:
172: if (ejbFile.isDirectory()) {
173: // Unpacked Jar
174: ejbArch = new FileArchive(ejbFile);
175: } else {
176: // Packed Jar
177: ejbArch = new JarArchive(ejbFile);
178: }
179: ejbjars.add(new EjbJar(ejbArch, this ));
180:
181: // add entries for Class-Path
182: addClassPathEntry(ejbArch);
183: }
184:
185: // add WebApps
186: Web[] webs = earDD.getWebTags();
187:
188: for (int i = 0; i < webs.length; i++) {
189: File webFile = new File(getRootFile(), webs[i].getWebUri());
190:
191: if (webFile.isDirectory()) {
192: // Unpacked Jar
193: webapps.add(new WebApp(new FileArchive(webFile), this ));
194: } else {
195: // Packed Jar
196: webapps.add(new WebApp(new JarArchive(webFile), this ));
197: }
198: }
199:
200: // add Clients
201: String[] clts = earDD.getClientTags();
202:
203: for (int i = 0; i < clts.length; i++) {
204: File clientFile = new File(getRootFile(), clts[i]);
205:
206: if (clientFile.isDirectory()) {
207: // Unpacked Jar
208: clients.add(new Client(new FileArchive(clientFile),
209: this ));
210: } else {
211: // Packed Jar
212: clients
213: .add(new Client(new JarArchive(clientFile),
214: this ));
215: }
216: }
217:
218: // Create ear ClassLoader (from MANIFEST/Class-Path general entry)
219: setModuleClassloader(createEARClassLoader());
220:
221: // Create EJB ClassLoader
222: ejbCL = createEJBClassLoader();
223:
224: loadDescriptors();
225: }
226:
227: /**
228: * Load Deployment Descriptor of an Application.
229: *
230: * @throws GenBaseException When parsing of application.xml fails
231: */
232: protected void loadDescriptors() throws GenBaseException {
233: try {
234: app = XMLUtils.newDocument(getApplicationInputStream(),
235: "META-INF/application.xml", isDTDsAllowed());
236: } catch (SAXException saxe) {
237: String err = getI18n().getMessage(
238: "Application.loadDescriptors.parseError");
239: throw new GenBaseException(err, saxe);
240: } catch (ParserConfigurationException pce) {
241: String err = getI18n().getMessage(
242: "Application.loadDescriptors.prepare");
243: throw new GenBaseException(err, pce);
244: } catch (IOException ioe) {
245: String err = getI18n().getMessage(
246: "Application.loadDescriptors.parseError");
247: throw new GenBaseException(err, ioe);
248: }
249:
250: descriptors = new Hashtable();
251: descriptors.put("META-INF/application.xml", app);
252: }
253:
254: /**
255: * Initialize the Archive.
256: * @throws GenBaseException When initialization fails.
257: */
258: public void initialize() throws GenBaseException {
259:
260: // init ejbjars
261: for (Iterator i = ejbjars.iterator(); i.hasNext();) {
262: EjbJar ejb = (EjbJar) i.next();
263: ejb.initialize();
264: }
265:
266: // init webapps
267: for (Iterator i = webapps.iterator(); i.hasNext();) {
268: WebApp web = (WebApp) i.next();
269: web.initialize();
270: }
271:
272: // init clients
273: for (Iterator i = clients.iterator(); i.hasNext();) {
274: Client client = (Client) i.next();
275: client.initialize();
276: }
277:
278: }
279:
280: /**
281: * Returns the name of the Archive. Overrides J2EEArchive.getName();
282: *
283: * @see org.objectweb.jonas_lib.genbase.archive.J2EEArchive#getName()
284: *
285: * @return the name of the Archive.
286: */
287: public String getName() {
288: return appFilename;
289: }
290:
291: /**
292: * Construct a ClassLoader for EJBs inside an application.
293: *
294: * @return the Ejb-Jar ClassLoader
295: *
296: * @throws GenBaseException When URLClassLoader cannot be created.
297: */
298: private URLClassLoader createEJBClassLoader()
299: throws GenBaseException {
300:
301: URL[] urls = new URL[pathFiles.size() + ejbjars.size()];
302: int index = 0;
303: for (Iterator i = pathFiles.iterator(); i.hasNext(); index++) {
304: File f = (File) i.next();
305: try {
306: urls[index] = f.toURL();
307: } catch (IOException ioe) {
308: String err = "Cannot convert " + f + " to URL.";
309: throw new GenBaseException(err, ioe);
310: }
311: }
312:
313: for (Iterator i = ejbjars.iterator(); i.hasNext(); index++) {
314: try {
315: urls[index] = ((EjbJar) i.next()).getRootFile().toURL();
316: } catch (IOException ioe) {
317: String err = "Cannot transform as a URL : "
318: + ioe.getMessage();
319: throw new GenBaseException(err, ioe);
320: }
321: }
322:
323: try {
324: return new EjbJarClassLoader(urls, getModuleClassloader());
325: } catch (IOException ioe) {
326: String err = "Cannot create EjbJarClassLoader";
327: throw new GenBaseException(err, ioe);
328: }
329:
330: }
331:
332: /**
333: * Construct a ClassLoader for the application.
334: *
335: * @return the Application ClassLoader
336: *
337: * @throws GenBaseException When URLClassLoader cannot be created.
338: */
339: private URLClassLoader createEARClassLoader()
340: throws GenBaseException {
341:
342: // find parent ClassLoader
343: ClassLoader parent = Thread.currentThread()
344: .getContextClassLoader();
345:
346: // get Manifest Attributes if any
347: String classpath = getManifest().getMainAttributes().getValue(
348: Attributes.Name.CLASS_PATH);
349: URL[] urls = new URL[0];
350: if (classpath != null) {
351: // Lookup specified files.
352: String[] paths = classpath.split(" ");
353: urls = new URL[paths.length];
354: for (int i = 0; i < paths.length; i++) {
355: try {
356: URL path = new File(getRootFile(), paths[i])
357: .toURL();
358: urls[i] = path;
359: } catch (IOException ioe) {
360: String err = "Cannot transform '" + paths[i]
361: + "' as a URL";
362: throw new GenBaseException(err, ioe);
363: }
364: }
365: }
366:
367: return new URLClassLoader(urls, parent);
368: }
369:
370: /**
371: * Search the given Archive for ClassPath Manifest entry and add the entries
372: * in the EAR classpath.
373: *
374: * @param a the Archive to explore
375: *
376: * @throws GenBaseException When a path cannot be added in the EAR classpath.
377: */
378: private void addClassPathEntry(Archive a) throws GenBaseException {
379: // get Manifest Attributes if any
380: String classpath = a.getManifest().getMainAttributes()
381: .getValue(Attributes.Name.CLASS_PATH);
382:
383: if (classpath != null) {
384: // Lookup specified files.
385: String[] paths = classpath.split(" ");
386: for (int i = 0; i < paths.length; i++) {
387: try {
388: File path = new File(a.getRootFile()
389: .getParentFile(), paths[i])
390: .getCanonicalFile();
391: if (!pathFiles.contains(path)) {
392: pathFiles.add(path);
393: }
394: } catch (IOException ioe) {
395: String err = "Cannot add in EAR classpath :"
396: + paths[i];
397: throw new GenBaseException(err, ioe);
398: }
399: }
400:
401: }
402:
403: }
404:
405: /**
406: * Returns the Document of the application.xml file.
407: *
408: * @return the Document of the application.xml file.
409: */
410: public Document getApplicationDoc() {
411: return app;
412: }
413:
414: /**
415: * Returns the InputStream of the application.xml file.
416: *
417: * @return the InputStream of the application.xml file.
418: *
419: * @throws IOException When InputStream of application.xml cannot be
420: * returned
421: */
422: public InputStream getApplicationInputStream() throws IOException {
423: InputStream is = null;
424:
425: if (isPacked()) {
426: is = getInputStream("META-INF/application.xml");
427: } else {
428: is = getInputStream("META-INF" + File.separator
429: + "application.xml");
430: }
431:
432: return is;
433: }
434:
435: /**
436: * Add a new EjbJar in the Application.
437: *
438: * @param ejbjar the added EjbJar
439: */
440: public void addEjbJar(EjbJar ejbjar) {
441: ejbjars.add(ejbjar);
442: // add module in application.xml
443: XMLUtils.addEjb(app, ejbjar);
444: }
445:
446: /**
447: * Add a new Client in the Application.
448: *
449: * @param client the added Client
450: */
451: public void addClient(Client client) {
452: clients.add(client);
453: // add module in application.xml
454: XMLUtils.addClient(app, client);
455: }
456:
457: /**
458: * Add a new WebApp in the Application.
459: *
460: * @param webapp the added webapp
461: * @param context context of the webapp
462: */
463: public void addWebApp(WebApp webapp, String context) {
464: if (!XMLUtils.isWebModuleAlreadyDeclared(app, webapp.getName())) {
465: // the web module is not already declared
466: webapps.add(webapp);
467: // add module in application.xml
468: XMLUtils.addWebApp(app, webapp, context);
469: }
470: // use latest file
471: addFile(webapp.getRootFile(), webapp.getName());
472: }
473:
474: /**
475: * Returns the Iterator of EjbJar contained in this Application.
476: *
477: * @return the Iterator of EjbJar contained in this Application.
478: */
479: public Iterator getEjbJars() {
480: return ejbjars.iterator();
481: }
482:
483: /**
484: * Returns the Iterator of WebApp contained in this Application.
485: *
486: * @return the Iterator of WebApp contained in this Application.
487: */
488: public Iterator getWebApps() {
489: return webapps.iterator();
490: }
491:
492: /**
493: * Returns the Iterator of WebApp contained in this Application.
494: *
495: * @return the Iterator of WebApp contained in this Application.
496: */
497: public Iterator getClients() {
498: return clients.iterator();
499: }
500:
501: /**
502: * Returns the ClassLoader of this ear archive.
503: *
504: * @return the ClassLoader of this ear archive.
505: */
506: public URLClassLoader getEARClassLoader() {
507: return commonCL;
508: }
509:
510: /**
511: * Returns the ClassLoader of the ejbs within this archive.
512: *
513: * @return the ClassLoader of the ejbs within this archive.
514: */
515: public URLClassLoader getEJBClassLoader() {
516: return ejbCL;
517: }
518:
519: /**
520: * Returns a Map of name to Document for each modified Descriptor of the
521: * archive.
522: *
523: * @return a Map of name to Document
524: */
525: public Map getDescriptors() {
526: return descriptors;
527: }
528:
529: /**
530: * Returns true if filename must be omitted in the archive.
531: *
532: * @param name filename to be tested
533: *
534: * @return true if filename must be omitted.
535: */
536: public boolean omit(String name) {
537: return (name.equals("META-INF/application.xml") || name
538: .equals("META-INF\\application.xml"));
539: }
540:
541: /**
542: * @param clients The clients to set.
543: */
544: public void setClients(List clients) {
545: this .clients = clients;
546: }
547:
548: /**
549: * @param ejbjars The ejbjars to set.
550: */
551: public void setEjbjars(List ejbjars) {
552: this .ejbjars = ejbjars;
553: }
554:
555: /**
556: * @param webapps The webapps to set.
557: */
558: public void setWebapps(List webapps) {
559: this .webapps = webapps;
560: }
561:
562: /**
563: * @return Returns the app.
564: */
565: public Document getApp() {
566: return app;
567: }
568:
569: /**
570: * Close this archive
571: */
572: public void close() {
573: super .close();
574: // Remove ear deployment desc
575: earDD = null;
576: // reset fields
577: descriptors = null;
578: app = null;
579: ejbCL = null;
580: commonCL = null;
581: pathFiles = null;
582:
583: // reset
584: for (Iterator i = getEjbJars(); i.hasNext();) {
585: EjbJar ejbjar = (EjbJar) i.next();
586: ejbjar.close();
587: }
588:
589: // fill webapp list
590: for (Iterator i = getWebApps(); i.hasNext();) {
591: WebApp webapp = (WebApp) i.next();
592: webapp.close();
593: }
594:
595: // fill client list
596: for (Iterator i = getClients(); i.hasNext();) {
597: Client client = (Client) i.next();
598: client.close();
599: }
600:
601: // reset internal lists
602: clients = null;
603: webapps = null;
604: ejbjars = null;
605:
606: }
607: }
|