blob: ee0901449c0348a759fe708d5a8fe550f062bdce [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.PrintWriter;
import java.util.List;
import java.util.Iterator;
import java.util.Properties;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
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.EnhancerOptions;
import org.apache.jdo.impl.enhancer.EnhancerUserException;
import org.apache.jdo.impl.enhancer.JdoMetaMain;
import org.apache.jdo.impl.enhancer.OutputStreamWrapper;
import org.apache.jdo.impl.enhancer.core.EnhancerFilter;
/**
* JDO command line enhancer.
*
* @author Martin Zaun
*/
public class EnhancerMain
extends JdoMetaMain
{
/**
* The options and arguments.
*/
protected EnhancerOptions options;
/**
* The byte code enhancer.
*/
protected ClassFileEnhancer enhancer;
/**
* Creates an instance.
*/
public EnhancerMain(PrintWriter out,
PrintWriter err)
{
this(out, err, new EnhancerOptions(out, err));
}
/**
* Creates an instance.
*/
public EnhancerMain(PrintWriter out,
PrintWriter err,
EnhancerOptions options)
{
super(out, err, options);
this.options = options;
}
// ----------------------------------------------------------------------
/**
* Enhances all files entered in the command line.
*
* @param classNames List of class names.
* @param classFileNames List of class file names.
* @param archiveFileNames List of archive file names.
*/
private int enhanceInputFiles(List classNames,
List classFileNames,
List archiveFileNames)
{
int res = 0;
try {
String name = null;
for (Iterator i = archiveFileNames.iterator(); i.hasNext();) {
try {
name = (String)i.next();
enhanceArchiveFile(name);
} catch (EnhancerUserException ex) {
printlnErr("Error while enhancing " + name, ex,
options.verbose.value);
res++;
continue;
}
}
for (Iterator i = classFileNames.iterator(); i.hasNext();) {
try {
name = (String)i.next();
enhanceClassFile(openFileInputStream(name));
} catch (EnhancerUserException ex) {
printlnErr("Error while enhancing " + name, ex,
options.verbose.value);
res++;
continue;
}
}
for (Iterator i = classNames.iterator(); i.hasNext();) {
try {
name = (String)i.next();
enhanceClassFile(openClassInputStream(name));
} catch (EnhancerUserException ex) {
printlnErr("Error while enhancing " + name, ex,
options.verbose.value);
res++;
continue;
}
}
} catch (IOException ex) {
printlnErr("IO Error while enhancing", ex, options.verbose.value);
return ++res;
} catch (EnhancerFatalError ex) {
// enhancer is not anymore guaranteed to be consistent
printlnErr("Fatal error while enhancing", ex, options.verbose.value);
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,
getClassFileName(wrapper.getClassName()), temp);
} finally {
closeInputStream(in);
closeOutputStream(out);
}
}
/**
* Enhances a archive file.
*
* @param fileName The filename of the archive file.
*/
private void enhanceArchiveFile(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 archive file
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);
}
}
/**
* Creates a file object that represents the output archive file for
* a given archive file to enhance.
*
* @param archiveFileName the input archive file name
* @return the output archive file
*/
private File createArchiveOutputFile(String archiveFileName)
{
return new File(options.destDir.value,
new File(archiveFileName).getName());
}
/**
* Creates the output file for an enhanced class- or archive file. 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 (options.noWrite.value
|| (!enhanced && !options.forceWrite.value)) {
temp.deleteOnExit();
return;
}
// create file and its parent directory
final File file = new File(options.destDir.value, fileName);
final File dir = file.getAbsoluteFile().getParentFile();
if (!dir.exists() && !dir.mkdirs()) {
throw new IOException("Error creating directory '"
+ dir.getAbsolutePath() + "'.");
}
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 output stream.
*
* @param out the output stream
*/
private void closeOutputStream(OutputStream out)
{
if (out != null) {
try {
out.close();
} catch (IOException ex) {
printlnErr("", ex, options.verbose.value);
}
}
}
// ----------------------------------------------------------------------
/**
* Initializes all components.
*/
protected void init()
throws EnhancerFatalError, EnhancerUserException
{
super.init();
final Properties props = new Properties();
if (options.verbose.value) {
props.put(EnhancerFilter.VERBOSE_LEVEL,
EnhancerFilter.VERBOSE_LEVEL_VERBOSE);
}
if (options.doTiming.value) {
props.put(EnhancerFilter.DO_TIMING_STATISTICS,
Boolean.TRUE.toString());
}
if (options.dumpClass.value) {
props.put(EnhancerFilter.DUMP_CLASS,
Boolean.TRUE.toString());
}
if (options.noAugment.value) {
props.put(EnhancerFilter.NO_AUGMENT,
Boolean.TRUE.toString());
}
if (options.noAnnotate.value) {
props.put(EnhancerFilter.NO_ANNOTATE,
Boolean.TRUE.toString());
}
try {
enhancer = new EnhancerFilter(jdoMeta, props, out, err);
if (options.doTiming.value) {
// wrap with timing byte-code enhancer
enhancer = new ClassFileEnhancerTimer(enhancer);
}
} catch (EnhancerUserException ex) {
printlnErr("Error while creating the enhancer", ex,
options.verbose.value);
throw ex;
} catch (EnhancerFatalError ex) {
// enhancer is not anymore guaranteed to be consistent
printlnErr("Fatal error while creating the enhancer", ex,
options.verbose.value);
enhancer = null;
throw ex;
} catch (RuntimeException ex) {
// enhancer is not anymore guaranteed to be consistent
printlnErr("Internal error while creating the enhancer", ex,
options.verbose.value);
enhancer = null;
throw new EnhancerFatalError(ex);
}
}
/**
* Run the enhancer.
*/
protected int process()
{
return enhanceInputFiles(options.classNames,
options.classFileNames,
options.archiveFileNames);
}
// ----------------------------------------------------------------------
/**
* Runs this class
*/
static public void main(String[] args)
{
final PrintWriter out = new PrintWriter(System.out, true);
out.println("--> EnhancerMain.main()");
//out.println("JDO RI Class-File Enhancer");
final EnhancerMain main = new EnhancerMain(out, out);
int res = main.run(args);
//out.println("done.");
out.println("<-- EnhancerMain.main(): exit = " + res);
System.exit(res);
}
}