org.datanucleus.enhancer.DataNucleusEnhancer Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of datanucleus-enhancer Show documentation
Show all versions of datanucleus-enhancer Show documentation
DataNucleus Enhancer is a Java byte-code enhancer for use with DataNucleus.
The newest version!
/**********************************************************************
Copyright (c) 2004 Andy Jefferson and others. All rights reserved.
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.
Contributors:
2004 Marco Schulze (NightLabs.de) - added verbose output of classpath
2004 Andy Jefferson - updated formatting of user output. Added version, vendor
2006 Andy Jefferson - restructured to have modular ClassEnhancer
2007 Andy Jefferson - swap across to using ASM
2008 Andy Jefferson - rewrite to match JDOEnhancer API
2008 Andy Jefferson - drop BCEL option
...
**********************************************************************/
package org.datanucleus.enhancer;
import java.io.File;
import java.io.IOException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.Map;
import java.util.Properties;
import java.util.StringTokenizer;
import org.datanucleus.ClassLoaderResolver;
import org.datanucleus.NucleusContext;
import org.datanucleus.enhancer.asm.ASMClassEnhancer;
import org.datanucleus.exceptions.ClassNotResolvedException;
import org.datanucleus.exceptions.NucleusException;
import org.datanucleus.exceptions.NucleusUserException;
import org.datanucleus.metadata.AbstractClassMetaData;
import org.datanucleus.metadata.ClassMetaData;
import org.datanucleus.metadata.ClassPersistenceModifier;
import org.datanucleus.metadata.FileMetaData;
import org.datanucleus.metadata.MetaDataManager;
import org.datanucleus.metadata.PackageMetaData;
import org.datanucleus.metadata.PersistenceUnitMetaData;
import org.datanucleus.util.CommandLine;
import org.datanucleus.util.NucleusLogger;
import org.datanucleus.util.Localiser;
import org.datanucleus.util.StringUtils;
import org.objectweb.asm.ClassVisitor;
/**
* DataNucleus Byte-Code Enhancer.
* This provides the entry point for enhancement. Enhancement is performed using a ClassEnhancer.
* Currently provides a ClassEnhancer using ASM.
*
* You can use the DataNucleusEnhancer in two ways :-
*
* - Via the command line, entering via the main method. This creates a DataNucleusEnhancer object,
* sets the options necessary and calls the execute method.
* - Programmatically, creating the DataNucleusEnhancer object settings options and running etc.
*
*
*
* The programmatic way would be something like this :-
*
* DataNucleusEnhancer enhancer = new DataNucleusEnhancer();
* enhancer.setVerbose();
* enhancer.enhancePersistenceUnit("myPersistenceUnit");
*
* enhancing all classes specified by the persistence unit.
*
*/
public class DataNucleusEnhancer
{
protected static final Localiser LOCALISER = Localiser.getInstance("org.datanucleus.enhancer.Localisation",
ClassEnhancer.class.getClassLoader());
/** Logger for enhancing. */
public static final NucleusLogger LOGGER = NucleusLogger.getLoggerInstance("DataNucleus.Enhancer");
/** ObjectManagerFactory Context **/
private NucleusContext nucleusContext;
/** ClassLoader resolver for use during the enhancement process. */
private ClassLoaderResolver clr;
/** Name of the API to use for enhancement (default is JDO). */
private String apiName = "JDO";
/** Optional output directory where any writing of enhanced classes goes. */
private String outputDirectory = null;
/** Whether we should provide verbose output. */
private boolean verbose = false;
/** Whether to output to System.out (as well as logging). */
private boolean systemOut = false;
/** Whether to allow generation of the PK when needed. */
private boolean generatePK = true;
/** Whether to allow generation of the default constructor when needed. */
private boolean generateConstructor = true;
/** Whether to use Detach Listener */
private boolean detachListener = false;
/** User-provided class loader. */
protected ClassLoader userClassLoader = null;
/** Set of components registered for enhancing. */
private Collection componentsToEnhance = new ArrayList();
/** Map storing input bytes of any classes to enhance, keyed by the class name (if specified using bytes). */
private Map bytesForClassesToEnhanceByClassName = null;
/**
* Map of the enhanced bytes of all classes just enhanced, keyed by the class name.
* Only populated after call of enhance().
*/
private Map enhancedBytesByClassName = null;
/**
* Map of the bytes of the PK classes created, keyed by the class name.
* Only populated after call of enhance().
*/
private Map pkClassBytesByClassName = null;
/**
* Constructor for an enhancer for JDO.
*/
public DataNucleusEnhancer()
{
this("JDO");
}
/**
* Constructor for an enhancer specifying the API.
* @param api The API to use.
*/
public DataNucleusEnhancer(String api)
{
this(api, null);
}
/**
* Constructor for an enhancer specifying the API and class enhancer and optional properties.
* @param apiName Name of the API to use; defines which MetaDataManager to utilise.
* @param props properties controlling enhancement
*/
public DataNucleusEnhancer(String apiName, Properties props)
{
LOGGER.info(LOCALISER.msg("Enhancer.ClassEnhancer", apiName));
this.apiName = apiName;
this.nucleusContext = new NucleusContext(apiName, NucleusContext.ContextType.ENHANCEMENT, props);
if (props != null)
{
// Superimpose any user-provided properties
nucleusContext.getPersistenceConfiguration().setPersistenceProperties(props);
}
this.clr = nucleusContext.getClassLoaderResolver(this.getClassLoader());
// Check if ASM is in CLASSPATH
// ClassUtils.assertClassForJarExistsInClasspath(clr, "org.objectweb.asm.Opcodes", "asm.jar");
Class asmCls = ClassVisitor.class;
if (asmCls != null && asmCls.isInterface())
{
throw new NucleusUserException("You seem to have ASM v3 in the CLASSPATH and you need ASM v4");
}
}
/**
* Initialise the enhancer plugin.
*/
private void init()
{
}
static class EnhanceComponent
{
public final static int CLASS = 0;
public final static int CLASS_FILE = 1;
public final static int MAPPING_FILE = 2;
public final static int JAR_FILE = 3;
public final static int PERSISTENCE_UNIT = 4;
int type;
Object value;
public EnhanceComponent(int type, Object value)
{
this.type = type;
this.value = value;
}
public Object getValue()
{
return value;
}
public int getType()
{
return type;
}
}
/**
* Accessor for the MetaDataManager.
* Allows users to register their own MetaData if defined dynamically.
* @return MetaData Manager
*/
public MetaDataManager getMetaDataManager()
{
return nucleusContext.getMetaDataManager();
}
/**
* Acessor for the output directory.
* @return the output directory
*/
public String getOutputDirectory()
{
return outputDirectory;
}
/**
* Mutator for the output directory where any classes will be written.
* The default is to use any current class file location and overwrite it
* @param dir the output directory
* @return The enhancer
*/
public DataNucleusEnhancer setOutputDirectory(String dir)
{
resetEnhancement();
this.outputDirectory = dir;
return this;
}
/**
* Accessor for the user-defined class loader for enhancement (if any).
* @return The class loader
*/
public ClassLoader getClassLoader()
{
return userClassLoader;
}
/**
* Method to set the class loader to use for loading the class(es) to be enhanced.
* @param loader The loader
* @return The enhancer
*/
public DataNucleusEnhancer setClassLoader(ClassLoader loader)
{
resetEnhancement();
this.userClassLoader = loader;
if (userClassLoader != null)
{
// Set it now in case the user wants to make use of MetaDataManager or something
clr.registerUserClassLoader(userClassLoader);
}
return this;
}
/**
* Acessor for the verbose
* @return the verbose
*/
public boolean isVerbose()
{
return verbose;
}
/**
* Mutator for the verbose flag
* @param verbose the verbose to set
* @return The enhancer
*/
public DataNucleusEnhancer setVerbose(boolean verbose)
{
resetEnhancement();
this.verbose = verbose;
return this;
}
/**
* Mutator for whether to output to system out.
* @param sysout Whether to use sysout
* @return The enhancer
*/
public DataNucleusEnhancer setSystemOut(boolean sysout)
{
resetEnhancement();
systemOut = sysout;
return this;
}
/**
* Mutator for whether to allow generation of PKs where needed.
* @param flag Whether to enable this
* @return The enhancer
*/
public DataNucleusEnhancer setGeneratePK(boolean flag)
{
resetEnhancement();
generatePK = flag;
return this;
}
/**
* Mutator for whether to allow generation of default constructor where needed.
* @param flag Whether to enable this
* @return The enhancer
*/
public DataNucleusEnhancer setGenerateConstructor(boolean flag)
{
resetEnhancement();
generateConstructor = flag;
return this;
}
/**
* Mutator for whether to allow generation of default constructor where needed.
* @param flag Whether to enable this
* @return The enhancer
*/
public DataNucleusEnhancer setDetachListener(boolean flag)
{
resetEnhancement();
detachListener = flag;
return this;
}
/**
* Method to add the specified class (and its input bytes) to the list of classes to enhance.
* @param className Name of the class (in the format "mydomain.MyClass")
* @param bytes Bytes of the class
* @return The enhancer
*/
public DataNucleusEnhancer addClass(String className, byte[] bytes)
{
init();
if (className == null)
{
return this;
}
if (bytesForClassesToEnhanceByClassName == null)
{
bytesForClassesToEnhanceByClassName = new HashMap();
}
bytesForClassesToEnhanceByClassName.put(className, bytes);
componentsToEnhance.add(new EnhanceComponent(EnhanceComponent.CLASS, className));
return this;
}
/**
* Method to add the specified classes to the list of classes to enhance.
* @param classNames Names of the classes
* @return The enhancer
*/
@SuppressWarnings("unchecked")
public DataNucleusEnhancer addClasses(String... classNames)
{
init();
if (classNames == null)
{
return this;
}
Collection names = new HashSet();
for (int i=0;i 0)
{
componentsToEnhance.add(new EnhanceComponent(EnhanceComponent.CLASS,
names.toArray(new String[names.size()])));
}
return this;
}
/**
* Method to add the specified files to the list of components to enhance.
* @param filenames Names of the files
* @return The enhancer
*/
public DataNucleusEnhancer addFiles(String... filenames)
{
init();
if (filenames == null)
{
return this;
}
// Split mapping files, class files, jar files into the respective type
Collection classFiles = new ArrayList(); // List to keep ordering
Collection mappingFiles = new ArrayList(); // List to keep ordering
Collection jarFiles = new HashSet();
for (int i=0;i 0)
{
componentsToEnhance.add(new EnhanceComponent(EnhanceComponent.MAPPING_FILE,
mappingFiles.toArray(new String[mappingFiles.size()])));
}
if (jarFiles.size() > 0)
{
componentsToEnhance.add(new EnhanceComponent(EnhanceComponent.JAR_FILE,
jarFiles.toArray(new String[jarFiles.size()])));
}
if (classFiles.size() > 0)
{
componentsToEnhance.add(new EnhanceComponent(EnhanceComponent.CLASS_FILE,
classFiles.toArray(new String[classFiles.size()])));
}
return this;
}
/**
* Method to add the classes defined by the specified jar to the list of components to enhance.
* @param jarFileName Name of the jar file
* @return The enhancer
*/
public DataNucleusEnhancer addJar(String jarFileName)
{
init();
if (jarFileName == null)
{
return this;
}
componentsToEnhance.add(new EnhanceComponent(EnhanceComponent.JAR_FILE, jarFileName));
return this;
}
/**
* Method to add the classes defined by the persistence-unit to the list of classes to enhance.
* @param persistenceUnitName Name of the persistence-unit
* @return The enhancer
*/
public DataNucleusEnhancer addPersistenceUnit(String persistenceUnitName)
{
init();
if (persistenceUnitName == null)
{
return this;
}
componentsToEnhance.add(new EnhanceComponent(EnhanceComponent.PERSISTENCE_UNIT, persistenceUnitName));
return this;
}
/**
* Method to enhance all classes defined by addClass, addClasses, addJar, addPersistenceUnit, addFiles.
* @return Number of classes enhanced
*/
public int enhance()
{
init();
if (componentsToEnhance.isEmpty())
{
return 0; // Nothing to enhance
}
// Load the meta-data for the registered components to enhance.
long startTime = System.currentTimeMillis();
Collection fileMetaData = getFileMetadataForInput();
// Enhance the classes implied by the FileMetaData
long inputTime = System.currentTimeMillis();
HashSet classNames = new HashSet();
Iterator filemdIter = fileMetaData.iterator();
boolean success = true;
while (filemdIter.hasNext())
{
FileMetaData filemd = filemdIter.next();
for (int packagenum = 0; packagenum < filemd.getNoOfPackages(); packagenum++)
{
PackageMetaData pmd = filemd.getPackage(packagenum);
for (int classnum = 0; classnum < pmd.getNoOfClasses(); classnum++)
{
ClassMetaData cmd = pmd.getClass(classnum);
if (classNames.contains(cmd.getFullClassName()))
{
// Already processed, maybe via annotations and this is MetaData
continue;
}
classNames.add(cmd.getFullClassName());
byte[] bytes = bytesForClassesToEnhanceByClassName != null ?
bytesForClassesToEnhanceByClassName.get(cmd.getFullClassName()) : null;
ClassEnhancer classEnhancer = getClassEnhancer(cmd, bytes);
// Enhance, but don't store if based on input bytes
boolean clsSuccess = enhanceClass(cmd, classEnhancer, bytes == null);
if (!clsSuccess)
{
success = false;
}
}
}
}
if (!success)
{
throw new NucleusException("Failure during enhancement of classes - see the log for details");
}
// Log info about timings
long enhanceTime = System.currentTimeMillis();
String msg = LOCALISER.msg("Enhancer.Success", classNames.size(), "" + (inputTime-startTime),
"" + (enhanceTime-inputTime), "" + (enhanceTime-startTime));
addMessage(msg, false);
// Remove the input specification
if (bytesForClassesToEnhanceByClassName != null)
{
bytesForClassesToEnhanceByClassName.clear();
bytesForClassesToEnhanceByClassName = null;
}
componentsToEnhance.clear();
return classNames.size();
}
/**
* Method to validate all classes defined by addClass, addClasses, addJar, addPersistenceUnit, addFiles.
* @return Number of classes validated
*/
public int validate()
{
init();
if (componentsToEnhance.isEmpty())
{
return 0; // Nothing to validate
}
// Load the meta-data for the registered components to enhance.
long startTime = System.currentTimeMillis();
Collection fileMetaData = getFileMetadataForInput();
// Validate the classes implied by the FileMetaData
long inputTime = System.currentTimeMillis();
HashSet classNames = new HashSet();
Iterator filemdIter = fileMetaData.iterator();
while (filemdIter.hasNext())
{
FileMetaData filemd = filemdIter.next();
for (int packagenum = 0; packagenum < filemd.getNoOfPackages(); packagenum++)
{
PackageMetaData pmd = filemd.getPackage(packagenum);
for (int classnum = 0; classnum < pmd.getNoOfClasses(); classnum++)
{
ClassMetaData cmd = pmd.getClass(classnum);
if (classNames.contains(cmd.getFullClassName()))
{
// Already processed, maybe via annotations and this is MetaData
continue;
}
classNames.add(cmd.getFullClassName());
byte[] bytes = bytesForClassesToEnhanceByClassName != null ?
bytesForClassesToEnhanceByClassName.get(cmd.getFullClassName()) : null;
ClassEnhancer classEnhancer = getClassEnhancer(cmd, bytes);
validateClass(cmd, classEnhancer);
}
}
}
// Log info about timings
long enhanceTime = System.currentTimeMillis();
String msg = LOCALISER.msg("Enhancer.Success", classNames.size(), "" + (inputTime-startTime),
"" + (enhanceTime-inputTime), "" + (enhanceTime-startTime));
addMessage(msg, false);
// Remove the input specification
if (bytesForClassesToEnhanceByClassName != null)
{
bytesForClassesToEnhanceByClassName.clear();
bytesForClassesToEnhanceByClassName = null;
}
componentsToEnhance.clear();
return classNames.size();
}
/**
* Method that processes the registered components to enhance, and loads the metadata for
* them into the MetaDataManager, returning the associated FileMetaData.
* @return The FileMetaData for the registered components.
*/
protected Collection getFileMetadataForInput()
{
MetaDataManager metadataMgr = getMetaDataManager();
Iterator iter = componentsToEnhance.iterator();
Collection fileMetaData = new ArrayList();
while (iter.hasNext())
{
EnhanceComponent comp = iter.next();
FileMetaData[] filemds = null;
switch (comp.getType())
{
case EnhanceComponent.CLASS : // Of the form "mydomain.MyClass"
if (comp.getValue() instanceof String)
{
// Single class
String className = (String)comp.getValue();
if (bytesForClassesToEnhanceByClassName != null &&
bytesForClassesToEnhanceByClassName.get(className) != null)
{
// Retrieve the meta-data "file"
AbstractClassMetaData cmd = metadataMgr.getMetaDataForClass(className, clr);
if (cmd != null)
{
filemds = new FileMetaData[] {cmd.getPackageMetaData().getFileMetaData()};
}
else
{
// No meta-data has been registered for this byte-defined class!
}
}
else
{
filemds = metadataMgr.loadClasses(new String[] {(String)comp.getValue()},
userClassLoader);
}
}
else
{
// Multiple classes
filemds = metadataMgr.loadClasses((String[])comp.getValue(), userClassLoader);
}
break;
case EnhanceComponent.CLASS_FILE : // Absolute/relative filename(s)
if (comp.getValue() instanceof String)
{
// Single class file
String className = null;
String classFilename = (String)comp.getValue();
if (!StringUtils.getFileForFilename(classFilename).exists())
{
String msg = LOCALISER.msg("Enhancer.InputFiles.Invalid", classFilename);
addMessage(msg, true);
}
else
{
className = ASMClassEnhancer.getClassNameForFileName(classFilename);
}
if (className != null)
{
filemds = metadataMgr.loadClasses(new String[] {className}, userClassLoader);
}
}
else
{
// Multiple class files
Collection classNames = new ArrayList();
String[] classFilenames = (String[])comp.getValue();
for (int i=0;i filemdsColl = new HashSet();
for (int i=0;i options = new HashSet();
if (generatePK)
{
options.add(ClassEnhancer.OPTION_GENERATE_PK);
}
if (generateConstructor)
{
options.add(ClassEnhancer.OPTION_GENERATE_DEFAULT_CONSTRUCTOR);
}
if (detachListener)
{
options.add(ClassEnhancer.OPTION_GENERATE_DETACH_LISTENER);
}
classEnhancer.setOptions(options);
return classEnhancer;
}
/**
* Method to add a message at the required output level.
* @param msg The message
* @param error Whether the message is an error, so log at error level (otherwise info)
*/
protected void addMessage(String msg, boolean error)
{
if (error)
{
LOGGER.error(msg);
}
else
{
LOGGER.info(msg);
}
if (systemOut)
{
System.out.println(msg);
}
}
/**
* Method to enhance the class defined by the MetaData.
* @param cmd MetaData for the class
* @param enhancer ClassEnhancer to use
* @param store Whether to store the class after enhancing
* @return Whether the operation performed without error
*/
protected boolean enhanceClass(ClassMetaData cmd, ClassEnhancer enhancer, boolean store)
{
boolean success = true;
try
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug(LOCALISER.msg("Enhancer.EnhanceClassStart", cmd.getFullClassName()));
}
boolean enhanced = enhancer.enhance();
if (enhanced)
{
// Store the enhanced bytes
if (enhancedBytesByClassName == null)
{
enhancedBytesByClassName = new HashMap();
}
enhancedBytesByClassName.put(cmd.getFullClassName(), enhancer.getClassBytes());
byte[] pkClassBytes = enhancer.getPrimaryKeyClassBytes();
if (pkClassBytes != null)
{
if (pkClassBytesByClassName == null)
{
pkClassBytesByClassName = new HashMap();
}
pkClassBytesByClassName.put(cmd.getFullClassName(), pkClassBytes);
}
if (store)
{
enhancer.save(getOutputDirectory());
}
if (isVerbose())
{
if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
{
addMessage("ENHANCED (PersistenceCapable) : " + cmd.getFullClassName(), false);
}
else if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_AWARE)
{
addMessage("ENHANCED (PersistenceAware) : " + cmd.getFullClassName(), false);
}
else
{
addMessage("NOT ENHANCED (NonPersistent) : " + cmd.getFullClassName(), false);
}
}
}
else
{
if (isVerbose())
{
if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
{
addMessage("ERROR (PersistenceCapable) : " + cmd.getFullClassName(), false);
}
else if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_AWARE)
{
addMessage("ERROR (PersistenceAware) : " + cmd.getFullClassName(), false);
}
else
{
addMessage("NOT ENHANCED (NonPersistent) : " + cmd.getFullClassName(), false);
}
}
if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE ||
cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_AWARE)
{
// Error in enhancement
success = false;
}
}
}
catch (IOException ioe)
{
// Exception thrown in saving the enhanced file
if (isVerbose())
{
addMessage("ERROR (NonPersistent) : " + cmd.getFullClassName(), false);
}
String msg = LOCALISER.msg("Enhancer.ErrorEnhancingClass", cmd.getFullClassName(), ioe.getMessage());
LOGGER.error(msg, ioe);
System.out.println(msg);
success = false;
}
if (LOGGER.isDebugEnabled())
{
LOGGER.debug(LOCALISER.msg("Enhancer.EnhanceClassEnd", cmd.getFullClassName()));
}
return success;
}
/**
* Method to validate the enhancement state of the class defined by the MetaData.
* @param cmd MetaData for the class
* @param enhancer ClassEnhancer to use
* @return Always returns true since there is nothing that can go wrong
*/
protected boolean validateClass(ClassMetaData cmd, ClassEnhancer enhancer)
{
if (LOGGER.isDebugEnabled())
{
LOGGER.debug(LOCALISER.msg("Enhancer.ValidateClassStart", cmd.getFullClassName()));
}
boolean enhanced = enhancer.validate();
if (enhanced)
{
if (isVerbose())
{
if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
{
addMessage("ENHANCED (PersistenceCapable) : " + cmd.getFullClassName(), false);
}
else if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_AWARE)
{
addMessage("ENHANCED (PersistenceAware) : " + cmd.getFullClassName(), false);
}
else
{
addMessage("NOT ENHANCED (NonPersistent) : " + cmd.getFullClassName(), false);
}
}
}
else
{
if (isVerbose())
{
if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_CAPABLE)
{
addMessage("NOT ENHANCED (PersistenceCapable) : " + cmd.getFullClassName(), false);
}
else if (cmd.getPersistenceModifier() == ClassPersistenceModifier.PERSISTENCE_AWARE)
{
addMessage("NOT ENHANCED (PersistenceAware) : " + cmd.getFullClassName(), false);
}
else
{
addMessage("NOT ENHANCED (NonPersistent) : " + cmd.getFullClassName(), false);
}
}
}
if (LOGGER.isDebugEnabled())
{
LOGGER.debug(LOCALISER.msg("Enhancer.ValidateClassEnd", cmd.getFullClassName()));
}
return true;
}
/**
* Accessor for global properties defining this enhancer.
* Provides "VersionNumber", "VendorName" as the minimum, but typically also returns
* "API", and "EnhancerName"
* @return The properties.
*/
public Properties getProperties()
{
Properties props = new Properties();
props.setProperty("VendorName", "DataNucleus");
props.setProperty("VersionNumber",
nucleusContext.getPluginManager().getVersionForBundle("org.datanucleus.enhancer"));
props.setProperty("API", apiName);
return props;
}
/**
* Entry point for command line enhancer.
* @param args Command line arguments
* @throws Exception Thrown if an error occurs
*/
public static void main(String args[])
throws Exception
{
// Create the enhancer, and set the various options
CommandLine cmd = new CommandLine();
cmd.addOption("pu", "persistenceUnit", "", "name of the persistence unit to enhance");
cmd.addOption("d", "dest", "", "output directory");
cmd.addOption("checkonly", "checkonly", null, "only check if the class is enhanced");
cmd.addOption("q", "quiet", null, "no output");
cmd.addOption("v", "verbose", null, "verbose output");
cmd.addOption("api", "api", "", "API Name (JDO, JPA, etc)");
cmd.addOption("generatePK", "generatePK", "", "Generate PK class where needed?");
cmd.addOption("generateConstructor", "generateConstructor", "",
"Generate default constructor where needed?");
cmd.addOption("detachListener", "detachListener", "", "Use Detach Listener?");
cmd.parse(args);
// Create the DataNucleusEnhancer to the required API/enhancer
String apiName = cmd.hasOption("api") ? cmd.getOptionArg("api") : "JDO";
// TODO Add a way of defining input properties for startup
Properties props = new Properties();
props.setProperty("datanucleus.plugin.allowUserBundles", "true");
DataNucleusEnhancer enhancer = new DataNucleusEnhancer(apiName, props);
boolean quiet = false;
if (cmd.hasOption("q"))
{
quiet = true;
if (cmd.hasOption("v")) // Verbose only recognised when not quiet
{
enhancer.setVerbose(true);
}
}
if (!quiet)
{
enhancer.setSystemOut(true);
}
if (cmd.hasOption("d"))
{
String destination = cmd.getOptionArg("d");
File tmp = new File(destination);
if (tmp.exists())
{
if (!tmp.isDirectory())
{
System.out.println(destination + " is not directory. please set directory.");
System.exit(1);
}
}
else
{
tmp.mkdirs();
}
enhancer.setOutputDirectory(destination);
}
if (cmd.hasOption("generateConstructor"))
{
String val = cmd.getOptionArg("generateConstructor");
if (val.equalsIgnoreCase("false"))
{
enhancer.setGenerateConstructor(false);
}
}
if (cmd.hasOption("generatePK"))
{
String val = cmd.getOptionArg("generatePK");
if (val.equalsIgnoreCase("false"))
{
enhancer.setGeneratePK(false);
}
}
if (cmd.hasOption("detachListener"))
{
String val = cmd.getOptionArg("detachListener");
if (val.equalsIgnoreCase("true"))
{
enhancer.setDetachListener(true);
}
}
boolean validating = cmd.hasOption("checkonly") ? true : false;
String persistenceUnitName = cmd.hasOption("pu") ? cmd.getOptionArg("pu") : null;
String[] filenames = cmd.getDefaultArgs();
// Debug Info : title
String msg = null;
String version = enhancer.nucleusContext.getPluginManager().getVersionForBundle("org.datanucleus.enhancer");
if (validating)
{
msg = LOCALISER.msg("Enhancer.Title.Validate", version);
}
else
{
msg = LOCALISER.msg("Enhancer.Title.Enhance", version);
}
LOGGER.info(msg);
if (!quiet)
{
System.out.println(msg);
}
// Debug Info : CLASSPATH
LOGGER.debug(LOCALISER.msg("Enhancer.Classpath"));
if (enhancer.isVerbose())
{
System.out.println(LOCALISER.msg("Enhancer.Classpath"));
}
StringTokenizer tokeniser = new StringTokenizer(System.getProperty("java.class.path"), File.pathSeparator);
while (tokeniser.hasMoreTokens())
{
String entry = LOCALISER.msg("Enhancer.Classpath.Entry", tokeniser.nextToken());
if (LOGGER.isDebugEnabled())
{
LOGGER.debug(entry);
}
if (enhancer.isVerbose())
{
System.out.println(entry);
}
}
if (enhancer.isVerbose())
{
System.out.flush();
}
// Perform the enhancement/validation using the specified input
int numClasses = 0;
try
{
if (persistenceUnitName != null)
{
// Process persistence-unit
enhancer.addPersistenceUnit(persistenceUnitName);
}
else
{
// Process class/mapping-files
enhancer.addFiles(filenames);
}
if (validating)
{
numClasses = enhancer.validate();
}
else
{
numClasses = enhancer.enhance();
}
}
catch (NucleusException jpe)
{
System.out.println(jpe.getMessage());
msg = LOCALISER.msg("Enhancer.Failure");
LOGGER.error(msg, jpe);
if (!quiet)
{
System.out.println(msg);
}
System.exit(1);
}
if (numClasses == 0)
{
msg = LOCALISER.msg("Enhancer.NoClassesEnhanced");
LOGGER.info(msg);
if (!quiet)
{
System.out.println(msg);
}
}
}
}