001: /*
002: * BEGIN_HEADER - DO NOT EDIT
003: *
004: * The contents of this file are subject to the terms
005: * of the Common Development and Distribution License
006: * (the "License"). You may not use this file except
007: * in compliance with the License.
008: *
009: * You can obtain a copy of the license at
010: * https://open-esb.dev.java.net/public/CDDLv1.0.html.
011: * See the License for the specific language governing
012: * permissions and limitations under the License.
013: *
014: * When distributing Covered Code, include this CDDL
015: * HEADER in each file and include the License file at
016: * https://open-esb.dev.java.net/public/CDDLv1.0.html.
017: * If applicable add the following below this CDDL HEADER,
018: * with the fields enclosed by brackets "[]" replaced with
019: * your own identifying information: Portions Copyright
020: * [year] [name of copyright owner]
021: */
022:
023: /*
024: * @(#)Archive.java
025: * Copyright 2004-2007 Sun Microsystems, Inc. All Rights Reserved.
026: *
027: * END_HEADER - DO NOT EDIT
028: */
029: package com.sun.jbi.management.repository;
030:
031: import com.sun.jbi.management.descriptor.Jbi;
032: import com.sun.jbi.management.descriptor.ServiceUnit;
033:
034: import java.io.File;
035: import java.io.InputStream;
036: import java.io.StringWriter;
037: import java.math.BigInteger;
038: import java.util.Calendar;
039: import java.util.HashMap;
040: import java.util.Iterator;
041: import java.util.List;
042: import java.util.Locale;
043: import java.util.zip.ZipEntry;
044: import java.util.zip.ZipFile;
045: import java.util.zip.ZipInputStream;
046:
047: import javax.xml.bind.JAXBContext;
048: import javax.xml.bind.Unmarshaller;
049: import javax.xml.bind.Marshaller;
050:
051: import javax.xml.transform.Transformer;
052: import javax.xml.transform.TransformerFactory;
053: import javax.xml.transform.stream.StreamResult;
054: import javax.xml.transform.stream.StreamSource;
055: import javax.xml.transform.OutputKeys;
056:
057: import com.sun.jbi.management.system.ManagementContext;
058:
059: /**
060: * Data object containing information about an archive in the ESB repsository.
061: */
062: public class Archive {
063: private static final String JBI_XML = "jbi.xml";
064: private static final String JBI_XML_BASE = "jbi_";
065: private static final String JBI_XML_EXT = ".xml";
066: private static final String JBI_XML_DIR = "META-INF/";
067: private static final String JBI_XML_PATH = JBI_XML_DIR + JBI_XML;
068:
069: /**
070: * registry schema file
071: */
072: public static final String JBI_DESCRIPTOR_SCHEMA = "jbi.xsd";
073:
074: /**
075: * registry schema subdir in JBI_HOME
076: */
077: public static final String JBI_DESCRIPTOR_SCHEMA_DIR = "schemas";
078:
079: /** JAXB content model for jbi.xml. */
080: private Jbi mJbiXml;
081: /** JAXB content model for localized jbi.xml. */
082: private Jbi mJbiXmlLocalized;
083: /** The String version of jbi.xml */
084: private String mJbiXmlString = null;
085: /** The localized String version of jbi.xml */
086: private String mJbiXmlStringLocalized;
087: /** The type of the archive. */
088: private ArchiveType mType;
089: /** Path to the archive. */
090: private String mPath;
091: /** The JBI name of the archive, as specified in jbi.xml. */
092: private String mJbiName;
093: /** The original file name for the archive. */
094: private String mFileName;
095: /** Time added to the repository. */
096: private Calendar mUploadTimestamp;
097: /** Last modified timestamp of jbi.xml file in archive package. */
098: private Calendar mJbiXmlTimestamp;
099: /** Size of the archive. */
100: private BigInteger mSize;
101: /** Map containing child archives -- archives within this archive. */
102: private HashMap mChildren;
103: /** Flag indicating that the archive jbi.xml should be validated against
104: * the JBI schema. */
105: private boolean mRequiresValidation;
106: /** Used to load JAXB object model for jbi.xml */
107: private Unmarshaller mReader;
108: private Marshaller mWriter;
109:
110: /**
111: * descriptor schema
112: */
113: private File mDescSchema;
114:
115: /** Create a new Archive object from the specified archive package. */
116: public Archive(File archiveZip, boolean validate)
117: throws java.io.IOException, RepositoryException {
118: mChildren = new HashMap();
119: mRequiresValidation = validate;
120:
121: try {
122: // setup JAXB
123: JAXBContext jc = JAXBContext.newInstance(
124: "com.sun.jbi.management.descriptor", Class.forName(
125: "com.sun.jbi.management.descriptor.Jbi")
126: .getClassLoader());
127: mReader = jc.createUnmarshaller();
128:
129: com.sun.jbi.EnvironmentContext envCtx = com.sun.jbi.util.EnvironmentAccess
130: .getContext();
131: File schemaDir = new File(envCtx.getJbiInstallRoot(),
132: JBI_DESCRIPTOR_SCHEMA_DIR);
133: mDescSchema = new File(schemaDir, JBI_DESCRIPTOR_SCHEMA);
134:
135: mReader
136: .setSchema(javax.xml.validation.SchemaFactory
137: .newInstance(
138: javax.xml.XMLConstants.W3C_XML_SCHEMA_NS_URI)
139: .newSchema(mDescSchema));
140:
141: mWriter = jc.createMarshaller();
142: mWriter.setProperty("jaxb.formatted.output", new Boolean(
143: true));
144:
145: } catch (Exception ex) {
146: throw new RepositoryException(ex.toString());
147: }
148:
149: parseArchive(archiveZip);
150:
151: // Get the String for the archive descriptor
152: initJbiXmlString(false);
153: initJbiXmlString(true);
154: }
155:
156: /** Return type of archive. */
157: public ArchiveType getType() {
158: return mType;
159: }
160:
161: /** Return the name of the archive as specified in jbi.xml. */
162: public String getJbiName() {
163: return mJbiName;
164: }
165:
166: /** Return the original name of the archive file. */
167: public String getFileName() {
168: return mFileName;
169: }
170:
171: /** Return the absolute path to the archive file. */
172: public String getPath() {
173: return mPath;
174: }
175:
176: /** Return a reference to the JAXB object model of the archive's
177: * deployment descriptor.
178: * @param localized set to <code>true</code> to return the localized
179: * version or <code>false</code> to return the default version.
180: * NOTE: Currently, only the server locale is supported.
181: */
182: public Jbi getJbiXml(boolean localized) {
183: if (localized) {
184: return mJbiXmlLocalized;
185: } else {
186: return mJbiXml;
187: }
188: }
189:
190: /** Return the XML String for the JBI XML
191: * @param localized set to <code>true</code> to return the localized
192: * version or <code>false</code> to return the default version.
193: * NOTE: Currently, only the server locale is supported.
194: */
195: public String getJbiXmlString(boolean localized) {
196: if (localized) {
197: return mJbiXmlStringLocalized;
198: } else {
199: return mJbiXmlString;
200: }
201: }
202:
203: /** Return the date that the archive was added to the repository. */
204: public java.util.Calendar getUploadTimestamp() {
205: return mUploadTimestamp;
206: }
207:
208: /** Return the size of the archive. */
209: public java.math.BigInteger getSize() {
210: return mSize;
211: }
212:
213: /** Return the timestamp of the jbi.xml file in the archive package. */
214: public java.util.Calendar getJbiXmlTimestamp() {
215: return mJbiXmlTimestamp;
216: }
217:
218: /** Sets the JBI name/id of this archive. */
219: public void setJbiName(String jbiName) {
220: mJbiName = jbiName;
221: }
222:
223: /** Sets the repository upload timestamp. */
224: public void setUploadTimestamp(Calendar timestamp) {
225: mUploadTimestamp = timestamp;
226: }
227:
228: /** Sets the path for the archive package. */
229: public void setPath(String path) {
230: mPath = path;
231: }
232:
233: /** Indicates whether this archive contains other archives. For example,
234: * a service assembly contains one or more service units.
235: */
236: public boolean hasChildren() {
237: return !mChildren.isEmpty();
238: }
239:
240: /** Lists the names of child archives, if any, contained within this
241: * archive.
242: */
243: public Iterator listChildren() {
244: return mChildren.keySet().iterator();
245: }
246:
247: /** Returns the path to the specified child archive.
248: * @param name the JBI name/id of the child archive
249: */
250: public String getChildPath(String name) {
251: return (String) mChildren.get(name);
252: }
253:
254: /**
255: * Given an archive zip file, return the entry for the jbi.xml for the
256: * specified locale, if one is available. If none is available, return the
257: * entry for the base jbi.xml. The search order is:
258: *
259: * 1. jbi_LL_CC.xml where "LL" is the language code and "CC" is the
260: * country code
261: * 2. jbi_LL.xml where "LL" is the language code
262: * 3. jbi.xml
263: *
264: * For information on the language and country codes, see the javadoc for
265: * java.util.ResourceBundle.
266: *
267: * @param zip the ZipFile representing the archive.
268: * @param locale the Locale for which the jbi.xml is to be obtained, or null
269: * to request the base jbi.xml file.
270: * @return the ZipEntry representing the jbi.xml that was found. If none
271: * was found, return value is null.
272: * @throws java.io.IOException if an I/O error occurs.
273: */
274: private ZipEntry getJbiXmlEntry(ZipFile archive, Locale locale)
275: throws java.io.IOException {
276: ZipEntry jbiXmlEntry;
277: String jbiXmlPath;
278:
279: if (null != locale) {
280: jbiXmlPath = JBI_XML_DIR + JBI_XML_BASE + locale.toString()
281: + JBI_XML_EXT;
282: jbiXmlEntry = archive.getEntry(jbiXmlPath);
283: if (null == jbiXmlEntry) {
284: jbiXmlPath = JBI_XML_DIR + JBI_XML_BASE
285: + locale.getLanguage() + JBI_XML_EXT;
286: jbiXmlEntry = archive.getEntry(jbiXmlPath);
287: if (null == jbiXmlEntry) {
288: jbiXmlEntry = archive.getEntry(JBI_XML_PATH);
289: }
290: }
291: } else {
292: jbiXmlEntry = archive.getEntry(JBI_XML_PATH);
293: }
294: return jbiXmlEntry;
295: }
296:
297: private void parseArchive(File archiveFile)
298: throws java.io.IOException, RepositoryException {
299: ZipFile zip;
300: ZipEntry jbiXmlEntry;
301: ZipEntry jbiXmlEntryLocalized;
302:
303: zip = new ZipFile(archiveFile);
304: mPath = archiveFile.getAbsolutePath();
305: mFileName = archiveFile.getName();
306:
307: jbiXmlEntry = getJbiXmlEntry(zip, null);
308: jbiXmlEntryLocalized = getJbiXmlEntry(zip, Locale.getDefault());
309:
310: // determine the size of the archive
311: mSize = BigInteger.valueOf(archiveFile.length());
312:
313: // process JBI metadata
314: try {
315: if (jbiXmlEntry == null) {
316: throw new java.io.FileNotFoundException(archiveFile
317: .getName()
318: + " : " + JBI_XML_PATH);
319: }
320:
321: mJbiXml = loadJbiXml(archiveFile.getName(), zip
322: .getInputStream(jbiXmlEntry));
323: if (jbiXmlEntry == jbiXmlEntryLocalized) {
324: mJbiXmlLocalized = mJbiXml;
325: } else {
326: mJbiXmlLocalized = loadJbiXml(archiveFile.getName(),
327: zip.getInputStream(jbiXmlEntryLocalized));
328: }
329:
330: parseJbiXml();
331:
332: if (mType.equals(ArchiveType.SERVICE_ASSEMBLY)) {
333: processServiceUnits(zip);
334: }
335: } catch (java.io.IOException ioex) {
336: throw new RepositoryException(ioex.toString());
337: } finally {
338: if (zip != null) {
339: zip.close();
340: }
341: }
342:
343: // set the jbi.xml timestamp value to its modified date in the zip
344: mJbiXmlTimestamp = Calendar.getInstance();
345: mJbiXmlTimestamp.setTimeInMillis(jbiXmlEntry.getTime());
346: }
347:
348: /** Parse the archive's jbi.xml using JAXB. Appropriate member variables
349: * are set based on jbi.xml content.
350: */
351: private void parseJbiXml() {
352: // figure out what we are dealing with -- there's really not an elegant
353: // way to do this.
354: if (mJbiXml.getComponent() != null) {
355: mJbiName = mJbiXml.getComponent().getIdentification()
356: .getName();
357: mType = ArchiveType.COMPONENT;
358: } else if (mJbiXml.getSharedLibrary() != null) {
359: mJbiName = mJbiXml.getSharedLibrary().getIdentification()
360: .getName();
361: mType = ArchiveType.SHARED_LIBRARY;
362: } else if (mJbiXml.getServiceAssembly() != null) {
363: mJbiName = mJbiXml.getServiceAssembly().getIdentification()
364: .getName();
365: mType = ArchiveType.SERVICE_ASSEMBLY;
366: } else {
367: mType = ArchiveType.SERVICE_UNIT;
368: }
369: }
370:
371: /** Find service unit archives and store them as children. */
372: private void processServiceUnits(ZipFile zip)
373: throws java.io.IOException, RepositoryException {
374: List suList;
375: ServiceUnit su;
376: String suName;
377: String suPath;
378: ZipEntry suZip;
379: InputStream suStream;
380:
381: suList = mJbiXml.getServiceAssembly().getServiceUnit();
382: for (int i = 0; i < suList.size(); i++) {
383: su = (ServiceUnit) suList.get(i);
384: suName = su.getIdentification().getName();
385: suPath = su.getTarget().getArtifactsZip();
386: suZip = zip.getEntry(suPath);
387:
388: if (suZip == null) {
389: throw new java.io.FileNotFoundException(localName(zip)
390: + " : " + suPath);
391: }
392:
393: // load the jbi.xml strictly for validation purposes
394: suStream = getZipStream(zip.getInputStream(suZip),
395: JBI_XML_PATH);
396: if (suStream.available() <= 0) {
397: throw new java.io.FileNotFoundException(suPath + " : "
398: + JBI_XML_PATH);
399: }
400:
401: loadJbiXml(suPath, suStream);
402:
403: // looks good, add SU archive to the list of children
404: mChildren.put(suName, suPath);
405: }
406: }
407:
408: /** Given an input stream, this method returns a zip input stream with the
409: * pointer at the position of the specified entry.
410: */
411: private InputStream getZipStream(InputStream zipStream, String name)
412: throws java.io.IOException {
413: ZipInputStream zis;
414: ZipEntry entry;
415:
416: zis = new ZipInputStream(zipStream);
417: while ((entry = zis.getNextEntry()) != null) {
418: if (entry.getName().equals(name)) {
419: // stream pointer is set to beginning of targeted entry
420: break;
421: }
422: }
423:
424: return zis;
425: }
426:
427: private Jbi loadJbiXml(String entryName, InputStream jbiXmlStream)
428: throws RepositoryException {
429: Jbi jbiXml;
430:
431: try {
432: jbiXml = (Jbi) mReader.unmarshal(jbiXmlStream);
433:
434: } catch (javax.xml.bind.JAXBException jEx) {
435: String message = jEx.getLinkedException() != null ? jEx
436: .getLinkedException().getMessage() : jEx
437: .getMessage();
438:
439: if (message == null) {
440: message = jEx.toString();
441: }
442: // schema validation failed
443: throw new RepositoryException(
444: "Schema validation failed for " + entryName + " : "
445: + JBI_XML + ". " + message);
446: }
447:
448: return jbiXml;
449: }
450:
451: /** Trims the absolute path provided by ZipFile.getName() down to
452: * the name of the file.
453: */
454: private String localName(ZipFile zip) {
455: return new File(zip.getName()).getName();
456: }
457:
458: /** Initialize the the XML String for the JBI XML
459: * @param localized set to <code>true</code> to initialize the localized
460: * string or <code>false</code> to initialize the default string.
461: */
462: private void initJbiXmlString(boolean localized)
463: throws RepositoryException {
464: StringWriter sw = null;
465: ZipFile zip = null;
466: StreamSource source = null;
467: StreamResult result = null;
468: try {
469: zip = new ZipFile(new File(mPath));
470: ZipEntry jbiXmlEntry;
471: if (localized) {
472: jbiXmlEntry = getJbiXmlEntry(zip, Locale.getDefault());
473: } else {
474: jbiXmlEntry = getJbiXmlEntry(zip, null);
475: }
476:
477: TransformerFactory tF = TransformerFactory.newInstance();
478: Transformer tr = tF.newTransformer();
479: tr.setOutputProperty(OutputKeys.INDENT, "yes");
480:
481: source = new StreamSource(zip.getInputStream(jbiXmlEntry));
482: sw = new StringWriter();
483: result = new StreamResult(sw);
484: tr.transform(source, result);
485: if (sw != null) {
486: if (localized) {
487: mJbiXmlStringLocalized = sw.toString();
488: } else {
489: mJbiXmlString = sw.toString();
490: }
491: }
492: } catch (Exception ex) {
493: throw new RepositoryException(ex.toString());
494: } finally {
495: try {
496: if (zip != null) {
497: zip.close();
498: }
499:
500: if (sw != null) {
501: sw.close();
502: }
503:
504: if (source != null) {
505: source.getInputStream().close();
506: }
507:
508: if (result != null) {
509: result.getOutputStream().close();
510: }
511: } catch (Exception ex) {
512: ;
513: }
514: }
515: }
516: }
|