Improve class using generics - java

I have a lot of identical classes. I want to improve it using generics:
public class PingInitializer extends AbstractHandler implements DataWarehouseInitializer<PingInteraction, PingInvocation> {
#Handler
#Override
public PingInteraction handle(Message message) throws IOException, MessageException {
checkMessageIsNotNull(message);
PingInvocation invocation = construct(message.getBody().toString());
l.debug("handling in initializer... {}", invocation);
return new PingInteraction(invocation);
}
public PingInvocation construct(String message) throws IOException, MessageException {
ObjectMapper mapper = new ObjectMapper();
PingInvocation invocation;
try {
invocation = mapper.readValue(message, PingInvocation.class);
} catch (IOException e) {
throw new MessageException("Can't deserialize message", e);
}
return invocation;
}
}
I want to create new abstract class AbstractInitializer and all child class just have to specify generic type:
public abstract class AbstractInitializer<INTERACTION, INVOCATION> extends AbstractHandler {
#Handler
public INTERACTION handle(Message message) throws IOException, MessageException {
checkMessageIsNotNull(message);
INVOCATION invocation = construct(message.getBody().toString());
l.debug("handling in initializer... {}", invocation);
return **new INTERACTION(invocation)**; //HERE!
}
public INVOCATION construct(String message) throws IOException, MessageException {
ObjectMapper mapper = new ObjectMapper();
INVOCATION invocation;
try {
invocation = mapper.readValue(message, **INVOCATION.class** /*<- and HERE */);
} catch (IOException e) {
throw new MessageException("Can't deserialize message", e);
}
return invocation;
}
}
But I have two compilers error and don't know how to bypass this issue. I mark them in code

Java generics are implemented with type erasure for compatibility reasons, which ultimately means you cannot use a mere type parameter to instantiate a new object.
What you'll need is to modify AbstractInitializer a bit...
private final Class<INTERACTION> interactionType;
private final Class<INVOCATION> invocationType;
private final Constructor<INTERACTION> interactionConstructor;
public AbstractInitializer(final Class<INTERACTION> interactionType,
final Class<INVOCATION> invocationType) throws NoSuchMethodException {
this.interactionType = interactionType;
this.invocationType = invocationType;
interactionConstructor = interactionType.getConstructor(invocationType);
}
public INTERACTION handle(Message message) throws IOException, MessageException,
InvocationTargetException, InstantiationException, IllegalAccessException {
checkMessageIsNotNull(message);
INVOCATION invocation = construct(message.getBody().toString());
l.debug("handling in initializer... {}", invocation);
return interactionConstructor.newInstance(invocation);
}
public INVOCATION construct(String message) throws IOException, MessageException {
ObjectMapper mapper = new ObjectMapper();
INVOCATION invocation;
try {
invocation = mapper.readValue(message, invocationType);
} catch (IOException e) {
throw new MessageException("Can't deserialize message", e);
}
return invocation;
}
then, as an e.g.
public final class PingInitializer
extends AbstractInitializer<PingInteraction, PingInvocation> {
public PingInitializer() {
super(PingInteraction.class, PingInvocation.class);
}
}
or, you could ditch making it abstract and use it like...
public static GenericInitializer<A, B> createInitializer(final Class<A> a,
final Class<B> b) {
return new GenericInitializer<A, B>(a, b);
}
final GenericInitializer<PingInteraction, PingInvocation> pingInitializer
= createInitializer(PingInteraction.class, PingInvocation.class);
I believe this should all be possible. I probably made some dumb errors, however, given I typed this in the response box in a hurry.

Related

How to throw a custom exception from a custom deserializer

