alvinalexander.com | career | drupal | java | mac | mysql | perl | scala | uml | unix  

What this is

This file is included in the DevDaily.com "Java Source Code Warehouse" project. The intent of this project is to help you "Learn Java by Example" TM.

Other links

The source code

/*
 *                 Sun Public License Notice
 *
 * The contents of this file are subject to the Sun Public License
 * Version 1.0 (the "License"). You may not use this file except in
 * compliance with the License. A copy of the License is available at
 * http://www.sun.com/
 *
 * The Original Code is NetBeans. The Initial Developer of the Original
 * Code is Sun Microsystems, Inc. Portions Copyright 1997-2003 Sun
 * Microsystems, Inc. All Rights Reserved.
 */

package org.netbeans.nbbuild;

import java.io.*;
import java.util.*;
import java.util.jar.*;
import java.util.zip.*;
import java.net.*;
import java.text.SimpleDateFormat;

import org.apache.tools.ant.BuildException;
import org.apache.tools.ant.Location;
import org.apache.tools.ant.Project;
import org.apache.tools.ant.Task;
import org.apache.tools.ant.taskdefs.Jar;
import org.apache.tools.ant.taskdefs.Zip;
import org.apache.tools.ant.taskdefs.SignJar;
import org.apache.tools.ant.types.FileSet;
import org.apache.tools.ant.types.ZipFileSet;


/** Makes a .nbm (NetBeans Module) file.
 *
 * @author Jesse Glick
 */
public class MakeNBM extends Task {
    
    private static final SimpleDateFormat DATE_FORMAT = new SimpleDateFormat ("yyyy/MM/dd"); // NOI18N
    
