Log the purposes of nested UOWs as well as the parent. Create a serialization proxy for mapped objects.

This commit is contained in:
Greg Burd 2017-10-30 09:40:41 -04:00
parent 52dab5872c
commit c3f9b83770
5 changed files with 70 additions and 22 deletions

View file

@ -44,6 +44,7 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
private final AbstractUnitOfWork<E> parent;
private final Table<String, String, Either<Object, List<Facet>>> cache = HashBasedTable.create();
protected String purpose;
protected List<String> nestedPurposes = new ArrayList<String>();
protected int cacheHits = 0;
protected int cacheMisses = 0;
protected int databaseLookups = 0;
@ -90,6 +91,11 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
return this;
}
@Override
public String getPurpose() {
return purpose;
}
@Override
public UnitOfWork setPurpose(String purpose) {
this.purpose = purpose;
@ -137,10 +143,11 @@ public abstract class AbstractUnitOfWork<E extends Exception> implements UnitOfW
double daf = (dat / e) * 100;
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 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,
(purpose == null ? "" : " " + purpose));
(purpose == null ? "" : " " + purpose), (nestedPurposes.isEmpty()) ? "" : ", " + x);
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.
parent.mergeCache(cache);
if (purpose != null) {parent.nestedPurposes.add(purpose);}
parent.cacheHits += cacheHits;
parent.cacheMisses += cacheMisses;
parent.databaseLookups += databaseLookups;

View file

@ -311,24 +311,22 @@ public final class HelenusSession extends AbstractSessionOperations implements C
}
public synchronized UnitOfWork begin(UnitOfWork parent) {
StringBuilder purpose = null;
if (LOG.isInfoEnabled()) {
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
int frame = 2;
if (trace[2].getMethodName().equals("begin")) {
frame = 3;
}
purpose = new StringBuilder().append(trace[frame].getClassName()).append(".")
.append(trace[frame].getMethodName()).append("(").append(trace[frame].getFileName()).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());
}
if (LOG.isInfoEnabled() && uow.getPurpose() == null) {
StringBuilder purpose = null;
StackTraceElement[] trace = Thread.currentThread().getStackTrace();
int frame = 2;
if (trace[2].getMethodName().equals("begin")) {
frame = 3;
}
purpose = new StringBuilder().append(trace[frame].getClassName()).append(".")
.append(trace[frame].getMethodName()).append("(").append(trace[frame].getFileName()).append(":")
.append(trace[frame].getLineNumber()).append(")");
uow.setPurpose(purpose.toString());
}
if (parent != null) {
parent.addNestedUnitOfWork(uow);
}

View file

@ -61,6 +61,7 @@ public interface UnitOfWork<X extends Exception> extends AutoCloseable {
List<Facet> cacheEvict(List<Facet> facets);
String getPurpose();
UnitOfWork setPurpose(String purpose);
void addDatabaseTime(String name, Stopwatch amount);

View file

@ -15,16 +15,21 @@
*/
package net.helenus.core.reflect;
import java.io.InvalidObjectException;
import java.io.ObjectInputStream;
import java.io.ObjectStreamException;
import java.io.Serializable;
import java.lang.invoke.MethodHandles;
import java.lang.reflect.Constructor;
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.HashMap;
import java.util.Map;
import net.helenus.core.Helenus;
import net.helenus.mapping.annotation.Transient;
import net.helenus.mapping.value.ValueProviderMap;
import net.helenus.support.HelenusException;
public class MapperInvocationHandler<E> implements InvocationHandler, Serializable {
@ -50,14 +55,20 @@ public class MapperInvocationHandler<E> implements InvocationHandler, Serializab
int.class);
constructor.setAccessible(true);
// Now we need to lookup and invoke special the default method on the interface
// class.
// Now we need to lookup and invoke special the default method on the interface class.
final Class<?> declaringClass = method.getDeclaringClass();
Object result = constructor.newInstance(declaringClass, MethodHandles.Lookup.PRIVATE)
.unreflectSpecial(method, declaringClass).bindTo(proxy).invokeWithArguments(args);
return result;
}
private Object writeReplace() {
return new SerializationProxy(this);
}
private void readObject(ObjectInputStream stream) throws InvalidObjectException {
throw new InvalidObjectException("Proxy required.");
}
@Override
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();
}
if ("writeReplace".equals(methodName)) {
return new SerializationProxy(this);
}
if ("readObject".equals(methodName)) {
throw new InvalidObjectException("Proxy required.");
}
if ("dsl".equals(methodName)) {
return Helenus.dsl(iface);
}
if (MapExportable.TO_MAP_METHOD.equals(methodName)) {
// return Collections.unmodifiableMap(src);
return src;
return src; // return Collections.unmodifiableMap(src);
}
Object value = src.get(methodName);
@ -132,4 +150,27 @@ public class MapperInvocationHandler<E> implements InvocationHandler, Serializab
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);
}
}
}

View file

@ -15,6 +15,7 @@
*/
package net.helenus.core.reflect;
import java.io.Serializable;
import java.lang.reflect.Proxy;
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) {
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;
}
}