Remove Subclassing/Dynamic Proxy support - beans must be enhanced

This commit is contained in:
Robin Bygrave
2013-04-19 16:25:10 +12:00
parent 66880aa340
commit 2567022d95
63 changed files with 233 additions and 2085 deletions
@@ -1,7 +1,5 @@
package com.avaje.ebean;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.Collection;
import java.util.Iterator;
import java.util.List;
@@ -138,22 +136,6 @@ public interface EbeanServer {
*/
public <T> T createEntityBean(Class<T> type);
/**
* Create a ObjectInputStream that can be used to deserialise "Proxy" or
* "SubClassed" entity beans.
* <p>
* This is NOT required when entity beans are "Enhanced" (via java agent or
* ant task etc).
* </p>
* <p>
* The reason this is needed to deserialise "Proxy" beans is because Ebean
* creates the "Proxy/SubClass" classes in a class loader - and generally the
* class loader deserialising the inputStream is not aware of these other
* classes.
* </p>
*/
public ObjectInputStream createProxyObjectInputStream(InputStream is);
/**
* Create a CsvReader for a given beanType.
*/
-19
View File
@@ -1,7 +1,5 @@
package com.avaje.ebean;
import com.avaje.ebean.config.ServerConfig;
import java.io.Serializable;
import java.util.List;
import java.util.Map;
@@ -959,23 +957,6 @@ public interface Query<T> extends Serializable {
*/
public Query<T> setDistinct(boolean isDistinct);
/**
* Set this to true and the beans and collections returned will be plain
* classes rather than Ebean generated dynamic subclasses etc.
* <p>
* This is *ONLY* relevant when you are not using enhancement (and using
* dynamic subclasses instead).
* </p>
* <p>
* Alternatively you can globally set the mode using ebean.vanillaMode=true in
* ebean.properties or {@link ServerConfig#setVanillaMode(boolean)}.
* </p>
*
* @see ServerConfig#setVanillaMode(boolean)
* @see ServerConfig#setVanillaRefMode(boolean)
*/
public Query<T> setVanillaMode(boolean vanillaMode);
/**
* Return the first row value.
*/
@@ -0,0 +1,8 @@
package com.avaje.ebean.bean;
/**
* Marker interface for classes enhanced to support Transactional methods.
*/
public interface EnhancedTransactional {
}
@@ -3,7 +3,6 @@ package com.avaje.ebean.bean;
import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.math.BigDecimal;
import java.net.URL;
@@ -536,27 +535,6 @@ public final class EntityBeanIntercept implements Serializable {
}
}
/**
* This is ONLY used for subclass entity beans.
* <p>
* This is not used when entity bean classes are enhanced via javaagent or ant
* etc - only when a subclass is generated.
* </p>
* Returns a Serializable instance that is either the 'byte code generated'
* object or a 'Vanilla' copy of this bean depending on
* SerializeControl.isVanillaBeans().
*/
public Object writeReplaceIntercept() throws ObjectStreamException {
if (!SerializeControl.isVanillaBeans()) {
return owner;
}
// creates a plain vanilla object and
// copies the values from the owner
return owner._ebean_createCopy();
}
/**
* Helper method to check if two objects are equal.
*/
@@ -1,130 +0,0 @@
package com.avaje.ebean.bean;
/**
* This is ONLY used for <b>subclassed</b> entity beans.
* <p>
* This is NOT USED for entity beans that are enhanced via a javaagent or ant
* task etc. This is only used when the entity beans are created as a subclass
* of the original class.
* </p>
* <p>
* Allows the developer to control whether beans and collections are serialized
* to plain 'vanilla' classes or left in byte code generated subclasses.
* <p>
* Vanilla beans are beans that have plain ordinary classes as opposed to
* specially generated classes that Ebean creates. Ebean creates classes (using
* ASM) to support lazy loading (reference beans) and concurrency checking etc.
* </p>
* <p>
* SerializeControl gives you the ability to control whether an object graph is
* serialized to plain 'vanilla' objects or in the special byte code generated
* form. There are pros and cons for both approaches depending on whether you
* want to support "FULL" concurrency checking and lazy loading when the object
* graph is deserialized.
* </p>
* <p>
* Note that BeanMap, BeanList and BeanSet are not byte code generated. They are
* ordinary classes. However you may wish to have these serialized to the
* underlying List Set and Map implementations for the benefit that they can be
* deserialised in a JVM without <em>ANY</em> ebean code at all.
* </p>
*/
public class SerializeControl {
private static final String BEANS = "com.avaje.ebean.vanillabeans";
private static final String COLLECTIONS = "com.avaje.ebean.vanillacollections";
private static Boolean getDefault(String key, Boolean dflt) {
String val = System.getProperty(key);
if (val != null) {
return val.equalsIgnoreCase("true");
}
return dflt;
}
private static ThreadLocal<Boolean> vanillaBeans = new ThreadLocal<Boolean>() {
protected synchronized Boolean initialValue() {
return getDefault(BEANS, Boolean.TRUE);
}
};
private static ThreadLocal<Boolean> vanillaCollections = new ThreadLocal<Boolean>() {
protected synchronized Boolean initialValue() {
return getDefault(COLLECTIONS, Boolean.TRUE);
}
};
/**
* Set the JVM wide default for Beans.
*/
public static void setDefaultForBeans(boolean vanillaOn) {
Boolean b = Boolean.valueOf(vanillaOn);
System.setProperty(BEANS, b.toString());
}
/**
* Set the JVM wide default for Collections.
*/
public static void setDefaultForCollections(boolean vanillaOn) {
Boolean b = Boolean.valueOf(vanillaOn);
System.setProperty(COLLECTIONS, b.toString());
}
/**
* Reset the mode for beans and collections back to the JVM wide default
* setting.
*/
public static void resetToDefault() {
Boolean beans = getDefault(BEANS, Boolean.FALSE);
setVanillaBeans(beans);
Boolean coll = getDefault(COLLECTIONS, Boolean.FALSE);
setVanillaCollections(coll);
}
/**
* Set the mode for both Beans and Collections.
*/
public static void setVanilla(boolean vanillaOn) {
if (vanillaOn) {
vanillaBeans.set(Boolean.TRUE);
vanillaCollections.set(Boolean.TRUE);
} else {
vanillaBeans.set(Boolean.FALSE);
vanillaCollections.set(Boolean.FALSE);
}
}
/**
* Return true if beans are serialized to Vanilla as opposed to byte code
* generated subclasses.
*/
public static boolean isVanillaBeans() {
return (Boolean) vanillaBeans.get();
}
/**
* Set whether beans should be serialized to Vanilla as opposed to byte code
* generated subclasses.
*/
public static void setVanillaBeans(boolean vanillaOn) {
vanillaBeans.set(vanillaOn);
}
/**
* Return true if collections are serialized to be plain Lists Sets or Maps as
* opposed to BeanList, BeanMap or BeanSet.
*/
public static boolean isVanillaCollections() {
return (Boolean) vanillaCollections.get();
}
/**
* Set whether collections should be serialized to Vanilla Lists Sets or Maps
* (instead of BeanList, BeanMap or BeanSet).
*/
public static void setVanillaCollections(boolean vanillaOn) {
vanillaCollections.set(vanillaOn);
}
}
@@ -1,6 +1,5 @@
package com.avaje.ebean.common;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.ArrayList;
import java.util.Collection;
@@ -11,7 +10,6 @@ import java.util.ListIterator;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.SerializeControl;
/**
* List capable of lazy loading.
@@ -46,20 +44,6 @@ public final class BeanList<E> extends AbstractBeanCollection<E> implements List
super(loader, ownerBean, propertyName);
}
Object readResolve() throws ObjectStreamException {
if (SerializeControl.isVanillaCollections()) {
return list;
}
return this;
}
Object writeReplace() throws ObjectStreamException {
if (SerializeControl.isVanillaCollections()) {
return list;
}
return this;
}
@SuppressWarnings("unchecked")
public void addBean(Object bean) {
list.add((E) bean);
@@ -1,6 +1,5 @@
package com.avaje.ebean.common;
import java.io.ObjectStreamException;
import java.util.Collection;
import java.util.Collections;
import java.util.Iterator;
@@ -9,7 +8,6 @@ import java.util.Map;
import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.SerializeControl;
/**
* Map capable of lazy loading.
@@ -39,20 +37,6 @@ public final class BeanMap<K, E> extends AbstractBeanCollection<E> implements Ma
super(ebeanServer, ownerBean, propertyName);
}
Object readResolve() throws ObjectStreamException {
if (SerializeControl.isVanillaCollections()) {
return map;
}
return this;
}
Object writeReplace() throws ObjectStreamException {
if (SerializeControl.isVanillaCollections()) {
return map;
}
return this;
}
public void internalAdd(Object bean) {
throw new RuntimeException("Not allowed for map");
}
@@ -1,6 +1,5 @@
package com.avaje.ebean.common;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.util.Collection;
import java.util.Iterator;
@@ -9,7 +8,6 @@ import java.util.Set;
import com.avaje.ebean.bean.BeanCollectionAdd;
import com.avaje.ebean.bean.BeanCollectionLoader;
import com.avaje.ebean.bean.SerializeControl;
/**
* Set capable of lazy loading.
@@ -40,20 +38,6 @@ public final class BeanSet<E> extends AbstractBeanCollection<E> implements Set<E
super(loader, ownerBean, propertyName);
}
Object readResolve() throws ObjectStreamException {
if (SerializeControl.isVanillaCollections()) {
return set;
}
return this;
}
Object writeReplace() throws ObjectStreamException {
if (SerializeControl.isVanillaCollections()) {
return set;
}
return this;
}
@SuppressWarnings("unchecked")
public void addBean(Object bean) {
set.add((E) bean);
@@ -5,10 +5,8 @@ import java.util.List;
import javax.sql.DataSource;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.EbeanServerFactory;
import com.avaje.ebean.LogLevel;
import com.avaje.ebean.Query;
import com.avaje.ebean.annotation.Encrypted;
import com.avaje.ebean.cache.ServerCacheFactory;
import com.avaje.ebean.cache.ServerCacheManager;
@@ -214,24 +212,6 @@ public class ServerConfig {
private ServerCacheManager serverCacheManager;
/**
* Set this to true when by default vanilla objects should be returned from
* queries rather than dynamic subclasses etc. Only relevant when not using
* enhancement (using dynamic subclasses).
*/
private boolean vanillaMode;
/**
* Controls whether the {@link EbeanServer#getReference(Class, Object)} method
* returns vanilla objects or not.
*/
private boolean vanillaRefMode;
/**
* Set to false to require enhancement to be used. Defaults to true.
*/
private boolean allowSubclassing = true;
/**
* Construct a Server Configuration for programmatically creating an
* EbeanServer.
@@ -466,56 +446,6 @@ public class ServerConfig {
this.serverCacheManager = serverCacheManager;
}
/**
* Return true if by default queries should return 'vanilla' objects rather
* than dynamic subclasses.
* <p>
* This setting is not relevant when using enhancement (only when using
* dynamic subclasses).
* </p>
*/
public boolean isVanillaMode() {
return vanillaMode;
}
/**
* Set this to true if by default queries should return 'vanilla' objects
* rather than dynamic subclasses.
* <p>
* This setting is not relevant when using enhancement (only when using
* dynamic subclasses).
* </p>
* <p>
* Alternatively you can set this on a specific query via
* {@link Query#setVanillaMode(boolean)}.
* </p>
*
* @see #setVanillaRefMode(boolean)
* @see Query#setVanillaMode(boolean)
*/
public void setVanillaMode(boolean vanillaMode) {
this.vanillaMode = vanillaMode;
}
/**
* Returns true if {@link EbeanServer#getReference(Class, Object)} should
* return vanilla objects or not.
*
* @see #setVanillaMode(boolean)
* @see Query#setVanillaMode(boolean)
*/
public boolean isVanillaRefMode() {
return vanillaRefMode;
}
/**
* Set this to true if you want
* {@link EbeanServer#getReference(Class, Object)} to return vanilla objects.
*/
public void setVanillaRefMode(boolean vanillaRefMode) {
this.vanillaRefMode = vanillaRefMode;
}
/**
* Return the log level used for "subclassing" enhancement.
*/
@@ -1151,20 +1081,6 @@ public class ServerConfig {
this.updateChangesOnly = updateChangesOnly;
}
/**
* Set to false to require enhancement to be used. Defaults to true.
*/
public void setAllowSubclassing(boolean allowSubclassing) {
this.allowSubclassing = allowSubclassing;
}
/**
* Returns whether this config supports subclassed entities.
*/
public boolean isAllowSubclassing() {
return allowSubclassing;
}
/**
* Returns the resource directory.
*/
@@ -1421,9 +1337,6 @@ public class ServerConfig {
packages = getSearchJarsPackages(packagesProp);
}
allowSubclassing = p.getBoolean("allowSubclassing", true);
vanillaMode = p.getBoolean("vanillaMode", false);
vanillaRefMode = p.getBoolean("vanillaRefMode", false);
updateChangesOnly = p.getBoolean("updateChangesOnly", true);
boolean batchMode = p.getBoolean("batch.mode", false);
@@ -33,11 +33,6 @@ public interface SpiEbeanServer extends EbeanServer, BeanLoader, BeanCollectionL
* Return true if UpdateNullProperties defaults to true for stateless updates.
*/
public boolean isDefaultUpdateNullProperties();
/**
* Return true if vanilla beans should be returned by queries by default.
*/
public boolean isVanillaMode();
/**
* Return the DatabasePlatform for this server.
@@ -522,11 +522,6 @@ public interface SpiQuery<T> extends Query<T> {
*/
public boolean isDistinct();
/**
* Return true if this query should build and return vanilla objects.
*/
public boolean isVanillaMode(boolean serverDefaultVanillaMode);
/**
* Set default select clauses where none have been explicitly defined.
*/
@@ -76,7 +76,7 @@ public class CachedBeanDataFromBean {
sharableBean = bean;
} else {
// create a readOnly sharable instance by copying the data
sharableBean = desc.createBean(false);
sharableBean = desc.createBean();
BeanProperty[] propertiesId = desc.propertiesId();
for (int i = 0; i < propertiesId.length; i++) {
Object v = propertiesId[i].getValue(bean);
@@ -34,13 +34,10 @@ public class DefaultBeanLoader {
private static final Logger logger = LoggerFactory.getLogger(DefaultBeanLoader.class);
private final DebugLazyLoad debugLazyLoad;
private final DefaultServer server;
protected DefaultBeanLoader(DefaultServer server, DebugLazyLoad debugLazyLoad) {
protected DefaultBeanLoader(DefaultServer server) {
this.server = server;
this.debugLazyLoad = debugLazyLoad;
}
/**
@@ -173,17 +170,13 @@ public class DefaultBeanLoader {
private void loadManyInternal(Object parentBean, String propertyName, Transaction t, boolean refresh, ObjectGraphNode node, boolean onlyIds) {
boolean vanilla = (parentBean instanceof EntityBean == false);
EntityBeanIntercept ebi = null;
PersistenceContext pc = null;
BeanCollection<?> beanCollection = null;
ExpressionList<?> filterMany = null;
if (!vanilla) {
ebi = ((EntityBean) parentBean)._ebean_getIntercept();
pc = ebi.getPersistenceContext();
}
ebi = ((EntityBean) parentBean)._ebean_getIntercept();
pc = ebi.getPersistenceContext();
BeanDescriptor<?> parentDesc = server.getBeanDescriptor(parentBean.getClass());
BeanPropertyAssocMany<?> many = (BeanPropertyAssocMany<?>) parentDesc.getBeanProperty(propertyName);
@@ -201,13 +194,13 @@ public class DefaultBeanLoader {
pc.put(parentId, parentBean);
}
boolean useManyIdCache = !vanilla && beanCollection != null && parentDesc.cacheIsUseManyId();
boolean useManyIdCache = beanCollection != null && parentDesc.cacheIsUseManyId();
if (useManyIdCache) {
Boolean readOnly = null;
if (ebi != null && ebi.isReadOnly()) {
readOnly = Boolean.TRUE;
}
if (parentDesc.cacheLoadMany(many, beanCollection, parentId, readOnly, false)) {
if (parentDesc.cacheLoadMany(many, beanCollection, parentId, readOnly)) {
return;
}
}
@@ -216,7 +209,7 @@ public class DefaultBeanLoader {
if (refresh) {
// populate a new collection
Object emptyCollection = many.createEmpty(vanilla);
Object emptyCollection = many.createEmpty(false);
many.setValue(parentBean, emptyCollection);
query.setLoadDescription("+refresh", null);
} else {
@@ -245,7 +238,6 @@ public class DefaultBeanLoader {
query.setMode(Mode.LAZYLOAD_MANY);
query.setLazyLoadManyPath(many.getName());
query.setPersistenceContext(pc);
query.setVanillaMode(vanilla);
if (ebi != null) {
if (ebi.isReadOnly()) {
@@ -370,15 +362,9 @@ public class DefaultBeanLoader {
private void refreshBeanInternal(Object bean, SpiQuery.Mode mode) {
boolean vanilla = (bean instanceof EntityBean == false);
EntityBeanIntercept ebi = null;
PersistenceContext pc = null;
if (!vanilla) {
ebi = ((EntityBean) bean)._ebean_getIntercept();
pc = ebi.getPersistenceContext();
}
EntityBeanIntercept ebi = ((EntityBean) bean)._ebean_getIntercept();;
PersistenceContext pc = ebi.getPersistenceContext();
BeanDescriptor<?> desc = server.getBeanDescriptor(bean.getClass());
Object id = desc.getId(bean);
@@ -429,7 +415,6 @@ public class DefaultBeanLoader {
if (mode.equals(SpiQuery.Mode.REFRESH_BEAN)) {
query.setUseCache(false);
}
query.setVanillaMode(vanilla);
if (ebi != null && ebi.isReadOnly()) {
query.setReadOnly(true);
@@ -1,8 +1,5 @@
package com.avaje.ebeaninternal.server.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collection;
@@ -138,13 +135,6 @@ public final class DefaultServer implements SpiEbeanServer {
private final boolean defaultDeleteMissingChildren;
private final boolean defaultUpdateNullProperties;
/**
* Set to true if vanilla objects should be returned by default from queries
* (with dynamic subclassing).
*/
private final boolean vanillaMode;
private final boolean vanillaRefMode;
/**
* Handles the save, delete, updateSql CallableSql.
*/
@@ -210,9 +200,6 @@ public final class DefaultServer implements SpiEbeanServer {
*/
public DefaultServer(InternalConfiguration config, ServerCacheManager cache) {
this.vanillaMode = config.getServerConfig().isVanillaMode();
this.vanillaRefMode = config.getServerConfig().isVanillaRefMode();
this.serverCacheManager = cache;
this.pstmtBatch = config.getPstmtBatch();
this.databasePlatform = config.getDatabasePlatform();
@@ -245,7 +232,7 @@ public final class DefaultServer implements SpiEbeanServer {
this.autoFetchManager = config.createAutoFetchManager(this);
this.adminAutofetch = new MAdminAutofetch(autoFetchManager);
this.beanLoader = new DefaultBeanLoader(this, config.getDebugLazyLoad());
this.beanLoader = new DefaultBeanLoader(this);
this.jsonContext = config.createJsonContext(this);
loadAndInitializePlugins(config);
@@ -290,10 +277,6 @@ public final class DefaultServer implements SpiEbeanServer {
return defaultUpdateNullProperties;
}
public boolean isVanillaMode() {
return vanillaMode;
}
public int getLazyLoadBatchSize() {
return lazyLoadBatchSize;
}
@@ -410,8 +393,7 @@ public final class DefaultServer implements SpiEbeanServer {
if (bean instanceof EntityBean) {
return new DefaultBeanState((EntityBean) bean);
}
// if using "subclassing" (not enhancement) this will
// return null for 'vanilla' instances (not subclassed)
// Not an entity bean
return null;
}
@@ -563,15 +545,6 @@ public final class DefaultServer implements SpiEbeanServer {
return (T) desc.createEntityBean();
}
public ObjectInputStream createProxyObjectInputStream(InputStream is) {
try {
return new ProxyBeanObjectInputStream(is, this);
} catch (IOException e) {
throw new PersistenceException(e);
}
}
/**
* Return a Reference bean.
* <p>
@@ -629,7 +602,7 @@ public final class DefaultServer implements SpiEbeanServer {
} else {
// use the default reference options
ref = desc.createReference(vanillaRefMode, null, id, null);
ref = desc.createReference(null, id, null);
}
if (ctx != null && (ref instanceof EntityBean)) {
@@ -1139,25 +1112,20 @@ public final class DefaultServer implements SpiEbeanServer {
return null;
}
// boolean readOnly = beanDescriptor.calculateReadOnly(query.isReadOnly());
boolean vanilla = query.isVanillaMode(vanillaMode);
Object cachedBean = beanDescriptor.cacheGetBean(query.getId(), vanilla, query.isReadOnly());
Object cachedBean = beanDescriptor.cacheGetBean(query.getId(), query.isReadOnly());
if (cachedBean != null) {
if (context == null) {
context = new DefaultPersistenceContext();
}
context.put(query.getId(), cachedBean);
if (!vanilla) {
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), false, null, false);
loadContext.setPersistenceContext(context);
DLoadContext loadContext = new DLoadContext(this, beanDescriptor, query.isReadOnly(), false, null, false);
loadContext.setPersistenceContext(context);
EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept();
ebi.setPersistenceContext(context);
loadContext.register(null, ebi);
}
EntityBeanIntercept ebi = ((EntityBean) cachedBean)._ebean_getIntercept();
ebi.setPersistenceContext(context);
loadContext.register(null, ebi);
}
return (T) cachedBean;
@@ -29,7 +29,6 @@ import com.avaje.ebeaninternal.server.query.DefaultOrmQueryEngine;
import com.avaje.ebeaninternal.server.query.DefaultRelationalQueryEngine;
import com.avaje.ebeaninternal.server.resource.ResourceManager;
import com.avaje.ebeaninternal.server.resource.ResourceManagerFactory;
import com.avaje.ebeaninternal.server.subclass.SubClassManager;
import com.avaje.ebeaninternal.server.text.json.DJsonContext;
import com.avaje.ebeaninternal.server.text.json.DefaultJsonValueAdapter;
import com.avaje.ebeaninternal.server.transaction.DefaultTransactionScopeManager;
@@ -54,8 +53,6 @@ public class InternalConfiguration {
private final BootupClasses bootupClasses;
private final SubClassManager subClassManager;
private final DeployInherit deployInherit;
private final ResourceManager resourceManager;
@@ -105,8 +102,6 @@ public class InternalConfiguration {
this.bootupClasses = bootupClasses;
this.expressionFactory = new DefaultExpressionFactory();
this.subClassManager = new SubClassManager(serverConfig);
this.typeManager = new DefaultTypeManager(serverConfig, bootupClasses);
this.binder = new Binder(typeManager);
@@ -215,10 +210,6 @@ public class InternalConfiguration {
return beanDescriptorManager;
}
public SubClassManager getSubClassManager() {
return subClassManager;
}
public DeployInherit getDeployInherit() {
return deployInherit;
}
@@ -39,8 +39,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
private final SpiQuery<T> query;
private final boolean vanillaMode;
private final BeanFinder<T> finder;
private final LoadContext graphContext;
@@ -74,7 +72,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
this.finder = beanDescriptor.getBeanFinder();
this.queryEngine = queryEngine;
this.query = query;
this.vanillaMode = query.isVanillaMode(server.isVanillaMode());
this.readOnly = query.isReadOnly();
this.graphContext = new DLoadContext(ebeanServer, beanDescriptor, readOnly, query);
@@ -226,11 +223,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
public boolean isFindById() {
return query.getType() == Type.BEAN;
}
public boolean isVanillaMode() {
return vanillaMode;
}
/**
* Execute the query as findById.
*/
@@ -269,8 +262,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
*/
@SuppressWarnings("unchecked")
public List<T> findList() {
BeanCollection<T> bc = queryEngine.findMany(this);
return (List<T>) (vanillaMode ? bc.getActualCollection() : bc);
return (List<T>) queryEngine.findMany(this);
}
/**
@@ -278,8 +270,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
*/
@SuppressWarnings("unchecked")
public Set<?> findSet() {
BeanCollection<T> bc = queryEngine.findMany(this);
return (Set<T>) (vanillaMode ? bc.getActualCollection() : bc);
return (Set<T>)queryEngine.findMany(this);
}
/**
@@ -296,8 +287,7 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
throw new PersistenceException(msg);
}
}
BeanCollection<T> bc = queryEngine.findMany(this);
return (Map<?, ?>) (vanillaMode ? bc.getActualCollection() : bc);
return (Map<?, ?>) queryEngine.findMany(this);
}
public SpiQuery.Type getQueryType() {
@@ -377,14 +367,6 @@ public final class OrmQueryRequest<T> extends BeanRequest implements BeanQueryRe
// TODO: Sort out returning BeanCollection from L2 cache
return null;
// BeanCollection<T> bc = beanDescriptor.queryCacheGet(cacheKey);
// if (bc != null && Boolean.FALSE.equals(query.isReadOnly())) {
// // Explicit readOnly=false for query cache
// CopyContext ctx = new CopyContext(vanillaMode, false);
// return new CopyBeanCollection<T>(bc, beanDescriptor, ctx, 5).copy();
// }
// return bc;
}
public void putToQueryCache(BeanCollection<T> queryResult) {
@@ -53,11 +53,6 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
protected final boolean isDirty;
/**
* True if this is a vanilla bean.
*/
protected final boolean vanilla;
/**
* The bean being persisted.
*/
@@ -113,8 +108,6 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
this.concurrencyMode = concurrencyMode;
this.loadedProps = updateProps;
this.changedProps = updateProps;
this.vanilla = true;
this.isDirty = true;
this.oldValues = bean;
if (bean instanceof EntityBean) {
@@ -137,41 +130,24 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
this.controller = beanDescriptor.getPersistController();
this.concurrencyMode = beanDescriptor.getConcurrencyMode();
if (bean instanceof EntityBean) {
this.intercept = ((EntityBean) bean)._ebean_getIntercept();
if (intercept.isReference()) {
// allowed to delete reference objects
// with no concurrency checking
this.concurrencyMode = ConcurrencyMode.NONE;
}
// this is ok to not use isNewOrDirty() as used for updates only
this.isDirty = intercept.isDirty();
if (!isDirty) {
this.changedProps = intercept.getChangedProps();
} else {
// merge changed properties on the bean with changed embedded beans
Set<String> beanChangedProps = intercept.getChangedProps();
Set<String> dirtyEmbedded = beanDescriptor.getDirtyEmbeddedProperties(bean);
this.changedProps = mergeChangedProperties(beanChangedProps, dirtyEmbedded);
}
this.loadedProps = intercept.getLoadedProps();
this.oldValues = (T) intercept.getOldValues();
this.vanilla = false;
} else {
// have to assume the vanilla bean is dirty
this.vanilla = true;
this.isDirty = true;
this.loadedProps = null;
this.changedProps = null;
this.intercept = null;
// degrade concurrency checking to none for vanilla bean
if (concurrencyMode.equals(ConcurrencyMode.ALL)) {
this.concurrencyMode = ConcurrencyMode.NONE;
}
this.intercept = ((EntityBean) bean)._ebean_getIntercept();
if (intercept.isReference()) {
// allowed to delete reference objects
// with no concurrency checking
this.concurrencyMode = ConcurrencyMode.NONE;
}
// this is ok to not use isNewOrDirty() as used for updates only
this.isDirty = intercept.isDirty();
if (!isDirty) {
this.changedProps = intercept.getChangedProps();
} else {
// merge changed properties on the bean with changed embedded beans
Set<String> beanChangedProps = intercept.getChangedProps();
Set<String> dirtyEmbedded = beanDescriptor.getDirtyEmbeddedProperties(bean);
this.changedProps = mergeChangedProperties(beanChangedProps, dirtyEmbedded);
}
this.loadedProps = intercept.getLoadedProps();
this.oldValues = (T) intercept.getOldValues();
}
/**
@@ -632,7 +608,7 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
* </p>
*/
public boolean isDynamicUpdateSql() {
return !vanilla && beanDescriptor.isUpdateChangesOnly() || (loadedProps != null);
return beanDescriptor.isUpdateChangesOnly() || (loadedProps != null);
}
/**
@@ -666,7 +642,9 @@ public class PersistRequestBean<T> extends PersistRequest implements BeanPersist
* update.
*/
public boolean hasChanged(BeanProperty prop) {
if (changedProps == null) {
return false;
}
return changedProps.contains(prop.getName());
}
@@ -1,92 +0,0 @@
package com.avaje.ebeaninternal.server.core;
import java.io.IOException;
import java.io.InputStream;
import java.io.ObjectInputStream;
import java.io.ObjectStreamClass;
import com.avaje.ebean.EbeanServer;
import com.avaje.ebean.bean.SerializeControl;
import com.avaje.ebeaninternal.api.ClassUtil;
import com.avaje.ebeaninternal.api.SpiEbeanServer;
import com.avaje.ebeaninternal.server.deploy.BeanDescriptor;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
/**
* Read an ObjectInputStream potentially containing "proxy" / "subclassed"
* entity objects.
* <p>
* This does not need to be used for "Enhanced" beans... but if you want to
* deserialise "proxy" / "subclassed" beans you need to use this
* ProxyBeanObjectInputStream. The reason is because it is required to resolve
* the class (The class with the $$EntityBean suffix). As this class is in
* another class loader typically as plain ObjectInputStream is unable to resolve
* the class - and hence we need to use this ProxyBeanObjectInputStream.
* </p>
*/
public class ProxyBeanObjectInputStream extends ObjectInputStream {
private final SpiEbeanServer ebeanServer;
/**
* Create with a given InputStream and EbeanServer.
* <p>
* The EbeanServer should be the one that created the 'proxy' classes that
* were serialised.
* </p>
*/
public ProxyBeanObjectInputStream(InputStream in, EbeanServer ebeanServer)
throws IOException {
super(in);
this.ebeanServer = (SpiEbeanServer) ebeanServer;
SerializeControl.setVanilla(false);
}
/**
* close and reset the serialization mode.
* <p>
* uses SerializeControl.resetToDefault().
* </p>
*/
public void close() throws IOException {
super.close();
SerializeControl.resetToDefault();
}
/**
* Resolve the generated Class potentially using reading the embedded
* MethodInfo.
*/
protected Class<?> resolveGenerated(ObjectStreamClass desc)
throws IOException, ClassNotFoundException {
String className = desc.getName();
String vanillaClassName = SubClassUtil.getSuperClassName(className);
Class<?> vanillaClass = ClassUtil.forName(vanillaClassName, this.getClass());
BeanDescriptor<?> d = ebeanServer.getBeanDescriptor(vanillaClass);
if (d == null) {
String msg = "Could not find BeanDescriptor for "+ vanillaClassName;
throw new IOException(msg);
} else {
return d.getFactoryType();
}
}
/**
* checks for generated subclasses and handles them appropriately.
*/
protected Class<?> resolveClass(ObjectStreamClass desc) throws IOException,
ClassNotFoundException {
String className = desc.getName();
if (SubClassUtil.isSubClass(className)) {
return resolveGenerated(desc);
}
return super.resolveClass(desc);
}
}
@@ -87,9 +87,9 @@ public class XmlConfigLoader {
if (classPath.isDirectory()) {
checkDir(searchFor, xmlList, classPath);
} else if (classPath.getName().endsWith(".jar")) {
} else if (classPath.getName().endsWith(".jar") || classPath.getName().endsWith(".war")) {
checkJar(searchFor, xmlList, classPath);
} else {
// this is not expected
String msg = "Not a Jar or Directory? " + classPath.getAbsolutePath();
@@ -936,7 +936,7 @@ public class BeanDescriptor<T> {
}
}
public boolean cacheLoadMany(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId, Boolean readOnly, boolean vanilla) {
public boolean cacheLoadMany(BeanPropertyAssocMany<?> many, BeanCollection<?> bc, Object parentId, Boolean readOnly) {
CachedManyIds ids = cacheGetCachedManyIds(parentId, many.getName());
if (ids == null) {
@@ -953,7 +953,7 @@ public class BeanDescriptor<T> {
bc.checkEmptyLazyLoad();
for (int i = 0; i < idList.size(); i++) {
Object id = idList.get(i);
Object refBean = targetDescriptor.createReference(vanilla, readOnly, id, null);
Object refBean = targetDescriptor.createReference(readOnly, id, null);
EntityBeanIntercept refEbi = ((EntityBean) refBean)._ebean_getIntercept();
many.add(bc, refBean);
@@ -999,22 +999,22 @@ public class BeanDescriptor<T> {
* Return a bean from the bean cache.
*/
@SuppressWarnings("unchecked")
public T cacheGetBean(Object id, boolean vanilla, Boolean readOnly) {
public T cacheGetBean(Object id, Boolean readOnly) {
CachedBeanData d = (CachedBeanData) getBeanCache().get(id);
if (d == null) {
return null;
}
if (cacheSharableBeans && !vanilla && !Boolean.FALSE.equals(readOnly)) {
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
Object bean = d.getSharableBean();
if (bean != null) {
return (T) bean;
}
}
T bean = (T) createBean(vanilla);
T bean = (T) createBean();
convertSetId(id, bean);
if (!vanilla && Boolean.TRUE.equals(readOnly)) {
if (Boolean.TRUE.equals(readOnly)) {
((EntityBean) bean)._ebean_getIntercept().setReadOnly(true);
}
@@ -1306,20 +1306,10 @@ public class BeanDescriptor<T> {
}
/**
* Create an EntityBean or "Vanilla" bean depending on the flag.
* Create an EntityBean.
*/
public Object createBean(boolean vanillaMode) {
return vanillaMode ? createVanillaBean() : createEntityBean();
}
/**
* Create a plain vanilla object.
* <p>
* Used for EmbeddedId Bean construction.
* </p>
*/
public Object createVanillaBean() {
return beanReflect.createVanillaBean();
public Object createBean() {
return createEntityBean();
}
/**
@@ -1341,9 +1331,9 @@ public class BeanDescriptor<T> {
* Create a reference bean based on the id.
*/
@SuppressWarnings("unchecked")
public T createReference(boolean vanillaMode, Boolean readOnly, Object id, Object parent) {
public T createReference(Boolean readOnly, Object id, Object parent) {
if (cacheSharableBeans && !vanillaMode && !Boolean.FALSE.equals(readOnly)) {
if (cacheSharableBeans && !Boolean.FALSE.equals(readOnly)) {
CachedBeanData d = (CachedBeanData) getBeanCache().get(id);
if (d != null) {
Object shareableBean = d.getSharableBean();
@@ -1353,26 +1343,24 @@ public class BeanDescriptor<T> {
}
}
try {
Object bean = createBean(vanillaMode);
Object bean = createBean();
convertSetId(id, bean);
if (!vanillaMode) {
EntityBean eb = (EntityBean) bean;
EntityBean eb = (EntityBean) bean;
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoaderByServerName(ebeanServer.getName());
EntityBeanIntercept ebi = eb._ebean_getIntercept();
ebi.setBeanLoaderByServerName(ebeanServer.getName());
if (parent != null) {
// Special case for a OneToOne ... parent
// needs to be added to context prior to query
ebi.setParentBean(parent);
}
// Note: not creating proxies for many's...
ebi.setReference();
if (parent != null) {
// Special case for a OneToOne ... parent
// needs to be added to context prior to query
ebi.setParentBean(parent);
}
// Note: not creating proxies for many's...
ebi.setReference();
return (T) bean;
} catch (Exception ex) {
@@ -2209,17 +2197,6 @@ public class BeanDescriptor<T> {
return propertyFirstVersion;
}
/**
* Return true if this an Insert (rather than Update) on a non-enhanced bean.
*/
public boolean isVanillaInsert(Object bean) {
if (propertyFirstVersion == null) {
return true;
}
Object versionValue = propertyFirstVersion.getValue(bean);
return DmlUtil.isNullOrZero(versionValue);
}
/**
* Return true if this is an Update (rather than insert) given that the bean
* is involved in a stateless update.
@@ -14,6 +14,9 @@ import java.util.Set;
import javax.persistence.PersistenceException;
import javax.sql.DataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import com.avaje.ebean.BackgroundExecutor;
import com.avaje.ebean.RawSql;
import com.avaje.ebean.RawSqlBuilder;
@@ -60,11 +63,7 @@ import com.avaje.ebeaninternal.server.reflect.BeanReflectFactory;
import com.avaje.ebeaninternal.server.reflect.BeanReflectGetter;
import com.avaje.ebeaninternal.server.reflect.BeanReflectSetter;
import com.avaje.ebeaninternal.server.reflect.EnhanceBeanReflectFactory;
import com.avaje.ebeaninternal.server.subclass.SubClassManager;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
import com.avaje.ebeaninternal.server.type.TypeManager;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates BeanDescriptors.
@@ -98,8 +97,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final BeanQueryAdapterManager beanQueryAdapterManager;
private final SubClassManager subClassManager;
private final NamingConvention namingConvention;
private final DeployCreateProperties createProperties;
@@ -153,8 +150,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
private final XmlConfig xmlConfig;
private final boolean allowSubclassing;
/**
* Create for a given database dbConfig.
*/
@@ -172,7 +167,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.bootupClasses = config.getBootupClasses();
this.createProperties = config.getDeployCreateProperties();
this.subClassManager = config.getSubClassManager();
this.typeManager = config.getTypeManager();
this.namingConvention = config.getServerConfig().getNamingConvention();
this.dbIdentity = config.getDatabasePlatform().getDbIdentity();
@@ -192,7 +186,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
this.reflectFactory = createReflectionFactory();
this.transientProperties = new TransientProperties();
this.allowSubclassing = config.getServerConfig().isAllowSubclassing();
}
public BeanDescriptor<?> getBeanDescriptorById(String descriptorId) {
@@ -201,17 +194,11 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
@SuppressWarnings("unchecked")
public <T> BeanDescriptor<T> getBeanDescriptor(Class<T> entityType) {
// remove $$EntityBean stuff
String className = SubClassUtil.getSuperClassName(entityType.getName());
return (BeanDescriptor<T>) descMap.get(className);
return (BeanDescriptor<T>) descMap.get(entityType.getName());
}
@SuppressWarnings("unchecked")
public <T> BeanDescriptor<T> getBeanDescriptor(String entityClassName) {
// remove $$EntityBean stuff
entityClassName = SubClassUtil.getSuperClassName(entityClassName);
return (BeanDescriptor<T>) descMap.get(entityClassName);
}
@@ -440,8 +427,6 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
}
public BeanManager<?> getBeanManager(String beanClassName) {
beanClassName = SubClassUtil.getSuperClassName(beanClassName);
return beanManagerMap.get(beanClassName);
}
@@ -1462,13 +1447,7 @@ public class BeanDescriptorManager implements BeanDescriptorMap {
desc.setFactoryType(beanClass);
} else {
if (!allowSubclassing) {
throw new PersistenceException("This configuration does not allow entity subclassing [" + beanClass + "]");
}
subclassClassCount++;
Class<?> subClass = subClassManager.resolve(beanClass.getName());
desc.setFactoryType(subClass);
subclassedEntities.add(desc.getName());
throw new PersistenceException("Entity type "+beanClass+" is not an enhanced entity bean. Subclassing is not longer supported in Ebean");
}
}
@@ -382,13 +382,12 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
if (embedded){
throw new RuntimeException();
} else {
boolean vanillaMode = false;
T ref = targetDescriptor.createReference(vanillaMode, Boolean.FALSE, cacheData, null);
T ref = targetDescriptor.createReference(Boolean.FALSE, cacheData, null);
setValue(bean, ref);
if (oldValues != null){
setValue(oldValues, ref);
}
if (readOnly && !vanillaMode){
if (readOnly){
((EntityBean)ref)._ebean_intercept().setReadOnly(true);
}
}
@@ -438,11 +437,11 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
/**
* Create a vanilla bean of the target type to be used as an embeddedId
* Create a bean of the target type to be used as an embeddedId
* value.
*/
public Object createEmbeddedId() {
return getTargetDescriptor().createVanillaBean();
return getTargetDescriptor().createBean();
}
/**
@@ -736,16 +735,14 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// parent always null for this case (but here to document)
Object parent = null;
boolean vanillaMode = ctx.isVanillaMode();
//ReferenceOptions options = ctx.getReferenceOptionsFor(beanProp);
Boolean readOnly = ctx.isReadOnly();
Object ref;
if (targetInheritInfo != null) {
// for inheritance hierarchy create the correct type for this row...
ref = rowDescriptor.createReference(vanillaMode, readOnly, id, parent);
ref = rowDescriptor.createReference(readOnly, id, parent);
} else {
ref = targetDescriptor.createReference(vanillaMode, readOnly, id, parent);
ref = targetDescriptor.createReference(readOnly, id, parent);
}
Object existingBean = ctx.getPersistenceContext().putIfAbsent(id, ref);
@@ -755,7 +752,7 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
// loaded a matching bean so we will use that instead.
ref = existingBean;
} else if (!vanillaMode){
} else {
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
if (Boolean.TRUE.equals(ctx.isReadOnly())){
ebi.setReadOnly(true);
@@ -831,18 +828,15 @@ public class BeanPropertyAssocOne<T> extends BeanPropertyAssoc<T> {
if (existing != null) {
return existing;
}
boolean vanillaMode = ctx.isVanillaMode();
Object parent = null;
Object ref = targetDescriptor.createReference(vanillaMode, ctx.isReadOnly(), id, parent);
Object ref = targetDescriptor.createReference(ctx.isReadOnly(), id, parent);
if (!vanillaMode){
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
if (Boolean.TRUE.equals(ctx.isReadOnly())) {
ebi.setReadOnly(true);
}
persistCtx.put(id, ref);
ctx.register(name, ebi);
EntityBeanIntercept ebi = ((EntityBean) ref)._ebean_getIntercept();
if (Boolean.TRUE.equals(ctx.isReadOnly())) {
ebi.setReadOnly(true);
}
persistCtx.put(id, ref);
ctx.register(name, ebi);
return ref;
}
@@ -13,35 +13,25 @@ import com.avaje.ebeaninternal.server.type.DataReader;
*/
public interface DbReadContext {
/**
* Return the state of the object graph.
*/
public Boolean isReadOnly();
/**
* Return the state of the object graph.
*/
public Boolean isReadOnly();
/**
* Propagate the state to the bean.
*/
public void propagateState(Object e);
/**
* Propagate the state to the bean.
*/
public void propagateState(Object e);
/**
* Return the DataReader.
*/
public DataReader getDataReader();
/**
* Return true if vanilla objects should be returned.
*/
public boolean isVanillaMode();
/**
* Return the DataReader.
*/
public DataReader getDataReader();
/**
* Return true if the query is using supplied SQL rather than generated SQL.
*/
public boolean isRawSql();
// /**
// * Return the reference options for a given bean property.
// */
// public ReferenceOptions getReferenceOptionsFor(BeanPropertyAssocOne<?> beanProperty);
/**
* Set the JoinNode - used by proxy/reference beans for profiling.
@@ -10,7 +10,6 @@ import com.avaje.ebeaninternal.server.core.InternString;
import com.avaje.ebeaninternal.server.deploy.id.IdBinder;
import com.avaje.ebeaninternal.server.deploy.parse.DeployInheritInfo;
import com.avaje.ebeaninternal.server.query.SqlTreeProperties;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
/**
* Represents a node in the Inheritance tree. Holds information regarding Super
@@ -224,8 +223,8 @@ public class InheritInfo {
/**
* Create an EntityBean for this type.
*/
public Object createBean(boolean vanillaMode) {
return descriptor.createBean(vanillaMode);
public Object createBean() {
return descriptor.createBean();
}
/**
@@ -284,8 +283,7 @@ public class InheritInfo {
* Return the InheritInfo for the given bean type.
*/
private InheritInfo getTypeByClass(Class<?> beanType) {
String clsName = SubClassUtil.getSuperClassName(beanType.getName());
return typeMap.get(clsName);
return typeMap.get(beanType.getName());
}
private void registerWithRoot(InheritInfo info) {
@@ -293,8 +291,7 @@ public class InheritInfo {
String stringDiscValue = info.getDiscriminatorStringValue();
discMap.put(stringDiscValue, info);
}
String clsName = SubClassUtil.getSuperClassName(info.getType().getName());
typeMap.put(clsName, info);
typeMap.put(info.getType().getName(), info);
}
/**
@@ -238,7 +238,7 @@ public final class IdBinderEmbedded implements IdBinder {
String msg = "Failed to split ["+idTermValue+"] using | for id.";
throw new PersistenceException(msg);
}
Object embId = idDesc.createVanillaBean();
Object embId = idDesc.createBean();
for (int i = 0; i < props.length; i++) {
Object v = props[i].getScalarType().parse(split[i]);
props[i].setValue(embId, v);
@@ -299,7 +299,7 @@ public final class IdBinderEmbedded implements IdBinder {
public Object readData(DataInput dataInput) throws IOException {
Object embId = idDesc.createVanillaBean();
Object embId = idDesc.createBean();
boolean notNull = true;
for (int i = 0; i < props.length; i++) {
@@ -332,7 +332,7 @@ public final class IdBinderEmbedded implements IdBinder {
public Object read(DbReadContext ctx) throws SQLException {
Object embId = idDesc.createVanillaBean();
Object embId = idDesc.createBean();
boolean notNull = true;
for (int i = 0; i < props.length; i++) {
@@ -253,8 +253,6 @@ public class PooledConnection extends ConnectionDelegator
*/
public void closeConnectionFully(boolean logErrors) {
//pool.removeConnection(this);
String msg = "Closing Connection[" + getName() + "]" + " psReuse[" + pstmtHitCounter
+ "] psCreate[" + pstmtMissCounter + "] psSize[" + pstmtCache.size() + "]";
@@ -262,14 +260,12 @@ public class PooledConnection extends ConnectionDelegator
try {
if (connection.isClosed()) {
msg = "Closing Connection[" + getName() + "] that is already closed?";
logger.error(msg);
logger.warn("Closing Connection[" + getName() + "] that is already closed?");
return;
}
} catch (SQLException ex) {
if (logErrors) {
msg = "Error when fully closing connection [" + getName() + "]";
logger.error(msg, ex);
logger.error("Error when fully closing connection [" + getName() + "]", ex);
}
}
@@ -288,11 +284,9 @@ public class PooledConnection extends ConnectionDelegator
try {
connection.close();
} catch (SQLException ex) {
if (logErrors) {
msg = "Error when fully closing connection [" + getName() + "]";
logger.error(msg, ex);
logger.error("Error when fully closing connection [" + getName() + "]", ex);
}
}
}
@@ -338,8 +338,7 @@ public class PooledConnectionQueue {
closeFreeConnections(true);
if (!busyList.isEmpty()) {
String msg = "A potential connection leak was detected. Busy connections: "+ busyList.size();
logger.warn(msg);
logger.warn("A potential connection leak was detected. Busy connections: "+ busyList.size());
dumpBusyConnectionInformation();
closeBusyConnections(0);
@@ -126,7 +126,7 @@ public class DLoadManyContext implements LoadManyContext, BeanCollectionLoader {
Object ownerBean = bc.getOwnerBean();
BeanDescriptor<? extends Object> parentDesc = desc.getBeanDescriptor(ownerBean.getClass());
Object parentId = parentDesc.getId(ownerBean);
if (parentDesc.cacheLoadMany(property, bc, parentId, parent.isReadOnly(), false)) {
if (parentDesc.cacheLoadMany(property, bc, parentId, parent.isReadOnly())) {
// we loaded the bean from cache
weakList.removeEntry(position);
return;
@@ -70,8 +70,8 @@ public final class DefaultPersister implements Persister {
private final BeanDescriptorManager beanDescriptorManager;
private final boolean defaultUpdateNullProperties;
private final boolean defaultDeleteMissingChildren;
// private final boolean defaultUpdateNullProperties;
// private final boolean defaultDeleteMissingChildren;
public DefaultPersister(SpiEbeanServer server, Binder binder, BeanDescriptorManager descMgr, PstmtBatch pstmtBatch) {
@@ -79,8 +79,8 @@ public final class DefaultPersister implements Persister {
this.beanDescriptorManager = descMgr;
this.persistExecute = new DefaultPersistExecute(binder, pstmtBatch);
this.defaultUpdateNullProperties = server.isDefaultUpdateNullProperties();
this.defaultDeleteMissingChildren = server.isDefaultDeleteMissingChildren();
// this.defaultUpdateNullProperties = server.isDefaultUpdateNullProperties();
// this.defaultDeleteMissingChildren = server.isDefaultDeleteMissingChildren();
}
/**
@@ -271,8 +271,7 @@ public final class DefaultPersister implements Persister {
}
if (bean instanceof EntityBean == false) {
saveVanillaRecurse(bean, t, parentBean);
return;
throw new IllegalArgumentException("This bean is of type ["+bean.getClass()+"] is not enhanced?");
}
PersistRequestBean<?> req = createRequest(bean, t, parentBean);
@@ -313,42 +312,6 @@ public final class DefaultPersister implements Persister {
}
}
/**
* Determine if this is an Insert or update for the 'vanilla' bean.
*/
private void saveVanillaRecurse(Object bean, Transaction t, Object parentBean) {
BeanManager<?> mgr = getBeanManager(bean);
if (mgr == null) {
throw new RuntimeException("No Mgr found for " + bean + " " + bean.getClass());
}
// use the version property to determine insert or update
if (mgr.getBeanDescriptor().isVanillaInsert(bean)) {
saveVanillaInsert(bean, t, parentBean, mgr);
} else {
// update non-null properties (no partial object knowledge with vanilla bean)
forceUpdateStateless(bean, t, parentBean, mgr, null, defaultDeleteMissingChildren, defaultUpdateNullProperties);
}
}
/**
* Perform insert on non-enhanced bean (effectively same as enhanced bean).
*/
private void saveVanillaInsert(Object bean, Transaction t, Object parentBean, BeanManager<?> mgr) {
PersistRequestBean<?> req = createRequest(bean, t, parentBean, mgr);
try {
req.initTransIfRequired();
insert(req);
req.commitTransIfRequired();
} catch (RuntimeException ex) {
req.rollbackTransIfRequired();
throw ex;
}
}
/**
* Insert the bean.
*/
@@ -72,8 +72,8 @@ public class MetaFactory {
embeddedFact.create(allList, desc, DmlMode.WHERE, false);
assocOneFact.create(allList, desc, DmlMode.WHERE);
Bindable setBindable = new BindableList(setList);
Bindable allBindable = new BindableList(allList);
BindableList setBindable = new BindableList(setList);
BindableList allBindable = new BindableList(allList);
return new UpdateMeta(emptyStringAsNull, desc, setBindable, id, ver, allBindable);
}
@@ -25,7 +25,7 @@ public final class UpdateMeta {
private final String sqlNone;
private final Bindable set;
private final BindableList set;
private final BindableId id;
private final Bindable version;
private final Bindable all;
@@ -37,7 +37,7 @@ public final class UpdateMeta {
private final boolean emptyStringAsNull;
public UpdateMeta(boolean emptyStringAsNull, BeanDescriptor<?> desc, Bindable set, BindableId id, Bindable version, Bindable all) {
public UpdateMeta(boolean emptyStringAsNull, BeanDescriptor<?> desc, BindableList set, BindableId id, Bindable version, Bindable all) {
this.emptyStringAsNull = emptyStringAsNull;
this.tableName = desc.getBaseTable();
this.set = set;
@@ -154,7 +154,12 @@ public final class UpdateMeta {
// build a bindableList that only contains the changed properties
List<Bindable> list = new ArrayList<Bindable>();
set.addChanged(persistRequest, list);
if (updatedProps == null) {
// update all the properties
set.addAll(list);
} else {
set.addChanged(persistRequest, list);
}
BindableList bindableList = new BindableList(list);
// build the SQL for this update statement
@@ -17,6 +17,12 @@ public class BindableList implements Bindable {
items = list.toArray(new Bindable[list.size()]);
}
public void addAll(List<Bindable> list) {
for (int i = 0; i < items.length; i++) {
list.add(items[i]);
}
}
public void addChanged(PersistRequestBean<?> request, List<Bindable> list) {
for (int i = 0; i < items.length; i++) {
items[i].addChanged(request, list);
@@ -317,13 +317,6 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
return queryMode;
}
/**
* Return true if we want to return vanilla (not enhanced) objects.
*/
public boolean isVanillaMode() {
return request.isVanillaMode();
}
public CQueryPredicates getPredicates() {
return predicates;
}
@@ -586,11 +579,11 @@ public class CQuery<T> implements DbReadContext, CancelableQuery {
currentDetailCollection = manyPropertyEl.elGetValue(loadedBean);
} else {
// create a new collection to populate and assign to the bean
currentDetailCollection = manyProperty.createEmpty(request.isVanillaMode());
currentDetailCollection = manyProperty.createEmpty(false);
manyPropertyEl.elSetValue(loadedBean, currentDetailCollection, false, false);
}
if (filterMany != null && !request.isVanillaMode()) {
if (filterMany != null) {
// remember the for use with a refresh
((BeanCollection<?>) currentDetailCollection).setFilterMany(filterMany);
}
@@ -263,10 +263,6 @@ public class CQueryFetchIds {
return dataReader;
}
public boolean isVanillaMode() {
return false;
}
public Boolean isReadOnly() {
return Boolean.FALSE;
}
@@ -196,7 +196,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
localType = null;
localDesc = desc;
} else {
localBean = localInfo.createBean(ctx.isVanillaMode());
localBean = localInfo.createBean();
localType = localInfo.getType();
localIdBinder = localInfo.getIdBinder();
localDesc = localInfo.getBeanDescriptor();
@@ -205,7 +205,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
} else {
localType = null;
localDesc = desc;
localBean = desc.createBean(ctx.isVanillaMode());
localBean = desc.createBean();
localIdBinder = idBinder;
}
@@ -295,11 +295,7 @@ public class SqlTreeNodeBean implements SqlTreeNode {
} else if (localBean != null) {
ctx.setCurrentPrefix(prefix, pathMap);
if (!ctx.isVanillaMode()) {
// only create lazy loading collection proxies
// when not in vanilla mode
createListProxies(localDesc, ctx, localBean);
}
createListProxies(localDesc, ctx, localBean);
localDesc.postLoad(localBean, includedProps);
@@ -128,11 +128,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
private String lazyLoadProperty;
private String lazyLoadManyPath;
/**
* Set to true when we want to return vanilla (not enhanced) objects.
*/
private Boolean vanillaMode;
/**
* Set to true if you want a DISTINCT query.
@@ -447,7 +442,6 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
copy.timeout = timeout;
copy.mapKey = mapKey;
copy.id = id;
copy.vanillaMode = vanillaMode;
copy.loadBeanCache = loadBeanCache;
copy.useBeanCache = useBeanCache;
copy.useQueryCache = useQueryCache;
@@ -776,20 +770,7 @@ public class DefaultOrmQuery<T> implements SpiQuery<T> {
return maxRows > 0 || firstRow > 0;
}
public boolean isVanillaMode(boolean defaultVanillaMode) {
if (vanillaMode != null) {
return vanillaMode.booleanValue();
}
return defaultVanillaMode;
}
public DefaultOrmQuery<T> setVanillaMode(boolean vanillaMode) {
this.vanillaMode = vanillaMode;
return this;
}
public Boolean isReadOnly() {
public Boolean isReadOnly() {
return readOnly;
}
@@ -1,16 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
/**
* The suffix used build a generated EntityBean class.
* <p>
* Note that the server name can be appended after
* </p>
*/
public interface GenSuffix {
/**
* The suffix added to the super class name.
*/
public static final String SUFFIX = "$$EntityBean";
}
@@ -1,37 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
import java.util.List;
import com.avaje.ebean.enhance.agent.ClassMeta;
import com.avaje.ebean.enhance.agent.EnhanceConstants;
import com.avaje.ebean.enhance.agent.FieldMeta;
import com.avaje.ebean.enhance.asm.ClassVisitor;
import com.avaje.ebean.enhance.asm.Opcodes;
public class GetterSetterMethods implements Opcodes, EnhanceConstants {
/**
* Add getters and setters to for interception.
* <p>
* Note that we don't intercept Id properties and we don't intercept setters
* on 'OneToMany' properties etc.
* </p>
*/
public static void add(ClassVisitor cv, ClassMeta classMeta) {
List<FieldMeta> localFields = classMeta.getLocalFields();
for (int x = 0; x < localFields.size(); x++) {
FieldMeta fieldMeta = localFields.get(x);
fieldMeta.addPublicGetSetMethods(cv, classMeta, true);
}
List<FieldMeta> inheritedFields = classMeta.getInheritedFields();
for (int i = 0; i < inheritedFields.size(); i++) {
FieldMeta fieldMeta = inheritedFields.get(i);
// for persistent inherited fields add a
// getter and setter to enable interception
fieldMeta.addPublicGetSetMethods(cv, classMeta, false);
}
}
}
@@ -1,45 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
import com.avaje.ebean.enhance.agent.ClassMeta;
import com.avaje.ebean.enhance.agent.EnhanceConstants;
import com.avaje.ebean.enhance.asm.ClassVisitor;
import com.avaje.ebean.enhance.asm.Label;
import com.avaje.ebean.enhance.asm.MethodVisitor;
import com.avaje.ebean.enhance.asm.Opcodes;
/**
* Add a writeReplace method to support optional serialization to vanilla beans.
*
* <pre><code>
* private Object writeReplace() throws ObjectStreamException {
* return ebeanIntercept.writeReplaceIntercept();
* }
* </code></pre>
*/
public class MethodWriteReplace implements Opcodes, EnhanceConstants {
/**
* Add a writeReplace() method.
*/
public static void add(ClassVisitor cv, ClassMeta classMeta) {
MethodVisitor mv = cv.visitMethod(ACC_PRIVATE, "writeReplace", "()Ljava/lang/Object;",
null, new String[] { "java/io/ObjectStreamException" });
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(1, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitFieldInsn(GETFIELD, classMeta.getClassName(), INTERCEPT_FIELD, L_INTERCEPT);
mv.visitMethodInsn(INVOKEVIRTUAL, C_INTERCEPT, "writeReplaceIntercept","()Ljava/lang/Object;");
mv.visitInsn(ARETURN);
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLocalVariable("this", "L"+classMeta.getClassName()+";", null, l0, l1, 0);
mv.visitMaxs(0, 0);
mv.visitEnd();
}
}
@@ -1,265 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
import com.avaje.ebean.enhance.agent.AlreadyEnhancedException;
import com.avaje.ebean.enhance.agent.ClassMeta;
import com.avaje.ebean.enhance.agent.EnhanceConstants;
import com.avaje.ebean.enhance.agent.EnhanceContext;
import com.avaje.ebean.enhance.agent.IndexFieldWeaver;
import com.avaje.ebean.enhance.agent.InterceptField;
import com.avaje.ebean.enhance.agent.MarkerField;
import com.avaje.ebean.enhance.agent.MethodEquals;
import com.avaje.ebean.enhance.agent.MethodIsEmbeddedNewOrDirty;
import com.avaje.ebean.enhance.agent.MethodNewInstance;
import com.avaje.ebean.enhance.agent.MethodPropertyChangeListener;
import com.avaje.ebean.enhance.agent.MethodSetEmbeddedLoaded;
import com.avaje.ebean.enhance.agent.NoEnhancementRequiredException;
import com.avaje.ebean.enhance.agent.VisitMethodParams;
import com.avaje.ebean.enhance.asm.AnnotationVisitor;
import com.avaje.ebean.enhance.asm.ClassAdapter;
import com.avaje.ebean.enhance.asm.ClassVisitor;
import com.avaje.ebean.enhance.asm.FieldVisitor;
import com.avaje.ebean.enhance.asm.MethodVisitor;
import com.avaje.ebean.enhance.asm.Opcodes;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
public class SubClassClassAdpater extends ClassAdapter implements EnhanceConstants {
private static final Logger logger = LoggerFactory.getLogger(SubClassClassAdpater.class);
final EnhanceContext enhanceContext;
final ClassLoader classLoader;
final ClassMeta classMeta;
final String subClassSuffix;
boolean firstMethod = true;
public SubClassClassAdpater(String subClassSuffix, ClassVisitor cv, ClassLoader classLoader, EnhanceContext context) {
super(cv);
this.subClassSuffix = subClassSuffix;
this.classLoader = classLoader;
this.enhanceContext = context;
this.classMeta = context.createClassMeta();
}
public boolean isLog(int level){
return classMeta.isLog(level);
}
public void log(String msg){
classMeta.log(msg);
}
/**
* Create the class definition replacing the className and super class.
*/
public void visit(int version, int access, String name, String signature, String superName,
String[] interfaces) {
// Note: interfaces can be an empty array but not null
int n = 1 + interfaces.length;
String[] c = new String[n];
for (int i = 0; i < interfaces.length; i++) {
c[i] = interfaces[i];
if (c[i].equals(C_ENTITYBEAN)) {
throw new AlreadyEnhancedException(name);
}
if (c[i].equals(C_SCALAOBJECT)) {
classMeta.setScalaInterface(true);
}
if (c[i].equals(C_GROOVYOBJECT)) {
classMeta.setGroovyInterface(true);
}
}
// Add the EntityBean interface
c[c.length - 1] = C_ENTITYBEAN;
if (!superName.equals("java/lang/Object")){
ClassMeta superMeta = enhanceContext.getSuperMeta(superName, classLoader);
if (superMeta != null) {
classMeta.setSuperMeta(superMeta);
if (classMeta.isLog(2)){
classMeta.log("entity inheritance "+superMeta.getDescription());
}
}
}
// adjust the superName and name as we
// are actually creating a subclass of
// the class being visited
superName = name;
name = name+subClassSuffix;
classMeta.setClassName(name, superName);
super.visit(version, access, name, signature, superName, c);
}
@Override
public AnnotationVisitor visitAnnotation(String desc, boolean visible) {
classMeta.addClassAnnotation(desc);
return super.visitAnnotation(desc, visible);
}
/**
* The ebeanIntercept field is added once but thats all. Note the other
* fields are defined in the superclass.
*/
public FieldVisitor visitField(int access, String name, String desc, String signature,
Object value) {
if ((access & Opcodes.ACC_STATIC) != 0) {
// no interception of static fields
if (isLog(2)){
log("Skip intercepting static field "+name);
}
return null;
}
if ((access & Opcodes.ACC_TRANSIENT) != 0) {
// no interception of transient fields
if (classMeta.isLog(2)){
classMeta.log("Skip intercepting transient field "+name);
}
return null;
}
// read the field and associated annotations...
if (classMeta.isLog(5)){
classMeta.log(" ... reading field:"+name+" desc:"+desc);
}
return classMeta.createLocalFieldVisitor(name, desc);
}
/**
* Replace the method code with calls to super. Add the intercept code as
* required.
*/
public MethodVisitor visitMethod(int access, String name, String desc, String signature,
String[] exceptions) {
if (firstMethod){
if (!classMeta.isEntityEnhancementRequired()) {
// skip the rest of the visiting etc
throw new NoEnhancementRequiredException();
}
// always add the marker field on every enhanced class
String marker = MarkerField.addField(cv, classMeta.getClassName());
if (isLog(4)){
log("... add marker field \""+marker+"\"");
log("... add intercept and identity fields");
}
// always add these fields for subclass generation
InterceptField.addField(cv, enhanceContext.isTransientInternalFields());
MethodEquals.addIdentityField(cv);
firstMethod = false;
}
VisitMethodParams params = new VisitMethodParams(cv, access, name, desc, signature, exceptions);
if (isDefaultConstructor(access, name, desc, signature, exceptions)){
SubClassConstructor.add(params, classMeta);
return null;
}
if (isSpecialMethod(access, name, desc)) {
return null;
}
// register the method so that we can check
// if it exists when GetterSetterMethods.add()
// is called. May not exist on read only type
// entity beans such as the internal meta beans.
classMeta.addExistingSuperMethod(name, desc);
return null;
}
/**
* Add methods to get and set the entityBeanIntercept. Also add the
* writeReplace method to control serialisation.
*/
public void visitEnd() {
if (!classMeta.isEntityEnhancementRequired()){
throw new NoEnhancementRequiredException();
}
if (!classMeta.hasDefaultConstructor()){
if (isLog(2)){
log("... adding default constructor");
}
SubClassConstructor.addDefault(cv, classMeta);
}
MarkerField.addGetMarker(cv, classMeta.getClassName());
// Add the _ebean_getIntercept() _ebean_setIntercept() methods
InterceptField.addGetterSetter(cv, classMeta.getClassName());
// Add add/removePropertyChangeListener methods
MethodPropertyChangeListener.addMethod(cv, classMeta);
// Add getter and setter methods for both local
// and inherited properties
GetterSetterMethods.add(cv, classMeta);
// Add extra methods such as getField(index) etc
IndexFieldWeaver.addMethods(cv, classMeta);
MethodSetEmbeddedLoaded.addMethod(cv, classMeta);
MethodIsEmbeddedNewOrDirty.addMethod(cv, classMeta);
MethodNewInstance.addMethod(cv, classMeta);
// add a writeReplace method to control serialisation
MethodWriteReplace.add(cv, classMeta);
// register with the context
enhanceContext.addClassMeta(classMeta);
super.visitEnd();
}
/**
* Return true if this is the default (no arg) constructor.
*/
private boolean isDefaultConstructor(int access, String name, String desc, String signature,
String[] exceptions){
if (name.equals("<init>") && desc.equals("()V")) {
classMeta.setHasDefaultConstructor(true);
return true;
}
return false;
}
/**
* Take note of hashcode and equals.
*/
private boolean isSpecialMethod(int access, String name, String desc) {
if (name.equals("hashCode") && desc.equals("()I")) {
classMeta.setHasEqualsOrHashcode(true);
return true;
}
if (name.equals("equals") && desc.equals("(Ljava/lang/Object;)Z")) {
classMeta.setHasEqualsOrHashcode(true);
return true;
}
return false;
}
}
@@ -1,56 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
import com.avaje.ebean.enhance.agent.ClassMeta;
import com.avaje.ebean.enhance.agent.EnhanceConstants;
import com.avaje.ebean.enhance.agent.VisitMethodParams;
import com.avaje.ebean.enhance.asm.ClassVisitor;
import com.avaje.ebean.enhance.asm.Label;
import com.avaje.ebean.enhance.asm.MethodVisitor;
import com.avaje.ebean.enhance.asm.Opcodes;
public class SubClassConstructor implements Opcodes, EnhanceConstants{
public static void addDefault(ClassVisitor cv, ClassMeta meta) {
VisitMethodParams params = new VisitMethodParams(cv, Opcodes.ACC_PUBLIC, "<init>", "()V", null, null);
add(params, meta);
}
public static void add(VisitMethodParams params, ClassMeta meta) {
String className = meta.getClassName();
String superClassName = meta.getSuperClassName();
if (params.forcePublic()){
if (meta.isLog(0)){
meta.log(" forcing ACC_PUBLIC ");
}
}
MethodVisitor mv = params.visitMethod();
//mv = cw.visitMethod(ACC_PUBLIC, "<init>", "()V", null, null);
mv.visitCode();
Label l0 = new Label();
mv.visitLabel(l0);
mv.visitLineNumber(17, l0);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, superClassName, "<init>", "()V");
Label l1 = new Label();
mv.visitLabel(l1);
mv.visitLineNumber(18, l1);
mv.visitVarInsn(ALOAD, 0);
mv.visitTypeInsn(NEW, C_INTERCEPT);
mv.visitInsn(DUP);
mv.visitVarInsn(ALOAD, 0);
mv.visitMethodInsn(INVOKESPECIAL, C_INTERCEPT, "<init>", "(Ljava/lang/Object;)V");
mv.visitFieldInsn(PUTFIELD, className, INTERCEPT_FIELD, L_INTERCEPT);
Label l2 = new Label();
mv.visitLabel(l2);
mv.visitLineNumber(19, l2);
mv.visitInsn(RETURN);
Label l3 = new Label();
mv.visitLabel(l3);
mv.visitLocalVariable("this", "L"+className+";", null, l0, l3, 0);
mv.visitMaxs(4, 1);
mv.visitEnd();
}
}
@@ -1,108 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
import java.io.IOException;
import java.io.InputStream;
import com.avaje.ebean.enhance.agent.ClassPathClassBytesReader;
import com.avaje.ebean.enhance.agent.EnhanceConstants;
import com.avaje.ebean.enhance.agent.EnhanceContext;
import com.avaje.ebean.enhance.asm.ClassReader;
import com.avaje.ebean.enhance.asm.ClassWriter;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates Classes that implement EntityBean for a given normal bean Class.
* <p>
* This dynamically creates a subclass of a normal bean class. The subclass has
* method interception to handle the lazy loading of references and old values
* creation.
* </p>
*/
public class SubClassFactory extends ClassLoader implements EnhanceConstants, GenSuffix {
private static final Logger logger = LoggerFactory.getLogger(SubClassFactory.class);
private static final int CLASS_WRITER_FLAGS = ClassWriter.COMPUTE_FRAMES + ClassWriter.COMPUTE_MAXS;
private final EnhanceContext enhanceContext;
private final ClassLoader parentClassLoader;
/**
* Create with a given ClassLoader.
*/
public SubClassFactory(ClassLoader parent, int logLevel) {
super(parent);
parentClassLoader = parent;
ClassPathClassBytesReader reader = new ClassPathClassBytesReader(null);
enhanceContext = new EnhanceContext(reader, true, "debug="+logLevel);
}
/**
* Create a subclass for the given bean class that implements EntityBean interface.
* <p>
* The transientGetters is a list of getter methods that are considered
* no persistent. That is, when they are called the bean should NOT
* trigger creation of an 'old values' copy of the beans values.
* </p>
*/
public Class<?> create(Class<?> normalClass, String serverName) throws IOException {
String subClassSuffix = EnhanceConstants.SUFFIX;
if (serverName != null){
subClassSuffix += "$"+serverName;
}
// Note: these have periods rather than slashes
String clsName = normalClass.getName();
String subClsName = clsName+subClassSuffix;
try {
byte[] newClsBytes = subclassBytes(clsName, subClassSuffix);
Class<?> newCls = defineClass(subClsName, newClsBytes, 0, newClsBytes.length);
return newCls;
} catch (IOException ex){
String m = "Error creating subclass for ["+clsName+"]";
logger.error(m, ex);
throw ex;
} catch (Throwable ex){
String m = "Error creating subclass for ["+clsName+"]";
logger.error(m, ex);
throw new RuntimeException(ex);
}
}
/**
* Return byte code for the subclass.
* <p>
* Note that if transientInfo is null, then no interception of getters or setters
* takes place.
* </p>
*/
private byte[] subclassBytes(String className, String subClassSuffix)
throws IOException {
String resName = className.replace('.', '/')+".class";
InputStream is = getResourceAsStream(resName);
ClassReader cr = new ClassReader(is);
ClassWriter cw = new ClassWriter(CLASS_WRITER_FLAGS);
SubClassClassAdpater ca = new SubClassClassAdpater(subClassSuffix, cw, parentClassLoader, enhanceContext);
if (ca.isLog(1)) {
ca.log(" enhancing " + className+subClassSuffix);
}
cr.accept(ca, 0);
return cw.toByteArray();
}
}
@@ -1,253 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
/**
* Used to generate a subclass based on a bean.
* <p>
* It does not have the fields or private methods of the read class. It replaces
* the method code with calls to super instead. It may need to add hashCode()
* and equals() methods to make sure a reference is loaded prior to either of
* these methods being called. It uses writeReplace() to modify the
* serialisation.
* </p>
*/
public class SubClassGenerator {// extends ClassAdapter implements Opcodes, GenConstants {
// private static final Logger logger = LogFactory.get(SubClassGenerator.class);
//
// boolean isInterceptFieldAdded = false;
//
// boolean isAddClonable = true;
//
// boolean superHasEquals = false;
//
// ClassInfo info;
//
// MethodInfo methodInfo;
//
// boolean hasSuperClass;
//
// /**
// * Create with the ClassInfo.
// */
// public SubClassGenerator(ClassVisitor cv, ClassInfo info) {
// super(cv);
// this.info = info;
// this.methodInfo = info.getMethodInfo();
// }
//
// /**
// * Create the class definition replacing the className and super class.
// */
// public void visit(int version, int access, String name, String signature, String superName,
// String[] interfaces) {
//
// // Note: These have slashes rather than periods!!
// String className = name+info.getSuffix();
// String superClassName;
// if ("java/lang/Object".endsWith(superName)){
// superClassName = name;
// } else {
// hasSuperClass = true;
// superClassName = name;//superName+info.getSuffix();
// }
//
// info.setClassName(className);
// info.setSuperClassName(superClassName);
//
//
// // Note: interfaces can be an empty array but not null
// int n = 1 + interfaces.length;
// String[] c = new String[n];
// System.arraycopy(interfaces, 0, c, 0, interfaces.length);
//
// // Add the EntityBean interface
// c[c.length - 1] = ENTITYBEAN;
//
// super.visit(version, access, className, signature, superClassName, c);
// }
//
// /**
// * The ebeanIntercept field is added once but thats all. Note the other
// * fields are defined in the superclass.
// */
// public FieldVisitor visitField(int access, String name, String desc, String signature,
// Object value) {
//
// if (!isInterceptFieldAdded) {
//
// FieldVisitor f0 = cv.visitField(ACC_PRIVATE + ACC_VOLATILE, IDENTITY_FIELD_NAME, "Ljava/lang/Object;", null, null);
// f0.visitEnd();
//
// FieldVisitor f1 = cv.visitField(0, INTERCEPT_FIELD_NAME, L_INTERCEPT, null, null);
// f1.visitEnd();
//
// isInterceptFieldAdded = true;
// return null;
// }
//
// return null;
// }
//
// /**
// * Replace the method code with calls to super. Add the intercept code as
// * required.
// */
// public MethodVisitor visitMethod(int access, String name, String desc, String signature,
// String[] exceptions) {
//
// boolean isPrivate = ((access & Opcodes.ACC_PRIVATE) != 0);
// boolean isStatic = ((access & Opcodes.ACC_STATIC) != 0);
// if (isPrivate || isStatic) {
// // no intercept on static or private methods
// return null;
// }
// // the key to look up in methodInfo
// String methodKey = name + ":" + desc;
//
// if (hasSuperClass){
// if (logger.isTraceEnabled()){
// String msg = "existing methods "+info.getClassName()+" "+methodKey;
// logger.trace(msg);
// }
// }
//
// VisitMethodParams params = new VisitMethodParams(cv, access, name, desc, signature, exceptions);
//
// if (methodInfo.isSet(methodKey)) {
// // for persistent properties excluding assoc Many's & id
// // ie. Old values not created for id or assoc many.
// return new ProxySetterMethod(params, info, methodInfo);
// }
//
// if (methodInfo.isGet(methodKey)) {
// // for persistent properties excluding id properties.
// // ie. reference loading not fired for id properties.
// return new ProxyGetterMethod(params, info);
// }
//
// if ("<init>".equals(name)) {
// return new ProxyConstructor(params, info);
// }
//
// if ("hashCode:()I".equals(methodKey)) {
// return new ProxyMethod(params, info);
// }
//
// if ("clone:()Ljava/lang/Object;".equals(methodKey)) {
// // SuperClass has a clone() method
// isAddClonable = false;
// return new MethodClone(params, info);
// }
//
// if ("toString:()Ljava/lang/String;".equals(methodKey)) {
// // No intercept on toString() as used by debuggers etc
// return null;
// }
// if ("hashCode:()I".equals(methodKey)) {
// return null;
// }
// if ("equals:(Ljava/lang/Object;)Z".equals(methodKey)) {
// superHasEquals = true;
// return null;
// }
//
// return null;
// }
//
// /**
// * Add methods to get and set the entityBeanIntercept. Also add the
// * writeReplace method to control serialisation.
// */
// public void visitEnd() {
//
// if (isAddClonable){
// // super has not overwritten the clone() method.
// // we will add the clone() method in case a super of the super has clone()
// String[] exceptions = new String[] { "java/lang/CloneNotSupportedException" };
// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, "clone", "()Ljava/lang/Object;", null, exceptions);
// MethodClone methodClone = new MethodClone(params, info);
// methodClone.visitCode();
// }
//
// MethodInfo methodInfo = info.getMethodInfo();
// if (methodInfo.isEmbedded()){
// // don't override equals etc when it is an embedded bean
// // Either EmbeddedId or a Embeddable
//
// } else if (methodInfo.overrideEquals(superHasEquals)) {
// // we want to generate a equals() hashCode() and ebeanGetIndentity()
// // methods so that the generated subclass has built in equals() support.
//
// if (methodInfo.getIdGetter() == null) {
// if (methodInfo.isSqlSelectBased()){
// // This could be common for reporting type beans based on
// // sql-select that use group by type queries.
// } else {
// String m = "Can not generate equals for ["+info.getClassName();
// m += "]. Concatinated id?";
// logger.warn(m);
// }
// } else {
//
// if (generateEbeanGetIdentityMethod()){
// // add equals()
// MethodEquals.add(cv, info);
//
// // add hashCode()
// MethodHashCode.add(cv, info);
// }
// }
// }
//
// // add additional getters from super class inheritance
// List<MethodDesc> additionalGetters = methodInfo.getAdditionalGetters();
// for (MethodDesc methodDesc : additionalGetters) {
// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, methodDesc);
// ProxyGetterMethod getter = new ProxyGetterMethod(params, info);
// getter.visitCode();
// }
//
// // add additional setters from super class inheritance
// List<MethodDesc> additionalSetters = methodInfo.getAdditionalSetters();
// for (MethodDesc methodDesc : additionalSetters) {
// VisitMethodParams params = new VisitMethodParams(cv, ACC_PUBLIC, methodDesc);
// ProxySetterMethod setter = new ProxySetterMethod(params, info, methodInfo);
// setter.visitCode();
// }
//
// // add set get methods for ebeanIntecept
// MethodGetSetIntercept.add(cv, info);
//
// // add a writeReplace method to control serialisation
// MethodWriteReplace.add(cv, info);
//
// super.visitEnd();
// }
//
// private boolean generateEbeanGetIdentityMethod() {
// String idGetterDesc = methodInfo.getIdGetterDesc();
// if (idGetterDesc.equals("()I")) {
// // int version of ebeanGetIndentity()
// MethodEbeanGetIdentityInt.add(cv, info);
// return true;
//
// } else if (idGetterDesc.equals("()J")) {
// // long version of ebeanGetIndentity()
// MethodEbeanGetIdentityLong.add(cv, info);
// return true;
//
// } else if (idGetterDesc.length() > 5) {
// // Object version of ebeanGetIndentity()
// MethodEbeanGetIdentity.add(cv, info);
// return true;
//
// } else {
// String m = "Can not generate equals for ["+info.getClassName();
// m += "] due to type of id property: "+idGetterDesc;
// logger.warn(m);
// return false;
// }
// }
}
@@ -1,103 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
import java.security.AccessController;
import java.security.PrivilegedActionException;
import java.security.PrivilegedExceptionAction;
import java.util.concurrent.ConcurrentHashMap;
import javax.persistence.PersistenceException;
import com.avaje.ebean.config.ServerConfig;
import com.avaje.ebean.enhance.agent.EnhanceConstants;
import com.avaje.ebeaninternal.api.ClassUtil;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Creates and caches the dynamically generated subclasses.
* <p>
* That is, the 'EntityBean' classes are dynamically generated subclasses of the
* 'vanilla' classes.
* </p>
*/
public class SubClassManager implements EnhanceConstants {
private static final Logger logger = LoggerFactory.getLogger(SubClassManager.class);
private final ConcurrentHashMap<String,Class<?>> clzMap;
private final SubClassFactory subclassFactory;
private final String serverName;
/**
* The log level for debugging subclass generation/enhancement.
*/
private final int logLevel;
/**
* Construct with the ClassLoader used to load Ebean.class.
*/
@SuppressWarnings({ "unchecked", "rawtypes" })
public SubClassManager(ServerConfig serverConfig) {
String s = serverConfig.getProperty("subClassManager.preferContextClassloader", "true");
final boolean preferContext = "true".equalsIgnoreCase(s);
this.serverName = serverConfig.getName();
this.logLevel = serverConfig.getEnhanceLogLevel();
this.clzMap = new ConcurrentHashMap<String, Class<?>>();
try {
subclassFactory = (SubClassFactory) AccessController
.doPrivileged(new PrivilegedExceptionAction() {
public Object run() {
ClassLoader cl = ClassUtil.getClassLoader(this.getClass(), preferContext);
logger.info("SubClassFactory parent ClassLoader ["+cl.getClass().getName()+"]");
return new SubClassFactory(cl, logLevel);
}
});
} catch (PrivilegedActionException e) {
throw new PersistenceException(e);
}
}
/**
* Resolve the Class for the class name.
* <p>
* The methodInfo is used to determine the method interception on the
* generated class.
* </p>
* <p>
* If the class has already been generated then it is returned out of a
* cache.
* </p>
*/
public Class<?> resolve(String name) {
synchronized (this) {
String superName = SubClassUtil.getSuperClassName(name);
Class<?> clz = clzMap.get(superName);
if (clz == null) {
clz = createClass(superName);
clzMap.put(superName, clz);
}
return clz;
}
}
private Class<?> createClass(String name) {
try {
Class<?> superClass = Class.forName(name, true, subclassFactory.getParent());
return subclassFactory.create(superClass, serverName);
} catch (Exception ex) {
String m = "Error creating subclass for [" + name + "]";
throw new PersistenceException(m, ex);
}
}
}
@@ -1,28 +0,0 @@
package com.avaje.ebeaninternal.server.subclass;
/**
* Helper methods for generated sub classes.
*/
public class SubClassUtil implements GenSuffix {
/**
* Return true if this is a generated class.
*/
public static boolean isSubClass(String className) {
return (className.lastIndexOf(SUFFIX) != -1);
}
/**
* Return the super class name given the generated className.
*/
public static String getSuperClassName(String className){
int dPos = className.lastIndexOf(SUFFIX);
if (dPos > -1){
return className.substring(0, dPos);
}
return className;
}
}
@@ -1,15 +0,0 @@
<HTML>
<HEAD>
<META HTTP-EQUIV="CONTENT-TYPE" CONTENT="text/html; charset=iso-8859-1">
<TITLE>Used to generate subclasses rather than weaving</TITLE>
</HEAD>
<Body BGCOLOR="#ffffff">
Used to generate subclasses rather than weaving
<p>
As alternative to weaving/enhancing the classes via javaagent or ant you
can use dynamically generated subclasses. These objects support that feature.
</p>
</Body>
</HTML>
@@ -7,7 +7,6 @@ import java.util.Map.Entry;
import com.avaje.ebean.bean.PersistenceContext;
import com.avaje.ebeaninternal.api.Monitor;
import com.avaje.ebeaninternal.server.subclass.SubClassUtil;
/**
* Default implementation of PersistenceContext.
@@ -120,9 +119,7 @@ public final class DefaultPersistenceContext implements PersistenceContext {
private ClassContext getClassContext(Class<?> beanType) {
// strip off $$EntityBean.. suffix...
String clsName = SubClassUtil.getSuperClassName(beanType.getName());
String clsName = beanType.getName();
ClassContext classMap = typeCache.get(clsName);
if (classMap == null) {
classMap = new ClassContext();
@@ -294,6 +294,10 @@ public class TransactionManager {
c.setTransactionIsolation(isolationLevel);
}
if (explicit && TXN_LOGGER.isTraceEnabled()) {
TXN_LOGGER.trace(t.getLogPrefix()+"Begin");
}
return t;
} catch (SQLException ex) {
@@ -371,6 +375,9 @@ public class TransactionManager {
public void notifyOfQueryOnly(boolean onCommit, SpiTransaction transaction, Throwable cause) {
// Nothing that interesting here
if (TXN_LOGGER.isTraceEnabled()) {
TXN_LOGGER.trace(transaction.getLogPrefix()+"Commit - query only");
}
}
private String formatThrowable(Throwable e){