    /** The same syntax may be used for either <license> or
     * <description> subelements.
     * 

By setting the property makenbm.nocdata to true, * you can avoid using XML CDATA (for compatibility with older versions * of Auto Update which could not handle it). */ public class Blurb { /** You may embed a <file> element inside the blurb. * If there is text on either side of it, that will be separated * with a line of dashes automatically. * But use nested <text> for this purpose. */ public class FileInsert { /** File location. */ public void setLocation (File file) throws BuildException { log("Including contents of " + file, Project.MSG_VERBOSE); long lmod = file.lastModified (); if (lmod > mostRecentInput) mostRecentInput = lmod; addSeparator (); try { InputStream is = new FileInputStream (file); try { Reader r = new InputStreamReader (is, "UTF-8"); //NOI18N char[] buf = new char[4096]; int len; while ((len = r.read (buf)) != -1) text.append (buf, 0, len); } finally { is.close (); } } catch (IOException ioe) { throw new BuildException ("Exception reading blurb from " + file, ioe, getLocation ()); } } } private StringBuffer text = new StringBuffer (); private String name = null; /** There may be freeform text inside the element. Prefer to use nested elements. */ public void addText (String t) { addSeparator (); // Strips indentation. Needed because of common style: // // Some text here. // And another line. // t = getProject().replaceProperties(t.trim()); int min = Integer.MAX_VALUE; StringTokenizer tok = new StringTokenizer (t, "\n"); //NOI18N boolean first = true; while (tok.hasMoreTokens ()) { String line = tok.nextToken (); if (first) { first = false; } else { int i; for (i = 0; i < line.length () && Character.isWhitespace (line.charAt (i)); i++) ; if (i < min) min = i; } } if (min == 0) { text.append (t); } else { tok = new StringTokenizer (t, "\n"); //NOI18N first = true; while (tok.hasMoreTokens ()) { String line = tok.nextToken (); if (first) { first = false; } else { text.append ('\n'); //NOI18N line = line.substring (min); } text.append (line); } } } /** Contents of a file to include. */ public FileInsert createFile () { return new FileInsert (); } /** Text to include literally. */ public class Text { public void addText(String t) { Blurb.this.addText(t); } } // At least on Ant 1.3, mixed content does not work: all the text is added // first, then all the file inserts. Need to use subelements to be sure. /** Include nested literal text. */ public Text createText() { return new Text(); } private void addSeparator () { if (text.length () > 0) { // some sort of separator if (text.charAt (text.length () - 1) != '\n') //NOI18N text.append ('\n'); //NOI18N text.append ("-----------------------------------------------------\n"); //NOI18N } } public String getText () { String nocdata = getProject().getProperty("makenbm.nocdata"); //NOI18N if (nocdata != null && Project.toBoolean(nocdata)) { return xmlEscape(text.toString()); } else { return ""; //NOI18N } } /** You can either set a name for the blurb, or using the file attribute does this. * The name is mandatory for licenses, as this identifies the license in * an update description. */ public void setName (String name) { this.name = name; } public String getName () { return name; } /** Include a file (and set the license name according to its basename). */ public void setFile (File file) { // This actually adds the text and so on: new FileInsert ().setLocation (file); // Default for the name too, as a convenience. if (name == null) name = file.getName (); } } public class ExternalPackage { String name = null; String targetName = null; String startUrl = null; String description = null; public void setName(String n) { this.name = n; } public void setTargetName(String t) { this.targetName = t; } public void setStartURL(String u) { this.startUrl = u; } public void setDescription(String d) { this.description = d; } } // Similar to org.openide.xml.XMLUtil methods. private static String xmlEscape(String s) { int max = s.length(); StringBuffer s2 = new StringBuffer((int)(max * 1.1 + 1)); for (int i = 0; i < max; i++) { char c = s.charAt(i); switch (c) { case '<': //NOI18N s2.append("<"); //NOI18N break; case '>': //NOI18N s2.append(">"); //NOI18N break; case '&': //NOI18N s2.append("&"); //NOI18N break; case '"': //NOI18N s2.append("""); //NOI18N break; default: s2.append(c); break; } } return s2.toString(); } /** <signature> subelement for signing the NBM. */ public /*static*/ class Signature { public File keystore; public String storepass, alias; /** Path to the keystore (private key). */ public void setKeystore(File f) { keystore = f; } /** Password for the keystore. * If a question mark (?), the NBM will not be signed * and a warning will be printed. */ public void setStorepass(String s) { storepass = s; } /** Alias for the private key. */ public void setAlias(String s) { alias = s; } } private File productDir = null; private File file = null; private File manifest = null; /** see #13850 for explanation */ private String moduleName = null; private String homepage = null; private String distribution = null; private String needsrestart = null; private String moduleauthor = null; private String releasedate = null; private Blurb license = null; private Blurb description = null; private Blurb notification = null; private Signature signature = null; private long mostRecentInput = 0L; private boolean isStandardInclude = true; private Vector externalPackages = null; /** Include netbeans directory - default is true */ public void setIsStandardInclude(boolean isStandardInclude) { this.isStandardInclude = isStandardInclude; } /** Directory of the product's files */ public void setProductDir( File dir ) { productDir = dir; } /** Name of resulting NBM file. */ public void setFile(File file) { this.file = file; } /** Module manifest needed for versioning. * @deprecated Use {@link #setModule} instead. */ public void setManifest(File manifest) { this.manifest = manifest; long lmod = manifest.lastModified(); if (lmod > mostRecentInput) mostRecentInput = lmod; log(getLocation() + "The 'manifest' attr on is deprecated, please use 'module' instead", Project.MSG_WARN); } /** Module JAR needed for generating the info file. * Information may be gotten either from its manifest, * or if it declares OpenIDE-Module-Localizing-Bundle in its * manifest, from that bundle. * The base locale variant, if any, is also checked if necessary * for the named bundle. * Currently no other locale variants of the module are examined; * the information is available but there is no published specification * of what the resulting variant NBMs (or variant information within * the NBM) should look like. */ public void setModule(String module) { this.moduleName = module; // mostRecentInput updated below... } /** URL to a home page describing the module. */ public void setHomepage (String homepage) { this.homepage = homepage; } /** Does module need IDE restart to be installed? */ public void setNeedsrestart (String needsrestart) { this.needsrestart = needsrestart; } /** Name of module's author. */ public void setModuleauthor (String author) { this.moduleauthor = author; } /** Release date of NBM. */ public void setReleasedate (String date) { this.releasedate = date; } /** URL where this NBM file is expected to be downloadable from. */ public void setDistribution (String distribution) throws BuildException { if (distribution.startsWith("http://")) { //NOI18N this.distribution = distribution; } else if (!(distribution.equals(""))) { // workaround for typical bug in build script this.distribution = "http://" + distribution; //NOI18N } else { throw new BuildException("Distribution URL is empty, check build.xml file", location); } // check the URL try { URI uri = java.net.URI.create(this.distribution); } catch (IllegalArgumentException ile) { throw new BuildException ("Distribution URL \""+this.distribution+"\" is not a valid URI", ile, location); } } public Blurb createLicense () { return (license = new Blurb ()); } public Blurb createNotification () { return (notification = new Blurb ()); } public Blurb createDescription () { log(getLocation() + "The subelement in is deprecated except for emergency patches, please ensure your module has an OpenIDE-Module-Long-Description instead", Project.MSG_WARN); return (description = new Blurb ()); } public Signature createSignature () { return (signature = new Signature ()); } public ExternalPackage createExternalPackage(){ ExternalPackage externalPackage = new ExternalPackage (); if (externalPackages == null) externalPackages = new Vector(); externalPackages.add( externalPackage ); return externalPackage; } public void execute () throws BuildException { if (productDir == null) throw new BuildException("must set directory of compiled product", location); if (file == null) throw new BuildException ("must set file for makenbm", location); if (manifest == null && moduleName == null) throw new BuildException ("must set module for makenbm", location); if (manifest != null && moduleName != null) throw new BuildException("cannot set both manifest and module for makenbm", location); File file; String rootDir = getProject ().getProperty ("nbm.target.dir"); if (rootDir != null && !rootDir.equals ("")) { file = new File (rootDir, this.file.getName ()); } else { file = this.file; } // If desired, override the license and/or URL. // overrideURLIfNeeded() ; overrideLicenseIfNeeded() ; File module = new File( productDir, moduleName ); // Will create a file Info/info.xml to be stored in tmp File infofile = null; Attributes attr = null; if (module != null) { // The normal case; read attributes from its manifest and maybe bundle. long mMod = module.lastModified(); if (mostRecentInput < mMod) mostRecentInput = mMod; try { JarFile modulejar = new JarFile(module); try { attr = modulejar.getManifest().getMainAttributes(); String bundlename = attr.getValue("OpenIDE-Module-Localizing-Bundle"); //NOI18N if (bundlename != null) { Properties p = new Properties(); ZipEntry bundleentry = modulejar.getEntry(bundlename); if (bundleentry != null) { InputStream is = modulejar.getInputStream(bundleentry); try { p.load(is); } finally { is.close(); } } else { // Not found in main JAR, check locale variant JAR. File variant = new File(new File(module.getParentFile(), "locale"), module.getName()); //NOI18N if (! variant.isFile()) throw new BuildException(bundlename + " not found in " + module, location); //NOI18N long vmMod = variant.lastModified(); if (mostRecentInput < vmMod) mostRecentInput = vmMod; ZipFile variantjar = new ZipFile(variant); try { bundleentry = variantjar.getEntry(bundlename); if (bundleentry == null) throw new BuildException(bundlename + " not found in " + module + " nor in " + variant, location); InputStream is = variantjar.getInputStream(bundleentry); try { p.load(is); } finally { is.close(); } } finally { variantjar.close(); } } // Now pick up attributes from the bundle. Iterator it = p.entrySet().iterator(); while (it.hasNext()) { Map.Entry entry = (Map.Entry)it.next(); String name = (String)entry.getKey(); if (! name.startsWith("OpenIDE-Module-")) continue; //NOI18N attr.putValue(name, (String)entry.getValue()); } } // else all loc attrs in main manifest, OK } finally { modulejar.close(); } } catch (IOException ioe) { throw new BuildException("exception while reading " + module, ioe, location); } } try { infofile = File.createTempFile("info",".xml"); OutputStream infoStream = new FileOutputStream (infofile); try { PrintWriter ps = new PrintWriter(new OutputStreamWriter(infoStream, "UTF-8")); //NOI18N // Begin writing XML. ps.println (""); //NOI18N ps.println(""); //NOI18N String codenamebase = attr.getValue ("OpenIDE-Module"); //NOI18N if (codenamebase == null) throw new BuildException ("invalid manifest, does not contain OpenIDE-Module", location); // Strip major release number if any. int idx = codenamebase.lastIndexOf ('/'); //NOI18N if (idx != -1) codenamebase = codenamebase.substring (0, idx); ps.println (""); //NOI18N if (description != null) { ps.print (" "); //NOI18N ps.print (description.getText ()); ps.println (""); //NOI18N } if (notification != null) { ps.print(" "); //NOI18N ps.print(notification.getText()); ps.println(""); //NOI18N } if (externalPackages != null) { Enumeration exp = externalPackages.elements(); while (exp.hasMoreElements()) { ExternalPackage externalPackage = (ExternalPackage) exp.nextElement(); if (externalPackage.name == null || externalPackage.targetName == null || externalPackage.startUrl == null) throw new BuildException("Must define name, targetname, starturl for external package"); ps.print(" "); //NOI18N } } // Write manifest attributes. ps.print (" Iterator it = attr.keySet().iterator(); while (it.hasNext()) { attrNames.add(((Attributes.Name)it.next()).toString()); } Collections.sort(attrNames); it = attrNames.iterator(); while (it.hasNext()) { String name = (String)it.next(); // Ignore irrelevant attributes (cf. www/www/dtds/autoupdate-catalog-2_0.dtd // and www/www/dtds/autoupdate-info-2_0.dtd): if (! name.startsWith("OpenIDE-Module")) continue; //NOI18N if (name.equals("OpenIDE-Module-Localizing-Bundle")) continue; //NOI18N if (name.equals("OpenIDE-Module-Install")) continue; //NOI18N if (name.equals("OpenIDE-Module-Layer")) continue; //NOI18N if (name.equals("OpenIDE-Module-Description")) continue; //NOI18N if (name.equals("OpenIDE-Module-Package-Dependency-Message")) continue; //NOI18N if (name.equals("OpenIDE-Module-Public-Packages")) continue; //NOI18N if (firstline) firstline = false; else ps.print (" "); ps.println(name + "=\"" + xmlEscape(attr.getValue(name)) + "\""); //NOI18N } ps.println (" />"); //NOI18N // Maybe write out license text. if (license != null) { ps.print (" "); //NOI18N ps.print (license.getText ()); ps.println (""); //NOI18N } ps.println (""); //NOI18N ps.flush(); } finally { infoStream.close (); } } catch (IOException e) { throw new BuildException ("exception when creating Info/info.xml", e, location); } infofile.deleteOnExit(); ZipFileSet infoXML = new ZipFileSet(); infoXML.setFile( infofile ); infoXML.setFullpath("Info/info.xml"); String codename = attr.getValue("OpenIDE-Module"); if (codename == null) new BuildException( "Can't get codenamebase" ); UpdateTracking tracking = new UpdateTracking(productDir.getAbsolutePath()); String files[] = tracking.getListOfNBM( codename ); ZipFileSet fs = new ZipFileSet(); fs.setDir( productDir ); for (int i=0; i < files.length; i++) fs.createInclude().setName( files[i] ); fs.setPrefix("netbeans/"); // JAR it all up together. long jarModified = file.lastModified (); // may be 0 //log ("Ensuring existence of NBM file " + file); Jar jar = (Jar) project.createTask ("jar"); //NOI18N jar.setJarfile (file); jar.addZipfileset(fs); jar.addFileset (infoXML); jar.setCompress(true); jar.setLocation (location); jar.init (); jar.execute (); // Print messages if we overrode anything. // if( file.lastModified () != jarModified) { if( overrideLicense()) { log( "Overriding license with: " + getLicenseOverride()) ; } if( overrideURL()) { log( "Overriding homepage URL with: " + getURLOverride()) ; } } // Maybe sign it. if (signature != null && file.lastModified () != jarModified) { if (signature.keystore == null) throw new BuildException ("must define keystore attribute on "); if (signature.storepass == null) throw new BuildException ("must define storepass attribute on "); if (signature.alias == null) throw new BuildException ("must define alias attribute on "); if (signature.storepass.equals ("?") || !signature.keystore.exists()) { log ("Not signing NBM file " + file + "; no stored-key password provided or keystore (" + signature.keystore.toString() + ") doesn't exist", Project.MSG_WARN); } else { log ("Signing NBM file " + file); SignJar signjar = (SignJar) project.createTask ("signjar"); //NOI18N //I have to use Reflection API, because there was changed API in ANT1.5 try { try { Class[] paramsT = {String.class}; Object[] paramsV1 = {signature.keystore.getAbsolutePath()}; Object[] paramsV2 = {file.getAbsolutePath()}; signjar.getClass().getDeclaredMethod( "setKeystore", paramsT ).invoke( signjar, paramsV1 ); //NOI18N signjar.getClass().getDeclaredMethod( "setJar", paramsT ).invoke( signjar, paramsV2 ); //NOI18N } catch (NoSuchMethodException ex1) { //Probably ANT 1.5 try { Class[] paramsT = {File.class}; Object[] paramsV1 = {signature.keystore}; Object[] paramsV2 = {file}; signjar.getClass().getDeclaredMethod( "setKeystore", paramsT ).invoke( signjar, paramsV1 ); //NOI18N signjar.getClass().getDeclaredMethod( "setJar", paramsT ).invoke( signjar, paramsV2 ); //NOI18N } catch (NoSuchMethodException ex2) { //Probably ANT1.5.2 try { Class[] paramsT1 = {File.class}; Class[] paramsT2 = {String.class}; Object[] paramsV1 = {signature.keystore.getAbsolutePath()}; Object[] paramsV2 = {file}; signjar.getClass().getDeclaredMethod( "setKeystore", paramsT2 ).invoke( signjar, paramsV1 ); //NOI18N signjar.getClass().getDeclaredMethod( "setJar", paramsT1 ).invoke( signjar, paramsV2 ); //NOI18N } catch (NoSuchMethodException ex3) { throw new BuildException("Unknown ANT version, only ANT 1.4.1 is currently supported and ANT 1.4.1+ is acceptable."); } } } } catch (IllegalAccessException ex3) { throw new BuildException(ex3); } catch (java.lang.reflect.InvocationTargetException ex4) { throw new BuildException(ex4); } signjar.setStorepass (signature.storepass); signjar.setAlias (signature.alias); signjar.setLocation (location); signjar.init (); signjar.execute (); } } } /** This returns true if the license should be overridden. */ protected boolean overrideLicense() { return( getLicenseOverride() != null) ; } /** Get the license to use if the license should be overridden, * otherwise return null. */ protected String getLicenseOverride() { String s = getProject().getProperty( "makenbm.override.license") ; //NOI18N if( s != null) { if( s.equals( "")) { s = null ; } } return( s) ; } /** This returns true if the homepage URL should be overridden. */ protected boolean overrideURL() { return( getURLOverride() != null) ; } /** Get the homepage URL to use if it should be overridden, * otherwise return null. */ protected String getURLOverride() { String s = getProject().getProperty( "makenbm.override.url") ; //NOI18N if( s != null) { if( s.equals( "")) { //NOI18N s = null ; } } return( s) ; } /** If required, this will create a new license using the override * license file. */ protected void overrideLicenseIfNeeded() { if( overrideLicense()) { license = new Blurb() ; license.setFile( new File( getLicenseOverride())) ; } } /** If required, this will set the homepage URL using the * override value. */ protected void overrideURLIfNeeded() { if( overrideURL()) { homepage = getURLOverride() ; } } }

... this post is sponsored by my books ...

#1 New Release!

FP Best Seller

 

new blog posts

 

Copyright 1998-2021 Alvin Alexander, alvinalexander.com
All Rights Reserved.

A percentage of advertising revenue from
pages under the /java/jwarehouse URI on this website is
paid back to open source projects.