Log the purposes of nested UOWs as well as the parent. Create a serialization proxy for mapped objects.
This commit is contained in:
parent
52dab5872c
commit
c3f9b83770
5 changed files with 70 additions and 22 deletions
|
@ -44,6 +44,7 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
|
||||||
private final AbstractUnitOfWork<E> parent;
|
private final AbstractUnitOfWork<E> parent;
|
||||||
private final Table<String, String, Either<Object, List<Facet>>> cache = HashBasedTable.create();
|
private final Table<String, String, Either<Object, List<Facet>>> cache = HashBasedTable.create();
|
||||||
protected String purpose;
|
protected String purpose;
|
||||||
|
protected List<String> nestedPurposes = new ArrayList<String>();
|
||||||
protected int cacheHits = 0;
|
protected int cacheHits = 0;
|
||||||
protected int cacheMisses = 0;
|
protected int cacheMisses = 0;
|
||||||
protected int databaseLookups = 0;
|
protected int databaseLookups = 0;
|
||||||
|
@ -90,6 +91,11 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
|
||||||
return this;
|
return this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getPurpose() {
|
||||||
|
return purpose;
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public UnitOfWork setPurpose(String purpose) {
|
public UnitOfWork setPurpose(String purpose) {
|
||||||
this.purpose = purpose;
|
this.purpose = purpose;
|
||||||
|
@ -137,10 +143,11 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
|
||||||
double daf = (dat / e) * 100;
|
double daf = (dat / e) * 100;
|
||||||
da = String.format(" consuming %,.3fms for data access, or %,2.2f%% of total UOW time.", dat, daf);
|
da = String.format(" consuming %,.3fms for data access, or %,2.2f%% of total UOW time.", dat, daf);
|
||||||
}
|
}
|
||||||
|
String x = nestedPurposes.stream().distinct().collect(Collectors.joining(", "));
|
||||||
String n = nested.stream().map(uow -> String.valueOf(uow.hashCode())).collect(Collectors.joining(", "));
|
String n = nested.stream().map(uow -> String.valueOf(uow.hashCode())).collect(Collectors.joining(", "));
|
||||||
String s = String.format(Locale.US, "UOW(%s%s) %s in %,.3fms%s%s%s%s", hashCode(),
|
String s = String.format(Locale.US, "UOW(%s%s) %s in %,.3fms%s%s%s%s%s", hashCode(),
|
||||||
(nested.size() > 0 ? ", [" + n + "]" : ""), what, e, cache, database, da,
|
(nested.size() > 0 ? ", [" + n + "]" : ""), what, e, cache, database, da,
|
||||||
(purpose == null ? "" : " " + purpose));
|
(purpose == null ? "" : " " + purpose), (nestedPurposes.isEmpty()) ? "" : ", " + x);
|
||||||
return s;
|
return s;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -277,7 +284,7 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
|
||||||
|
|
||||||
// Merge cache and statistics into parent if there is one.
|
// Merge cache and statistics into parent if there is one.
|
||||||
parent.mergeCache(cache);
|
parent.mergeCache(cache);
|
||||||
|
if (purpose != null) {parent.nestedPurposes.add(purpose);}
|
||||||
parent.cacheHits += cacheHits;
|
parent.cacheHits += cacheHits;
|
||||||
parent.cacheMisses += cacheMisses;
|
parent.cacheMisses += cacheMisses;
|
||||||
parent.databaseLookups += databaseLookups;
|
parent.databaseLookups += databaseLookups;
|
||||||
|
|
|
@ -311,8 +311,12 @@ public final class HelenusSession extends AbstractSessionOperations implements C
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized UnitOfWork begin(UnitOfWork parent) {
|
public synchronized UnitOfWork begin(UnitOfWork parent) {
|
||||||
|
try {
|
||||||
|
Class<? extends UnitOfWork> clazz = unitOfWorkClass;
|
||||||
|
Constructor<? extends UnitOfWork> ctor = clazz.getConstructor(HelenusSession.class, UnitOfWork.class);
|
||||||
|
UnitOfWork uow = ctor.newInstance(this, parent);
|
||||||
|
if (LOG.isInfoEnabled() && uow.getPurpose() == null) {
|
||||||
StringBuilder purpose = null;
|
StringBuilder purpose = null;
|
||||||
if (LOG.isInfoEnabled()) {
|
|
||||||
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
|
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
|
||||||
int frame = 2;
|
int frame = 2;
|
||||||
if (trace[2].getMethodName().equals("begin")) {
|
if (trace[2].getMethodName().equals("begin")) {
|
||||||
|
@ -321,12 +325,6 @@ public final class HelenusSession extends AbstractSessionOperations implements C
|
||||||
purpose = new StringBuilder().append(trace[frame].getClassName()).append(".")
|
purpose = new StringBuilder().append(trace[frame].getClassName()).append(".")
|
||||||
.append(trace[frame].getMethodName()).append("(").append(trace[frame].getFileName()).append(":")
|
.append(trace[frame].getMethodName()).append("(").append(trace[frame].getFileName()).append(":")
|
||||||
.append(trace[frame].getLineNumber()).append(")");
|
.append(trace[frame].getLineNumber()).append(")");
|
||||||
}
|
|
||||||
try {
|
|
||||||
Class<? extends UnitOfWork> clazz = unitOfWorkClass;
|
|
||||||
Constructor<? extends UnitOfWork> ctor = clazz.getConstructor(HelenusSession.class, UnitOfWork.class);
|
|
||||||
UnitOfWork uow = ctor.newInstance(this, parent);
|
|
||||||
if (LOG.isInfoEnabled() && purpose != null) {
|
|
||||||
uow.setPurpose(purpose.toString());
|
uow.setPurpose(purpose.toString());
|
||||||
}
|
}
|
||||||
if (parent != null) {
|
if (parent != null) {
|
||||||
|
|
|
@ -61,6 +61,7 @@ public interface UnitOfWork<X extends Exception> extends AutoCloseable {
|
||||||
|
|
||||||
List<Facet> cacheEvict(List<Facet> facets);
|
List<Facet> cacheEvict(List<Facet> facets);
|
||||||
|
|
||||||
|
String getPurpose();
|
||||||
UnitOfWork setPurpose(String purpose);
|
UnitOfWork setPurpose(String purpose);
|
||||||
|
|
||||||
void addDatabaseTime(String name, Stopwatch amount);
|
void addDatabaseTime(String name, Stopwatch amount);
|
||||||
|
|
|
@ -15,16 +15,21 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
|
import java.io.InvalidObjectException;
|
||||||
|
import java.io.ObjectInputStream;
|
||||||
|
import java.io.ObjectStreamException;
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.lang.invoke.MethodHandles;
|
import java.lang.invoke.MethodHandles;
|
||||||
import java.lang.reflect.Constructor;
|
import java.lang.reflect.Constructor;
|
||||||
import java.lang.reflect.InvocationHandler;
|
import java.lang.reflect.InvocationHandler;
|
||||||
import java.lang.reflect.Method;
|
import java.lang.reflect.Method;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
import net.helenus.core.Helenus;
|
import net.helenus.core.Helenus;
|
||||||
import net.helenus.mapping.annotation.Transient;
|
import net.helenus.mapping.annotation.Transient;
|
||||||
|
import net.helenus.mapping.value.ValueProviderMap;
|
||||||
import net.helenus.support.HelenusException;
|
import net.helenus.support.HelenusException;
|
||||||
|
|
||||||
public class MapperInvocationHandler<E> implements InvocationHandler, Serializable {
|
public class MapperInvocationHandler<E> implements InvocationHandler, Serializable {
|
||||||
|
@ -50,14 +55,20 @@ public class MapperInvocationHandler<E> implements InvocationHandler, Serializab
|
||||||
int.class);
|
int.class);
|
||||||
constructor.setAccessible(true);
|
constructor.setAccessible(true);
|
||||||
|
|
||||||
// Now we need to lookup and invoke special the default method on the interface
|
// Now we need to lookup and invoke special the default method on the interface class.
|
||||||
// class.
|
|
||||||
final Class<?> declaringClass = method.getDeclaringClass();
|
final Class<?> declaringClass = method.getDeclaringClass();
|
||||||
Object result = constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
|
Object result = constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
|
||||||
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
|
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private Object writeReplace() {
|
||||||
|
return new SerializationProxy(this);
|
||||||
|
}
|
||||||
|
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
|
||||||
|
throw new InvalidObjectException("Proxy required.");
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||||
|
|
||||||
|
@ -96,13 +107,20 @@ public class MapperInvocationHandler<E> implements InvocationHandler, Serializab
|
||||||
return iface.getSimpleName() + ": " + src.toString();
|
return iface.getSimpleName() + ": " + src.toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if ("writeReplace".equals(methodName)) {
|
||||||
|
return new SerializationProxy(this);
|
||||||
|
}
|
||||||
|
|
||||||
|
if ("readObject".equals(methodName)) {
|
||||||
|
throw new InvalidObjectException("Proxy required.");
|
||||||
|
}
|
||||||
|
|
||||||
if ("dsl".equals(methodName)) {
|
if ("dsl".equals(methodName)) {
|
||||||
return Helenus.dsl(iface);
|
return Helenus.dsl(iface);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
|
if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
|
||||||
// return Collections.unmodifiableMap(src);
|
return src; // return Collections.unmodifiableMap(src);
|
||||||
return src;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Object value = src.get(methodName);
|
Object value = src.get(methodName);
|
||||||
|
@ -132,4 +150,27 @@ public class MapperInvocationHandler<E> implements InvocationHandler, Serializab
|
||||||
|
|
||||||
return value;
|
return value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
static class SerializationProxy implements Serializable {
|
||||||
|
|
||||||
|
private static final long serialVersionUID = -5617583940055969353L;
|
||||||
|
|
||||||
|
private final Class<?> iface;
|
||||||
|
private final Map<String, Object> src;
|
||||||
|
|
||||||
|
public SerializationProxy(MapperInvocationHandler mapper) {
|
||||||
|
this.iface = mapper.iface;
|
||||||
|
if (mapper.src instanceof ValueProviderMap) {
|
||||||
|
this.src = new HashMap<String, Object>(mapper.src.size());
|
||||||
|
this.src.putAll(src);
|
||||||
|
} else {
|
||||||
|
this.src = mapper.src;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Object readResolve() throws ObjectStreamException {
|
||||||
|
return Helenus.map(iface, src);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,7 @@
|
||||||
*/
|
*/
|
||||||
package net.helenus.core.reflect;
|
package net.helenus.core.reflect;
|
||||||
|
|
||||||
|
import java.io.Serializable;
|
||||||
import java.lang.reflect.Proxy;
|
import java.lang.reflect.Proxy;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
|
|
||||||
|
@ -28,7 +29,7 @@ public enum ReflectionMapperInstantiator implements MapperInstantiator {
|
||||||
public <E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) {
|
public <E> E instantiate(Class<E> iface, Map<String, Object> src, ClassLoader classLoader) {
|
||||||
|
|
||||||
MapperInvocationHandler<E> handler = new MapperInvocationHandler<E>(iface, src);
|
MapperInvocationHandler<E> handler = new MapperInvocationHandler<E>(iface, src);
|
||||||
E proxy = (E) Proxy.newProxyInstance(classLoader, new Class[]{iface, MapExportable.class}, handler);
|
E proxy = (E) Proxy.newProxyInstance(classLoader, new Class[]{iface, MapExportable.class, Serializable.class}, handler);
|
||||||
return proxy;
|
return proxy;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue