package com.avaje.ebean.enhance.agent; import java.io.PrintStream; import java.util.HashMap; import java.util.logging.Level; import java.util.logging.Logger; /** * Used to hold meta data, arguments and log levels for the enhancement. */ public class EnhanceContext { private static final Logger logger = Logger.getLogger(EnhanceContext.class.getName()); private final IgnoreClassHelper ignoreClassHelper; private final HashMap<String, String> agentArgsMap; private final boolean readOnly; private final boolean transientInternalFields; private final boolean checkNullManyFields; private final ClassMetaReader reader; private final ClassBytesReader classBytesReader; private PrintStream logout; private int logLevel; private HashMap<String, ClassMeta> map = new HashMap<String, ClassMeta>(); /** * Construct a context for enhancement or subclass generation. * * @param subclassing * true if generating subclasses (false for javaagent etc) * @param agentArgs * parameters for enhancement such as log level */ public EnhanceContext(ClassBytesReader classBytesReader, String agentArgs) { this.ignoreClassHelper = new IgnoreClassHelper(agentArgs); this.agentArgsMap = ArgParser.parse(agentArgs); this.logout = System.out; this.classBytesReader = classBytesReader; this.reader = new ClassMetaReader(this); String debugValue = agentArgsMap.get("debug"); if (debugValue != null) { try { logLevel = Integer.parseInt(debugValue); } catch (NumberFormatException e) { String msg = "Agent debug argument [" + debugValue+ "] is not an int?"; logger.log(Level.WARNING, msg); } } this.readOnly = getPropertyBoolean("readonly", false); this.transientInternalFields = getPropertyBoolean("transientInternalFields", false); this.checkNullManyFields = getPropertyBoolean("checkNullManyFields", true); } public byte[] getClassBytes(String className, ClassLoader classLoader){ return classBytesReader.getClassBytes(className, classLoader); } /** * Return a value from the agent arguments using its key. */ public String getProperty(String key){ return agentArgsMap.get(key.toLowerCase()); } public boolean getPropertyBoolean(String key, boolean dflt){ String s = getProperty(key); if (s == null){ return dflt; } else { return s.trim().equalsIgnoreCase("true"); } } /** * Return true if this class should be ignored. That is JDK classes and * known libraries JDBC drivers etc can be skipped. */ public boolean isIgnoreClass(String className) { return ignoreClassHelper.isIgnoreClass(className); } /** * Change the logout to something other than system out. */ public void setLogout(PrintStream logout) { this.logout = logout; } /** * Create a new meta object for enhancing a class. */ public ClassMeta createClassMeta() { return new ClassMeta(this, logLevel, logout); } /** * Read the class meta data for a super class. * <p> * Typically used to read meta data for inheritance hierarchy. * </p> */ public ClassMeta getSuperMeta(String superClassName, ClassLoader classLoader) { try { if (isIgnoreClass(superClassName)){ return null; } return reader.get(false, superClassName, classLoader); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } /** * Read the class meta data for an interface. * <p> * Typically used to check the interface to see if it is transactional. * </p> */ public ClassMeta getInterfaceMeta(String interfaceClassName, ClassLoader classLoader) { try { if (isIgnoreClass(interfaceClassName)){ return null; } return reader.get(true, interfaceClassName, classLoader); } catch (ClassNotFoundException e) { throw new RuntimeException(e); } } public void addClassMeta(ClassMeta meta) { map.put(meta.getClassName(), meta); } public ClassMeta get(String className) { return map.get(className); } /** * Log some debug output. */ public void log(int level, String msg) { if (logLevel >= level) { logout.println(msg); } } public void log(String className, String msg) { if (className != null) { msg = "cls: " + className + " msg: " + msg; } logout.println("transform> " + msg); } public boolean isLog(int level){ return logLevel >= level; } /** * Log an error. */ public void log(Throwable e) { e.printStackTrace(logout); } /** * Return the log level. */ public int getLogLevel() { return logLevel; } /** * Return true if this should go through the enhancement process but not * actually save the enhanced classes. * <p> * Set this to true to run through the enhancement process without actually * doing the enhancement for debugging etc. * </p> */ public boolean isReadOnly() { return readOnly; } /** * Return true if internal ebean fields in entity classes should be transient. */ public boolean isTransientInternalFields() { return transientInternalFields; } /** * Return true if we should add null checking on *ToMany fields. * <p> * On getting a many that is null Ebean will create an empty List, Set or * Map. If it is a ManyToMany it will turn on Modify listening. * </p> */ public boolean isCheckNullManyFields() { return checkNullManyFields; } }