removed bloat-code from JsonComponents in DTOs
This commit is contained in:
@ -0,0 +1,9 @@
|
||||
package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
/**
|
||||
* A marker interface for DTO classes which can be used by {@link JsonSerializerWithAccessFilter} and {@link JsonDeserializerWithAccessFilter}.
|
||||
*/
|
||||
public interface AccessMappings extends Serializable {
|
||||
}
|
@ -10,8 +10,6 @@ import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.lang.annotation.Annotation;
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
import static com.google.common.base.Verify.verify;
|
||||
|
||||
@ -67,28 +65,16 @@ abstract class JSonAccessFilter<T> {
|
||||
|
||||
final ParentId parentIdAnnot = parentIdField.getAnnotation(ParentId.class);
|
||||
final Class<? extends IdToDtoResolver> parentDtoLoader = parentIdAnnot.resolver();
|
||||
final Class<?> parentDtoClass = getGenericClassParameter(parentDtoLoader);
|
||||
final Long parentId = (Long) ReflectionUtil.getValue(dto, parentIdField);
|
||||
final Class<IdToDtoResolver> rawType = IdToDtoResolver.class;
|
||||
|
||||
final Class<?> parentDtoClass = ReflectionUtil.<T>determineGenericInterfaceParameter(parentDtoLoader, rawType, 0);
|
||||
final Long parentId = ReflectionUtil.getValue(dto, parentIdField);
|
||||
final Role roleOnParent = SecurityUtils.getLoginUserRoleFor(parentDtoClass, parentId);
|
||||
|
||||
final Object parentEntity = loadDto(parentDtoLoader, parentId);
|
||||
return Role.broadest(baseRole, getLoginUserRoleOnAncestorOfDtoClassIfHigher(roleOnParent, parentEntity));
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
private Class<T> getGenericClassParameter(Class<? extends IdToDtoResolver> parentDtoLoader) {
|
||||
for (Type genericInterface : parentDtoLoader.getGenericInterfaces()) {
|
||||
if (genericInterface instanceof ParameterizedType) {
|
||||
final ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
|
||||
if (parameterizedType.getRawType()== IdToDtoResolver.class) {
|
||||
return (Class<T>) parameterizedType.getActualTypeArguments()[0];
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
throw new AssertionError(parentDtoLoader.getSimpleName() + " expected to implement " + IdToDtoResolver.class.getSimpleName() + "<...DTO>");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
protected Object loadDto(final Class<? extends IdToDtoResolver> resolverClass, final Long id) {
|
||||
verify(id != null, "id must not be null for " + resolverClass.getSimpleName());
|
||||
|
@ -18,12 +18,18 @@ import java.util.Set;
|
||||
|
||||
import static org.hostsharing.hsadminng.service.util.ReflectionUtil.unchecked;
|
||||
|
||||
public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
/** Actual implementation of JSON deserialization, where {link JSonDeserializerWithAccessFilter}
|
||||
* is a stateless bean, {@link JSonDeserializationWithAccessFilter} exists only during the actual
|
||||
* deserialization and contains a deserialization state.
|
||||
*
|
||||
* @param <T> DTO class to serialize
|
||||
*/
|
||||
public class JSonDeserializationWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
|
||||
private final TreeNode treeNode;
|
||||
private final Set<Field> writtenFields = new HashSet<>();
|
||||
|
||||
public JSonDeserializerWithAccessFilter(final ApplicationContext ctx, final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) {
|
||||
public JSonDeserializationWithAccessFilter(final ApplicationContext ctx, final JsonParser jsonParser, final DeserializationContext deserializationContext, Class<T> dtoClass) {
|
||||
super(ctx, unchecked(dtoClass::newInstance));
|
||||
this.treeNode = unchecked(() -> jsonParser.getCodec().readTree(jsonParser));
|
||||
}
|
||||
@ -113,7 +119,7 @@ public class JSonDeserializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
} else if (Boolean.class.isAssignableFrom(field.getType()) || boolean.class.isAssignableFrom(field.getType())) {
|
||||
ReflectionUtil.setValue(dto, field, Boolean.valueOf(value.toString()));
|
||||
} else if (field.getType().isEnum()) {
|
||||
ReflectionUtil.setValue(dto, field, Enum.valueOf((Class<Enum>) field.getType(), value.toString()));
|
||||
ReflectionUtil.setValue(dto, field, ReflectionUtil.asEnumValue(field.getType(), value));
|
||||
} else if (LocalDate.class.isAssignableFrom(field.getType())) {
|
||||
ReflectionUtil.setValue(dto, field, LocalDate.parse(value.toString()));
|
||||
} else {
|
@ -12,14 +12,20 @@ import java.lang.reflect.Field;
|
||||
import java.math.BigDecimal;
|
||||
import java.time.LocalDate;
|
||||
|
||||
public class JSonSerializerWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
/** Actual implementation of JSON serialization, where {link JsonSerializerWithAccessFilter}
|
||||
* is a stateless bean, {@link JSonSerializationWithAccessFilter} exists only during the actual
|
||||
* serialization and contains a serialization state.
|
||||
*
|
||||
* @param <T> DTO class to serialize
|
||||
*/
|
||||
public class JSonSerializationWithAccessFilter<T> extends JSonAccessFilter<T> {
|
||||
private final JsonGenerator jsonGenerator;
|
||||
private final SerializerProvider serializerProvider;
|
||||
|
||||
public JSonSerializerWithAccessFilter(final ApplicationContext ctx,
|
||||
final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider,
|
||||
final T dto) {
|
||||
public JSonSerializationWithAccessFilter(final ApplicationContext ctx,
|
||||
final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider,
|
||||
final T dto) {
|
||||
super(ctx, dto);
|
||||
this.jsonGenerator = jsonGenerator;
|
||||
this.serializerProvider = serializerProvider;
|
@ -0,0 +1,24 @@
|
||||
package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import org.hostsharing.hsadminng.service.util.ReflectionUtil;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
public abstract class JsonDeserializerWithAccessFilter<T extends AccessMappings> extends JsonDeserializer<T> {
|
||||
|
||||
private final ApplicationContext ctx;
|
||||
|
||||
public JsonDeserializerWithAccessFilter(final ApplicationContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public T deserialize(final JsonParser jsonParser,
|
||||
final DeserializationContext deserializationContext) {
|
||||
|
||||
final Class<T> dtoClass = ReflectionUtil.determineGenericClassParameter(this.getClass(), JsonDeserializerWithAccessFilter.class, 0);
|
||||
return new JSonDeserializationWithAccessFilter<>(ctx, jsonParser, deserializationContext, dtoClass).deserialize();
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package org.hostsharing.hsadminng.service.accessfilter;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import java.io.IOException;
|
||||
|
||||
/** A base class for a Spring bean for JSON serialization with field-based access filters.
|
||||
* Where {@link JSonSerializationWithAccessFilter} is the actual stateful implementation and
|
||||
* it's instances only exist during the process of serialization, this class is a stateless just
|
||||
* used for service and context injection.
|
||||
*
|
||||
* @param <T> DTO class to serialize
|
||||
*/
|
||||
public abstract class JsonSerializerWithAccessFilter<T extends AccessMappings> extends JsonSerializer<T> {
|
||||
|
||||
protected final ApplicationContext ctx;
|
||||
|
||||
public JsonSerializerWithAccessFilter(final ApplicationContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final T dto, final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider) throws IOException {
|
||||
|
||||
new JSonSerializationWithAccessFilter<>(ctx, jsonGenerator, serializerProvider, dto).serialize();
|
||||
}
|
||||
}
|
@ -1,11 +1,5 @@
|
||||
package org.hostsharing.hsadminng.service.dto;
|
||||
|
||||
import com.fasterxml.jackson.core.JsonGenerator;
|
||||
import com.fasterxml.jackson.core.JsonParser;
|
||||
import com.fasterxml.jackson.databind.DeserializationContext;
|
||||
import com.fasterxml.jackson.databind.JsonDeserializer;
|
||||
import com.fasterxml.jackson.databind.JsonSerializer;
|
||||
import com.fasterxml.jackson.databind.SerializerProvider;
|
||||
import org.hostsharing.hsadminng.domain.enumeration.CustomerKind;
|
||||
import org.hostsharing.hsadminng.domain.enumeration.VatRegion;
|
||||
import org.hostsharing.hsadminng.service.CustomerService;
|
||||
@ -14,15 +8,13 @@ import org.springframework.boot.jackson.JsonComponent;
|
||||
import org.springframework.context.ApplicationContext;
|
||||
|
||||
import javax.validation.constraints.*;
|
||||
import java.io.IOException;
|
||||
import java.io.Serializable;
|
||||
import java.time.LocalDate;
|
||||
import java.util.Objects;
|
||||
|
||||
/**
|
||||
* A DTO for the Customer entity.
|
||||
*/
|
||||
public class CustomerDTO extends FluentBuilder<CustomerDTO> implements Serializable {
|
||||
public class CustomerDTO extends FluentBuilder<CustomerDTO> implements AccessMappings {
|
||||
|
||||
@SelfId(resolver = CustomerService.class)
|
||||
@AccessFor(read = Role.ANY_CUSTOMER_USER)
|
||||
@ -265,36 +257,18 @@ public class CustomerDTO extends FluentBuilder<CustomerDTO> implements Serializa
|
||||
}
|
||||
|
||||
@JsonComponent
|
||||
public static class CustomerJsonSerializer extends JsonSerializer<CustomerDTO> {
|
||||
|
||||
private final ApplicationContext ctx;
|
||||
public static class CustomerJsonSerializer extends JsonSerializerWithAccessFilter<CustomerDTO> {
|
||||
|
||||
public CustomerJsonSerializer(final ApplicationContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void serialize(final CustomerDTO customerDTO, final JsonGenerator jsonGenerator,
|
||||
final SerializerProvider serializerProvider) throws IOException {
|
||||
|
||||
new JSonSerializerWithAccessFilter<>(ctx, jsonGenerator, serializerProvider, customerDTO).serialize();
|
||||
super(ctx);
|
||||
}
|
||||
}
|
||||
|
||||
@JsonComponent
|
||||
public static class CustomerJsonDeserializer extends JsonDeserializer<CustomerDTO> {
|
||||
|
||||
private final ApplicationContext ctx;
|
||||
public static class CustomerJsonDeserializer extends JsonDeserializerWithAccessFilter<CustomerDTO> {
|
||||
|
||||
public CustomerJsonDeserializer(final ApplicationContext ctx) {
|
||||
this.ctx = ctx;
|
||||
}
|
||||
|
||||
@Override
|
||||
public CustomerDTO deserialize(final JsonParser jsonParser,
|
||||
final DeserializationContext deserializationContext) {
|
||||
|
||||
return new JSonDeserializerWithAccessFilter<>(ctx, jsonParser, deserializationContext, CustomerDTO.class).deserialize();
|
||||
super(ctx);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,16 +1,19 @@
|
||||
package org.hostsharing.hsadminng.service.util;
|
||||
|
||||
import java.lang.reflect.Field;
|
||||
import java.lang.reflect.ParameterizedType;
|
||||
import java.lang.reflect.Type;
|
||||
|
||||
public class ReflectionUtil {
|
||||
|
||||
public static <T> void setValue(final T dto, final String fieldName, final Object value) {
|
||||
public static Field getField(final Class<?> aClass, final String fieldName) {
|
||||
try {
|
||||
final Field field = dto.getClass().getDeclaredField(fieldName);
|
||||
field.setAccessible(true);
|
||||
field.set(dto, value);
|
||||
} catch (NoSuchFieldException | IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
return aClass.getDeclaredField(fieldName);
|
||||
} catch (final NoSuchFieldException e) {
|
||||
if (aClass.getSuperclass() != Object.class) {
|
||||
return getField(aClass.getSuperclass(), fieldName);
|
||||
}
|
||||
throw new IllegalArgumentException(e);
|
||||
}
|
||||
}
|
||||
|
||||
@ -18,20 +21,81 @@ public class ReflectionUtil {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
field.set(dto, value);
|
||||
} catch (IllegalAccessException e) {
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
public static <T> T getValue(final T dto, final Field field) {
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T, R> R getValue(final T dto, final Field field) {
|
||||
try {
|
||||
field.setAccessible(true);
|
||||
return (T) field.get(dto);
|
||||
} catch (IllegalAccessException e) {
|
||||
return (R) field.get(dto);
|
||||
} catch (final IllegalAccessException e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the annotations of 'clazz' for an implemented interface 'rawInterface' and returns the class of the actual generics parameter at the specified index.
|
||||
*
|
||||
* @param clazz a class which implements the generic interface 'rawInterface'
|
||||
* @param rawInterface a generic interface
|
||||
* @param paramIndex the index of the generics parameter within 'rawInterface'
|
||||
* @param <T> the expected class of the generics parameter at position 'index' in 'rawInterface'
|
||||
* @return the actual generics parameter
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> determineGenericInterfaceParameter(final Class<?> clazz, final Class<?> rawInterface, final int paramIndex) {
|
||||
for (Type genericInterface : clazz.getGenericInterfaces()) {
|
||||
if (genericInterface instanceof ParameterizedType) {
|
||||
final ParameterizedType parameterizedType = (ParameterizedType) genericInterface;
|
||||
if (parameterizedType.getRawType() == rawInterface) {
|
||||
return (Class<T>) parameterizedType.getActualTypeArguments()[paramIndex];
|
||||
}
|
||||
}
|
||||
}
|
||||
if (clazz.getSuperclass() != Object.class) {
|
||||
return determineGenericInterfaceParameter(clazz.getSuperclass(), rawInterface, paramIndex);
|
||||
}
|
||||
for (Class<?> implementedInterface : clazz.getInterfaces()) {
|
||||
final Class<T> found = determineGenericInterfaceParameter(implementedInterface, rawInterface, paramIndex);
|
||||
if (found != null) {
|
||||
return found;
|
||||
}
|
||||
}
|
||||
throw new AssertionError(clazz.getSimpleName() + " expected to implement " + rawInterface.getSimpleName() + "<...>");
|
||||
}
|
||||
|
||||
/**
|
||||
* Searches the annotations of 'clazz' for an extended class 'rawClass' and returns the class of the actual generics parameter at the specified index.
|
||||
*
|
||||
* @param clazz a class which implements the generic interface 'rawClass'
|
||||
* @param rawClass a generic class
|
||||
* @param paramIndex the index of the generics parameter within 'rawClass'
|
||||
* @param <T> the expected class of the generics parameter at position 'index' in 'rawClass'
|
||||
* @return the actual generics parameter
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <T> Class<T> determineGenericClassParameter(final Class<?> clazz, final Class<?> rawClass, final int paramIndex) {
|
||||
final Type genericClass = clazz.getGenericSuperclass();
|
||||
if (genericClass instanceof ParameterizedType) {
|
||||
final ParameterizedType parameterizedType = (ParameterizedType) genericClass;
|
||||
if (parameterizedType.getRawType() == rawClass) {
|
||||
return (Class<T>) parameterizedType.getActualTypeArguments()[paramIndex];
|
||||
}
|
||||
}
|
||||
if (clazz.getSuperclass() != Object.class) {
|
||||
return determineGenericClassParameter(clazz.getSuperclass(), rawClass, paramIndex);
|
||||
}
|
||||
throw new AssertionError(clazz.getSimpleName() + " expected to extend " + rawClass.getSimpleName() + "<...>");
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public static <E extends Enum<E>> Enum<E> asEnumValue(final Class<?> type, final Object value) {
|
||||
return Enum.valueOf((Class<E>) type, value.toString());
|
||||
}
|
||||
|
||||
@FunctionalInterface
|
||||
public interface ThrowingSupplier<T> {
|
||||
T get() throws Exception;
|
||||
@ -40,7 +104,7 @@ public class ReflectionUtil {
|
||||
public static <T> T unchecked(final ThrowingSupplier<T> supplier) {
|
||||
try {
|
||||
return supplier.get();
|
||||
} catch (Exception e) {
|
||||
} catch (final Exception e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user