Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/enhancer/Main.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/enhancer/Main.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/enhancer/Main.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/enhancer/Main.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,957 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jdo.enhancer; + +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.File; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.DataInputStream; +import java.io.DataOutputStream; +import java.io.FileInputStream; +import java.io.FileOutputStream; +import java.io.BufferedInputStream; +import java.io.BufferedOutputStream; +import java.io.PrintWriter; +import java.io.FileReader; +import java.io.BufferedReader; + +import java.util.Map; +import java.util.List; +import java.util.Collection; +import java.util.Enumeration; +import java.util.Iterator; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.Hashtable; +import java.util.Properties; +import java.util.StringTokenizer; + +import java.util.zip.ZipFile; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; +import java.util.zip.ZipException; + +import java.net.URL; + +import org.apache.jdo.impl.enhancer.ClassFileEnhancer; +import org.apache.jdo.impl.enhancer.ClassFileEnhancerHelper; +import org.apache.jdo.impl.enhancer.ClassFileEnhancerTimer; +import org.apache.jdo.impl.enhancer.EnhancerFatalError; +import org.apache.jdo.impl.enhancer.EnhancerUserException; +import org.apache.jdo.impl.enhancer.OutputStreamWrapper; +import org.apache.jdo.impl.enhancer.core.EnhancerFilter; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaData; +import org.apache.jdo.impl.enhancer.meta.EnhancerMetaDataFatalError; +import org.apache.jdo.impl.enhancer.meta.model.EnhancerMetaDataJDOModelImpl; +import org.apache.jdo.impl.enhancer.meta.prop.EnhancerMetaDataPropertyImpl; +import org.apache.jdo.impl.enhancer.meta.util.EnhancerMetaDataBaseModel; +import org.apache.jdo.impl.enhancer.meta.util.EnhancerMetaDataTimer; +import org.apache.jdo.impl.enhancer.util.CombinedResourceLocator; +import org.apache.jdo.impl.enhancer.util.ListResourceLocator; +import org.apache.jdo.impl.enhancer.util.PathResourceLocator; +import org.apache.jdo.impl.enhancer.util.ResourceLocator; +import org.apache.jdo.impl.enhancer.util.ResourceLocatorTimer; +import org.apache.jdo.impl.enhancer.util.Support; + + + + + +/** + * Main is the starting point for the persistent filter tool. + */ +public class Main + extends Support +{ + // return values of main() + static public final int OK = 0; + static public final int USAGE_ERROR = -1; + static public final int METADATA_ERROR = -2; + static public final int CLASS_LOCATOR_ERROR = -3; + static public final int INTERNAL_ERROR = -4; + + /** + * The stream to write messages to. + */ + private final PrintWriter out = new PrintWriter(System.out, true); + + /** + * The stream to write error messages to. + */ + private final PrintWriter err = new PrintWriter(System.err, true); + + /** + * The command line options. + */ + private final CmdLineOptions opts = new CmdLineOptions(); + + /** + * The byte code enhancer. + */ + private ClassFileEnhancer enhancer; + + /** + * The locator for classes. + */ + protected ResourceLocator classLocator; + + /** + * The metadata for the enhancer. + */ + private EnhancerMetaData jdoMetaData; + + /** + * Construct a filter tool instance + */ + public Main() + {} + + // ---------------------------------------------------------------------- + + /** + * This is where it all starts. + */ + public static void main(String[] argv) + { + int res; + final Main main = new Main(); + + //@olsen: added support for timing statistics + try { + res = main.process(argv); + } catch (RuntimeException ex) { + main.out.flush(); + main.err.println("Internal error while postprocessing: " + + ex.getMessage()); + ex.printStackTrace(main.err); + main.err.flush(); + res = INTERNAL_ERROR; + } finally { + //@olsen: added support for timing statistics + if (main.opts.doTiming) { + Support.timer.print(); + } + } + System.exit(res); + } + + /** + * Process command line options and run enhancer. + */ + public int process(String[] argv) + { + int res; + + if ((res = opts.processArgs(argv)) != OK) { + printMessage("aborted with errors."); + return res; + } + + //@olsen: added support for timing statistics + try { + if (opts.doTiming) { + timer.push("Main.process(String[])"); + } + + if ((res = createEnhancer()) != OK) { + printMessage("aborted with errors."); + return res; + } + + if ((res = enhanceInputFiles(opts.classNames, + opts.classFileNames, + opts.zipFileNames, + opts.jdoFileNames)) != OK) { + printMessage("aborted with errors."); + return res; + } + + printMessage("done."); + return 0; + } finally { + if (opts.doTiming) { + timer.pop(); + } + } + } + + // ---------------------------------------------------------------------- + + /** + * A class for holding the command line options. + */ + private class CmdLineOptions + { + final List classNames = new ArrayList(); + final List classFileNames = new ArrayList(); + final List zipFileNames = new ArrayList(); + final List jdoFileNames = new ArrayList(); + String sourcePath = null; + String destinationDirectory = null; + String propertiesFileName = null; + boolean doTiming = false; + boolean verbose = false; + boolean quiet = false; + boolean forceWrite = false; + boolean noWrite = false; + boolean dumpClass = false; + boolean noAugment = false; + boolean noAnnotate = false; + + /** + * Print a usage message to System.err. + */ + public void usage() { + err.println("Usage: main <options> <arguments>..."); + err.println("Options:"); + err.println(" -h, --help print usage message and exit gently"); + err.println(" -v, --verbose print verbose messages"); + err.println(" -q, --quiet supress warnings"); + err.println(" -s, --sourcepath <path> source path for jdo and classfiles"); + err.println(" -d, --destdir <dir> destination directory for output files"); + err.println(" -f, --force overwrite output files"); + err.println(" -n, --nowrite never write output files"); + err.println(" -t, --timing do timing messures"); + err.println(); + err.println("Debugging Options:"); + err.println(" --properties <file> use property file for meta data"); + err.println(" --dumpclass print out disassembled code of classes"); + err.println(" --noaugment do not enhance for persistence-capability"); + err.println(" --noannotate do not enhance for persistence-awareness"); + err.println(); + err.println("Arguments:"); + //err.println(" <class> the fully qualified name of a Java class"); + err.println(" <jdofile> the name of a .jdo file"); + err.println(" <classfile> the name of a .class file"); + //err.println(" <zipfile> the name of a .zip or .jar file"); + err.println(); + err.println("Returns a non-zero value in case of errors."); + } + + /** + * Process command line options. + */ + protected int processArgs(String[] argv) + { + final Collection inputNames = new ArrayList(); + for (int i = 0; i < argv.length; i++) { + final String arg = argv[i]; + if (arg.equals("-h") + || arg.equals("--help")) { + usage(); + return OK; + } + if (arg.equals("-v") + || arg.equals("--verbose")) { + verbose = true; + quiet = false; + continue; + } + if (arg.equals("-q") + || arg.equals("--quiet")) { + quiet = true; + verbose = false; + continue; + } + if (arg.equals("-t") || + arg.equals("--timing")) { + doTiming = true; + continue; + } + if (arg.equals("-f") + || arg.equals("--force")) { + forceWrite = true; + noWrite = false; + continue; + } + if (arg.equals("-n") + || arg.equals("--nowrite")) { + noWrite = true; + forceWrite = false; + continue; + } + if (arg.equals("--dumpclass")) { + dumpClass = true; + continue; + } + if (arg.equals("--noaugment")) { + noAugment = true; + continue; + } + if (arg.equals("--noannotate")) { + noAnnotate = true; + continue; + } + if (arg.equals("-s") + || arg.equals("--sourcepath")) { + if (argv.length - i < 2) { + printError("Missing argument to the -s/--sourcepath option", null); + usage(); + return USAGE_ERROR; + } + sourcePath = argv[++i]; + continue; + } + if (arg.equals("-d") + || arg.equals("--destdir")) { + if (argv.length - i < 2) { + printError("Missing argument to the -d/-destdir option", null); + usage(); + return USAGE_ERROR; + } + destinationDirectory = argv[++i]; + continue; + } + if (arg.equals("--properties")) { + if (argv.length - i < 2) { + printError("Missing argument to the --properties option", null); + usage(); + return USAGE_ERROR; + } + propertiesFileName = argv[++i]; + continue; + } + if (arg.length() > 0 && arg.charAt(0) == '-') { + printError("Unrecognized option:" + arg, null); + usage(); + return USAGE_ERROR; + } + if (arg.length() == 0) { + printMessage("Ignoring empty command line argument."); + continue; + } + + inputNames.add(arg); + } + + // group input file arguments + for (Iterator names = inputNames.iterator(); names.hasNext();) { + final String name = (String)names.next(); + if (isJdoFileName(name)) { + jdoFileNames.add(name); + } else if (isClassFileName(name)) { + classFileNames.add(name); + } else if (isZipFileName(name)) { + zipFileNames.add(name); + } else { + classNames.add(name); + } + } + + if (verbose) { + printArgs(); + } + return checkArgs(); + } + + /** + * Check command line options. + */ + protected int checkArgs() + { + // at least one class must be specified + if (classNames.isEmpty() + && classFileNames.isEmpty() + && zipFileNames.isEmpty()) { + final String msg + = "No classes specified"; + printError(msg, null); + usage(); + return USAGE_ERROR; + } + + // at least one meta-data source must be specified for classfiles + if (classFileNames.size() > 0 + && (jdoFileNames.isEmpty() + && propertiesFileName == null + && sourcePath == null)) { + final String msg + = "No JDO meta-data source specified for class files"; + printError(msg, null); + usage(); + return USAGE_ERROR; + } + + // either jdo files or jdo properties specified + if (!jdoFileNames.isEmpty() && propertiesFileName != null) { + final String msg + = "Cannot have both jdo files and properties specified"; + printError(msg, null); + usage(); + return USAGE_ERROR; + } + + return OK; + } + + /** + * Print command line options. + */ + protected void printArgs() + { + out.println("Enhancer: options:"); + out.println(" verbose = " + verbose); + out.println(" quiet = " + quiet); + out.println(" forceWrite = " + forceWrite); + out.println(" noWrite = " + noWrite); + out.println(" sourcePath = " + sourcePath); + out.println(" destinationDirectory = " + destinationDirectory); + out.println(" propertiesFileName = " + propertiesFileName); + out.println(" doTiming = " + doTiming); + out.println(" classNames = {"); + for (Iterator i = classNames.iterator(); i.hasNext();) { + out.println(" " + i.next()); + } + out.println(" }"); + out.println(" jdoFileNames = {"); + for (Iterator i = jdoFileNames.iterator(); i.hasNext();) { + out.println(" " + i.next()); + } + out.println(" }"); + out.println(" classFileNames = {"); + for (Iterator i = classFileNames.iterator(); i.hasNext();) { + out.println(" " + i.next()); + } + out.println(" }"); + out.println(" zipFileNames = {"); + for (Iterator i = zipFileNames.iterator(); i.hasNext();) { + out.println(" " + i.next()); + } + out.println(" }"); + out.println(" dumpClass = " + dumpClass); + out.println(" noAugment = " + noAugment); + out.println(" noAnnotate = " + noAnnotate); + } + } + + private int initClassLocator() + { + final boolean verbose = opts.verbose; + final List classFileNames = opts.classFileNames; + final List zipFileNames = opts.zipFileNames; + final String sourcePath = opts.sourcePath; + try { + final List locators = new ArrayList(); + + // create resource locator for specified class files + if (classFileNames != null && !classFileNames.isEmpty()) { + final ResourceLocator classes + = new ListResourceLocator(out, verbose, classFileNames); + if (verbose) { + out.println("Class Locator: using class files: {"); + for (Iterator i = classFileNames.iterator(); i.hasNext();) { + out.println(" " + i.next()); + } + out.println("}"); + } + locators.add(classes); + } + + // create resource locator for specified zip files + if (zipFileNames != null && !zipFileNames.isEmpty()) { + final StringBuffer s = new StringBuffer(); + final Iterator i = zipFileNames.iterator(); + s.append(i.next()); + while (i.hasNext()) { + s.append(File.pathSeparator + i.next()); + } + final ResourceLocator zips + = new PathResourceLocator(out, verbose, s.toString()); + if (verbose) + out.println("Class Locator: using jar/zip files: " + + s.toString()); + locators.add(zips); + } + + // create resource locator for specified source path + if (sourcePath != null && sourcePath.length() > 0) { + final ResourceLocator path + = new PathResourceLocator(out, verbose, sourcePath); + if (verbose) + out.println("Class Locator: using source path: " + + sourcePath); + locators.add(path); + } + + // print warning if no classes specified + affirm(!locators.isEmpty()); + //if (locators.isEmpty()) { + // printWarning(getI18N("enhancer.using_no_classes")); + //} + + // init class locators + classLocator + = new CombinedResourceLocator(out, verbose, locators); + + // wrap with timing class locator + if (opts.doTiming) { + classLocator = new ResourceLocatorTimer(classLocator); + } + } catch (IOException ex) { + printError("Cannot initialize resource locator for classes", ex); + return CLASS_LOCATOR_ERROR; + } + return OK; + } + + private int initEnhancerMetaData() + { + final boolean verbose = opts.verbose; + final String propertiesFileName = opts.propertiesFileName; + final List jdoFileNames = opts.jdoFileNames; + final List zipFileNames = opts.zipFileNames; + final String sourcePath = opts.sourcePath; + try { + if (propertiesFileName != null) { + jdoMetaData + = new EnhancerMetaDataPropertyImpl(out, verbose, + propertiesFileName); + } else { + jdoMetaData + = new EnhancerMetaDataJDOModelImpl(out, verbose, + jdoFileNames, + zipFileNames, + sourcePath); + } + + // wrap with timing meta data object + if (opts.doTiming) { + jdoMetaData = new EnhancerMetaDataTimer(jdoMetaData); + } + } catch (EnhancerMetaDataFatalError ex) { + printError("Cannot initialize JDO meta-data source", ex); + return METADATA_ERROR; + } + return OK; + } + + private int createEnhancer() + { + int res0 = initClassLocator(); + if (res0 < 0) { + return res0; + } + affirm(classLocator != null); + + int res = initEnhancerMetaData(); + if (res < 0) { + return res; + } + affirm(jdoMetaData != null); + + final Properties props = new Properties(); + if (opts.verbose) { + props.put(EnhancerFilter.VERBOSE_LEVEL, + EnhancerFilter.VERBOSE_LEVEL_VERBOSE); + } + + if (opts.doTiming) { + props.put(EnhancerFilter.DO_TIMING_STATISTICS, + Boolean.TRUE.toString()); + } + + if (opts.dumpClass) { + props.put(EnhancerFilter.DUMP_CLASS, + Boolean.TRUE.toString()); + } + + if (opts.noAugment) { + props.put(EnhancerFilter.NO_AUGMENT, + Boolean.TRUE.toString()); + } + + if (opts.noAnnotate) { + props.put(EnhancerFilter.NO_ANNOTATE, + Boolean.TRUE.toString()); + } + + try { + enhancer = new EnhancerFilter(jdoMetaData, props, out, err); + if (opts.doTiming) { + // wrap with timing byte-code enhancer + enhancer = new ClassFileEnhancerTimer(enhancer); + } + return 0; + } catch (EnhancerUserException ex) { + printError("Error while creating the enhancer", ex); + return -1; + } catch (EnhancerFatalError ex) { + // enhancer is not anymore guaranteed to be consistent + printError("Fatal error while creating the enhancer", ex); + enhancer = null; + return -1; + } + } + + // ---------------------------------------------------------------------- + + /** + * Enhances all files entered in the command line. + * + * @param classNames List of class names. + * @param classFileNames List of class file names. + * @param zipFileNames List of zip file names. + * @param jdoFileNames List of jdo file names. + */ + private int enhanceInputFiles(List classNames, + List classFileNames, + List zipFileNames, + List jdoFileNames) + { + int res = 0; + try { + String name = null; + for (Iterator i = zipFileNames.iterator(); i.hasNext();) { + try { + name = (String)i.next(); + enhanceZipFile(name); + } catch (EnhancerUserException ex) { + printError("Error while enhancing " + name, ex); + res++; + continue; + } + } + for (Iterator i = classFileNames.iterator(); i.hasNext();) { + try { + name = (String)i.next(); + enhanceClassFile(openFileInputStream(name)); + } catch (EnhancerUserException ex) { + printError("Error while enhancing " + name, ex); + res++; + continue; + } + } + for (Iterator i = classNames.iterator(); i.hasNext();) { + try { + name = (String)i.next(); + enhanceClassFile(openClassInputStream(name)); + } catch (EnhancerUserException ex) { + printError("Error while enhancing " + name, ex); + res++; + continue; + } + } + } catch (IOException ex) { + printError("IO Error while enhancing", ex); + return ++res; + } catch (EnhancerFatalError ex) { + // enhancer is not anymore guaranteed to be consistent + printError("Fatal error while enhancing", ex); + enhancer = null; + return ++res; + } + return res; + } + + /** + * Enhances a classfile. + * + * @param in The input stream of the classfile. + */ + private void enhanceClassFile(InputStream in) + throws IOException, EnhancerUserException, EnhancerFatalError + { + OutputStream out = null; + try { + final File temp = File.createTempFile("enhancer", ".class"); + out = new BufferedOutputStream(new FileOutputStream(temp)); + + //enhance + final OutputStreamWrapper wrapper = new OutputStreamWrapper(out); + final boolean enhanced = enhancer.enhanceClassFile(in, wrapper); + + closeOutputStream(out); + out = null; + createOutputFile(enhanced, + createClassFileName(wrapper.getClassName()), + temp); + } finally { + closeInputStream(in); + closeOutputStream(out); + } + } + + /** + * Enhances a zipfile. + * + * @param filename The filename of the zipfile. + */ + private void enhanceZipFile(String filename) + throws IOException, EnhancerUserException, EnhancerFatalError + { + ZipInputStream in = null; + ZipOutputStream out = null; + try { + final File temp = File.createTempFile("enhancer", ".zip"); + in = new ZipInputStream(new BufferedInputStream( + new FileInputStream(new File(filename)))); + out = new ZipOutputStream(new BufferedOutputStream( + new FileOutputStream(temp))); + + //enhance the zipfile + final boolean enhanced + = ClassFileEnhancerHelper.enhanceZipFile(enhancer, in, out); + + //create the output file + closeOutputStream(out); + out = null; + createOutputFile(enhanced, new File(filename).getName(), temp); + } finally { + closeOutputStream(out); + closeInputStream(in); + } + } + + /** + * Opens an input stream for the given filename + * + * @param filename The name of the file. + * @return The input stream. + * @exception FileNotFoundException If the file could not be found. + */ + static private InputStream openFileInputStream(String filename) + throws FileNotFoundException + { + return new BufferedInputStream(new FileInputStream(new File(filename))); + } + + /** + * Opens an input stream for the given classname. The input stream is + * created via an URL that is obtained by the value of the sourcepath + * option and zip/jar file arguments. + * + * @param classname The name of the class (dot-notation). + * @return The input stream. + * @exception IOException If an I/O error occured. + */ + private InputStream openClassInputStream(String classname) + throws IOException + { + final String resourcename = createClassFileName(classname); + return classLocator.getInputStreamForResource(resourcename); + } + + /** + * Creates a file object that represents the output zipfile for a given + * zipfile to enhance. + * + * @param zipfilename The input zipfile name. + * @return The output zipfile name. + */ + private File createZipOutputFile(String zipfilename) + { + return new File(opts.destinationDirectory, + new File(zipfilename).getName()); + } + + /** + * Creates the output file for an enhaced class- or zipfile. If the + * enhanced file is written back depends on the command line options. + * + * @param enhanced Has the input file been enhanced? + * @param filename The name of the output file. + * @param temp The temp file, the output is written to. + * @exception IOException If the file could not be created. + */ + private void createOutputFile(boolean enhanced, + String filename, + File temp) + throws IOException + { + //noWrite or (not enhanced and not forceWrite) + if (opts.noWrite || (!enhanced && !opts.forceWrite)) { + temp.deleteOnExit(); + return; + } + + File file = new File(opts.destinationDirectory, filename); + createPathOfFile(file); + file.delete(); //delete old file if exists + boolean renamed = temp.renameTo(file); + if (!renamed) { + //@dave: empirical evidence shows that renameTo does not allow for + // crossing filesystem boundaries. If it fails, try "by hand". + InputStream in = null; + OutputStream out = null; + try { + in = new FileInputStream(temp); + out = new FileOutputStream(file); + int PAGESIZE = 4096; // Suggest a better size? + byte data[] = new byte[PAGESIZE]; + while (in.available() > 0) { + int numRead = in.read(data, 0, PAGESIZE); + out.write(data, 0, numRead); + } + renamed = true; + } catch (IOException ex) { + throw new IOException("Could not rename temp file '" + + temp.getAbsolutePath() + + "' to '" + file.getAbsolutePath() + + "': " + ex); + } finally { + closeInputStream(in); + closeOutputStream(out); + } + if (renamed) { + temp.delete(); //delete temporary file + } + else { + throw new IOException("Could not rename temp file '" + + temp.getAbsolutePath() + + "' to '" + file.getAbsolutePath() + "'."); + } + } + } + + /** + * Closes an input stream. + * + * @param in The input stream. + */ + private void closeInputStream(InputStream in) + { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + printError(null, ex); + } + } + } + + /** + * Closes an output stream. + * + * @param out The output stream. + */ + private void closeOutputStream(OutputStream out) + { + if (out != null) { + try { + out.close(); + } catch (IOException ex) { + printError(null, ex); + } + } + } + + /** + * Tests if a filename is a classfile name (by testing if the filename + * ends with <code>".class"</code>). + * + * @param filename The name of the file. + * @return Do we have a potential classfile? + */ + static private boolean isClassFileName(String filename) + { + return filename.toLowerCase().endsWith(".class"); + } + + /** + * Tests if a filename is a zipfile (by testing if the filename + * ends with <code>".zip"</code> or <code>".jar"</code>). + * + * @param filename The name of the file. + */ + static private boolean isZipFileName(String filename) + { + final int n = filename.length(); + if (n < 5) { + return false; + } + final String ext = filename.substring(n - 4); + return ext.equalsIgnoreCase(".zip") || ext.equalsIgnoreCase(".jar"); + } + + /** + * Tests if a filename is a jdo file name (by testing if the filename + * ends with <code>".jdo"</code>). + * + * @param filename The name of the file. + * @return Do we have a potential jdo file? + */ + static private boolean isJdoFileName(String filename) + { + return filename.toLowerCase().endsWith(".jdo"); + } + + /** + * Creates a filename from a classname. + * This is done by replacing <code>'.'</code> by <code>'/'</code>. + * + * @param classname The classname. + * @return The filename. + */ + static private String createClassFileName(String classname) + { + return classname.replace('.', '/') + ".class"; + } + + /** + * Creates only the path of the given file. + * + * @param file The file. + * @exception IOException If an error occured. + */ + static private void createPathOfFile(File file) + throws IOException + { + File dir = file.getAbsoluteFile().getParentFile(); + if (!dir.exists() && !dir.mkdirs()) { + throw new IOException("Error creating directory '" + + dir.getAbsolutePath() + "'."); + } + } + + // ---------------------------------------------------------------------- + + /** + * Prints out an error. + * + * @param msg The error message (can be <code>null</code>). + * @param ex An optional exception (can be <code>null</code>). + */ + private void printError(String msg, + Throwable ex) + { + out.flush(); + if (msg != null) { + err.println(msg); + } + if (ex != null) { + if (opts.verbose) { + ex.printStackTrace(err); + } + else { + err.println(ex.toString()); + } + } + } + + /** + * Prints out a message. + * + * @param msg The message. + */ + private void printMessage(String msg) + { + out.println(msg); + } +}
Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/enhancer/package.html URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/enhancer/package.html?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/enhancer/package.html (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/enhancer/package.html Fri Mar 18 17:02:29 2005 @@ -0,0 +1,26 @@ +<!-- + Copyright 2005 The Apache Software Foundation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. +--> + +<html> +<head> +<title>Package org.apache.jdo.enhancer</title> +<meta http-equiv="Content-Type" content="text/html; charset=iso-8859-1"/> +</head> + +<body bgcolor="#FFFFFF"> +<p>This package contains th eenhancer interface.</p> +</body> +</html> Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/Bundle.properties URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/Bundle.properties?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/Bundle.properties (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/Bundle.properties Fri Mar 18 17:02:29 2005 @@ -0,0 +1,220 @@ + + Copyright 2005 The Apache Software Foundation. + + Licensed under the Apache License, Version 2.0 (the "License"); + you may not use this file except in compliance with the License. + You may obtain a copy of the License at + + http://www.apache.org/licenses/LICENSE-2.0 + + Unless required by applicable law or agreed to in writing, software + distributed under the License is distributed on an "AS IS" BASIS, + WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + See the License for the specific language governing permissions and + limitations under the License. + + +# ResourceBundle properties file for model messages +# key consists of: enhancer.<subpackage>.<description> +# <subpackage> - is optional, package under model +# <description> - short description (use _ to represent space) +# all keys should be in lowercase + +# +# Global Enhancer Metadata messages +# + +# {0}=detail message +enhancer.metadata.message=JDO ENHANCER: Metadata: {0} + +# {0}=error message +enhancer.metadata.error=JDO ENHANCER: Metadata Error: {0} + +# {0}=warning message +enhancer.metadata.warning=JDO ENHANCER: Metadata Warning: {0} + +# {0}=missing resource file +enhancer.metadata.cannot_read_resource=Cannot read directory or jar file: {0} + +# +# Detail Enhancer Metadata messages +# + +# {0}=exception message +enhancer.metadata.jdomodel_error=A JDO model exception has occurred: {0} + +# {0}=exception message +enhancer.metadata.io_error=An I/O exception has occurred: {0} + +# {0}=filename +enhancer.metadata.using_properties=using properties file: {0} + +# {0}=filenames +enhancer.metadata.using_jdo_files=using jdo files: {0} + +# {0}=path +enhancer.metadata.using_jar_files=using jar/zip files: {0} + +# {0}=path +enhancer.metadata.using_source_path=using source path: {0} + +# {0}= +enhancer.metadata.using_no_metadata=no JDO metadata source was specified + + +# +# Global enhancer messages +# + +# {0}=detailed message +enhancer.message=JDO ENHANCER: {0} + +# {0}=detailed message +enhancer.error=JDO ENHANCER: At least one error occurred while enhancing input class for Transparent Persistence; detailed message(s): {0} + +# {0}=error number, {1}=detailed message +enhancer.enumerated_error=JDO ENHANCER: Error ({0}): {1} + +# {0}=detailed message +enhancer.internal_error=JDO ENHANCER: An internal error occurred while enhancing input class; detailed message: {0} + +# {0}=detailed message +enhancer.warning=JDO ENHANCER: Warning: {0} + +# +# Enhancer core messages +# + +# +enhancer.class_format_error=I/O error while reading input stream or input stream does not denote a valid Java class. + +# +enhancer.io_error_while_writing_stream=I/O error while writing output stream. + +# {0}=dir name +enhancer.unable_to_create_dir=Unable to create the directory named {0}. + +# {0}=class name +enhancer.class_previously_annotated=The class {0} was previously annotated. + +# {0}=boolean +enhancer.class_implements_jdo_pc=implements the JDO PersistenceCapable interface: {0}. + +# +enhancer.class_has_default_constructor=has default constructor. +enhancer.class_has_not_default_constructor=has not default constructor. + +# +enhancer.class_has_static_initializer=has static initializer. +enhancer.class_has_not_static_initializer=has not static initializer. + +# +enhancer.class_has_clone_method=has clone method. +enhancer.class_has_not_clone_method=has not clone method. + +# +enhancer.class_has_writeObject_method=has writeObject method. +enhancer.class_has_not_writeObject_method=has not writeObject method. + +# +enhancer.class_has_writeReplace_method=has writeReplace method. +enhancer.class_has_not_writeReplace_method=has not writeReplace method. + +# +enhancer.class_has_readObject_method=has readObject method. +enhancer.class_has_not_readObject_method=has not readObject method. + +# {0}=class name +enhancer.class_missing_default_constructor=The persistence-capable class {0} does not provide a default (no-argument) constructor. + +# {0}=boolean +enhancer.class_has_generic_jdo_fields=has the generic JDO fields: {0}. + +# {0}=boolean +enhancer.class_has_generic_jdo_methods=has the generic JDO methods: {0}. + +# {0}=boolean +enhancer.class_has_generic_jdo_members=has the generic JDO fields and methods: {0}. + +# {0}=boolean +enhancer.class_has_specific_jdo_fields=has the specific JDO fields: {0}. + +# {0}=boolean +enhancer.class_has_specific_jdo_methods=has the specific JDO methods: {0}. + +# {0}=boolean +enhancer.class_has_specific_jdo_members=has the specific JDO fields and methods: {0}. + +# {0}=boolean +enhancer.class_has_oid_handling_jdo_methods=has the ObjectId-handling JDO methods: {0}. + +# {0}=boolean +enhancer.class_has_callback_jdo_methods=has the instance-callback JDO methods: {0}. + +# {0}=class name, {1}=method name +enhancer.class_defines_jdo_method=The persistence-capable class {0} defines a JDO method called {1}. + +# {0}=class name, {1}=field name +enhancer.class_defines_jdo_field=The persistence-capable class {0} defines a JDO field called {1}. + +# {0}=class name, {1}=member name +enhancer.class_has_jdo_like_member=The persistence-capable class {0} defines a member {1} whose name starts with the reserved jdo prefix. + +# {0}=class name, {1}=method name, +# {2}=expected signature, {3}=found signature +# {4}=expected modifiers, {5}=found modifiers +enhancer.class_has_illegally_declared_jdo_member=The persistence-capable class {0} has an illegally declared JDO member: {1}; expected signature: ''{2}''; found signature: ''{3}''; expected access modifier flags: {4}; found access modifier flags: {5}. + +# {0}=class name, {1}=found method names, {2}=missing method names +enhancer.class_has_inconsistently_declared_jdo_members=The persistence-capable class {0} has an incosistent set of declared JDO members; found: {1}; missing: {2}. + +# +# Other Enhancer messages +# + +# {0}=filenames +enhancer.using_jdo_files=using jdo files: {0} + +# {0}=path +enhancer.using_archive_files=using jar/zip files: {0} + +# {0}=path +enhancer.using_source_path=using source path: {0} + +# {0}=resource file name +enhancer.using_file=using file: {0} + +# {0}=resource path element +enhancer.using_path_element=using path element: {0} + +# {0}=class name, {1}=file name, {2}=file name +enhancer.class_already_entered=The class {0} from {1} is already entered from {2}. + +# {0}=dir name, {1}=dir name +enhancer.destination_directory_already_set=Attempt to set the destination directory to {0}, but it has already been set to {1}. + +# {0}=dir name +enhancer.destination_directory_not_exist=The destination directory {0} does not exist. + +# {0}=resource name +enhancer.found_resource=found resource: {0} +enhancer.not_found_resource=not found resource: {0} + +# {0}=resource name +enhancer.cannot_read_resource=resource does not exist or cannot be read: {0} + +# {0}=resource name {1}=exception message +enhancer.io_error_while_reading_resource=An I/O exception has occurred while reading resource: {0}, exception: {1} + +# {0}=resource name +enhancer.io_error_while_reading_resource=An I/O exception has occurred: {0} + +# {0}=resource name +enhancer.illegal_path_element=illegal element source path: {0} + +# +# Enhancer exception messages +# + +MSG_CauseThrowable=CauseThrowable: +MSG_CauseThrowableStackTrace=CauseThrowableStackTrace: Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassArgMain.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassArgMain.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassArgMain.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassArgMain.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,180 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jdo.impl.enhancer; + +import java.io.PrintWriter; +import java.io.IOException; +import java.io.FileNotFoundException; +import java.io.InputStream; +import java.io.FileInputStream; +import java.io.BufferedInputStream; +import java.io.File; + +import org.apache.jdo.impl.enhancer.util.PathResourceLocator; + + + +/** + * Base class for JDO command line enhancer and tests. + * + * @author Martin Zaun + */ +public class ClassArgMain + extends GenericMain +{ + /** + * The options and arguments. + */ + protected ClassArgOptions options; + + /** + * The locator for classes. + */ + protected PathResourceLocator classes; + + /** + * Creates an instance. + */ + public ClassArgMain(PrintWriter out, + PrintWriter err) + { + this(out, err, new ClassArgOptions(out, err)); + } + + /** + * Creates an instance. + */ + public ClassArgMain(PrintWriter out, + PrintWriter err, + ClassArgOptions options) + { + super(out, err, options); + this.options = options; + } + + // ---------------------------------------------------------------------- + + /** + * Initializes the class locator. + */ + protected void initClassLocator() + throws IOException + { + // create resource locator for specified source path + final String path = options.sourcePath.value; + if (path != null) { + affirm(path.length() > 0); + final boolean verbose = options.verbose.value; + classes = new PathResourceLocator(out, verbose, path); + } + } + + /** + * Initializes all components. + */ + protected void init() + throws EnhancerFatalError, EnhancerUserException + { + try { + initClassLocator(); + } catch (Exception ex) { + throw new EnhancerFatalError(ex); + } + } + + // ---------------------------------------------------------------------- + + /** + * Returns the file name for a class name. + * This is done by replacing <code>'.'</code> by <code>'/'</code>. + * + * @param className the classname + * @return the filename + */ + static protected String getClassFileName(String className) + { + return className.replace('.', '/') + ".class"; + } + + /** + * Opens an input stream for the given filename + * + * @param fileName the name of the file + * @return the input stream + * @exception FileNotFoundException if the file could not be found + */ + protected InputStream openFileInputStream(String fileName) + throws FileNotFoundException + { + affirm(fileName != null); + //^olsen: support for timing + //if (options.doTiming.value) {...} + return new BufferedInputStream(new FileInputStream(fileName)); + } + + /** + * Opens an input stream for the given classname. The input stream is + * created via an URL that is obtained by the value of the sourcepath + * option and zip/jar file arguments. + * + * @param className the name of the class (dot-notation) + * @return the input stream + * @exception IOException if an I/O error occured + */ + protected InputStream openClassInputStream(String className) + throws IOException + { + affirm(className != null); + final String resName = className.replace('.', '/') + ".class"; + //^olsen: support for timing + //if (options.doTiming.value) {...} + final InputStream s = classes.getInputStreamForResource(resName); + affirm(s != null); + return new BufferedInputStream(s); + } + + /** + * Closes an input stream. + * + * @param in the input stream + */ + protected void closeInputStream(InputStream in) + { + if (in != null) { + try { + in.close(); + } catch (IOException ex) { + printlnErr("", ex); + } + } + } + + // ---------------------------------------------------------------------- + + /** + * Runs this class + */ + static public void main(String[] args) + { + final PrintWriter out = new PrintWriter(System.out, true); + out.println("--> ClassArgMain.main()"); + final ClassArgMain main = new ClassArgMain(out, out); + int res = main.run(args); + out.println("<-- ClassArgMain.main(): exit = " + res); + System.exit(res); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassArgOptions.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassArgOptions.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassArgOptions.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassArgOptions.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,220 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jdo.impl.enhancer; + +import java.io.PrintWriter; + +import java.util.List; +import java.util.Iterator; +import java.util.ArrayList; + + +/** + * Set of options used by the JDO enhancer and its test programs. + * + * @author Martin Zaun + */ +public class ClassArgOptions + extends GenericOptions +{ + /** + * Tests if a filename has suffix <code>".class"</code> (ignoring case). + * + * @param filename the name of the file + * @return true if filename has a class file suffix + */ + static private boolean isClassFileName(String filename) + { + return filename.toLowerCase().endsWith(".class"); + } + + /** + * Tests if a filename has suffix <code>".jar"</code> or + * <code>".zip"</code> (ignoring case). + * + * @param filename the name of the file + * @return true if filename has an archive file suffix + */ + static private boolean isArchiveFileName(String filename) + { + final String s = filename.toLowerCase(); + return (s.endsWith(".jar") || s.endsWith(".zip")); + } + + // ---------------------------------------------------------------------- + + /** + * The source path option. + */ + public final StringOption sourcePath + = createStringOption("sourcepath", "s", + "<path> : path for lookup of class files"); + + /** + * The list of class name arguments. + */ + public final List classNames = new ArrayList(); + + /** + * The list of class file name arguments. + */ + public final List classFileNames = new ArrayList(); + + /** + * The list of archive file name arguments. + */ + public final List archiveFileNames = new ArrayList(); + + /** + * Creates an instance. + */ + public ClassArgOptions(PrintWriter out, + PrintWriter err) + { + super(out, err); + } + + // ---------------------------------------------------------------------- + + /** + * Print a usage message to System.err. + */ + public void printUsageHeader() + { + printlnErr("Usage: <options>.. <arguments>.."); + printlnErr(indent + + "-s <path> <classname>.."); + printlnErr(indent + + " <classfile>.."); + //^olsen: re-enable support for archive files + //printlnErr(indent + // + " <archivefile>.."); + } + + /** + * Print a usage message to System.err. + */ + public void printArgumentUsage() + { + printlnErr(indent + + "<classname> the fully qualified name of a Java class"); + printlnErr(indent + + "<classfile> the name of a .class file"); + printlnErr(indent + + "<archivefile> the name of a .zip or .jar file"); + } + + /** + * Print arguments. + */ + public void printArguments() + { + println(); + println(argumentsHeader); + printListArgument("classNames", classNames); + printListArgument("classFileNames", classFileNames); + printListArgument("archiveFileNames", archiveFileNames); + } + + /** + * Print argument of list type. + */ + public void printListArgument(String name, List list) + { + print(indent); + final StringBuffer s = new StringBuffer(); + for (Iterator i = list.iterator(); i.hasNext();) { + s.append(" " + i.next()); + } + println(name + " = {" + s.toString() + " }"); + println(); + } + + /** + * Check options and arguments. + */ + public int check() + { + int res; + if ((res = super.check()) != OK) { + return res; + } + + // group input file arguments + for (Iterator names = arguments.iterator(); names.hasNext();) { + final String name = (String)names.next(); + if (isClassFileName(name)) { + classFileNames.add(name); + } else if (isArchiveFileName(name)) { + archiveFileNames.add(name); + } else { + classNames.add(name); + } + } + + if (verbose.value) { + printAll(); + } + + // check class arguments + final int argTypes = ((classNames.isEmpty() ? 0 : 1) + + (classFileNames.isEmpty() ? 0 : 1) + + (archiveFileNames.isEmpty() ? 0 : 1)); + if (argTypes == 0) { + printUsageError("No class arguments: specify classes either by class name, class file, or archive file"); + return USAGE_ERROR; + } + if (argTypes > 1) { + printUsageError("Mixed class arguments: specify classes by either class name, class file, or archive file"); + return USAGE_ERROR; + } + + // check sourcepath option + if (sourcePath.value == null && !classNames.isEmpty()) { + printUsageError("No source-path specified for lookup of classes"); + return USAGE_ERROR; + } + if (sourcePath.value != null && classNames.isEmpty()) { + printUsageError("No source-path can be specified with class or archive files"); + return USAGE_ERROR; + } + + //^olsen: re-enable support for archive files + if (!archiveFileNames.isEmpty()) { + printUsageError("Sorry, support for archive files currently disabled"); + return USAGE_ERROR; + } + + return OK; + } + + // ---------------------------------------------------------------------- + + /** + * Tests the class. + */ + static public void main(String[] args) + { + final PrintWriter out = new PrintWriter(System.out, true); + out.println("--> ClassArgOptions.main()"); + final ClassArgOptions options = new ClassArgOptions(out, out); + out.println(" options.process() ..."); + int res = options.process(args); + out.println(" return value: " + res); + out.println("<-- ClassArgOptions.main()"); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancer.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancer.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancer.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancer.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,65 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jdo.impl.enhancer; + +import java.io.InputStream; +import java.io.OutputStream; + +/** + * A JDO enhancer, or byte-code enhancer, modifies the byte-codes of + * Java class files to enable transparent loading and storing of the + * fields of the persistent instances. + * + * @author Martin Zaun + */ +public interface ClassFileEnhancer +{ + /** + * Enhances a given class according to the JDO meta-data. If the + * input class has been enhanced or not - the output stream is + * always written, either with the enhanced class or with the + * non-enhanced class. + * + * @param in The byte-code of the class to be enhanced. + * @param out The byte-code of the enhanced class. + * @return <code>true</code> if the class has been enhanced, + * <code>false</code> otherwise. + */ + boolean enhanceClassFile(InputStream in, + OutputStream out) + throws EnhancerUserException, EnhancerFatalError; + + + /** + * Enhances a given class according to the JDO meta-data. If the + * input class has been enhanced or not - the output stream is + * always written, either with the enhanced class or with the + * non-enhanced class. + * <p> + * Furthermore, the enhancer has to set the classname of + * the enhanced class to the output stream wrapper object (it's + * possible to get the input stream without knowing the classname). + * + * @param in The byte-code of the class to be enhanced. + * @param out The byte-code of the enhanced class. + * @return <code>true</code> if the class has been enhanced, + * <code>false</code> otherwise. + */ + boolean enhanceClassFile(InputStream in, + OutputStreamWrapper out) + throws EnhancerUserException, EnhancerFatalError; +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancerHelper.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancerHelper.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancerHelper.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancerHelper.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,204 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jdo.impl.enhancer; + +import java.io.IOException; +import java.io.InputStream; +import java.io.OutputStream; +import java.io.ByteArrayInputStream; +import java.io.ByteArrayOutputStream; + +import java.util.zip.CRC32; +import java.util.zip.ZipEntry; +import java.util.zip.ZipInputStream; +import java.util.zip.ZipOutputStream; + + +/** + * This is a helper-class to perform some useful operations outside a + * byte code enhancer and delegate the real work to the enhancer. + */ +public class ClassFileEnhancerHelper +{ + /** + * Enhances a classfile. + * + * @param enhancer The enhancer to delegate the work to. + * @param in The input stream with the Java class. + * @param out The output stream to write the enhanced class to. + * + * @return Has the input stream been enhanced? + * + * @exception EnhancerUserException If something went wrong. + * @exception EnhancerFatalError If something went wrong. + * + * @see ClassFileEnhancer#enhanceClassFile + */ + static public boolean enhanceClassFile(ClassFileEnhancer enhancer, + InputStream in, + OutputStream out) + throws EnhancerUserException, + EnhancerFatalError + { + return enhancer.enhanceClassFile(in, new OutputStreamWrapper(out)); + } + + /** + * Enhances a zip file. The zip file is given as a uip input stream. + * It's entries are read and - if necessary - individually enhanced. + * The output stream has the same compressÃon (if any) as the input + * stream. + * + * @param enhancer The enhancer. + * @param zip_in The zip input stream. + * @param zip_out The zip output stream. + * + * @return <code>true</code> if at least one entry of the zip file has + * been enhanced, <code>false</code> otherwise. + * + * @exception EnhancerUserException If something went wrong. + * @exception EnhancerFatalError If something went wrong. + * + * @see ClassFileEnhancer#enhanceClassFile + */ + static public boolean enhanceZipFile(ClassFileEnhancer enhancer, + ZipInputStream zip_in, + ZipOutputStream zip_out) + throws EnhancerUserException, + EnhancerFatalError + { + boolean enhanced = false; + try { + CRC32 crc32 = new CRC32(); + ZipEntry entry; + while ((entry = zip_in.getNextEntry()) != null) { + InputStream in = zip_in; + final ZipEntry out_entry = new ZipEntry(entry); + + // try to enhance + if (isClassFileEntry(entry)) { + // enhance the classfile + // we have to copy the classfile, because if it won't be + // enhanced, the OutputStream is empty and we have to + // re-read the InputStream, which is impossible with a + // ZipInputStream (no mark/reset) + in = openZipEntry(zip_in); + in.mark(Integer.MAX_VALUE); + final ByteArrayOutputStream tmp + = new ByteArrayOutputStream(); + if (enhancer.enhanceClassFile(in, tmp)) { + enhanced = true; + final byte[] bytes = tmp.toByteArray(); + tmp.close(); + in.close(); + modifyZipEntry(out_entry, bytes, crc32); + in = new ByteArrayInputStream(bytes); + } else { + // the classfile has not been enhanced + in.reset(); + } + } + + // copy the entry + zip_out.putNextEntry(out_entry); + copyZipEntry(in, zip_out); + zip_out.closeEntry(); + + if (in != zip_in) { + in.close(); + } + } + } catch (IOException ex) { + throw new EnhancerFatalError(ex); + } + + return enhanced; + } + + /** + * Copies a zip entry from one stream to another. + * + * @param in The inout stream. + * @param out The output stream. + * + * @exception IOException If the stream access failed. + */ + static private void copyZipEntry(InputStream in, + OutputStream out) + throws IOException + { + int b; + while ((in.available() > 0) && (b = in.read()) > -1) { + out.write(b); + } + } + + /** + * Opens the next zip entry of a zip input stream and copies it to + * a <code>java.io.ByteArrayOutputStream</code>. It's byte array is made + * available via an <code>java.io.ByteArrayInputStream</code> which is + * returned. + * + * @param in The zip input stream. + * + * @return The newly created input stream with the next zip entry. + * + * @exception IOException If an I/O operation failed. + */ + static private InputStream openZipEntry(ZipInputStream in) + throws IOException + { + ByteArrayOutputStream out = new ByteArrayOutputStream(); + copyZipEntry(in, out); + + return new ByteArrayInputStream(out.toByteArray()); + } + + /** + * Modifies the given zip entry so that it can be added to zip file. + * The given zip entry represents an enhanced class, so the zip entry + * has to get the correct size and checksum (but only if the entry won't + * be compressed). + * + * @param entry The zip entry to modify. + * @param bytes The uncompressed byte representation of the classfile. + * @param crc32 The checksum evaluator. + */ + private static void modifyZipEntry(ZipEntry entry, + byte [] bytes, + CRC32 crc32) + { + entry.setSize(bytes.length); + if (entry.getMethod() == 0) { + //no compression (ZipInputStream.STORED - not accessible) + crc32.reset(); + crc32.update(bytes); + entry.setCrc(crc32.getValue()); + entry.setCompressedSize(bytes.length); + } + } + + /** + * Determines if a given entry represents a classfile. + * + * @return Does the given entry represent a classfile? + */ + private static boolean isClassFileEntry(ZipEntry entry) + { + return entry.getName().endsWith(".class"); + } +} Added: incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancerTimer.java URL: http://svn.apache.org/viewcvs/incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancerTimer.java?view=auto&rev=158176 ============================================================================== --- incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancerTimer.java (added) +++ incubator/jdo/trunk/ri11/src/java/org/apache/jdo/impl/enhancer/ClassFileEnhancerTimer.java Fri Mar 18 17:02:29 2005 @@ -0,0 +1,70 @@ +/* + * Copyright 2005 The Apache Software Foundation. + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +package org.apache.jdo.impl.enhancer; + +import java.io.InputStream; +import java.io.OutputStream; + +import org.apache.jdo.impl.enhancer.util.Support; + + + +/** + * Timer-wrapper for ClassFileEnhancer instances. + * + * @author Martin Zaun + */ +public final class ClassFileEnhancerTimer + extends Support + implements ClassFileEnhancer +{ + // delegate + final protected ClassFileEnhancer delegate; + + /** + * Creates an instance. + */ + public ClassFileEnhancerTimer(ClassFileEnhancer delegate) + { + affirm(delegate); + this.delegate = delegate; + } + + public boolean enhanceClassFile(InputStream inClassFile, + OutputStream outClassFile) + throws EnhancerUserException, EnhancerFatalError + { + try { + timer.push("ClassFileEnhancer.enhanceClassFile(InputStream,OutputStream)"); + return delegate.enhanceClassFile(inClassFile, outClassFile); + } finally { + timer.pop(); + } + } + + public boolean enhanceClassFile(InputStream inClassFile, + OutputStreamWrapper outClassFile) + throws EnhancerUserException, EnhancerFatalError + { + try { + timer.push("ClassFileEnhancer.enhanceClassFile(InputStream,OutputStreamWrapper)"); + return delegate.enhanceClassFile(inClassFile, outClassFile); + } finally { + timer.pop(); + } + } +}