I have a class deserialized by my custom deserializer and I need to throw my custom exception.
public class MyClass {
}
public static void main(String[] args) {
try {
ObjectMapper mapper = new ObjectMapped();
MyClass myClass = mapper.readValue(json, MyClass.class);
} catch(Exception e) {
System.out.println(e); // JsonMappingException
System.out.println(e.getCause()); // null, but I need to get my CustomException.class
}
}
public class MyDeserializer extends StdDeserializer<MyClass> {
#Override
public MyClass deserialize(JsonParser jsonParser, DeserializationContext deserializationContext) throws IOException {
throw new CustomException("TestException", 1);
}
}
public class CustomException extends IOException {
private int code;
public CustomException(String message, int code) {
super(message);
this.code = code;
}
public int getCode() {
return code;
}
}
What can I do to return my custom exception from the custom deserializer?
First you should add custom desializer to ObjectMapper or target Object like below
#JsonDeserialize(using = MyDeserializer.class)
public class MyClass {
}
getCause() return null because you already have caused Exception, check getCause implementation
public synchronized Throwable getCause() {
return (cause==this ? null : cause);
}
shorty you don't need to getCause, e is your CustomException instance. Just do first step and remove getCause, it ll be fine.

Generic type for static method

Is there a way I can make the static method toObject generic by passing the T class and return type T?
public class JsonUtil {
private JsonUtil() {
}
public static Object toObject(String jsonString, Class clazz, boolean unwrapRootValue) throws TechnicalException {
ObjectMapper mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter();
if (unwrapRootValue) mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
try {
return mapper.readValue(jsonString, clazz);
} catch (IOException e) {
throw new TechnicalException("Exception while converting JSON to Object", e);
}
}
}
Sure. Just specify a generic type parameter on the method itself, and use it for both the return type and the clazz parameter:
public static <T> T toObject(String jsonString, Class<T> clazz,
boolean unwrapRootValue) throws TechnicalException {
/* ... */
}
public class JsonUtil {
private JsonUtil() {
}
public static <T> T toObject(String jsonString, Class<? extends T> clazz, boolean unwrapRootValue) throws TechnicalException {
ObjectMapper mapper = new ObjectMapper();
mapper.writerWithDefaultPrettyPrinter();
if (unwrapRootValue) mapper.enable(DeserializationFeature.UNWRAP_ROOT_VALUE);
try {
return mapper.readValue(jsonString, clazz);
} catch (IOException e) {
throw new TechnicalException("Exception while converting JSON to Object", e);
}
}
}

Execute InvocationHandler invoke method for each method at my proxied class

