0001: /*
0002: * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS HEADER.
0003: *
0004: * Copyright 1997-2007 Sun Microsystems, Inc. All rights reserved.
0005: *
0006: * The contents of this file are subject to the terms of either the GNU
0007: * General Public License Version 2 only ("GPL") or the Common
0008: * Development and Distribution License("CDDL") (collectively, the
0009: * "License"). You may not use this file except in compliance with the
0010: * License. You can obtain a copy of the License at
0011: * http://www.netbeans.org/cddl-gplv2.html
0012: * or nbbuild/licenses/CDDL-GPL-2-CP. See the License for the
0013: * specific language governing permissions and limitations under the
0014: * License. When distributing the software, include this License Header
0015: * Notice in each file and include the License file at
0016: * nbbuild/licenses/CDDL-GPL-2-CP. Sun designates this
0017: * particular file as subject to the "Classpath" exception as provided
0018: * by Sun in the GPL Version 2 section of the License file that
0019: * accompanied this code. If applicable, add the following below the
0020: * License Header, with the fields enclosed by brackets [] replaced by
0021: * your own identifying information:
0022: * "Portions Copyrighted [year] [name of copyright owner]"
0023: *
0024: * Contributor(s):
0025: *
0026: * The Original Software is NetBeans. The Initial Developer of the Original
0027: * Software is Sun Microsystems, Inc. Portions Copyright 1997-2007 Sun
0028: * Microsystems, Inc. All Rights Reserved.
0029: *
0030: * If you wish your version of this file to be governed by only the CDDL
0031: * or only the GPL Version 2, indicate your decision by adding
0032: * "[Contributor] elects to include this software in this distribution
0033: * under the [CDDL or GPL Version 2] license." If you do not indicate a
0034: * single choice of license, a recipient has the option to distribute
0035: * your version of this file under either the CDDL, the GPL Version 2 or
0036: * to extend the choice of license to its licensees as provided above.
0037: * However, if you add GPL Version 2 code and therefore, elected the GPL
0038: * Version 2 license, then the option applies only if the new code is
0039: * made subject to such option by the copyright holder.
0040: */
0041:
0042: package org.netbeans.nbbuild;
0043:
0044: import java.io.BufferedReader;
0045: import java.io.File;
0046: import java.io.FileInputStream;
0047: import java.io.FileOutputStream;
0048: import java.io.IOException;
0049: import java.io.InputStream;
0050: import java.io.InputStreamReader;
0051: import java.io.OutputStream;
0052: import java.io.UnsupportedEncodingException;
0053: import java.net.URI;
0054: import java.text.SimpleDateFormat;
0055: import java.util.ArrayList;
0056: import java.util.Collections;
0057: import java.util.Date;
0058: import java.util.Iterator;
0059: import java.util.List;
0060: import java.util.Locale;
0061: import java.util.Map;
0062: import java.util.Properties;
0063: import java.util.StringTokenizer;
0064: import java.util.jar.Attributes;
0065: import java.util.jar.Attributes.Name;
0066: import java.util.jar.JarFile;
0067: import java.util.zip.CRC32;
0068: import java.util.zip.ZipEntry;
0069: import javax.xml.parsers.DocumentBuilderFactory;
0070: import javax.xml.parsers.ParserConfigurationException;
0071: import org.apache.tools.ant.BuildException;
0072: import org.apache.tools.ant.Project;
0073: import org.apache.tools.ant.Task;
0074: import org.apache.tools.ant.taskdefs.Jar;
0075: import org.apache.tools.ant.taskdefs.SignJar;
0076: import org.apache.tools.ant.types.ZipFileSet;
0077: import org.w3c.dom.DOMImplementation;
0078: import org.w3c.dom.Document;
0079: import org.w3c.dom.Element;
0080:
0081: /** Makes a <code>.nbm</code> (<b>N</b>et<b>B</b>eans <b>M</b>odule) file.
0082: *
0083: * @author Jesse Glick
0084: */
0085: public class MakeNBM extends Task {
0086:
0087: private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat(
0088: "yyyy/MM/dd");
0089:
0090: /** The same syntax may be used for either <samp><license></samp> or
0091: * <samp><description></samp> subelements.
0092: * <p>By setting the property <code>makenbm.nocdata</code> to <code>true</code>,
0093: * you can avoid using XML <code>CDATA</code> (for compatibility with older versions
0094: * of Auto Update which could not handle it).
0095: */
0096: public class Blurb {
0097: /** You may embed a <samp><file></samp> element inside the blurb.
0098: * If there is text on either side of it, that will be separated
0099: * with a line of dashes automatically.
0100: * But use nested <samp><text></samp> for this purpose.
0101: */
0102: public class FileInsert {
0103: /** File location. */
0104: public void setLocation(File file) throws BuildException {
0105: boolean html = file.getName().endsWith(".html")
0106: || file.getName().endsWith(".htm");
0107: log("Including contents of " + file + " (HTML mode: "
0108: + html + ")", Project.MSG_VERBOSE);
0109: long lmod = file.lastModified();
0110: if (lmod > mostRecentInput)
0111: mostRecentInput = lmod;
0112: addSeparator();
0113: try {
0114: InputStream is = new FileInputStream(file);
0115: try {
0116: BufferedReader r = new BufferedReader(
0117: new InputStreamReader(is, "UTF-8"));
0118: String line;
0119: while ((line = r.readLine()) != null) {
0120: if (html) {
0121: // Clean out any markup first. First tags:
0122: line = line
0123: .replaceAll(
0124: "</?[a-zA-Z0-9_.:-]+( +[a-zA-Z0-9_.:-]+( *= *([^ \"]+|\"[^\"]*\"))?)*/?>",
0125: "");
0126: // DOCTYPE:
0127: line = line.replaceAll(
0128: "<![a-zA-Z]+[^>]*>", "");
0129: // Comments (single-line only at the moment):
0130: line = line.replaceAll(
0131: "<!--([^-]|-[^-])*-->", "");
0132: // Common named character entities:
0133: line = line.replaceAll(""", "\"");
0134: line = line.replaceAll(" ", " ");
0135: line = line.replaceAll("©",
0136: "\u00A9");
0137: line = line.replaceAll("'", "'");
0138: line = line.replaceAll("<", "<");
0139: line = line.replaceAll(">", ">");
0140: line = line.replaceAll("&", "&");
0141: }
0142: line = line.replaceAll(
0143: "[\\p{Cntrl}&&[^\t]]", ""); // #74546
0144: text.append(line);
0145: text.append('\n');
0146: }
0147: } finally {
0148: is.close();
0149: }
0150: } catch (IOException ioe) {
0151: throw new BuildException(
0152: "Exception reading blurb from " + file,
0153: ioe, getLocation());
0154: }
0155: }
0156: }
0157:
0158: private StringBuffer text = new StringBuffer();
0159: private String name = null;
0160:
0161: /** There may be freeform text inside the element. Prefer to use nested elements. */
0162: public void addText(String t) {
0163: addSeparator();
0164: // Strips indentation. Needed because of common style:
0165: // <description>
0166: // Some text here.
0167: // And another line.
0168: // </description>
0169: t = getProject().replaceProperties(t.trim());
0170: int min = Integer.MAX_VALUE;
0171: StringTokenizer tok = new StringTokenizer(t, "\n");
0172: boolean first = true;
0173: while (tok.hasMoreTokens()) {
0174: String line = tok.nextToken();
0175: if (first) {
0176: first = false;
0177: } else {
0178: int i;
0179: for (i = 0; i < line.length()
0180: && Character.isWhitespace(line.charAt(i)); i++)
0181: ;
0182: if (i < min)
0183: min = i;
0184: }
0185: }
0186: if (min == 0) {
0187: text.append(t);
0188: } else {
0189: tok = new StringTokenizer(t, "\n");
0190: first = true;
0191: while (tok.hasMoreTokens()) {
0192: String line = tok.nextToken();
0193: if (first) {
0194: first = false;
0195: } else {
0196: text.append('\n');
0197: line = line.substring(min);
0198: }
0199: text.append(line);
0200: }
0201: }
0202: }
0203:
0204: /** Contents of a file to include. */
0205: public FileInsert createFile() {
0206: return new FileInsert();
0207: }
0208:
0209: /** Text to include literally. */
0210: public class Text {
0211: public void addText(String t) {
0212: Blurb.this .addText(t);
0213: }
0214: }
0215:
0216: // At least on Ant 1.3, mixed content does not work: all the text is added
0217: // first, then all the file inserts. Need to use subelements to be sure.
0218: /** Include nested literal text. */
0219: public Text createText() {
0220: return new Text();
0221: }
0222:
0223: private void addSeparator() {
0224: if (text.length() > 0) {
0225: // some sort of separator
0226: if (text.charAt(text.length() - 1) != '\n')
0227: text.append('\n');
0228: text
0229: .append("-----------------------------------------------------\n");
0230: }
0231: }
0232:
0233: public org.w3c.dom.Text getTextNode(Document ownerDoc) {
0234: // XXX Current XMLUtil.write anyway does not preserve CDATA sections, it seems.
0235: String nocdata = getProject()
0236: .getProperty("makenbm.nocdata");
0237: if (nocdata != null && Project.toBoolean(nocdata)) {
0238: return ownerDoc.createTextNode(text.toString());
0239: } else {
0240: return ownerDoc.createCDATASection(text.toString());
0241: }
0242: }
0243:
0244: /** @deprecated */
0245: @Deprecated
0246: public void setName(String name) {
0247: getProject()
0248: .log(
0249: getLocation()
0250: + ": the 'name' attribute on <license> is deprecated",
0251: Project.MSG_WARN);
0252: }
0253:
0254: public String getName() {
0255: if (name == null) {
0256: name = crcOf(text);
0257: }
0258: return name;
0259: }
0260:
0261: private String crcOf(StringBuffer text) {
0262: CRC32 crc = new CRC32();
0263: try {
0264: crc.update(text.toString().replaceAll("\\s+", " ")
0265: .trim().getBytes("UTF-8"));
0266: } catch (UnsupportedEncodingException ex) {
0267: throw new BuildException(ex);
0268: }
0269: return Long.toHexString(crc.getValue()).toUpperCase(
0270: Locale.ENGLISH);
0271: }
0272:
0273: /** Include a file (and set the license name according to its basename). */
0274: public void setFile(File file) {
0275: // This actually adds the text and so on:
0276: new FileInsert().setLocation(file);
0277: }
0278: }
0279:
0280: public class ExternalPackage {
0281: String name = null;
0282: String targetName = null;
0283: String startUrl = null;
0284: String description = null;
0285:
0286: public void setName(String n) {
0287: this .name = n;
0288: }
0289:
0290: public void setTargetName(String t) {
0291: this .targetName = t;
0292: }
0293:
0294: public void setStartURL(String u) {
0295: this .startUrl = u;
0296: }
0297:
0298: public void setDescription(String d) {
0299: this .description = d;
0300: }
0301:
0302: }
0303:
0304: /** <samp><signature></samp> subelement for signing the NBM. */
0305: public/*static*/class Signature {
0306: public File keystore;
0307: public String storepass, alias;
0308:
0309: /** Path to the keystore (private key). */
0310: public void setKeystore(File f) {
0311: keystore = f;
0312: }
0313:
0314: /** Password for the keystore.
0315: * If a question mark (<samp>?</samp>), the NBM will not be signed
0316: * and a warning will be printed.
0317: */
0318: public void setStorepass(String s) {
0319: storepass = s;
0320: }
0321:
0322: /** Alias for the private key. */
0323: public void setAlias(String s) {
0324: alias = s;
0325: }
0326: }
0327:
0328: private File productDir = null;
0329: private File file = null;
0330: private File manifest = null;
0331: /** see #13850 for explanation */
0332: private String moduleName = null;
0333: private String homepage = null;
0334: private String distribution = "";
0335: private String needsrestart = null;
0336: private String moduleauthor = null;
0337: private String releasedate = null;
0338: private String global = null;
0339: private String targetcluster = null;
0340: private String jarSignerMaxMemory = "96m";
0341: private Blurb license = null;
0342: private Blurb description = null;
0343: private Blurb notification = null;
0344: private Signature signature = null;
0345: private long mostRecentInput = 0L;
0346: private boolean isStandardInclude = true;
0347: private ArrayList<ExternalPackage> externalPackages = null;
0348: private ArrayList<String> locales = null;
0349: private ArrayList<Attributes> moduleAttributes = null;
0350: private Attributes englishAttr = null;
0351:
0352: /** Try to find and create localized info.xml files */
0353: public void setLocales(String s) {
0354: locales = new ArrayList<String>();
0355: for (String st : s.split("[, ]+")) {
0356: locales.add(st);
0357: }
0358: }
0359:
0360: /** Include netbeans directory - default is true */
0361: public void setIsStandardInclude(boolean isStandardInclude) {
0362: this .isStandardInclude = isStandardInclude;
0363: }
0364:
0365: /** Directory of the product's files */
0366: public void setProductDir(File dir) {
0367: productDir = dir;
0368: }
0369:
0370: /** Name of resulting NBM file. */
0371: public void setFile(File file) {
0372: this .file = file;
0373: }
0374:
0375: /** Module manifest needed for versioning.
0376: * @deprecated Use {@link #setModule} instead.
0377: */
0378: @Deprecated
0379: public void setManifest(File manifest) {
0380: this .manifest = manifest;
0381: long lmod = manifest.lastModified();
0382: if (lmod > mostRecentInput)
0383: mostRecentInput = lmod;
0384: log(
0385: getLocation()
0386: + "The 'manifest' attr on <makenbm> is deprecated, please use 'module' instead",
0387: Project.MSG_WARN);
0388: }
0389:
0390: /** Module JAR needed for generating the info file.
0391: * Information may be gotten either from its manifest,
0392: * or if it declares OpenIDE-Module-Localizing-Bundle in its
0393: * manifest, from that bundle.
0394: * The base locale variant, if any, is also checked if necessary
0395: * for the named bundle.
0396: * Currently no other locale variants of the module are examined;
0397: * the information is available but there is no published specification
0398: * of what the resulting variant NBMs (or variant information within
0399: * the NBM) should look like.
0400: */
0401: public void setModule(String module) {
0402: this .moduleName = module;
0403: // mostRecentInput updated below...
0404: }
0405:
0406: /** URL to a home page describing the module. */
0407: public void setHomepage(String homepage) {
0408: this .homepage = homepage;
0409: }
0410:
0411: /** Does module need IDE restart to be installed? */
0412: public void setNeedsrestart(String needsrestart) {
0413: this .needsrestart = needsrestart;
0414: }
0415:
0416: /** Sets name of module author */
0417: public void setModuleauthor(String author) {
0418: this .moduleauthor = author;
0419: }
0420:
0421: /** Install globally? */
0422: public void setGlobal(String isGlobal) {
0423: this .global = isGlobal;
0424: }
0425:
0426: /** Sets pattern for target cluster */
0427: public void setTargetcluster(String targetCluster) {
0428: this .targetcluster = targetCluster;
0429: }
0430:
0431: /** Maximum memory allowed to be used by jarsigner task. Default is 96 MB. */
0432: public void setJarSignerMaxMemory(String jsmm) {
0433: this .jarSignerMaxMemory = jsmm;
0434: }
0435:
0436: /** Release date of NBM. */
0437: public void setReleasedate(String date) {
0438: this .releasedate = date;
0439: }
0440:
0441: /** URL where this NBM file is expected to be downloadable from. */
0442: public void setDistribution(String distribution)
0443: throws BuildException {
0444: this .distribution = distribution;
0445: if (!(this .distribution.equals(""))) {
0446: // check the URL
0447: try {
0448: URI uri = java.net.URI.create(this .distribution);
0449: } catch (IllegalArgumentException ile) {
0450: throw new BuildException("Distribution URL \""
0451: + this .distribution + "\" is not a valid URI",
0452: ile, getLocation());
0453: }
0454: }
0455: }
0456:
0457: public Blurb createLicense() {
0458: return (license = new Blurb());
0459: }
0460:
0461: public Blurb createNotification() {
0462: return (notification = new Blurb());
0463: }
0464:
0465: public Blurb createDescription() {
0466: log(
0467: getLocation()
0468: + "The <description> subelement in <makenbm> is deprecated except for emergency patches, please ensure your module has an OpenIDE-Module-Long-Description instead",
0469: Project.MSG_WARN);
0470: return (description = new Blurb());
0471: }
0472:
0473: public Signature createSignature() {
0474: return (signature = new Signature());
0475: }
0476:
0477: public ExternalPackage createExternalPackage() {
0478: ExternalPackage externalPackage = new ExternalPackage();
0479: if (externalPackages == null)
0480: externalPackages = new ArrayList<ExternalPackage>();
0481: externalPackages.add(externalPackage);
0482: return externalPackage;
0483: }
0484:
0485: private ZipFileSet main = null;
0486:
0487: public ZipFileSet createMain() {
0488: return (main = new ZipFileSet());
0489: }
0490:
0491: private Attributes getModuleAttributesForLocale(String locale)
0492: throws BuildException {
0493: if (locale == null) {
0494: throw new BuildException("Unknown locale: null",
0495: getLocation());
0496: }
0497: log("Processing module attributes for locale '" + locale + "'",
0498: Project.MSG_VERBOSE);
0499: Attributes attr = null;
0500: if ((!locale.equals("")) && (englishAttr != null)) {
0501: attr = new Attributes(englishAttr);
0502: attr.putValue("locale", locale);
0503: log(
0504: "Copying English module attributes to localized attributes in locale "
0505: + locale, Project.MSG_VERBOSE);
0506: String om = attr.getValue("OpenIDE-Module");
0507: String omn = attr.getValue("OpenIDE-Module-Name");
0508: String omdc = attr
0509: .getValue("OpenIDE-Module-Display-Category");
0510: String omsd = attr
0511: .getValue("OpenIDE-Module-Short-Description");
0512: String omld = attr
0513: .getValue("OpenIDE-Module-Long-Description");
0514: if (om != null)
0515: log("OpenIDE-Module"
0516: + (locale.equals("") ? "" : "_" + locale)
0517: + " is " + om, Project.MSG_DEBUG);
0518: if (omn != null)
0519: log("OpenIDE-Module-Name"
0520: + (locale.equals("") ? "" : "_" + locale)
0521: + " is " + omn, Project.MSG_DEBUG);
0522: if (omdc != null)
0523: log("OpenIDE-Module-Display-Category"
0524: + (locale.equals("") ? "" : "_" + locale)
0525: + " is " + omdc, Project.MSG_DEBUG);
0526: if (omsd != null)
0527: log("OpenIDE-Module-Short-Description"
0528: + (locale.equals("") ? "" : "_" + locale)
0529: + " is " + omsd, Project.MSG_DEBUG);
0530: if (omld != null)
0531: log("OpenIDE-Module-Long-Description"
0532: + (locale.equals("") ? "" : "_" + locale)
0533: + " is " + omld, Project.MSG_DEBUG);
0534: } else {
0535: attr = new Attributes();
0536: attr.putValue("locale", locale);
0537: }
0538: moduleName = moduleName.replace(File.separatorChar, '/');
0539: String jarName = moduleName;
0540: String filename;
0541: String fname;
0542: String fext;
0543: if (!locale.equals("")) {
0544: // update file name for current locale
0545: filename = moduleName
0546: .substring(moduleName.lastIndexOf('/') + 1);
0547: fname = filename.substring(0, filename.lastIndexOf('.'));
0548: fext = filename.substring(filename.lastIndexOf('.'));
0549: jarName = moduleName.substring(0, moduleName
0550: .lastIndexOf('/'))
0551: + "/locale/" + fname + "_" + locale + fext;
0552: }
0553: log("Going to open jarfile " + jarName, Project.MSG_VERBOSE);
0554: File mfile = new File(productDir, jarName);
0555: if ((mfile == null) || (!mfile.exists())) {
0556: // localizing jarfile does not exist, try to return english data
0557: if (englishAttr != null) {
0558: Attributes xattr = new Attributes(englishAttr);
0559: xattr.putValue("locale", locale);
0560: return xattr;
0561: } else {
0562: throw new BuildException(
0563: "Unable to find English/localized data about module (locale is '"
0564: + locale + "')", getLocation());
0565: }
0566: }
0567: try {
0568: JarFile mjar = new JarFile(mfile);
0569: try {
0570: if (attr.getValue("OpenIDE-Module") == null) {
0571: attr = mjar.getManifest().getMainAttributes();
0572: attr.putValue("locale", locale);
0573: }
0574: String bundlename = mjar.getManifest()
0575: .getMainAttributes().getValue(
0576: "OpenIDE-Module-Localizing-Bundle");
0577: if ((bundlename == null) && (englishAttr != null)) {
0578: String bname = englishAttr
0579: .getValue("OpenIDE-Module-Localizing-Bundle");
0580: String bfname;
0581: String bfext;
0582: if (bname != null) {
0583: bname = bname.replace(File.separatorChar, '/');
0584: bfname = bname.substring(0, bname
0585: .lastIndexOf('.'));
0586: bfext = bname.substring(bname.lastIndexOf('.'));
0587: bundlename = bfname + "_" + locale + bfext;
0588: log("Determined (" + locale
0589: + ") localizing bundle name: "
0590: + bundlename, Project.MSG_VERBOSE);
0591: }
0592: }
0593: if (bundlename != null) {
0594: Properties p = new Properties();
0595: ZipEntry bundleentry = mjar.getEntry(bundlename);
0596: if (bundleentry != null) {
0597: InputStream is = mjar
0598: .getInputStream(bundleentry);
0599: try {
0600: p.load(is);
0601: } finally {
0602: is.close();
0603: }
0604: // Now pick up attributes from the bundle.
0605: Iterator it = p.entrySet().iterator();
0606: while (it.hasNext()) {
0607: Map.Entry entry = (Map.Entry) it.next();
0608: String name = (String) entry.getKey();
0609: if (!name.startsWith("OpenIDE-Module-"))
0610: continue;
0611: attr.putValue(name, (String) entry
0612: .getValue());
0613: }
0614: }
0615: }
0616: } finally {
0617: mjar.close();
0618: }
0619: } catch (IOException ioe) {
0620: throw new BuildException("exception while reading "
0621: + mfile.getName(), ioe, getLocation());
0622: }
0623: if (locale.equals("") && (englishAttr == null)) {
0624: log("Populating English module attributes",
0625: Project.MSG_VERBOSE);
0626: englishAttr = new Attributes(attr);
0627: }
0628: String om = attr.getValue("OpenIDE-Module");
0629: String omn = attr.getValue("OpenIDE-Module-Name");
0630: String omdc = attr.getValue("OpenIDE-Module-Display-Category");
0631: String omsd = attr.getValue("OpenIDE-Module-Short-Description");
0632: String omld = attr.getValue("OpenIDE-Module-Long-Description");
0633: if (om != null)
0634: log("OpenIDE-Module"
0635: + (locale.equals("") ? "" : "_" + locale) + " is "
0636: + om, Project.MSG_VERBOSE);
0637: if (omn != null)
0638: log("OpenIDE-Module-Name"
0639: + (locale.equals("") ? "" : "_" + locale) + " is "
0640: + omn, Project.MSG_VERBOSE);
0641: if (omdc != null)
0642: log("OpenIDE-Module-Display-Category"
0643: + (locale.equals("") ? "" : "_" + locale) + " is "
0644: + omdc, Project.MSG_VERBOSE);
0645: if (omsd != null)
0646: log("OpenIDE-Module-Short-Description"
0647: + (locale.equals("") ? "" : "_" + locale) + " is "
0648: + omsd, Project.MSG_VERBOSE);
0649: if (omld != null)
0650: log("OpenIDE-Module-Long-Description"
0651: + (locale.equals("") ? "" : "_" + locale) + " is "
0652: + omld, Project.MSG_VERBOSE);
0653: return attr;
0654: }
0655:
0656: @Override
0657: public void execute() throws BuildException {
0658: if (productDir == null) {
0659: throw new BuildException(
0660: "must set directory of compiled product",
0661: getLocation());
0662: }
0663: if (file == null) {
0664: throw new BuildException("must set file for makenbm",
0665: getLocation());
0666: }
0667: if (manifest == null && moduleName == null) {
0668: throw new BuildException("must set module for makenbm",
0669: getLocation());
0670: }
0671: if (manifest != null && moduleName != null) {
0672: throw new BuildException(
0673: "cannot set both manifest and module for makenbm",
0674: getLocation());
0675: }
0676: if (locales == null) {
0677: locales = new ArrayList<String>();
0678: }
0679:
0680: File file;
0681: String rootDir = getProject().getProperty("nbm.target.dir");
0682: if (rootDir != null && !rootDir.equals("")) {
0683: file = new File(rootDir, this .file.getName());
0684: } else {
0685: file = this .file;
0686: }
0687:
0688: // If desired, override the license and/or URL. //
0689: overrideURLIfNeeded();
0690: overrideLicenseIfNeeded();
0691:
0692: moduleAttributes = new ArrayList<Attributes>();
0693: moduleAttributes.add(getModuleAttributesForLocale(""));
0694: for (String locale : locales) {
0695: Attributes a = getModuleAttributesForLocale(locale);
0696: if (a != null)
0697: moduleAttributes.add(a);
0698: }
0699:
0700: File module = new File(productDir, moduleName);
0701: // Will create a file Info/info.xml to be stored in tmp
0702: if (module != null) {
0703: // The normal case; read attributes from its manifest and maybe bundle.
0704: long mMod = module.lastModified();
0705: if (mostRecentInput < mMod)
0706: mostRecentInput = mMod;
0707: }
0708:
0709: if (mostRecentInput < file.lastModified()) {
0710: log(
0711: "Skipping NBM creation as most recent input is younger: "
0712: + mostRecentInput
0713: + " than the target file: "
0714: + file.lastModified(), Project.MSG_VERBOSE);
0715: return;
0716: } else {
0717: log("Most recent input: " + mostRecentInput + " file: "
0718: + file.lastModified(), Project.MSG_DEBUG);
0719: }
0720:
0721: ArrayList<ZipFileSet> infoXMLFileSets = new ArrayList<ZipFileSet>();
0722: for (Attributes modAttr : moduleAttributes) {
0723: Document infoXmlContents = createInfoXml(modAttr);
0724: File infofile;
0725: String loc = modAttr.getValue("locale");
0726: if (loc == null)
0727: throw new BuildException(
0728: "Found attributes without assigned locale code",
0729: getLocation());
0730: try {
0731: infofile = File.createTempFile("info_" + loc, ".xml");
0732: OutputStream infoStream = new FileOutputStream(infofile);
0733: try {
0734: XMLUtil.write(infoXmlContents, infoStream);
0735: } finally {
0736: infoStream.close();
0737: }
0738: } catch (IOException e) {
0739: throw new BuildException(
0740: "exception when creating Info/info.xml for locale '"
0741: + loc + "'", e, getLocation());
0742: }
0743: infofile.deleteOnExit();
0744: ZipFileSet infoXML = new ZipFileSet();
0745: infoXML.setFile(infofile);
0746: if (loc.equals("")) {
0747: infoXML.setFullpath("Info/info.xml");
0748: } else {
0749: infoXML.setFullpath("Info/locale/info_" + loc + ".xml");
0750: log("Adding Info/locale/info_" + loc + ".xml file",
0751: Project.MSG_VERBOSE);
0752: }
0753: infoXMLFileSets.add(infoXML);
0754: }
0755: String codename = englishAttr.getValue("OpenIDE-Module");
0756: if (codename == null)
0757: new BuildException("Can't get codenamebase");
0758:
0759: UpdateTracking tracking = new UpdateTracking(productDir
0760: .getAbsolutePath());
0761: String files[] = tracking.getListOfNBM(codename);
0762: ZipFileSet fs = new ZipFileSet();
0763: fs.setDir(productDir);
0764: for (int i = 0; i < files.length; i++)
0765: fs.createInclude().setName(files[i]);
0766: fs.setPrefix("netbeans/");
0767:
0768: // JAR it all up together.
0769: long jarModified = file.lastModified(); // may be 0
0770: //log ("Ensuring existence of NBM file " + file);
0771: Jar jar = (Jar) getProject().createTask("jar");
0772:
0773: jar.setDestFile(file);
0774: jar.addZipfileset(fs);
0775: for (ZipFileSet zfs : infoXMLFileSets) {
0776: jar.addFileset(zfs);
0777: }
0778:
0779: if (main != null) { // Add the main dir
0780: main.setPrefix("main"); // use main prefix
0781: jar.addZipfileset(main);
0782: }
0783:
0784: jar.setCompress(true);
0785: jar.setLocation(getLocation());
0786: jar.init();
0787: jar.execute();
0788:
0789: // Print messages if we overrode anything. //
0790: if (file.lastModified() != jarModified) {
0791: if (overrideLicense()) {
0792: log("Overriding license with: " + getLicenseOverride());
0793: }
0794: if (overrideURL()) {
0795: log("Overriding homepage URL with: " + getURLOverride());
0796: }
0797: }
0798:
0799: // Maybe sign it.
0800: if (signature != null && file.lastModified() != jarModified) {
0801: if (signature.keystore == null)
0802: throw new BuildException(
0803: "must define keystore attribute on <signature/>");
0804: if (signature.storepass == null)
0805: throw new BuildException(
0806: "must define storepass attribute on <signature/>");
0807: if (signature.alias == null)
0808: throw new BuildException(
0809: "must define alias attribute on <signature/>");
0810: if (signature.storepass.equals("?")
0811: || signature.storepass.indexOf("${") != -1
0812: || !signature.keystore.exists()) {
0813: log(
0814: "Not signing NBM file "
0815: + file
0816: + "; no stored-key password provided or keystore ("
0817: + signature.keystore.toString()
0818: + ") doesn't exist", Project.MSG_WARN);
0819: } else {
0820: log("Signing NBM file " + file);
0821: SignJar signjar = (SignJar) getProject().createTask(
0822: "signjar");
0823: try { // Signatures changed in various Ant versions.
0824: try {
0825: SignJar.class.getMethod("setKeystore",
0826: File.class).invoke(signjar,
0827: signature.keystore);
0828: } catch (NoSuchMethodException x) {
0829: SignJar.class.getMethod("setKeystore",
0830: String.class).invoke(signjar,
0831: signature.keystore.getAbsolutePath());
0832: }
0833: try {
0834: SignJar.class.getMethod("setJar", File.class)
0835: .invoke(signjar, file);
0836: } catch (NoSuchMethodException x) {
0837: SignJar.class
0838: .getMethod("setJar", String.class)
0839: .invoke(signjar, file.getAbsolutePath());
0840: }
0841: } catch (BuildException x) {
0842: throw x;
0843: } catch (Exception x) {
0844: throw new BuildException(x);
0845: }
0846: signjar.setStorepass(signature.storepass);
0847: signjar.setAlias(signature.alias);
0848: signjar.setLocation(getLocation());
0849: signjar.setMaxmemory(this .jarSignerMaxMemory);
0850: signjar.init();
0851: signjar.execute();
0852: }
0853: }
0854: }
0855:
0856: private Document createInfoXml(final Attributes attr)
0857: throws BuildException {
0858: DOMImplementation domimpl;
0859: try {
0860: domimpl = DocumentBuilderFactory.newInstance()
0861: .newDocumentBuilder().getDOMImplementation();
0862: } catch (ParserConfigurationException x) {
0863: throw new BuildException(x, getLocation());
0864: }
0865: String loc = attr.getValue("locale");
0866: if (loc == null) {
0867: throw new BuildException(
0868: "Got module attributes for undefined locale",
0869: getLocation());
0870: } else {
0871: log("Creating info.xml from module attributes for locale '"
0872: + loc + "'", Project.MSG_VERBOSE);
0873: }
0874:
0875: String pub, sys;
0876: if (attr.getValue("AutoUpdate-Show-In-Client") != null
0877: || attr.getValue("AutoUpdate-Essential-Module") != null) {
0878: pub = "-//NetBeans//DTD Autoupdate Module Info 2.5//EN";
0879: sys = "http://www.netbeans.org/dtds/autoupdate-info-2_5.dtd";
0880: } else if (targetcluster != null && !("".equals(targetcluster))) {
0881: pub = "-//NetBeans//DTD Autoupdate Module Info 2.4//EN";
0882: sys = "http://www.netbeans.org/dtds/autoupdate-info-2_4.dtd";
0883: } else {
0884: // #74866: no need for targetcluster, so keep compat w/ 5.0 AU.
0885: pub = "-//NetBeans//DTD Autoupdate Module Info 2.3//EN";
0886: sys = "http://www.netbeans.org/dtds/autoupdate-info-2_3.dtd";
0887: }
0888: Document doc = domimpl.createDocument(null, "module", domimpl
0889: .createDocumentType("module", pub, sys));
0890: String codenamebase = attr.getValue("OpenIDE-Module");
0891: if (codenamebase == null) {
0892: Iterator it = attr.keySet().iterator();
0893: Name key;
0894: String val;
0895: while (it.hasNext()) {
0896: key = (Name) it.next();
0897: val = attr.getValue(key);
0898: log(key + " is '" + val + "'", Project.MSG_VERBOSE);
0899: }
0900: throw new BuildException(
0901: "invalid manifest, does not contain OpenIDE-Module",
0902: getLocation());
0903: }
0904: // Strip major release number if any.
0905: int idx = codenamebase.lastIndexOf('/');
0906: if (idx != -1)
0907: codenamebase = codenamebase.substring(0, idx);
0908: Element module = doc.getDocumentElement();
0909: module.setAttribute("codenamebase", codenamebase);
0910: if (homepage != null) {
0911: module.setAttribute("homepage", homepage);
0912: }
0913: if (distribution != null) {
0914: module.setAttribute("distribution", distribution);
0915: } else {
0916: throw new BuildException("NBM distribution URL is not set",
0917: getLocation());
0918: }
0919: // Here we only write a name for the license.
0920: if (license != null) {
0921: String name = license.getName();
0922: if (name == null) {
0923: throw new BuildException(
0924: "Every license must have a name or file attribute",
0925: getLocation());
0926: }
0927: module.setAttribute("license", name);
0928: }
0929: module.setAttribute("downloadsize", "0");
0930: if (needsrestart != null) {
0931: module.setAttribute("needsrestart", needsrestart);
0932: }
0933: if (global != null && !("".equals(global))) {
0934: module.setAttribute("global", global);
0935: }
0936: if (targetcluster != null && !("".equals(targetcluster))) {
0937: module.setAttribute("targetcluster", targetcluster);
0938: }
0939: if (moduleauthor != null) {
0940: module.setAttribute("moduleauthor", moduleauthor);
0941: }
0942: if (releasedate == null || "".equals(releasedate)) {
0943: // if date is null, set today
0944: releasedate = DATE_FORMAT.format(new Date(System
0945: .currentTimeMillis()));
0946: }
0947: module.setAttribute("releasedate", releasedate);
0948: if (description != null) {
0949: module.appendChild(doc.createElement("description"))
0950: .appendChild(description.getTextNode(doc));
0951: }
0952: if (notification != null) {
0953: module
0954: .appendChild(
0955: doc.createElement("module_notification"))
0956: .appendChild(notification.getTextNode(doc));
0957: }
0958: if (externalPackages != null) {
0959: Iterator<ExternalPackage> exp = externalPackages.iterator();
0960: while (exp.hasNext()) {
0961: ExternalPackage externalPackage = exp.next();
0962: if (externalPackage.name == null
0963: || externalPackage.targetName == null
0964: || externalPackage.startUrl == null)
0965: throw new BuildException(
0966: "Must define name, targetname, starturl for external package");
0967: Element el = doc.createElement("external_package");
0968: el.setAttribute("name", externalPackage.name);
0969: el.setAttribute("target_name",
0970: externalPackage.targetName);
0971: el.setAttribute("start_url", externalPackage.startUrl);
0972: if (externalPackage.description != null) {
0973: el.setAttribute("description",
0974: externalPackage.description);
0975: }
0976: module.appendChild(el);
0977: }
0978: }
0979: // Write manifest attributes.
0980: Element el = doc.createElement("manifest");
0981: List<String> attrNames = new ArrayList<String>(attr.size());
0982: Iterator it = attr.keySet().iterator();
0983: while (it.hasNext()) {
0984: attrNames.add(((Attributes.Name) it.next()).toString());
0985: }
0986: Collections.sort(attrNames);
0987: it = attrNames.iterator();
0988: while (it.hasNext()) {
0989: String name = (String) it.next();
0990: // Ignore irrelevant attributes (cf. www/www/dtds/autoupdate-catalog-*.dtd
0991: // and www/www/dtds/autoupdate-info-*.dtd):
0992: // XXX better would be to enumerate the attrs it *does* recognize!
0993: if (!name.startsWith("OpenIDE-Module")
0994: && !name.startsWith("AutoUpdate-"))
0995: continue;
0996: if (name.equals("OpenIDE-Module-Localizing-Bundle"))
0997: continue;
0998: if (name.equals("OpenIDE-Module-Install"))
0999: continue;
1000: if (name.equals("OpenIDE-Module-Layer"))
1001: continue;
1002: if (name.equals("OpenIDE-Module-Description"))
1003: continue;
1004: if (name
1005: .equals("OpenIDE-Module-Package-Dependency-Message"))
1006: continue;
1007: if (name.equals("OpenIDE-Module-Public-Packages"))
1008: continue;
1009: if (name.equals("OpenIDE-Module-Friends"))
1010: continue;
1011: el.setAttribute(name, attr.getValue(name));
1012: }
1013: module.appendChild(el);
1014: // Maybe write out license text.
1015: if (license != null) {
1016: el = doc.createElement("license");
1017: el.setAttribute("name", license.getName());
1018: el.appendChild(license.getTextNode(doc));
1019: module.appendChild(el);
1020: }
1021: return doc;
1022: }
1023:
1024: /** This returns true if the license should be overridden. */
1025: protected boolean overrideLicense() {
1026: return (getLicenseOverride() != null);
1027: }
1028:
1029: /** Get the license to use if the license should be overridden,
1030: * otherwise return null.
1031: */
1032: protected String getLicenseOverride() {
1033: String s = getProject().getProperty("makenbm.override.license");
1034: if (s != null) {
1035: if (s.equals("")) {
1036: s = null;
1037: }
1038: }
1039: return (s);
1040: }
1041:
1042: /** This returns true if the homepage URL should be overridden. */
1043: protected boolean overrideURL() {
1044: return (getURLOverride() != null);
1045: }
1046:
1047: /** Get the homepage URL to use if it should be overridden,
1048: * otherwise return null.
1049: */
1050: protected String getURLOverride() {
1051: String s = getProject().getProperty("makenbm.override.url");
1052: if (s != null) {
1053: if (s.equals("")) {
1054: s = null;
1055: }
1056: }
1057: return (s);
1058: }
1059:
1060: /** If required, this will create a new license using the override
1061: * license file.
1062: */
1063: protected void overrideLicenseIfNeeded() {
1064: if (overrideLicense()) {
1065: license = new Blurb();
1066: license.setFile(getProject().resolveFile(
1067: getLicenseOverride()));
1068: }
1069: }
1070:
1071: /** If required, this will set the homepage URL using the
1072: * override value.
1073: */
1074: protected void overrideURLIfNeeded() {
1075: if (overrideURL()) {
1076: homepage = getURLOverride();
1077: }
1078: }
1079:
1080: }
|