Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

#806 Support custom ClassLookup in WireTypeConverter #807

Draft
wants to merge 3 commits into
base: develop
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
18 changes: 14 additions & 4 deletions src/main/java/net/openhft/chronicle/wire/WireTypeConverter.java
Original file line number Diff line number Diff line change
@@ -1,27 +1,37 @@
package net.openhft.chronicle.wire;

import net.openhft.chronicle.core.pool.ClassLookup;
import net.openhft.chronicle.wire.internal.WireTypeConverterInternal;
import org.jetbrains.annotations.NotNull;

public class WireTypeConverter {
private final WireTypeConverterInternal delegate;

public WireTypeConverter(Validate validate) {
public WireTypeConverter(@NotNull Validate validate, @NotNull ClassLookup classLookup) {
delegate = new WireTypeConverterInternal(validate, classLookup);
}

public WireTypeConverter(@NotNull ClassLookup classLookup) {
delegate = new WireTypeConverterInternal(classLookup);
}

public WireTypeConverter(@NotNull Validate validate) {
delegate = new WireTypeConverterInternal(validate);
}

public WireTypeConverter() {
delegate = new WireTypeConverterInternal();
}

public CharSequence jsonToYaml(CharSequence json) throws Exception {
public CharSequence jsonToYaml(CharSequence json) {
return delegate.jsonToYaml(json);
}

public CharSequence yamlToJson(CharSequence yaml) throws Exception {
public CharSequence yamlToJson(CharSequence yaml) {
return delegate.yamlToJson(yaml);
}

public void addAlias(Class newClass, String oldTypeName) {
public void addAlias(Class<?> newClass, String oldTypeName) {
delegate.addAlias(newClass, oldTypeName);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package net.openhft.chronicle.wire.internal;

import net.openhft.chronicle.core.io.IORuntimeException;
import net.openhft.chronicle.core.io.InvalidMarshallableException;
import net.openhft.chronicle.wire.SelfDescribingMarshallable;
import net.openhft.chronicle.wire.WireIn;
import net.openhft.chronicle.wire.WireOut;
import org.jetbrains.annotations.NotNull;

import java.util.LinkedHashMap;
import java.util.Map;

public abstract class UnknownClassBase extends SelfDescribingMarshallable {
private Map<Object, Object> map = new LinkedHashMap<>();

@Override
public void readMarshallable(@NotNull WireIn wire) throws IORuntimeException, InvalidMarshallableException {
map = wire.readAllAsMap(Object.class, Object.class, new LinkedHashMap<>());
}

@Override
public void writeMarshallable(@NotNull WireOut wire) throws InvalidMarshallableException {
wire.writeAllAsMap(Object.class, Object.class, map);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,55 @@
package net.openhft.chronicle.wire.internal;

import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.OS;
import net.openhft.chronicle.core.pool.ClassLookup;
import net.openhft.chronicle.core.util.ClassNotFoundRuntimeException;
import net.openhft.compiler.CachedCompiler;

import java.io.File;

public class UnknownClassLookup implements ClassLookup {
public static final CachedCompiler CACHED_COMPILER =
new CachedCompiler(Jvm.isDebug() ? new File(OS.getTarget(), "generated-test-sources") : null, null);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This class should be moved to test sources or target has to be adjusted respectively.


private final ClassLookup delegate;

public UnknownClassLookup(ClassLookup delegate) {
this.delegate = delegate;
}

@Override
public Class<?> forName(CharSequence name) throws ClassNotFoundRuntimeException {
try {
return delegate.forName(name);
} catch (Exception e) {
String[] parts = name.toString().split("\\.");
String className = parts[parts.length - 1];
Class<?> unknownClass;
try {
unknownClass = CACHED_COMPILER.loadFromJava(className,
"public class " + className + " extends " + UnknownClassBase.class.getName() + "{}");
} catch (ClassNotFoundException ex) {
throw new ClassNotFoundRuntimeException(ex);
}

addAlias(unknownClass, className);
return delegate.forName(name);
}
}

@Override
public String nameFor(Class<?> clazz) throws IllegalArgumentException {
return delegate.nameFor(clazz);
}

@Override
public void addAlias(Class<?>... classes) {
delegate.addAlias(classes);
}

@Override
public void addAlias(Class<?> clazz, String names) {
delegate.addAlias(clazz, names);
}
}
Original file line number Diff line number Diff line change
@@ -1,104 +1,129 @@
package net.openhft.chronicle.wire.internal;

import net.openhft.chronicle.bytes.Bytes;
import net.openhft.chronicle.core.Jvm;
import net.openhft.chronicle.core.pool.ClassAliasPool;
import net.openhft.chronicle.core.pool.ClassLookup;
import net.openhft.chronicle.core.util.ClassNotFoundRuntimeException;
import net.openhft.chronicle.wire.Validate;
import net.openhft.chronicle.wire.Wire;
import net.openhft.chronicle.wire.WireType;
import org.jetbrains.annotations.NotNull;

import java.util.function.Consumer;

public class WireTypeConverterInternal {
private static final Validate NO_OP = x -> {
};
private final Bytes bytes = Bytes.allocateElasticOnHeap();
private final Bytes<?> bytes = Bytes.allocateElasticOnHeap();
private final Wire yamlWire = WireType.YAML_ONLY.apply(bytes);
private final Wire jsonWire = WireType.JSON_ONLY.apply(bytes);

private final Validate validate;

private final ExceptionCatchingClassLookup exceptionCatchingClassLookup;

private Exception e;

public WireTypeConverterInternal(@NotNull Validate validate) {
public WireTypeConverterInternal(@NotNull Validate validate, @NotNull ClassLookup classLookup) {
this.validate = validate;
replaceClassLookup(jsonWire);
replaceClassLookup(yamlWire);
this.exceptionCatchingClassLookup =
new ExceptionCatchingClassLookup(classLookup, this::onException);
jsonWire.classLookup(exceptionCatchingClassLookup);
yamlWire.classLookup(exceptionCatchingClassLookup);
}

public WireTypeConverterInternal(@NotNull Validate validate) {
this(validate, ClassAliasPool.CLASS_ALIASES);
}

public WireTypeConverterInternal(@NotNull ClassLookup classLookup) {
this(NO_OP, classLookup);
}

public WireTypeConverterInternal() {
this.validate = NO_OP;
replaceClassLookup(jsonWire);
replaceClassLookup(yamlWire);
this(NO_OP, ClassAliasPool.CLASS_ALIASES);
}

private void onException(Exception e) {
this.e = e;
}

public CharSequence jsonToYaml(CharSequence json) throws Exception {
public CharSequence jsonToYaml(CharSequence json) {
e = null;
jsonWire.reset();
jsonWire.bytes().append(json);

Object object = jsonWire.getValueIn().object();
if (e != null)
throw e;
throw Jvm.rethrow(e);

validate.validate(object);
yamlWire.reset();
yamlWire.getValueOut().object(object);
if (e != null)
throw e;
throw Jvm.rethrow(e);

return yamlWire.bytes();
}

public CharSequence yamlToJson(CharSequence yaml) throws Exception {
public CharSequence yamlToJson(CharSequence yaml) {
e = null;
yamlWire.reset();
yamlWire.bytes().clear().append(yaml);
Object object = yamlWire.getValueIn().object();

if (e != null)
throw e;
throw Jvm.rethrow(e);

validate.validate(object);
jsonWire.reset();
jsonWire.bytes().clear();
jsonWire.getValueOut().object(object);
if (e != null)
throw e;
throw Jvm.rethrow(e);

return jsonWire.bytes();
}

private void replaceClassLookup(Wire wire) {
final ClassLookup delegate = wire.classLookup();
wire.classLookup(new ClassLookup() {

@Override
public Class<?> forName(CharSequence name) throws ClassNotFoundRuntimeException {
try {
return delegate.forName(name);
} catch (Exception e) {
WireTypeConverterInternal.this.e = e;
throw e;
}
private static class ExceptionCatchingClassLookup implements ClassLookup {
private final ClassLookup delegate;
private final Consumer<Exception> onException;

private ExceptionCatchingClassLookup(ClassLookup delegate, Consumer<Exception> onException) {
this.delegate = delegate;
this.onException = onException;
}

@Override
public Class<?> forName(CharSequence name) throws ClassNotFoundRuntimeException {
try {
return delegate.forName(name);
} catch (Exception e) {
onException.accept(e);
throw e;
}

@Override
public String nameFor(Class<?> clazz) throws IllegalArgumentException {
try {
return delegate.nameFor(clazz);
} catch (Exception e) {
WireTypeConverterInternal.this.e = e;
throw e;
}
}

@Override
public String nameFor(Class<?> clazz) throws IllegalArgumentException {
try {
return delegate.nameFor(clazz);
} catch (Exception e) {
onException.accept(e);
throw e;
}
}

@Override
public void addAlias(Class<?>... classes) {
delegate.addAlias(classes);
}
@Override
public void addAlias(Class<?>... classes) {
delegate.addAlias(classes);
}

@Override
public void addAlias(Class<?> clazz, String names) {
delegate.addAlias(clazz, names);
}
});
@Override
public void addAlias(Class<?> clazz, String names) {
delegate.addAlias(clazz, names);
}
}

/**
Expand All @@ -107,8 +132,7 @@ public void addAlias(Class<?> clazz, String names) {
* @param newClass to use instead
* @param oldTypeName to support
*/
public void addAlias(Class newClass, String oldTypeName) {
jsonWire.classLookup().addAlias(newClass, oldTypeName);
yamlWire.classLookup().addAlias(newClass, oldTypeName);
public void addAlias(Class<?> newClass, String oldTypeName) {
exceptionCatchingClassLookup.addAlias(newClass, oldTypeName);
}
}
Loading