0001: /*
0002: * ParManifest.java
0003: *
0004: * Created on October 25, 2001, 10:12 AM
0005: */
0006:
0007: package com.sun.portal.desktop.deployment;
0008:
0009: import java.util.Map;
0010: import java.util.StringTokenizer;
0011: import java.util.Vector;
0012: import java.util.Iterator;
0013: import java.util.Date;
0014: import java.util.Set;
0015:
0016: import java.util.jar.Manifest;
0017: import java.util.jar.Attributes;
0018:
0019: import java.util.zip.ZipEntry;
0020:
0021: import java.text.SimpleDateFormat;
0022: import java.text.DateFormat;
0023: import java.text.FieldPosition;
0024:
0025: import java.io.PrintStream;
0026: import java.io.InputStream;
0027: import java.io.File;
0028:
0029: import javax.xml.parsers.DocumentBuilder;
0030: import javax.xml.parsers.DocumentBuilderFactory;
0031:
0032: import org.w3c.dom.Document;
0033:
0034: /**
0035: * Specialized manifest used in .par files (portal server specialized .jar files).
0036: *
0037: * The .par file contains three types of files:
0038: *
0039: * 1 - class files
0040: * 2 - XML display profile files, which use the psdp.dtd to express properties
0041: * for installing providers and/or channels.
0042: * 3 - other files, presumed to be "rooted" via some property name on the
0043: * target server, the applicable property being part of the path in the
0044: * .par file
0045: *
0046: * The manifest allows us to determine what the .par file contains, and unpack this
0047: * stuff. This class translates between the portal server concepts, and manifest headers
0048: * Most importantly, it hides the top level directory structure contained in the archive.
0049: *
0050: * Manifests contain "main" attribute sections, and "entry" attribute sections, which are
0051: * associated with specific files in the archive.
0052: *
0053: * For .par files, the "entries" correspond to files of type 2 above - the DP XML files.
0054: * Each file of this type should have an entry, and corresponds to a single channel and/or
0055: * provider.
0056: *
0057: * Main attributes:
0058: *
0059: * .par file manifests contain specialized headers providing a version, which also
0060: * serves as a sanity check that an archive is really a .par file.
0061: *
0062: * There are also headers which provide the names of the top level archive directories
0063: * for each of the three files listed above. At most one of these may be null, indicating
0064: * that those files are realized at the root of the entire archive. By default, this is
0065: * done with the class files, so that the .par file may be used as a normal .jar file in
0066: * class paths, and it will work.
0067: *
0068: * However, this class is intended to allow arbitrary assignment of the root directories
0069: * for the 3 file types, provided that at most one is null, and the specified ones are distinct.
0070: *
0071: * Entry attributes:
0072: *
0073: * The entry corresponds to a particular XML describing a "relocatable" channel and/or
0074: * provider. Three headers are defined for each entry:
0075: *
0076: * An "include" header, which defines the other files of the archive which go with this
0077: * particular entry, and for which particular types of extractions (provider vs. channel)
0078: *
0079: * An optional "Autoextract" operation. This header describes an automatic operation which may
0080: * be done with this entry of the archive. It uses the same format as would be used to specify
0081: * the operation as a command line argument.
0082: *
0083: * Default Entry:
0084: *
0085: * The first entry made to the manifest will become the "default" entry, which may be accessed
0086: * with a null entry name. If the .par file only contains one entry, this means that the user
0087: * does not need to know an explicit entry name to access it.
0088: *
0089: * .par file pacakaging below the root directory
0090: *
0091: * class files - normal .jar file packaging, with a directory structure reflecting the java packages
0092: *
0093: * dp files - xml files directly under the root.
0094: *
0095: * prop driven files - first component is the property name used to locate the file, the rest is
0096: * the file name relative to that directory.
0097: *
0098: * ParManifest API usage:
0099: *
0100: * This class provides the translation for ZipEntry's and manipulation of path information for these
0101: * file types. The ZipEntry is the way in which standard .jar file API's reference contents of
0102: * a jar. The caller view these files the following ways:
0103: *
0104: * class files - either full class name or package name + class name.
0105: *
0106: * prop drive files - a property name plus a path.
0107: *
0108: * DP XML files - a simple name, usually corresponding to the name given by the "name" attribute
0109: * of the psdp:parentry tag of the XML file, though it technically doesn't have to.
0110: *
0111: * When the caller is building up a manifest, they call addDPEntry(), addDPEntryInclude*() and
0112: * addDPEntryAutoExtract() to register the files and operations they are going to package in the
0113: * .par file.
0114: *
0115: * When the caller is reading a .par file, they obtain the normal Manifest object, and call
0116: * static makeManifest() to obtain a ParManifest, which can handle the header information appropriately.
0117: * API calls allow the user to find the entry names contained in the manifest, and get the included
0118: * files and automated operations defined with each entry.
0119: *
0120: * In all cases, the ParManifest get*Zip() methods may be used to obtain the appropriate ZipEntry
0121: * structures that allow the caller to stream in and out of the archive through the normal .jar file
0122: * I/O mechanisms.
0123: *
0124: * @author yabob
0125: * @version
0126: */
0127:
0128: public class ParManifest extends Manifest {
0129:
0130: // types of files included in manifest.
0131:
0132: public static final int CLASSFILE = 1;
0133: public static final int DPFILE = 2;
0134: public static final int PBFILE = 3;
0135: public static final int STATFILE = 4;
0136: public static final int WARFILE = 5;
0137: public static final int CONFFILE = 6;
0138:
0139: // default string for the par file root directories
0140: public static final String DEF_PBFILES_STR = "pbfiles";
0141: public static final String DEF_DP_STR = "dp";
0142: public static final String DEF_STATIC_STR = "static";
0143: public static final String DEF_WAR_STR = "war";
0144: public static final String DEF_CONFIG_STR = "config";
0145:
0146: // Construct a fresh ParManifest - used when building a new .par file
0147: //
0148: // We really ought to produce an exception if more than one argument is
0149: // null, but exceptions thrown in constructors are annoying.
0150: //
0151: // If more than one root is null, it will surface as ambiguities when
0152: // paths are interpreted.
0153:
0154: public ParManifest(String classroot, String dproot,
0155: String pbfileroot, String sroot, String wroot,
0156: String croot, String backupVersion) {
0157:
0158: super ();
0159:
0160: m_CRoot = classroot;
0161: m_DPRoot = dproot;
0162: m_PBFRoot = pbfileroot;
0163: m_SRoot = sroot;
0164: m_WRoot = wroot;
0165: m_ConfRoot = croot;
0166:
0167: Attributes ma = getMainAttributes();
0168:
0169: // If you don't insert a manifest version, the main attributes don't
0170: // get streamed out in the archive.
0171:
0172: ma.put(Attributes.Name.MANIFEST_VERSION, "1.0");
0173:
0174: // Now stamp "our" version
0175:
0176: ma.putValue(MAKEY_VERSION, Integer.toString(VERSION));
0177:
0178: // Backup file version
0179: if (backupVersion != null) {
0180: ma.putValue(MAKEY_BACKUP_VERSION, backupVersion);
0181:
0182: // Timestamp
0183: long msec = System.currentTimeMillis();
0184: Date d = new Date(msec);
0185: SimpleDateFormat formatter = new SimpleDateFormat(
0186: "MM/dd/yyyy K:mm a z");
0187: StringBuffer buf = new StringBuffer();
0188: formatter.format(d, buf, new java.text.FieldPosition(
0189: DateFormat.YEAR_FIELD));
0190: buf.append(" | ").append(Long.toString(msec));
0191:
0192: ma.putValue(MAKEY_BACKUP_TIMESTAMP, buf.toString());
0193: }
0194:
0195: // and our root information
0196:
0197: if (classroot != null) {
0198: ma.putValue(MAKEY_CLASSROOT, classroot);
0199: }
0200: if (dproot != null) {
0201: ma.putValue(MAKEY_DPROOT, dproot);
0202: }
0203: if (pbfileroot != null) {
0204: ma.putValue(MAKEY_PBFILEROOT, pbfileroot);
0205: }
0206: if (sroot != null) {
0207: ma.putValue(MAKEY_STATROOT, sroot);
0208: }
0209: if (wroot != null) {
0210: ma.putValue(MAKEY_WARROOT, wroot);
0211: }
0212: if (croot != null) {
0213: ma.putValue(MAKEY_CONFROOT, croot);
0214: }
0215: }
0216:
0217: // Default the roots. By default, we set class root to null,
0218: // so that using the archive as a regular jar library works, at
0219: // least to load classes.
0220:
0221: public ParManifest() {
0222: this (null, DEF_DP_STR, DEF_PBFILES_STR, DEF_STATIC_STR,
0223: DEF_WAR_STR, DEF_CONFIG_STR, null);
0224: }
0225:
0226: // This constructor takes a backup version as the input, use the
0227: // default for all other parameters
0228: public ParManifest(String backupVersion) {
0229: this (null, DEF_DP_STR, DEF_PBFILES_STR, DEF_STATIC_STR,
0230: DEF_WAR_STR, DEF_CONFIG_STR, backupVersion);
0231: }
0232:
0233: // Construct a ParManifest from an existing Manifest - used when reading
0234: // a ParFile (through makeManifest())
0235: //
0236: // This constructor is private because we want to error check the manifest
0237: // before allowing a ParManifest to be a copy of a Manifest file.
0238:
0239: private ParManifest(Manifest man) {
0240:
0241: super (man);
0242:
0243: Attributes ma = getMainAttributes();
0244:
0245: m_CRoot = ma.getValue(MAKEY_CLASSROOT);
0246: m_DPRoot = ma.getValue(MAKEY_DPROOT);
0247: m_PBFRoot = ma.getValue(MAKEY_PBFILEROOT);
0248: m_SRoot = ma.getValue(MAKEY_STATROOT);
0249: if (ma.getValue(MAKEY_WARROOT) != null) {
0250: m_WRoot = ma.getValue(MAKEY_WARROOT);
0251: }
0252: if (ma.getValue(MAKEY_CONFROOT) != null) {
0253: m_ConfRoot = ma.getValue(MAKEY_CONFROOT);
0254: }
0255: if (ma.getValue(MAKEY_BACKUP_VERSION) != null) {
0256: m_BackupVersion = ma.getValue(MAKEY_BACKUP_VERSION);
0257: }
0258: }
0259:
0260: // Public static method for obtaining a ParManifest copied from a Manifest
0261:
0262: public static ParManifest makeManifest(Manifest man)
0263: throws ParFileException {
0264:
0265: Attributes ma = man.getMainAttributes();
0266:
0267: String ver = ma.getValue(MAKEY_VERSION);
0268: if (ver == null) {
0269: throw new ParFileException("errorManifestNoVersion");
0270: }
0271: try {
0272: if (Integer.parseInt(ver) > VERSION) {
0273: throw new ParFileException("errorManifestBadVersion");
0274: }
0275: } catch (Exception ex) {
0276: throw new ParFileException("errorManifestBadVersion");
0277: }
0278:
0279: return new ParManifest(man);
0280: }
0281:
0282: // Methods to obtain zip entries based on particular roots
0283:
0284: public ZipEntry getClassZip(String pkg, String classname) {
0285: return new ZipEntry(getClassPath(pkg, classname));
0286: }
0287:
0288: public ZipEntry getClassZip(String fullclassname) {
0289: return new ZipEntry(getClassPath(fullclassname));
0290: }
0291:
0292: public ZipEntry getDPZip(String name) throws ParFileException {
0293: return new ZipEntry(getDPPath(name, false));
0294: }
0295:
0296: public ZipEntry getDPDocZip(String name) throws ParFileException {
0297: return new ZipEntry(getDPDocPath(name));
0298: }
0299:
0300: public ZipEntry getPBFileZip(String prop, String path) {
0301: return new ZipEntry(getPBFPath(prop, path));
0302: }
0303:
0304: public ZipEntry getStatZip(String path) {
0305: return new ZipEntry(getStaticPath(path));
0306: }
0307:
0308: public ZipEntry getPropertiesZip(String name)
0309: throws ParFileException {
0310: return new ZipEntry(getPropertiesPath(name));
0311: }
0312:
0313: public ZipEntry getWarZip(String name) throws ParFileException {
0314: return new ZipEntry(getWarPath(name));
0315: }
0316:
0317: public ZipEntry getConfZip(String name) throws ParFileException {
0318: return new ZipEntry(getConfPath(name));
0319: }
0320:
0321: // Methods for manipulating named entries. The external "name"
0322: // argument for this usually corresponds to the "name" attribute
0323: // of an psdp:parentry document. The internal entry name is
0324: // the path for the corresponding XML file.
0325:
0326: // Yes, the use of the "get" methods will probably result in multiple
0327: // parsing of the same path string. Since this class is used around
0328: // stream I/O operations which will swamp the cost of the string
0329: // manipulation we aren't going to worry about it.
0330:
0331: public void addDPEntry(String name) throws ParFileException {
0332: getDPPath(name, true); // side effect does the right thing.
0333: }
0334:
0335: // All of the methods for including specific kinds of entities use internal methods to construct
0336: // the specific path, and addDPEntryInclude() which actually updates the list attribute.
0337:
0338: public void addDPEntryIncludeClass(String name, String pkg,
0339: String classname, int types) throws ParFileException {
0340: addDPEntryInclude(name, getClassPath(pkg, classname), types);
0341: }
0342:
0343: public void addDPEntryIncludeClass(String name,
0344: String fullclassname, int types) throws ParFileException {
0345: addDPEntryInclude(name, getClassPath(fullclassname), types);
0346: }
0347:
0348: public void addDPEntryIncludePBFile(String name, String prop,
0349: String path, int types) throws ParFileException {
0350: addDPEntryInclude(name, getPBFPath(prop, path), types);
0351: }
0352:
0353: public void addDPEntryIncludeStat(String name, String path,
0354: int types) throws ParFileException {
0355: addDPEntryInclude(name, getStaticPath(path), types);
0356: }
0357:
0358: public void addDPEntryIncludeWar(String name, String path, int types)
0359: throws ParFileException {
0360: addDPEntryInclude(name, getWarPath(path), types);
0361: }
0362:
0363: public void addDPEntryIncludeConf(String name, String path,
0364: int types) throws ParFileException {
0365: addDPEntryInclude(name, getConfPath(path), types);
0366: }
0367:
0368: public void addDPEntryAutoExtract(String name, ExtractOp op)
0369: throws ParFileException {
0370:
0371: Attributes a = getAttributes(getDPPath(name, false));
0372:
0373: // Make a copy of the operation, and force its name to be correct.
0374:
0375: op = new ExtractOp(op);
0376: op.setEntryName(name);
0377: a.putValue(EAKEY_AUTOEXTRACT, op.toArg());
0378: }
0379:
0380: // Add the list of dp XML files in the DP-Docs attribute
0381: public void addDPEntryDPDocs(String name, Set keys)
0382: throws ParFileException {
0383: StringBuffer sb = new StringBuffer();
0384:
0385: for (Iterator i = keys.iterator(); i.hasNext();) {
0386: String key = (String) i.next();
0387: sb.append(key).append(".xml").append(INCLUDE_DELIM);
0388: }
0389: sb.deleteCharAt(sb.length() - 1);
0390:
0391: Attributes a = getAttributes(getDPPath(name, false));
0392: a.putValue(EAKEY_DP_DOCS, sb.toString());
0393: }
0394:
0395: // Add the list of attribute properties files in the
0396: // Attributes-Properties attribute and values.
0397: public void addDPEntryAttrProps(String name, Set keys)
0398: throws ParFileException {
0399: StringBuffer sb = new StringBuffer();
0400:
0401: for (Iterator i = keys.iterator(); i.hasNext();) {
0402: String key = (String) i.next();
0403:
0404: sb.append(key).append(".properties").append(INCLUDE_DELIM);
0405: }
0406: sb.deleteCharAt(sb.length() - 1);
0407:
0408: Attributes a = getAttributes(getDPPath(name, false));
0409: a.putValue(EAKEY_ATTR_PROPS, sb.toString());
0410:
0411: }
0412:
0413: // Get the names of all the DP entries contained in this manifest. We just get all the entries,
0414: // extract the user visible names, and return a vector containing them all.
0415:
0416: public Vector getDPEntryList() throws ParFileException {
0417:
0418: Vector v = new Vector();
0419:
0420: Iterator it = getEntries().keySet().iterator();
0421: while (it.hasNext()) {
0422: v.add(getNameFromDPPath((String) it.next()));
0423: }
0424:
0425: return v;
0426: }
0427:
0428: // Get the list of operation masked files associated with a particular named entry.
0429: // This has to parse apart the syntax built up by private method addDPEntryInclude()
0430: //
0431: // There is a second overload which provides the actual type masks for each item.
0432:
0433: public Vector getDPEntryIncludeList(String name, int types)
0434: throws ParFileException {
0435:
0436: Vector v = new Vector();
0437: getDPEntryIncludeList(name, types, v, null);
0438:
0439: return v;
0440: }
0441:
0442: public void getDPEntryIncludeList(String name, int types, Vector v,
0443: Vector vt) throws ParFileException {
0444:
0445: v.clear();
0446: if (vt != null) {
0447: vt.clear();
0448: }
0449:
0450: String list = getAttributes(getDPPath(name, false)).getValue(
0451: EAKEY_INCLUDE);
0452: if (list == null) {
0453: return;
0454: }
0455:
0456: StringTokenizer tok = new StringTokenizer(list, INCLUDE_DELIM);
0457: while (tok.hasMoreTokens()) {
0458: StringTokenizer subtok = new StringTokenizer(tok
0459: .nextToken(), INCLUDE_CLOSE + INCLUDE_OPEN);
0460:
0461: if (!subtok.hasMoreTokens())
0462: continue;
0463:
0464: // Assume that members with no type mask apply to all types of operations.
0465:
0466: String fn = subtok.nextToken();
0467: if (!subtok.hasMoreTokens()) {
0468: v.add(fn);
0469: if (vt != null) {
0470: vt.add(new Integer(ExtractOp.ALLTYPES));
0471: }
0472: continue;
0473: }
0474:
0475: try {
0476: int ftyp = Integer.parseInt(subtok.nextToken());
0477: if ((ftyp & types) != 0) {
0478: v.add(fn);
0479: if (vt != null) {
0480: vt.add(new Integer(ftyp));
0481: }
0482: }
0483: } catch (Exception ex) {
0484: throw new ParFileException("errorManifestInclusion");
0485: }
0486: }
0487: }
0488:
0489: //
0490: // Returns list of dp documents in the dp entry
0491: //
0492: public Vector getDPEntryDPDocList(String name)
0493: throws ParFileException {
0494: Vector v = new Vector();
0495:
0496: String list = getAttributes(getDPPath(name, false)).getValue(
0497: EAKEY_DP_DOCS);
0498: if (list == null) {
0499: return null;
0500: }
0501:
0502: StringTokenizer tok = new StringTokenizer(list, INCLUDE_DELIM);
0503: while (tok.hasMoreTokens()) {
0504:
0505: v.add(tok.nextToken());
0506: }
0507:
0508: return v;
0509: }
0510:
0511: //
0512: // Returns list of dp documents in the dp entry
0513: //
0514: public Vector getDPEntryAttrPropList(String name)
0515: throws ParFileException {
0516: Vector v = new Vector();
0517:
0518: String list = getAttributes(getDPPath(name, false)).getValue(
0519: EAKEY_ATTR_PROPS);
0520: if (list == null) {
0521: return null;
0522: }
0523:
0524: StringTokenizer tok = new StringTokenizer(list, INCLUDE_DELIM);
0525: while (tok.hasMoreTokens()) {
0526:
0527: v.add(tok.nextToken());
0528: }
0529:
0530: return v;
0531: }
0532:
0533: // Returned the AutoExtract operation associated with a named entry, or null.
0534:
0535: public ExtractOp getDPEntryAutoExtract(String name)
0536: throws ParFileException {
0537:
0538: String opstr = getAttributes(getDPPath(name, false)).getValue(
0539: EAKEY_AUTOEXTRACT);
0540: if (opstr == null) {
0541: return null;
0542: }
0543:
0544: return ExtractOp.makeOpFromArgument(opstr);
0545: }
0546:
0547: // Get the type (CLASSFILE, DPFILE, PBFILE or STATFILE ) associated with a particular file in the
0548: // archive. Knowing this, you can then extract the important bits of the path with the
0549: // other methods.
0550:
0551: public int getPathRootType(String path) throws ParFileException {
0552:
0553: // Simply call an internal version which takes an optional StringBuffer argument
0554:
0555: return getPathRootType(path, null);
0556: }
0557:
0558: public String getPropertyFromPBFPath(String path)
0559: throws ParFileException {
0560:
0561: StringBuffer rest = new StringBuffer();
0562: if (getPathRootType(path, rest) != PBFILE) {
0563: throw new ParFileException("errorManifestPathType");
0564: }
0565:
0566: StringTokenizer tok = new StringTokenizer(rest.toString(), "/");
0567: if (!tok.hasMoreTokens()) {
0568: throw new ParFileException("errorManifestPathSyntax");
0569: }
0570:
0571: return tok.nextToken().replace('/', FSC);
0572: }
0573:
0574: public String getPathFromPBFPath(String path)
0575: throws ParFileException {
0576:
0577: StringBuffer rest = new StringBuffer();
0578: if (getPathRootType(path, rest) != PBFILE) {
0579: throw new ParFileException("errorManifestPathType");
0580: }
0581:
0582: StringTokenizer tok = new StringTokenizer(rest.toString(), "/");
0583: if (!tok.hasMoreTokens()) {
0584: throw new ParFileException("errorManifestPathSyntax");
0585: }
0586:
0587: tok.nextToken(); // skip property
0588:
0589: StringBuffer buf = new StringBuffer();
0590: String pfx = "";
0591: while (tok.hasMoreTokens()) {
0592: buf.append(pfx);
0593: buf.append(tok.nextToken());
0594: pfx = "/";
0595: }
0596:
0597: return buf.toString().replace('/', FSC);
0598: }
0599:
0600: public String getFullFromClassPath(String path)
0601: throws ParFileException {
0602:
0603: StringBuffer rest = new StringBuffer();
0604: if (getPathRootType(path, rest) != CLASSFILE) {
0605: throw new ParFileException("errorManifestPathType");
0606: }
0607:
0608: StringBuffer cb = new StringBuffer();
0609: StringTokenizer tok = new StringTokenizer(rest.toString(), "/");
0610: String pfx = "";
0611: while (tok.hasMoreTokens()) {
0612: cb.append(pfx);
0613: String part = tok.nextToken();
0614: int dotidx = part.indexOf(".");
0615: if (dotidx > 0) {
0616: part = part.substring(0, dotidx);
0617: }
0618: cb.append(part);
0619: pfx = ".";
0620: }
0621:
0622: return cb.toString().replace('/', FSC);
0623: }
0624:
0625: public String getPathFromStaticPath(String path)
0626: throws ParFileException {
0627:
0628: StringBuffer rest = new StringBuffer();
0629: if (getPathRootType(path, rest) != STATFILE) {
0630: throw new ParFileException("errorManifestPathType");
0631: }
0632:
0633: return rest.toString();
0634: }
0635:
0636: public String getPathFromWarPath(String path)
0637: throws ParFileException {
0638:
0639: StringBuffer rest = new StringBuffer();
0640: if (getPathRootType(path, rest) != WARFILE) {
0641: throw new ParFileException("errorManifestPathType");
0642: }
0643:
0644: return rest.toString().replace('/', FSC);
0645: }
0646:
0647: public String getPathFromConfPath(String path)
0648: throws ParFileException {
0649:
0650: StringBuffer rest = new StringBuffer();
0651: if (getPathRootType(path, rest) != CONFFILE) {
0652: throw new ParFileException("errorManifestPathType");
0653: }
0654:
0655: return rest.toString().replace('/', FSC);
0656: }
0657:
0658: public void transferFileEntries(ParFileBuilder pfb, ParFile oldpf)
0659: throws ParFileException {
0660:
0661: Iterator it = getEntries().keySet().iterator();
0662: while (it.hasNext()) {
0663:
0664: String ekey = (String) it.next();
0665: String ename = getNameFromDPPath(ekey);
0666:
0667: // Get the include list, and extraction op for the entry.
0668:
0669: ExtractOp op = getDPEntryAutoExtract(ename);
0670: Vector vif = new Vector();
0671: Vector vift = new Vector();
0672: getDPEntryIncludeList(ename, ExtractOp.ALLTYPES, vif, vift);
0673:
0674: // Build array of ProviderPackageFile's which represent transfer
0675: // of files from old archive.
0676:
0677: Vector vppf = new Vector();
0678: for (int i = 0; i < vif.size(); ++i) {
0679: String fn = (String) vif.elementAt(i);
0680: Integer fntyp = (Integer) vift.elementAt(i);
0681: vppf.add(new ParXferPPF(oldpf, fn, fntyp.intValue()));
0682: }
0683:
0684: // add new entry to file builder.
0685:
0686: pfb.addDPEntry(getDPDoc(ekey, oldpf), vppf, op);
0687: }
0688: }
0689:
0690: public void describe(PrintStream out, ParFile pf)
0691: throws ParFileException {
0692: out.println(Par.getLocalizedString("parDescClassRoot") + ": "
0693: + (m_CRoot == null ? "/" : "/" + m_CRoot));
0694: out.println(Par.getLocalizedString("parDescPBRoot") + ": "
0695: + (m_PBFRoot == null ? "/" : "/" + m_PBFRoot));
0696: out.println(Par.getLocalizedString("parDescDPRoot") + ": "
0697: + (m_DPRoot == null ? "/" : "/" + m_DPRoot));
0698: out.println(Par.getLocalizedString("parDescStaticRoot") + ": "
0699: + (m_SRoot == null ? "/" : "/" + m_SRoot));
0700:
0701: Iterator it = getEntries().keySet().iterator();
0702: while (it.hasNext()) {
0703: String ekey = (String) it.next();
0704: String ename = getNameFromDPPath(ekey);
0705:
0706: out.println("");
0707: out.println(Par.getLocalizedString("parDescEntry") + ": "
0708: + ename);
0709:
0710: ExtractOp op = getDPEntryAutoExtract(ename);
0711: if (op != null) {
0712: out.println(Par
0713: .getLocalizedString("parDescAutoExtract")
0714: + ": " + op.toArg());
0715: }
0716:
0717: Par.describeDPDoc(getDPDoc(ekey, pf), out);
0718:
0719: Vector v = new Vector();
0720: Vector vt = new Vector();
0721: getDPEntryIncludeList(ename, ExtractOp.ALLTYPES, v, vt);
0722:
0723: for (int i = 0; i < v.size(); ++i) {
0724:
0725: String fn = (String) v.elementAt(i);
0726: int ty = ((Integer) vt.elementAt(i)).intValue();
0727: StringBuffer bf = new StringBuffer();
0728:
0729: bf.append(Par.getLocalizedString("parDescIncludes")
0730: + ": ");
0731: describeFile(bf, fn);
0732: bf.append(" (");
0733: ExtractOp.describeTypes(bf, ty);
0734: bf.append(")");
0735: out.println(bf.toString());
0736: }
0737: }
0738: }
0739:
0740: public String describeAll(ParFile pf) throws ParFileException {
0741: StringBuffer sb = new StringBuffer();
0742: sb.append(Par.getLocalizedString("parDescClassRoot") + ": "
0743: + (m_CRoot == null ? "/" : "/" + m_CRoot));
0744: sb.append("\n");
0745: sb.append(Par.getLocalizedString("parDescPBRoot") + ": "
0746: + (m_PBFRoot == null ? "/" : "/" + m_PBFRoot));
0747: sb.append("\n");
0748: sb.append(Par.getLocalizedString("parDescDPRoot") + ": "
0749: + (m_DPRoot == null ? "/" : "/" + m_DPRoot));
0750: sb.append("\n");
0751: sb.append(Par.getLocalizedString("parDescStaticRoot") + ": "
0752: + (m_SRoot == null ? "/" : "/" + m_SRoot));
0753: sb.append("\n");
0754: sb.append(Par.getLocalizedString("parDescWarRoot") + ": "
0755: + (m_WRoot == null ? "/" : "/" + m_WRoot));
0756: sb.append("\n");
0757:
0758: Iterator it = getEntries().keySet().iterator();
0759: while (it.hasNext()) {
0760: String ekey = (String) it.next();
0761: String ename = getNameFromDPPath(ekey);
0762:
0763: sb.append("");
0764: sb.append(Par.getLocalizedString("parDescEntry") + ": "
0765: + ename);
0766: sb.append("\n");
0767:
0768: ExtractOp op = getDPEntryAutoExtract(ename);
0769: if (op != null) {
0770: sb.append(Par.getLocalizedString("parDescAutoExtract")
0771: + ": " + op.toArg());
0772: sb.append("\n");
0773: }
0774: //Implement this for at least when type of par is "provider"
0775: //Par.describeDPDoc(getDPDoc(ekey,pf),System.out);
0776:
0777: Vector dpVec = getDPEntryDPDocList(ename);
0778: if (dpVec != null) {
0779: for (int i = 0; i < dpVec.size(); ++i) {
0780: String fn = (String) dpVec.elementAt(i);
0781: StringBuffer bf = new StringBuffer();
0782: bf.append(Par.getLocalizedString("parDescIncludes")
0783: + ": ");
0784: describeFile(bf, fn);
0785: sb.append(bf.toString());
0786: sb.append("\n");
0787: }
0788: }
0789:
0790: Vector v = new Vector();
0791: Vector vt = new Vector();
0792: getDPEntryIncludeList(ename, ExtractOp.ALLTYPES, v, vt);
0793:
0794: for (int i = 0; i < v.size(); ++i) {
0795:
0796: String fn = (String) v.elementAt(i);
0797: int ty = ((Integer) vt.elementAt(i)).intValue();
0798: StringBuffer bf = new StringBuffer();
0799:
0800: bf.append(Par.getLocalizedString("parDescIncludes")
0801: + ": ");
0802: describeFile(bf, fn);
0803: bf.append(" (");
0804: ExtractOp.describeTypes(bf, ty);
0805: bf.append(")");
0806: sb.append(bf.toString());
0807: sb.append("\n");
0808: }
0809: }
0810: return sb.toString();
0811: }
0812:
0813: public String describeAll(ParFile pf, Map types)
0814: throws ParFileException {
0815: StringBuffer sb = new StringBuffer();
0816: sb.append(Par.getLocalizedString("parDescClassRoot") + ": "
0817: + (m_CRoot == null ? "/" : "/" + m_CRoot));
0818: sb.append("\n");
0819: sb.append(Par.getLocalizedString("parDescPBRoot") + ": "
0820: + (m_PBFRoot == null ? "/" : "/" + m_PBFRoot));
0821: sb.append("\n");
0822: sb.append(Par.getLocalizedString("parDescDPRoot") + ": "
0823: + (m_DPRoot == null ? "/" : "/" + m_DPRoot));
0824: sb.append("\n");
0825: sb.append(Par.getLocalizedString("parDescStaticRoot") + ": "
0826: + (m_SRoot == null ? "/" : "/" + m_SRoot));
0827: sb.append("\n");
0828: sb.append(Par.getLocalizedString("parDescWarRoot") + ": "
0829: + (m_WRoot == null ? "/" : "/" + m_WRoot));
0830: sb.append("\n");
0831:
0832: Iterator it = getEntries().keySet().iterator();
0833: while (it.hasNext()) {
0834: String ekey = (String) it.next();
0835: String ename = getNameFromDPPath(ekey);
0836:
0837: sb.append("");
0838: sb.append(Par.getLocalizedString("parDescEntry") + ": "
0839: + ename);
0840: sb.append("\n");
0841:
0842: ExtractOp op = getDPEntryAutoExtract(ename);
0843: if (op != null) {
0844: sb.append(Par.getLocalizedString("parDescAutoExtract")
0845: + ": " + op.toArg());
0846: sb.append("\n");
0847: }
0848: //Implement this for at least when type of par is "provider"
0849: //Par.describeDPDoc(getDPDoc(ekey,pf),out);
0850:
0851: Vector dpVec = getDPEntryDPDocList(ename);
0852: if (dpVec != null) {
0853: for (int i = 0; i < dpVec.size(); ++i) {
0854: String fn = (String) dpVec.elementAt(i);
0855: StringBuffer bf = new StringBuffer();
0856: bf.append(Par.getLocalizedString("parDescIncludes")
0857: + ": ");
0858: describeFile(bf, fn);
0859: sb.append(bf.toString());
0860: sb.append("\n");
0861: }
0862: }
0863:
0864: Vector v = new Vector();
0865: Vector vt = new Vector();
0866: getDPEntryIncludeList(ename, ExtractOp.ALLTYPES, v, vt);
0867:
0868: for (int i = 0; i < v.size(); ++i) {
0869: String fn = (String) v.elementAt(i);
0870: //check if this type has to be displayed
0871: int type = getPathRootType(fn);
0872: Boolean toDisplay = (Boolean) types.get(new Integer(
0873: type));
0874:
0875: if (toDisplay != null) {
0876: if (toDisplay.booleanValue()) {
0877: int ty = ((Integer) vt.elementAt(i)).intValue();
0878: StringBuffer bf = new StringBuffer();
0879: bf.append(Par
0880: .getLocalizedString("parDescIncludes")
0881: + ": ");
0882:
0883: describeFile(bf, fn);
0884: bf.append(" (");
0885: ExtractOp.describeTypes(bf, ty);
0886: bf.append(")");
0887: sb.append(bf.toString());
0888: sb.append("\n");
0889: }
0890: }
0891: }
0892: }
0893: return sb.toString();
0894: }
0895:
0896: public String getBackupVersion() {
0897: return m_BackupVersion;
0898: }
0899:
0900: private Document getDPDoc(String path, ParFile pf)
0901: throws ParFileException {
0902: try {
0903: InputStream is = pf.getInputStream(pf.getEntry(path));
0904: DocumentBuilderFactory dbf = DocumentBuilderFactory
0905: .newInstance();
0906: DocumentBuilder db = dbf.newDocumentBuilder();
0907: return db.parse(is);
0908: } catch (Exception ex) {
0909: throw new ParFileException("errorDescribingDPDocument", ex);
0910: }
0911: }
0912:
0913: private void describeFile(StringBuffer bf, String fn)
0914: throws ParFileException {
0915:
0916: switch (getPathRootType(fn)) {
0917: case CLASSFILE:
0918: Object tokc[] = { fn };
0919: bf.append(Par.getLocalizedString("parDescClassFile", tokc));
0920: break;
0921: case PBFILE:
0922: Object tokpb[] = { getPropertyFromPBFPath(fn),
0923: getPathFromPBFPath(fn) };
0924: bf.append(Par.getLocalizedString("parDescPBFile", tokpb));
0925: break;
0926: case STATFILE:
0927: Object toks[] = { fn };
0928: bf
0929: .append(Par.getLocalizedString("parDescStaticFile",
0930: toks));
0931: break;
0932: case WARFILE:
0933: Object tokw[] = { fn };
0934: bf.append(Par.getLocalizedString("parDescWarFile", tokw));
0935: break;
0936: default:
0937: throw new ParFileException("errorManifestPathType");
0938: }
0939: }
0940:
0941: // getNameFromDPPath is private - logically, the entity unpackaging the
0942: // .par file doesn't need to know this - we just use this method internally
0943: // when returning the list of entity names.
0944:
0945: private String getNameFromDPPath(String path)
0946: throws ParFileException {
0947:
0948: StringBuffer rest = new StringBuffer();
0949: if (getPathRootType(path, rest) != DPFILE) {
0950: throw new ParFileException("errorManifestPathType");
0951: }
0952:
0953: String rstr = rest.toString();
0954: int idx = rstr.lastIndexOf('.');
0955: if (idx <= 0) {
0956: throw new ParFileException("Empty DP path");
0957: }
0958:
0959: return rstr.substring(0, idx);
0960: }
0961:
0962: // Yes, getDPPath() has side effects to produce the manifest entry - private method, we can
0963: // cope with the semantics.
0964:
0965: private String getDPPath(String name, boolean create)
0966: throws ParFileException {
0967:
0968: if (name == null) {
0969: if (create) {
0970: throw new ParFileException("errorNullDPEntry");
0971: }
0972: Attributes ma = getMainAttributes();
0973: name = ma.getValue(MAKEY_DEFAULTENTRY);
0974: if (name == null) {
0975: throw new ParFileException("errorNullDPEntry");
0976: }
0977: }
0978:
0979: StringBuffer buf = new StringBuffer();
0980:
0981: if (m_DPRoot != null) {
0982: buf.append(m_DPRoot);
0983: buf.append("/");
0984: }
0985: buf.append(name);
0986: buf.append(".xml");
0987:
0988: String key = buf.toString();
0989:
0990: if (getAttributes(key) == null) {
0991: if (!create)
0992: throw new ParFileException("errorBadDPEntry");
0993: Attributes ea = new Attributes();
0994: ea.putValue(EAKEY_NAME, name);
0995: getEntries().put(key, ea);
0996: Attributes ma = getMainAttributes();
0997: if (ma.getValue(MAKEY_DEFAULTENTRY) == null) {
0998: ma.putValue(MAKEY_DEFAULTENTRY, name);
0999: }
1000:
1001: return key;
1002: }
1003:
1004: if (create) {
1005: throw new ParFileException("errorBadDPEntry");
1006: }
1007:
1008: return key;
1009: }
1010:
1011: private String getDPDocPath(String name) throws ParFileException {
1012:
1013: StringBuffer buf = new StringBuffer();
1014:
1015: if (m_DPRoot != null) {
1016: buf.append(m_DPRoot);
1017: buf.append("/");
1018: }
1019: buf.append(name);
1020: buf.append(".xml");
1021:
1022: return buf.toString();
1023: }
1024:
1025: // get*Path routines which glue syntax together with the appropriate roots.
1026:
1027: private String getClassPath(String fullclassname) {
1028:
1029: String cfile = Par.classToFile(fullclassname);
1030: if (m_CRoot == null) {
1031: return cfile;
1032: }
1033: return m_CRoot + "/" + cfile;
1034: }
1035:
1036: private String getClassPath(String pkg, String classname) {
1037:
1038: String cfile = Par.classToFile(pkg, classname);
1039: if (m_CRoot == null) {
1040: return cfile;
1041: }
1042: return m_CRoot + "/" + cfile;
1043: }
1044:
1045: private String getPBFPath(String prop, String path) {
1046: StringBuffer buf = new StringBuffer();
1047:
1048: if (m_PBFRoot != null) {
1049: buf.append(m_PBFRoot);
1050: buf.append("/");
1051: }
1052:
1053: path = path.replace(FSC, '/');
1054: buf.append(prop);
1055: buf.append("/");
1056: buf.append(path);
1057:
1058: return buf.toString();
1059: }
1060:
1061: private String getStaticPath(String path) {
1062: StringBuffer buf = new StringBuffer();
1063:
1064: if (m_SRoot != null) {
1065: buf.append(m_SRoot);
1066: buf.append("/");
1067: }
1068:
1069: path = path.replace(FSC, '/');
1070: buf.append(path);
1071:
1072: return buf.toString();
1073: }
1074:
1075: private String getWarPath(String path) {
1076:
1077: StringBuffer buf = new StringBuffer();
1078:
1079: if (m_WRoot != null) {
1080: buf.append(m_WRoot);
1081: buf.append("/");
1082: }
1083:
1084: path = path.replace(FSC, '/');
1085: buf.append(path);
1086:
1087: return buf.toString();
1088: }
1089:
1090: private String getConfPath(String path) {
1091:
1092: StringBuffer buf = new StringBuffer();
1093:
1094: if (m_ConfRoot != null) {
1095: buf.append(m_ConfRoot);
1096: buf.append("/");
1097: }
1098:
1099: path = path.replace(FSC, '/');
1100: buf.append(path);
1101:
1102: return buf.toString();
1103: }
1104:
1105: private String getPropertiesPath(String name) {
1106:
1107: StringBuffer buf = new StringBuffer();
1108:
1109: if (m_PBFRoot != null) {
1110: buf.append(m_DPRoot);
1111: buf.append("/");
1112: }
1113:
1114: buf.append(name);
1115: buf.append(".properties");
1116:
1117: return buf.toString();
1118: }
1119:
1120: // Maintain the "include" list.
1121: //
1122: // Syntax consists of an INCLUDE_DELIM (,) seperated list of files, appended with a
1123: // numeric type mask enclosed in INCLUDE_OPEN and INCLUDE_CLOSE (parentheses). Prevents
1124: // duplicate entries - the types mask last specified takes precedence.
1125:
1126: private void addDPEntryInclude(String name, String path, int types)
1127: throws ParFileException {
1128:
1129: Attributes a = getAttributes(getDPPath(name, false));
1130:
1131: String list = a.getValue(EAKEY_INCLUDE);
1132:
1133: StringBuffer buf = new StringBuffer();
1134: String pfx = "";
1135:
1136: // Go through the list, and remove the old entry, if there is one.
1137:
1138: if (list != null) {
1139: StringTokenizer toks = new StringTokenizer(list,
1140: INCLUDE_DELIM);
1141: while (toks.hasMoreTokens()) {
1142: String p = toks.nextToken();
1143: if (p.startsWith(path)) {
1144: continue;
1145: }
1146: buf.append(pfx);
1147: pfx = INCLUDE_DELIM;
1148: buf.append(p);
1149: }
1150: }
1151:
1152: buf.append(pfx);
1153: buf.append(path);
1154: buf.append(INCLUDE_OPEN);
1155: buf.append(types);
1156: buf.append(INCLUDE_CLOSE);
1157:
1158: a.putValue(EAKEY_INCLUDE, buf.toString());
1159: }
1160:
1161: // private version of getPathRootType returns remaining path
1162: // after root is stripped off in a StringBuffer
1163:
1164: private int getPathRootType(String path, StringBuffer rem)
1165: throws ParFileException {
1166:
1167: StringTokenizer tok = new StringTokenizer(path, "/");
1168:
1169: if (!tok.hasMoreTokens()) {
1170: throw new ParFileException("errorManifestPathSyntax");
1171: }
1172:
1173: String root = tok.nextToken();
1174:
1175: // try the explicit roots first
1176:
1177: if (rem != null) {
1178: rem.setLength(0);
1179: String pfx = "";
1180: while (tok.hasMoreTokens()) {
1181: rem.append(pfx);
1182: rem.append(tok.nextToken());
1183: pfx = "/";
1184: }
1185: }
1186:
1187: if (m_CRoot != null && m_CRoot.equals(root)) {
1188: return CLASSFILE;
1189: }
1190:
1191: if (m_DPRoot != null && m_DPRoot.equals(root)) {
1192: return DPFILE;
1193: }
1194:
1195: if (m_PBFRoot != null && m_PBFRoot.equals(root)) {
1196: return PBFILE;
1197: }
1198:
1199: if (m_SRoot != null && m_SRoot.equals(root)) {
1200: return STATFILE;
1201: }
1202:
1203: if (m_WRoot != null && m_WRoot.equals(root)) {
1204: return WARFILE;
1205: }
1206:
1207: if (m_ConfRoot != null && m_ConfRoot.equals(root)) {
1208: return CONFFILE;
1209: }
1210:
1211: // Not one of those. Try the null roots
1212:
1213: if (rem != null) {
1214: rem.setLength(0);
1215: rem.append(path);
1216: }
1217:
1218: if (m_CRoot == null) {
1219: return CLASSFILE;
1220: }
1221:
1222: if (m_DPRoot == null) {
1223: return DPFILE;
1224: }
1225:
1226: if (m_PBFRoot == null) {
1227: return PBFILE;
1228: }
1229:
1230: if (m_SRoot == null) {
1231: return STATFILE;
1232: }
1233:
1234: if (m_WRoot == null) {
1235: return WARFILE;
1236: }
1237:
1238: if (m_ConfRoot == null) {
1239: return CONFFILE;
1240: }
1241:
1242: throw new ParFileException("errorManifestPathSyntax");
1243: }
1244:
1245: private String m_CRoot;
1246: private String m_PBFRoot;
1247: private String m_DPRoot;
1248: private String m_SRoot;
1249: private String m_WRoot;
1250: private String m_ConfRoot;
1251: private String m_BackupVersion;
1252:
1253: // Manifest file version. Existence of version header also serves as an
1254: // error check on manifest format
1255: // VERSION 1: JES2 and earlier PS versions: PS6.0 ~ PS6.3
1256: // VERSION 2: JES3
1257:
1258: private static final int VERSION = 2;
1259:
1260: // Delimeters used in parsing "include" line for entry.
1261:
1262: private static final String INCLUDE_DELIM = ",";
1263: private static final String INCLUDE_OPEN = "(";
1264: private static final String INCLUDE_CLOSE = ")";
1265:
1266: // Prefix used on all our portal server specific specific manifest headers
1267:
1268: private static final String KEY_PFX = "PS-";
1269:
1270: // Main attributes headers
1271:
1272: private static final String MAKEY_VERSION = KEY_PFX + "Version";
1273: private static final String MAKEY_PBFILEROOT = KEY_PFX
1274: + "PBFileRoot";
1275: private static final String MAKEY_DPROOT = KEY_PFX + "DPRoot";
1276: private static final String MAKEY_CLASSROOT = KEY_PFX + "ClassRoot";
1277: private static final String MAKEY_STATROOT = KEY_PFX + "StaticRoot";
1278: private static final String MAKEY_DEFAULTENTRY = KEY_PFX
1279: + "DefaultEntry";
1280:
1281: // The following attributes are added in version 2
1282:
1283: private static final String MAKEY_WARROOT = KEY_PFX + "WARRoot";
1284: private static final String MAKEY_CONFROOT = KEY_PFX + "ConfigRoot";
1285: private static final String MAKEY_BACKUP_VERSION = KEY_PFX
1286: + "Backup-Version";
1287: private static final String MAKEY_BACKUP_TIMESTAMP = KEY_PFX
1288: + "Backup-Timestamp";
1289:
1290: // Entry attributes headers
1291:
1292: private static final String EAKEY_NAME = KEY_PFX + "EntryName";
1293: private static final String EAKEY_INCLUDE = KEY_PFX + "Include";
1294: private static final String EAKEY_AUTOEXTRACT = KEY_PFX
1295: + "AutoExtract";
1296:
1297: // The following entries are added in version 2
1298:
1299: private static final String EAKEY_DP_DOCS = KEY_PFX + "DP-Docs";
1300: private static final String EAKEY_ATTR_PROPS = KEY_PFX
1301: + "Attributes-Properties";
1302:
1303: // File separator
1304: private static final char FSC = File.separatorChar;
1305: }
|