Last active
January 11, 2021 02:06
-
-
Save ZenLiuCN/2e1000d50a20120c761b27ca2def5157 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package tester; | |
import io.protostuff.LinkedBuffer; | |
import io.protostuff.ProtostuffIOUtil; | |
import io.protostuff.Schema; | |
import io.protostuff.runtime.DefaultIdStrategy; | |
import io.protostuff.runtime.IdStrategy; | |
import io.protostuff.runtime.RuntimeSchema; | |
import lombok.SneakyThrows; | |
import lombok.ToString; | |
import lombok.Value; | |
import org.jooq.lambda.Seq; | |
import org.jooq.lambda.Sneaky; | |
import org.jooq.lambda.tuple.Tuple2; | |
import org.jooq.lambda.tuple.Tuple3; | |
import java.lang.reflect.Field; | |
import java.lang.reflect.InvocationHandler; | |
import java.lang.reflect.Method; | |
import java.lang.reflect.Proxy; | |
import java.util.*; | |
import java.util.concurrent.ConcurrentHashMap; | |
import java.util.function.Function; | |
import java.util.regex.Pattern; | |
import java.util.stream.Collectors; | |
import static org.jooq.lambda.tuple.Tuple.tuple; | |
/** | |
* @author Zen.Liu | |
* @apiNote | |
* @since 2021-01-10 | |
*/ | |
public interface ProtoUtil { | |
DefaultIdStrategy STRATEGY = new DefaultIdStrategy( | |
IdStrategy.DEFAULT_FLAGS | |
| IdStrategy.ALLOW_NULL_ARRAY_ELEMENT | |
| IdStrategy.MORPH_COLLECTION_INTERFACES | |
| IdStrategy.MORPH_MAP_INTERFACES | |
| IdStrategy.MORPH_NON_FINAL_POJOS | |
); | |
LinkedBuffer buffer = LinkedBuffer.allocate(512); | |
Map<String, Schema<Object>> schemaPool = new ConcurrentHashMap<>(); | |
Function<String, Optional<Schema<Object>>> schemaOf = Sneaky.function(util::classFromString).andThen(util::getSchema); | |
Function<Object, Optional<Schema<Object>>> schemaFrom = Sneaky.function(Object::getClass).andThen(util::getSchema); | |
static Optional<byte[]> to(Object o) { | |
Object instance; | |
try { | |
Field h = o.getClass().getSuperclass().getDeclaredField("h"); | |
h.setAccessible(true); | |
instance = h.get(o); | |
} catch (Exception ex) { | |
instance = o; | |
} | |
final Object target = instance; | |
return schemaFrom.apply(target).map(s -> { | |
try { | |
return ProtostuffIOUtil.toByteArray(target, s, buffer); | |
} finally { | |
buffer.clear(); | |
} | |
}); | |
} | |
static Optional<Object> from(byte[] data, String clz) { | |
return schemaOf.apply(clz).map(s -> { | |
Object o = s.newMessage(); | |
ProtostuffIOUtil.mergeFrom(data, o, s); | |
return o; | |
}); | |
} | |
static <T> Optional<T> from(byte[] data, Class<T> clz) { | |
return util.getSchema(clz).map(s -> { | |
Object o = s.newMessage(); | |
ProtostuffIOUtil.mergeFrom(data, o, s); | |
return (T) o; | |
}); | |
} | |
final class util { | |
@SuppressWarnings("unchecked") | |
static Optional<Schema<Object>> getSchema(Class<?> clz) { | |
Schema<Object> schema = schemaPool.get(clz.getCanonicalName()); | |
if (schema == null) { | |
schema = (Schema<Object>) RuntimeSchema.createFrom(clz, STRATEGY); | |
schemaPool.put(clz.getCanonicalName(), schema); | |
return Optional.of(schema); | |
} | |
return Optional.of(schema); | |
} | |
static Class<?> classFromString(String name) throws ClassNotFoundException { | |
//@formatter:off | |
if (long.class.getCanonicalName().equals(name)) {return long.class;} | |
else if (int.class.getCanonicalName().equals(name)) {return int.class;} | |
else if (byte.class.getCanonicalName().equals(name)) {return byte.class;} | |
else if (short.class.getCanonicalName().equals(name)) {return short.class;} | |
else if (boolean.class.getCanonicalName().equals(name)) {return boolean.class;} | |
else if (double.class.getCanonicalName().equals(name)) {return double.class;} | |
else if (float.class.getCanonicalName().equals(name)) {return float.class;} | |
else if (name.endsWith("[]")) { | |
//array | |
final String arrayName = "[L" + name.replace(Pattern.quote("[]"), ";"); | |
return Class.forName(arrayName); | |
} | |
return name.getClass().getClassLoader().loadClass(name); | |
//@formatter:on | |
} | |
} | |
@Value(staticConstructor = "of") | |
class Request { | |
String domain; | |
long timestamp = System.currentTimeMillis(); | |
Object[] args; | |
int[] proxies; | |
String[] proxyType; | |
} | |
@Value(staticConstructor = "of") | |
class Response { | |
String domain; | |
Object result; | |
int[] proxies; | |
String[] proxyType; | |
} | |
static <T> void registerService(T service, Class<T> serviceKlass) { | |
} | |
static <T> T registerClient(Class<T> clientKlass) { | |
return null; | |
} | |
static <T> T delegate(T instance, Class<T> type) { | |
final Map<String, Object> values = Delegator.copy(instance, type); | |
return Delegator.proxy(type, values); | |
} | |
static <T> T delegate(Class<T> type, Map<String, Object> values) { | |
return Delegator.proxy(type, values); | |
} | |
static Object delegate(Object instance) { | |
if (instance.getClass().isInterface()) | |
throw new IllegalStateException("no instance will be a interface :" + instance.getClass()); | |
final Class<?>[] interfaces = instance.getClass().getInterfaces(); | |
if (interfaces == null || interfaces.length == 0) | |
throw new IllegalStateException(" instance implements none interface :" + instance.getClass()); | |
return delegate(instance, (Class<Object>) interfaces[0]); | |
} | |
@ToString | |
final class Delegator implements InvocationHandler { | |
public final String type; | |
public final Map<String, Object> values; | |
Delegator(String type) { | |
this.type = type; | |
this.values = new HashMap<>(); | |
} | |
Delegator(String type, Map<String, Object> values) { | |
this.type = type; | |
this.values = values; | |
} | |
@SuppressWarnings("unchecked") | |
public <T> T proxy(Class<T> clz) { | |
return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{clz}, this); | |
} | |
@SneakyThrows | |
public Object delegate() { | |
final Class<?> aClass = Class.forName(type); | |
return proxy(aClass); | |
} | |
@Override | |
public Object invoke(Object proxy, Method m, Object[] args) throws Throwable { | |
if (m.getName().startsWith("is")) { | |
return values.get(m.getName().substring(2)); | |
} else if (m.getName().startsWith("get")) { | |
return values.get(m.getName().substring(3)); | |
} else if (m.getName().startsWith("set")) { | |
return values.put(m.getName().substring(3), args); | |
} else if (m.getName().equals("toString")) { | |
return type + values.toString(); | |
} else { | |
throw new IllegalStateException("not accepted call:" + m.getName()); | |
// return m.invoke(proxy,args); | |
} | |
} | |
@SuppressWarnings("unchecked") | |
public static <T> T proxy(Class<T> clz, Map<String, Object> init) { | |
return (T) Proxy.newProxyInstance(clz.getClassLoader(), new Class[]{clz}, init != null ? new Delegator(clz.getCanonicalName(), init) : new Delegator(clz.getCanonicalName())); | |
} | |
static final Map<String, Function<Object, Map<String, Object>>> copier = new ConcurrentHashMap<>(); | |
public static Map<String, Object> copy(Object instance, Class<?> face) { | |
if (copier.containsKey(face.getCanonicalName())) { | |
return copier.get(face.getCanonicalName()).apply(instance); | |
} | |
final List<Tuple2<String, Function<Object, Object>>> m = Seq.of(face.getMethods()).map(x -> | |
x.getName().startsWith("is") ? tuple(x.getName().substring(2), Sneaky.function(x::invoke)) : | |
x.getName().startsWith("get") ? tuple(x.getName().substring(3), Sneaky.function(x::invoke)) : null | |
).filter(Objects::nonNull).collect(Collectors.toList()); | |
Function<Object, Map<String, Object>> extractor = o -> { | |
final Map<String, Object> values = new HashMap<>(); | |
for (Tuple2<String, Function<Object, Object>> func : m) { | |
values.put(func.v1, func.v2.apply(o)); | |
} | |
return values; | |
}; | |
synchronized (copier) { | |
copier.put(face.getCanonicalName(), extractor); | |
} | |
return extractor.apply(instance); | |
} | |
} | |
public interface RsTest { | |
static void main(String[] args) { | |
nested(); | |
} | |
static void nested() { | |
final SomeOther iface = ProtoUtil.delegate(SomeOther.class, Seq.of(tuple("Id", 1L)).toMap(Tuple2::v1, Tuple2::v2)); | |
final SomeTest someTest = ProtoUtil.delegate(SomeTest.class, Seq.of( | |
tuple("Id", 1L) | |
, tuple("Other", Seq.of(tuple("1", iface)).toMap(Tuple2::v1, Tuple2::v2)) | |
, tuple("Others", Arrays.asList(iface, iface)) | |
).toMap(Tuple2::v1, Tuple2::v2)); | |
final Optional<byte[]> data = ProtoUtil.to(someTest); | |
System.out.println(Arrays.toString(data.get())+":"+data.get().length); | |
System.out.println(new String(data.get())); | |
final Optional<Delegator> result = ProtoUtil.from(data.get(), Delegator.class); | |
final Delegator delegator = result.get(); | |
final SomeTest som = (SomeTest)delegator.delegate(); | |
System.out.println(som); | |
System.out.println(som.getId()); | |
System.out.println(som.getOther()); | |
System.out.println(som.getOther().get("1")); | |
System.out.println(som.getOther().get("1") instanceof SomeOther); | |
System.out.println(som.getOther().get("1").getId()); | |
System.out.println(som.getOthers().size()); | |
System.out.println(som.getOthers().get(0)); | |
} | |
static void simple() { | |
final SomeOther iface = ProtoUtil.delegate(SomeOther.class, Seq.of(tuple("Id", 1L)).toMap(Tuple2::v1, Tuple2::v2)); | |
final Optional<byte[]> data = ProtoUtil.to(iface); | |
System.out.println(Arrays.toString(data.get())); | |
System.out.println(new String(data.get())); | |
final Optional<Delegator> result = ProtoUtil.from(data.get(), Delegator.class); | |
System.out.println(result); | |
final SomeOther someOther = result.get().proxy(SomeOther.class); | |
final SomeOther someOther1 = (SomeOther) result.get().delegate(); | |
System.out.println(someOther.getId()); | |
System.out.println(someOther1.getId()); | |
final Map<String, Object> values = Delegator.copy(someOther, SomeOther.class); | |
System.out.println(values); | |
} | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment