co.paralleluniverse.actors.InstanceUpgrader Maven / Gradle / Ivy
/*
* Quasar: lightweight threads and actors for the JVM.
* Copyright (c) 2013-2014, Parallel Universe Software Co. All rights reserved.
*
* This program and the accompanying materials are dual-licensed under
* either the terms of the Eclipse Public License v1.0 as published by
* the Eclipse Foundation
*
* or (per the licensee's choosing)
*
* under the terms of the GNU Lesser General Public License version 3.0
* as published by the Free Software Foundation.
*/
package co.paralleluniverse.actors;
import co.paralleluniverse.common.util.Exceptions;
import com.google.common.collect.ImmutableList;
import com.google.common.collect.ImmutableMap;
import com.google.common.collect.MapMaker;
import java.lang.annotation.Annotation;
import java.lang.reflect.Constructor;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.concurrent.ConcurrentMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import sun.reflect.ReflectionFactory;
/**
* Copies fields from an instance of a previous version of a class to the current version
*
* @author pron
*/
class InstanceUpgrader {
private static final Logger LOG = LoggerFactory.getLogger(InstanceUpgrader.class);
private static final Object reflFactory;
static final ClassValue> instanceUpgrader = new ClassValue>() {
@Override
protected InstanceUpgrader> computeValue(Class> type) {
return new InstanceUpgrader(type);
}
};
static {
Object rf = null;
try {
rf = ReflectionFactory.getReflectionFactory();
} catch (Throwable t) {
}
reflFactory = rf;
}
public static InstanceUpgrader get(Class clazz) {
return (InstanceUpgrader) instanceUpgrader.get(clazz);
}
private final Class toClass;
private final Map fields;
private final Map staticFields;
private final ConcurrentMap copiers;
private final Constructor ctor;
private final List onUpgradeInstance;
private final List onUpgradeStatic;
public InstanceUpgrader(Class toClass) {
this.toClass = toClass;
this.copiers = new MapMaker().weakKeys().makeMap();
Map fs = getInstanceFields(toClass, new HashMap());
ImmutableMap.Builder builder = ImmutableMap.builder();
for (Map.Entry entry : fs.entrySet()) {
Field f = entry.getValue();
f.setAccessible(true);
Constructor innerClassCtor = null;
if (Objects.equals(f.getType().getEnclosingClass(), toClass)) {
try {
innerClassCtor = f.getType().getDeclaredConstructor(toClass);
innerClassCtor.setAccessible(true);
} catch (NoSuchMethodException e) {
}
}
builder.put(entry.getKey(), new FieldInfo(f, innerClassCtor));
}
this.fields = builder.build();
this.staticFields = ImmutableMap.copyOf(getStaticFields(toClass, new HashMap()));
for (Field sf : staticFields.values())
sf.setAccessible(true);
this.ctor = getNoArgConstructor(toClass);
List upgradeMethods = getAnnotatedMethods(toClass, OnUpgrade.class, new ArrayList());
ImmutableList.Builder ouib = ImmutableList.builder();
ImmutableList.Builder ousb = ImmutableList.builder();
for (Method m : upgradeMethods) {
if (m.getParameterTypes().length > 0) {
LOG.warn("@OnUpgrade method {} takes arguments and will therefore not be invoked.", m);
} else {
m.setAccessible(true);
if (Modifier.isStatic(m.getModifiers()))
ousb.add(m);
else
ouib.add(m);
}
}
onUpgradeInstance = ouib.build();
onUpgradeStatic = ousb.build();
}
private static Constructor getNoArgConstructor(Class clazz) {
if (reflFactory == null)
return getNoArgConstructor1(clazz);
else
return getNoArgConstructor2(clazz);
}
private static Constructor getNoArgConstructor1(Class clazz) {
try {
Constructor cons = clazz.getDeclaredConstructor();
cons.setAccessible(true);
return cons;
} catch (NoSuchMethodException e) {
return null;
}
}
private static Constructor getNoArgConstructor2(Class clazz) {
Class> initCl = Actor.class.isAssignableFrom(clazz) ? Actor.class : Object.class;
try {
Constructor cons = initCl.getDeclaredConstructor();
// int mods = cons.getModifiers();
// if ((mods & Modifier.PRIVATE) != 0
// || ((mods & (Modifier.PUBLIC | Modifier.PROTECTED)) == 0
// && !packageEquals(cl, initCl))) {
// return null;
// }
cons = ((ReflectionFactory) reflFactory).newConstructorForSerialization(clazz, cons);
cons.setAccessible(true);
return cons;
} catch (NoSuchMethodException ex) {
return null;
}
}
// private static boolean packageEquals(Class> cl1, Class> cl2) {
// return cl1.getPackage().getName().equals(cl2.getPackage().getName()); // && cl1.getClassLoader() == cl2.getClassLoader();
// }
public T copy(T from, T to) {
assert toClass.isInstance(to);
return getCopier((Class) from.getClass()).copy(from, to);
}
public T copy(T from) {
return getCopier((Class) from.getClass()).copy(from);
}
private Copier getCopier(Class> fromClass) {
Copier copier = copiers.get(fromClass);
if (copier == null) {
copier = new Copier(fromClass);
Copier temp = copiers.putIfAbsent(fromClass, copier);
if (temp != null)
copier = temp;
}
return copier;
}
private class Copier {
private final Class fromClass;
private final Field[] fromFields;
private final Field[] toFields;
private final Constructor[] innerClassConstructor;
private final Copier[] fieldCopier;
Copier(Class fromClass) {
if (!fromClass.getName().equals(toClass.getName()))
throw new IllegalArgumentException("'fromClass' " + fromClass.getName() + " is not a version of 'toClass' " + toClass.getName());
this.fromClass = fromClass;
// static fields
synchronized (InstanceUpgrader.this) {
try {
Map sfs = getStaticFields(fromClass, new HashMap());
for (Map.Entry e : sfs.entrySet()) {
Field tf = staticFields.get(e.getKey());
Field ff = e.getValue();
ff.setAccessible(true);
if (tf != null && !Modifier.isFinal(tf.getModifiers())) {
final Object fromFieldValue = ff.get(null);
final Object toFieldValue;
if (tf.getType().isAssignableFrom(ff.getType()))
toFieldValue = fromFieldValue;
else if (tf.getType().getName().equals(ff.getType().getName()))
toFieldValue = ((Copier
© 2015 - 2025 Weber Informatics LLC | Privacy Policy