I have implement a dynamic proxy in order to do some operations before my methods started.
now I have a problem when invoking two methods from the proxied class, here is the code:
Dynamic proxy class:
public class IPageProxy implements InvocationHandler {
private Class <? extends IPage> screenClazz;
public IPageProxy(final Class <? extends IPage> screenClazz) {
this.screenClazz = screenClazz;
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type)
throws InstantiationException, IllegalAccessException {
List<Class<?>> interfaces = new ArrayList<>();
interfaces.addAll(Arrays.asList(type.getInterfaces()));
return (T) Proxy.newProxyInstance(
type.getClassLoader(),
findInterfaces(type),
new IPageProxy(type)
);
}
static Class<?>[] findInterfaces(final Class<? extends IPage> type) {
Class<?> current = type;
do {
final Class<?>[] interfaces = current.getInterfaces();
if (interfaces.length != 0) {
return interfaces;
}
} while ((current = current.getSuperclass()) != Object.class);
throw new UnsupportedOperationException("The type does not implement any interface");
}
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws InvocationTargetException,
IllegalAccessException, IllegalArgumentException, InstantiationException, ParserConfigurationException, XPathExpressionException, NoSuchFieldException, SecurityException {
// before method executed this code will be done
System.out.println("* Dynamic proxy invoke method executed for " + method.getName());
// Invoke original method
return method.invoke(screenClazz.newInstance(), args);
}
}
Main class:
public static void main(String[] args) {
try {
//IEventDesignDialog a = new EventDesignDialog();
IEventDesignDialog a = (IEventDesignDialog)getInstance(EventDesignDialog.class);
a.getEventType().getShow();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.getInstance(type);
}
Proxied class:
public class EventDesignDialog implements IEventDesignDialog{
private String show;
private String dateAndTimeDisplayFormat;
private String eventType;
#Entity(visibileName = "Show")
public IEventDesignDialog getShow() {
System.out.println("get show method invokde successfully");
return this;
}
#Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invokde successfully");
return this;
}
#Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType() {
System.out.println("get event type method invokde successfully");
return this;
}
}
Actual output:
*** Dynamic proxy invoke method executed for getEventType
get event type method invokde successfully
get show method invokde successfully**
as shown invoke method executed only at the first method invocation after initializing the proxy, second method invoked directly, without proxy functionality is done.
my goal is to execute invoke method each time a method appears at my collection is invoked, the expected result should be as shown below.
Expected output:
*** Dynamic proxy invoke method executed for getEventType
get event type method invokde successfully
* Dynamic proxy invoke method executed for getShow
get show method invokde successfully**
please let me know if more explanations needed.
I have solve this issue by creating an Interface with default method that return proxy instance, then returned it after executing the invoked method functionality:
updated code:
public interface IPage {
default <T extends IPage> T getProxyInstance() {
try {
return (T) IPageProxy.getInstance(this.getClass());
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
return null;
}
}
My Page Interface:
#Page(path = "MyPath")
public interface IEventDesignDialog extends IPage{
#Entity(visibileName = "Show")
public IEventDesignDialog getShow();
#Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat();
#Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType();
}
My Page class:
#Page(path = "MyPath")
public class EventDesignDialog implements IEventDesignDialog{
#Entity(visibileName = "Show")
public IEventDesignDialog getShow() {
System.out.println("get show method invokde successfully");
return getProxyInstance();
}
#Entity(visibileName = "Date And Time display format")
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invokde successfully");
return getProxyInstance();
}
#Entity(visibileName = "Event Type")
public IEventDesignDialog getEventType() {
System.out.println("get event type method invokde successfully");
return getProxyInstance();
}
}
main class:
public class Main {
public static void main(String[] args) {
try {
IEventDesignDialog a = ((IEventDesignDialog)getInstance(EventDesignDialog.class)).getEventType().getShow();
((IShowDesignDialog)getInstance(ShowDesignDialog.class)).getShowName().getShowType();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T getInstance(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.getInstance(type);
}
}
IProxy Page stay the same without changes.
What is happening is that first you proxy only the first invocation , but then you invoke getShow() on a non proxified class, and this is why you get the result like you mentioned. If you want to achieve the goal you mentioned you need to make another proxy based on created instance rather than on just on the class.
Update:
I will provide example code, you can paste in any java file and execute it.
Where you see the TODO, you can place your own logic depending how you want to supply proxy. See NOTE for important moments. I placed all classes in one file for simplicity of demonstration.
import java.lang.reflect.InvocationHandler;
import java.lang.reflect.Method;
import java.lang.reflect.Proxy;
import java.util.Arrays;
import java.util.List;
class Scratch {
public static void main(String[] args) {
try {
IEventDesignDialog a = proxy(EventDesignDialog.class);
a.getEventType().getShow();
a.getDateAndTimeDisplayFormat().getShow();
} catch (InstantiationException | IllegalAccessException e) {
e.printStackTrace();
}
}
#SuppressWarnings("unchecked")
private static <T extends IPage> T proxy(final Class<? extends IPage> type) throws InstantiationException, IllegalAccessException {
return (T) IPageProxy.proxy(type);
}
}
interface IPage{}
interface IEventDesignDialog extends IPage{
IEventDesignDialog getShow();
IEventDesignDialog getEventType();
IEventDesignDialog getDateAndTimeDisplayFormat();
}
class EventDesignDialog implements IEventDesignDialog{
public IEventDesignDialog getShow() {
System.out.println("get show method invoked successfully");
//NOTE: this will be treated as same proxy but not this
return this;
}
public IEventDesignDialog getDateAndTimeDisplayFormat() {
System.out.println("get date and time display format method invoked successfully");
// NOTE: we supply some iinstance which will be proxied
return new MyIEventDesignDialog();
}
public IEventDesignDialog getEventType() {
System.out.println("get event type method invoked successfully");
//NOTE: this will be treated as same proxy but not this
return this;
}
}
class IPageProxy implements InvocationHandler {
private IPage instance;
private List<Class<?>> interfaces;
public IPageProxy(IPage instance, List<Class<?>> interfaces) {
this.instance = instance;
this.interfaces = interfaces;
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T proxy(final Class<? extends IPage> type)
throws InstantiationException, IllegalAccessException {
List<Class<?>> interfaces = Arrays.asList(type.getInterfaces());
//TODO: get interfaces properly recursively
return (T) Proxy.newProxyInstance(
type.getClassLoader(),
type.getInterfaces(),
new IPageProxy(type.newInstance(), interfaces)
);
}
#SuppressWarnings("unchecked")
public static <T extends IPage> T proxy(T object) {
//TODO: get interfaces properly recursively
List<Class<?>> interfaces = Arrays.asList(object.getClass().getInterfaces());
return (T) Proxy.newProxyInstance(
object.getClass().getClassLoader(),
object.getClass().getInterfaces(),
new IPageProxy(object, interfaces)
);
}
#Override
public Object invoke(final Object proxy, final Method method, final Object[] args) throws Exception {
// before method executed this code will be done
System.out.println("* Dynamic proxy invoke method executed for " + method.getName());
// Invoke original method
Object invoke = method.invoke(instance, args);
if (invoke == null) {
return null;
}
//If some of the method returns the original object
//we swap the returned object by our proxy
if (invoke == instance) {
return proxy;
}
//TODO: check if you want to swap in place
//other interfaces
if (interfaces.contains(method.getReturnType())) {
return IPageProxy.proxy((IPage)invoke);
}
return invoke;
}
}
class MyIEventDesignDialog implements IEventDesignDialog {
#Override
public IEventDesignDialog getShow() {
return null;
}
#Override
public IEventDesignDialog getEventType() {
return null;
}
#Override
public IEventDesignDialog getDateAndTimeDisplayFormat() {
return null;
}
}
Output:
* Dynamic proxy invoke method executed for getEventType
get event type method invoked successfully
* Dynamic proxy invoke method executed for getShow
get show method invoked successfully
* Dynamic proxy invoke method executed for getDateAndTimeDisplayFormat
get date and time display format method invoked successfully
* Dynamic proxy invoke method executed for getShow
You can get ideas from how Mockito is working. Please check this page: https://static.javadoc.io/org.mockito/mockito-core/2.27.0/org/mockito/Mockito.html#spy-T-
I know it is for testing, but you still can get ideas from it.
So you can apply spy() on a class and on an object to spy on it.

How to alter the value of a JsonNode while constructing it from a string in Jackson

I have a JSON string and I want to alter the value while constructing the JsonNode using Jackson library.
eg:-
input: {"name":"xyz","price":"90.00"}
output:{"name":"xyz-3","price":90.90}
I created my own JsonFactory and passed my own Parser. but I can only alter the keys, not the values associated with a key.
code:
private static ObjectMapper create() {
ObjectMapper objectMapper = new ObjectMapper(new JsonFactory() {
#Override
protected JsonParser _createParser(byte[] data, int offset, int len, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(data, offset, len, ctxt));
}
#Override
protected JsonParser _createParser(InputStream in, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(in, ctxt));
}
#Override
protected JsonParser _createParser(Reader r, IOContext ctxt) throws IOException {
return new MyParser(super._createParser(r, ctxt));
}
#Override
protected JsonParser _createParser(char[] data, int offset, int len, IOContext ctxt, boolean recyclable)
throws IOException {
return new MyParser(super._createParser(data, offset, len, ctxt, recyclable));
}
});
private static final class MyParser extends JsonParserDelegate {
private MyParser(JsonParser d) {
super(d);
}
#Override
public String getCurrentName() throws IOException, JsonParseException {
....
}
#Override
public String getText() throws IOException, JsonParseException {
...
}
#Override
public Object getCurrentValue() {
...
}
#Override
public String getValueAsString() throws IOException {
...
}
#Override
public String getValueAsString(String defaultValue) throws IOException {
...
}
}
Below is the code to construct the JsonNode from the string.
mapper.readTree(jsonStr);
In this case when the readTree method is called the getCurrentValue or getValueAsString methods are not called, so I am not able to alter the value while creating the JsonNode itself.
Also the json strings can be different. Basically I want to construct a JsonNode from the string. so tying to a specific schema/bean is not a good choice here.
How to address this ? TIA
Adding the updated code for version 2.7.4:-
static class MyParser extends JsonParserDelegate {
MyParser(final JsonParser delegate) {
super(delegate);
}
#Override
public String getText() throws IOException {
final String text = super.getText();
if ("name".equals(getCurrentName())) {
return text + "-3";
}
return text;
}
#Override
public JsonToken nextToken() throws IOException {
if ("price".equals(getCurrentName())) {
// Advance token anyway
super.nextToken();
return JsonToken.VALUE_NUMBER_FLOAT;
}
return super.nextToken();
}
#Override
public int getCurrentTokenId() {
try {
if ("price".equals(getCurrentName())) {
return JsonTokenId.ID_NUMBER_FLOAT;
}
} catch (final IOException e) {
//
}
return super.getCurrentTokenId();
}
#Override
public NumberType getNumberType() throws IOException {
if ("price".equals(getCurrentName())) {
return NumberType.FLOAT;
}
return super.getNumberType();
}
#Override
public float getFloatValue() throws IOException {
return Float.parseFloat(getValueAsString("0")) + 0.09F;
}
#Override
public double getDoubleValue() throws IOException {
return Double.parseDouble(getValueAsString("0")) + 0.09D;
}
}
pom.xml:-
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-yaml</artifactId>
<version>2.8.7</version>
<!--<scope>test</scope>-->
</dependency>
<dependency>
<groupId>com.fasterxml.jackson.dataformat</groupId>
<artifactId>jackson-dataformat-xml</artifactId>
<version>2.8.7</version>
</dependency>
Edit: there is a subtle difference between 2.7.* and 2.9.*.
While 2.9.* is able to differentiate between double and float with
getDoubleValue()
getFloatValue()
instead 2.7.* only uses
getDoubleValue()
even for ID_NUMBER_FLOAT tokens.
So, you need to decide if you want to maintain retro-compatibility or not.
You can also override both, like I did here.
This is all what you need for your custom MyParser
static class MyParser extends JsonParserDelegate {
MyParser(final JsonParser delegate) {
super(delegate);
}
#Override
public String getText() throws IOException {
final String text = super.getText();
if ("name".equals(getCurrentName())) {
return text + "-3";
}
return text;
}
#Override
public JsonToken nextToken() throws IOException {
if ("price".equals(getCurrentName())) {
// Advance token anyway
super.nextToken();
return JsonToken.VALUE_NUMBER_FLOAT;
}
return super.nextToken();
}
#Override
public int getCurrentTokenId() {
try {
if ("price".equals(getCurrentName())) {
return JsonTokenId.ID_NUMBER_FLOAT;
}
} catch (final IOException e) {
//
}
return super.getCurrentTokenId();
}
#Override
public NumberType getNumberType() throws IOException {
if ("price".equals(getCurrentName())) {
return NumberType.FLOAT;
}
return super.getNumberType();
}
#Override
public float getFloatValue() throws IOException {
return Float.parseFloat(getValueAsString("0")) + 0.09F;
}
#Override
public double getDoubleValue() throws IOException {
return Double.parseDouble(getValueAsString("0")) + 0.09D;
}
}
Output: {"name":"xyz-3","price":90.09}
Your code seems fine, and it's tested and working ;)
Are you really sure that regarding the Separation of Concerns it is a good idea to mix parsing and changes within the parsed data?
If you still want to do this, you could use a Custom Deserializer and treat your wanted field names and types the way you want it, like:
class CustomDeserializer extends StdDeserializer<Entity> {
public CustomDeserializer(Class<Entity> t) {
super(t);
}
#Override
public Entity deserialize(JsonParser jp, DeserializationContext dc) throws IOException {
String name = null;
float price = 0;
JsonToken currentToken = null;
while ((currentToken = jp.nextValue()) != null) {
switch (currentToken) {
case VALUE_STRING:
switch (jp.getCurrentName()) {
case "name":
name = jp.getText() + "-3"; // change this text to whatever you want;
break;
case "price":
price = Float.parseFloat(jp.getText()); // parse
break;
default:
break;
}
break;
default:
break;
}
}
return new Entity(name, price);
}
}
And after registering your custom deserializer it works on any object mapper you want:
#Test
public void customDeserialization() throws IOException {
// given
ObjectMapper mapper = new ObjectMapper();
SimpleModule module = new SimpleModule();
module.addDeserializer(Entity.class, new CustomDeserializer(Entity.class));
mapper.registerModule(module);
// when
Entity entity = mapper.readValue("{\"name\":\"xyz\",\"price\":\"90.00\"}", Entity.class);
// then
assertThat(entity.getName()).isEqualTo("xyz-3");
assertThat(entity.getPrice()).isEqualTo(90f);
}

How can I call Test class ctor with params?

I have unit test class and a static main entry method.
I know this is how I run the test class from my main method:
public class SingleJUnitTestRunner {
public static void main(String... args) throws ClassNotFoundException
{
String[] classAndMethod = args[0].split("#");
Request request = Request.method(Class.forName(classAndMethod[0]), classAndMethod[1]);
Result result = new JUnitCore().run(request);
System.exit(result.wasSuccessful() ? 0 : 1);
}
}
Is there a way to call a test-calls ctor with params and then run the tests?
I took a look at the Junit source code and came up with this:
public static void main(String... args) throws ClassNotFoundException {
String[] classAndMethod = args[0].split("#");
Object[] parameters = new Object[] {"constructor parameter"};
Class<?> className = Class.forName(classAndMethod[0]);
String methodName = classAndMethod[1];
Request request = createRequest(parameters, className, methodName);
Result result = new JUnitCore().run(request);
System.exit(result.wasSuccessful() ? 0 : 1);
}
private static Request createRequest(Object[] parameters, Class<?> className, String methodName) {
Description method = Description.createTestDescription(className, methodName);
return new ConstructorParameterRequest(className, parameters).filterWith(method);
}
Custom Request class so that we can use our own runner:
public class ConstructorParameterRequest extends Request {
private Class<?> clazz;
private Object[] parameters;
public ConstructorParameterRequest(Class<?> clazz, Object[] parameters) {
this.clazz = clazz;
this.parameters = parameters;
}
#Override
public Runner getRunner() {
try {
return new ConstructorParameterRunner(clazz, parameters);
} catch (Throwable e) {
return new ErrorReportingRunner(clazz, e);
}
}
}
Custom Runner class which creates the test class with constructor parameters. validateConstructor has to be overriden because BlockJUnit4ClassRunner checks for a zero argument constructor:
public class ConstructorParameterRunner extends BlockJUnit4ClassRunner {
private Object[] parameters;
public ConstructorParameterRunner(Class<?> clazz, Object[] parameters) throws InitializationError {
super(clazz);
this.parameters = parameters;
}
#Override
protected void validateConstructor(List<Throwable> errors) {
validateOnlyOneConstructor(errors);
}
#Override
protected Object createTest() throws Exception {
return getTestClass().getOnlyConstructor().newInstance(parameters);
}
}

Categories

Resources