Skip to content

Commit

Permalink
OJRestApplications now provide a JSON serialization mechanism for OJB…
Browse files Browse the repository at this point in the history
…eans
  • Loading branch information
SimonDan committed Apr 22, 2020
1 parent 4b8f7c3 commit a267fe2
Show file tree
Hide file tree
Showing 5 changed files with 119 additions and 14 deletions.
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package de.adito.ojcms.sql.datasource.util;
package de.adito.ojcms.beans.exceptions.bean;

/**
* Exception for bean serialization problems.
Expand All @@ -20,10 +20,11 @@ public BeanSerializationException(String pMessage)
/**
* Creates a new serialization exception.
*
* @param pThrowable the cause for the exception
* @param pMessage a detailed error message
* @param pThrowable the cause of the exception
*/
public BeanSerializationException(Throwable pThrowable)
public BeanSerializationException(String pMessage, Throwable pThrowable)
{
super(pThrowable);
super(pMessage, pThrowable);
}
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package de.adito.ojcms.rest.application;

import de.adito.ojcms.rest.serialization.GSONSerializationProvider;
import de.adito.ojcms.rest.serialization.*;

import javax.ws.rs.*;
import javax.ws.rs.core.Application;
Expand Down Expand Up @@ -36,6 +36,7 @@ protected OJRestApplication(Class<?>... pRestResources)
}

providerAndResourceTypes.add(GSONSerializationProvider.class);
providerAndResourceTypes.add(BeanSerializationProvider.class);
}

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,102 @@
package de.adito.ojcms.rest.serialization;

import de.adito.ojcms.beans.IBean;
import de.adito.ojcms.beans.exceptions.bean.BeanSerializationException;
import de.adito.ojcms.beans.literals.fields.serialization.ISerializableField;
import sun.reflect.generics.reflectiveObjects.ParameterizedTypeImpl;

import javax.ws.rs.*;
import javax.ws.rs.core.*;
import javax.ws.rs.ext.*;
import java.io.*;
import java.lang.annotation.Annotation;
import java.lang.reflect.*;
import java.util.Map;

import static de.adito.ojcms.rest.auth.util.GSONFactory.GSON;
import static java.util.stream.Collectors.toMap;

/**
* JSON serialization provider for {@link IBean} instances. Only possible for beans that contain {@link ISerializableField} only.
*
* @author Simon Danner, 21.04.2020
*/
@Provider
@Consumes(MediaType.APPLICATION_JSON)
@Produces(MediaType.APPLICATION_JSON)
public class BeanSerializationProvider implements MessageBodyReader<IBean>, MessageBodyWriter<IBean>
{
@Override
public boolean isReadable(Class<?> pType, Type pGenericType, Annotation[] pAnnotations, MediaType pMediaType)
{
return true;
}

@Override
public IBean readFrom(Class<IBean> pType, Type pGenericType, Annotation[] pAnnotations, MediaType pMediaType,
MultivaluedMap<String, String> pHttpHeaders, InputStream pEntityStream) throws IOException, WebApplicationException
{
try (InputStreamReader reader = new InputStreamReader(pEntityStream))
{
final ParameterizedType mapType = ParameterizedTypeImpl.make(Map.class, new Type[]{String.class, Serializable.class}, IBean.class);
final Map<String, Serializable> serialBeanValues = GSON.fromJson(reader, mapType);

try
{
final IBean bean = createEmptyBeanInstance(pType);
for (Map.Entry<String, Serializable> entry : serialBeanValues.entrySet())
{
final ISerializableField field = (ISerializableField) bean.getFieldByName(entry.getKey());
//noinspection unchecked
bean.setValue(field, field.fromPersistent(entry.getValue()));
}

return bean;
}
catch (IllegalAccessException | InvocationTargetException | InstantiationException | NoSuchMethodException pE)
{
throw new BeanSerializationException("Provide a default constructor for " + pType.getName() + "! May be private!", pE);
}
}
}

@Override
public boolean isWriteable(Class<?> pType, Type pGenericType, Annotation[] pAnnotations, MediaType pMediaType)
{
return true;
}

@Override
public void writeTo(IBean pBean, Class<?> pType, Type pGenericType, Annotation[] pAnnotations, MediaType pMediaType,
MultivaluedMap<String, Object> pHttpHeaders, OutputStream pEntityStream) throws IOException, WebApplicationException
{
if (pBean.streamFields().allMatch(pField -> pField instanceof ISerializableField))
throw new BeanSerializationException("Bean of type " + pType.getName() + " has non serializable fields!");

//noinspection unchecked
final Map<String, Serializable> serialBeanValues = pBean.stream() //
.collect(toMap(pTuple -> pTuple.getField().getName(),
pTuple -> ((ISerializableField) pTuple.getField()).toPersistent(pTuple.getValue())));

try (PrintWriter writer = new PrintWriter(pEntityStream))
{
writer.write(GSON.toJson(serialBeanValues));
}
}

/**
* Creates an empty bean instance by calling its default constructor.
*
* @param pBeanType the bean type to instantiate
* @return the create bean instance
*/
private static IBean createEmptyBeanInstance(Class<IBean> pBeanType)
throws IllegalAccessException, InvocationTargetException, InstantiationException, NoSuchMethodException
{
final Constructor<IBean> defaultConstructor = pBeanType.getDeclaredConstructor();
if (!defaultConstructor.isAccessible())
defaultConstructor.setAccessible(true);

return defaultConstructor.newInstance();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -24,32 +24,32 @@
public class GSONSerializationProvider implements MessageBodyReader<Object>, MessageBodyWriter<Object>
{
@Override
public boolean isReadable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
public boolean isReadable(Class<?> pType, Type pGenericType, Annotation[] pAnnotations, MediaType pMediaType)
{
return true;
}

@Override
public Object readFrom(Class<Object> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, String> httpHeaders, InputStream entityStream) throws IOException, WebApplicationException
public Object readFrom(Class<Object> pType, Type pGenericType, Annotation[] pAnnotations, MediaType pMediaType,
MultivaluedMap<String, String> pHttpHeaders, InputStream pEntityStream) throws IOException, WebApplicationException
{
try (InputStreamReader reader = new InputStreamReader(entityStream))
try (InputStreamReader reader = new InputStreamReader(pEntityStream))
{
return GSON.fromJson(reader, type);
return GSON.fromJson(reader, pType);
}
}

@Override
public boolean isWriteable(Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType)
public boolean isWriteable(Class<?> pType, Type pGenericType, Annotation[] pAnnotations, MediaType pMediaType)
{
return true;
}

@Override
public void writeTo(Object pObject, Class<?> type, Type genericType, Annotation[] annotations, MediaType mediaType,
MultivaluedMap<String, Object> httpHeaders, OutputStream entityStream) throws WebApplicationException
public void writeTo(Object pObject, Class<?> pType, Type pGenericType, Annotation[] pAnnotations, MediaType pMediaType,
MultivaluedMap<String, Object> pHttpHeaders, OutputStream pEntityStream) throws WebApplicationException
{
try (PrintWriter writer = new PrintWriter(entityStream))
try (PrintWriter writer = new PrintWriter(pEntityStream))
{
writer.write(GSON.toJson(pObject));
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package de.adito.ojcms.sql.datasource.util;

import de.adito.ojcms.beans.exceptions.bean.BeanSerializationException;
import de.adito.ojcms.beans.literals.fields.IField;
import de.adito.ojcms.beans.literals.fields.serialization.ISerializableField;
import de.adito.ojcms.beans.literals.fields.util.*;
Expand Down

0 comments on commit a267fe2

Please sign in to comment.