Skip to content

Commit

Permalink
Merge pull request #2828 from ebean-orm/feature/jackson-mapper-module
Browse files Browse the repository at this point in the history
Extract jackson mapper module
  • Loading branch information
rbygrave committed Oct 13, 2022
2 parents 62adaa7 + 7f5f0f6 commit 19bbe6e
Show file tree
Hide file tree
Showing 20 changed files with 290 additions and 118 deletions.
6 changes: 6 additions & 0 deletions composites/ebean/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@
<version>13.9.3-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.9.3-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-datasource</artifactId>
Expand Down
6 changes: 6 additions & 0 deletions ebean-bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -122,6 +122,12 @@
<version>13.9.3-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-jackson-mapper</artifactId>
<version>13.9.3-SNAPSHOT</version>
</dependency>

<dependency>
<groupId>io.ebean</groupId>
<artifactId>ebean-ddl-generator</artifactId>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package io.ebean.core.type;

import io.ebean.annotation.MutationDetection;

/**
* Holds the configured mapper and default mutation detection mode to be used.
*/
public interface ScalarJsonManager {

/**
* Return the default mutation detection mode.
*/
MutationDetection mutationDetection();

/**
* Return the object mapper supplied in DatabaseConfig.
*/
Object mapper();

/**
* Return the Postgres type given the jdbc type.
*/
String postgresType(int dbType);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package io.ebean.core.type;

import io.avaje.lang.Nullable;

import java.lang.annotation.Annotation;

/**
* Supports JSON bean properties that use something like Jackson ObjectMapper to convert between
* JSON content and the logical bean property value.
*/
public interface ScalarJsonMapper {

/**
* Return a ScalarType for the given JSON property.
*/
ScalarType<?> createType(ScalarJsonRequest request);

/**
* Return a marker annotation to detect when the JSON mapper should be used explicitly.
*/
@Nullable
<A extends Annotation> Class<A> markerAnnotation();
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package io.ebean.core.type;

import io.ebean.annotation.MutationDetection;

/**
* A request to create a ScalarType for a bean property (typically annotated with {@code @DbJson}).
*/
public final class ScalarJsonRequest {

private final ScalarJsonManager manager;
private final int dbType;
private final DocPropertyType docType;
private final Class<?> beanType;
private final MutationDetection mode;
private final String name;

public ScalarJsonRequest(ScalarJsonManager manager, int dbType, DocPropertyType docType, Class<?> beanType, MutationDetection mode, String name) {
this.manager = manager;
this.dbType = dbType;
this.docType = docType;
this.beanType = beanType;
this.mode = mode;
this.name = name;
}

/**
* Return the manager.
*/
public ScalarJsonManager manager() {
return manager;
}

/**
* Return the jdbc type this property maps to (e.g. JSON, JSONB etc).
*/
public int dbType() {
return dbType;
}

/**
* Return the document store docType.
*/
public DocPropertyType docType() {
return docType;
}

/**
* Return the type of the bean that this property belongs to.
*/
public Class<?> beanType() {
return beanType;
}

/**
* Return the mutation detection mode the property should use.
*/
public MutationDetection mode() {
return mode;
}

/**
* Return the property name.
*/
public String name() {
return name;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -152,7 +152,6 @@ public int compare(DeployBeanProperty o1, DeployBeanProperty o2) {
private DocStoreMode docStoreDelete;
private DeployBeanProperty idProperty;
private TableJoin primaryKeyJoin;
private Object jacksonAnnotatedClass;

/**
* Construct the BeanDescriptor.
Expand Down Expand Up @@ -1130,14 +1129,4 @@ private String getDeployWord(String expression) {
}
}

/**
* Returns the jackson annotated class, if jackson is present.
*/
@SuppressWarnings("unchecked")
Object /*AnnotatedClass*/ getJacksonAnnotatedClass() {
if (jacksonAnnotatedClass == null) {
jacksonAnnotatedClass = new DeployBeanObtainJackson(config, beanType).obtain();
}
return jacksonAnnotatedClass;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1042,20 +1042,6 @@ public void setElementProperty() {
this.elementProperty = true;
}

/**
* Returns the jackson annotated field, if jackson is present.
*/
public Object /*AnnotatedField*/ getJacksonField() {
com.fasterxml.jackson.databind.introspect.AnnotatedClass jac =
(com.fasterxml.jackson.databind.introspect.AnnotatedClass) getDesc().getJacksonAnnotatedClass();
for (com.fasterxml.jackson.databind.introspect.AnnotatedField candidate : jac.fields()) {
if (candidate.getName().equals(getName())) {
return candidate;
}
}
return null;
}

public void initMetaAnnotations(Set<Class<?>> metaAnnotationsFilter) {
metaAnnotations = AnnotationUtil.metaFindAllFor(field, metaAnnotationsFilter);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -65,13 +65,14 @@ public final class DefaultTypeManager implements TypeManager {
private final Object objectMapper;
private final boolean objectMapperPresent;
private final boolean postgres;
private final TypeJsonManager jsonManager;
private final ScalarJsonManager jsonManager;
private final boolean offlineMigrationGeneration;
private final EnumType defaultEnumType;
private final DatabasePlatform databasePlatform;

private final PlatformArrayTypeFactory arrayTypeListFactory;
private final PlatformArrayTypeFactory arrayTypeSetFactory;
private final ScalarJsonMapper jsonMapper;
private GeoTypeBinder geoTypeBinder;

/**
Expand All @@ -94,6 +95,9 @@ public DefaultTypeManager(DatabaseConfig config, BootupClasses bootupClasses) {
this.offlineMigrationGeneration = DbOffline.isGenerateMigration();
this.defaultEnumType = config.getDefaultEnumType();

ServiceLoader<ScalarJsonMapper> mappers = ServiceLoader.load(ScalarJsonMapper.class);
jsonMapper = mappers.findFirst().orElse(null);

initialiseStandard(config);
initialiseJavaTimeTypes(config);
loadTypesFromProviders(config, objectMapper);
Expand Down Expand Up @@ -293,34 +297,24 @@ private boolean isEnumType(Type valueType) {
@Override
public ScalarType<?> dbJsonType(DeployBeanProperty prop, int dbType, int dbLength) {
Class<?> type = prop.getPropertyType();
Type genericType = prop.getGenericType();
boolean hasJacksonAnnotations = objectMapperPresent && checkJacksonAnnotations(prop);

if (type.equals(String.class)) {
return ScalarTypeJsonString.typeFor(postgres, dbType);
}
if (type.equals(List.class)) {
DocPropertyType docType = docType(genericType);
if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) {
return ScalarTypeJsonList.typeFor(postgres, dbType, docType, prop.isNullable(), jsonManager.keepSource(prop));
} else {
return createJsonObjectMapperType(prop, dbType, docType);
if (jsonMapper != null) {
var markerAnnotation = jsonMapper.markerAnnotation();
if (markerAnnotation != null && !prop.getMetaAnnotations(markerAnnotation).isEmpty()) {
return createJsonObjectMapperType(prop, dbType, docPropertyType(prop, type));
}
}
if (type.equals(Set.class)) {
DocPropertyType docType = docType(genericType);
if (!hasJacksonAnnotations && isValueTypeSimple(genericType)) {
return ScalarTypeJsonSet.typeFor(postgres, dbType, docType, prop.isNullable(), jsonManager.keepSource(prop));
} else {
return createJsonObjectMapperType(prop, dbType, docType);
}
Type genericType = prop.getGenericType();
if (type.equals(List.class) && isValueTypeSimple(genericType)) {
return ScalarTypeJsonList.typeFor(postgres, dbType, docType(genericType), prop.isNullable(), keepSource(prop));
}
if (type.equals(Map.class)) {
if (!hasJacksonAnnotations && isMapValueTypeObject(genericType)) {
return ScalarTypeJsonMap.typeFor(postgres, dbType, jsonManager.keepSource(prop));
} else {
return createJsonObjectMapperType(prop, dbType, DocPropertyType.OBJECT);
}
if (type.equals(Set.class) && isValueTypeSimple(genericType)) {
return ScalarTypeJsonSet.typeFor(postgres, dbType, docType(genericType), prop.isNullable(), keepSource(prop));
}
if (type.equals(Map.class) && isMapValueTypeObject(genericType)) {
return ScalarTypeJsonMap.typeFor(postgres, dbType, keepSource(prop));
}
if (objectMapperPresent && prop.getMutationDetection() == MutationDetection.DEFAULT) {
ScalarTypeSet<?> typeSet = typeSets.get(type);
Expand All @@ -331,12 +325,15 @@ public ScalarType<?> dbJsonType(DeployBeanProperty prop, int dbType, int dbLengt
return createJsonObjectMapperType(prop, dbType, DocPropertyType.OBJECT);
}

/**
* Returns TRUE, if there is any jackson annotation on that property. All jackson annotations
* are annotated with the &#64;JacksonAnnotation meta annotation. So detection is easy.
*/
private boolean checkJacksonAnnotations(DeployBeanProperty prop) {
return prop.getMetaAnnotation(com.fasterxml.jackson.annotation.JacksonAnnotation.class) != null;
private boolean keepSource(DeployBeanProperty prop) {
if (prop.getMutationDetection() == MutationDetection.DEFAULT) {
prop.setMutationDetection(jsonManager.mutationDetection());
}
return prop.getMutationDetection() == MutationDetection.SOURCE;
}

private DocPropertyType docPropertyType(DeployBeanProperty prop, Class<?> type) {
return type.equals(List.class) || type.equals(Set.class) ? docType(prop.getGenericType()) : DocPropertyType.OBJECT;
}

private DocPropertyType docType(Type genericType) {
Expand Down Expand Up @@ -370,11 +367,14 @@ private boolean isMapValueTypeObject(Type genericType) {
}

private ScalarType<?> createJsonObjectMapperType(DeployBeanProperty prop, int dbType, DocPropertyType docType) {
Class<?> type = prop.getPropertyType();
if (objectMapper == null) {
throw new IllegalArgumentException("Type [" + type + "] unsupported for @DbJson mapping - Jackson ObjectMapper not present");
if (jsonMapper == null) {
throw new IllegalArgumentException("Unsupported @DbJson mapping - Jackson ObjectMapper not present for " + prop);
}
if (MutationDetection.DEFAULT == prop.getMutationDetection()) {
prop.setMutationDetection(jsonManager.mutationDetection());
}
return ScalarTypeJsonObjectMapper.createTypeFor(jsonManager, prop, dbType, docType);
var req = new ScalarJsonRequest(jsonManager, dbType, docType, prop.getDesc().getBeanType(), prop.getMutationDetection(), prop.getName());
return jsonMapper.createType(req);
}

/**
Expand Down
Original file line number Diff line number Diff line change
@@ -1,44 +1,35 @@
package io.ebeaninternal.server.type;

import com.fasterxml.jackson.databind.ObjectMapper;
import io.ebean.ModifyAwareType;
import io.ebean.annotation.MutationDetection;
import io.ebean.config.dbplatform.DbPlatformType;
import io.ebean.core.type.PostgresHelper;
import io.ebeaninternal.server.deploy.meta.DeployBeanProperty;
import io.ebean.core.type.ScalarJsonManager;

final class TypeJsonManager {
final class TypeJsonManager implements ScalarJsonManager {

private final boolean postgres;
private final ObjectMapper objectMapper;
private final Object mapper;
private final MutationDetection mutationDetection;

TypeJsonManager(boolean postgres, Object objectMapper, MutationDetection mutationDetection) {
TypeJsonManager(boolean postgres, Object mapper, MutationDetection mutationDetection) {
this.postgres = postgres;
this.objectMapper = (ObjectMapper) objectMapper;
this.mapper = mapper;
this.mutationDetection = mutationDetection;
}

MutationDetection mutationDetection() {
@Override
public MutationDetection mutationDetection() {
return mutationDetection;
}

ObjectMapper objectMapper() {
return objectMapper;
@Override
public Object mapper() {
return mapper;
}

boolean keepSource(DeployBeanProperty prop) {
if (prop.getMutationDetection() == MutationDetection.SOURCE) {
return true;
} else if (prop.getMutationDetection() == MutationDetection.DEFAULT) {
prop.setMutationDetection(mutationDetection);
return mutationDetection == MutationDetection.SOURCE;
} else {
return false;
}
}

String postgresType(int dbType) {
@Override
public String postgresType(int dbType) {
if (postgres) {
switch (dbType) {
case DbPlatformType.JSON:
Expand Down
1 change: 1 addition & 0 deletions ebean-core/src/main/java/module-info.java
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
uses io.ebean.datasource.DataSourceAlertFactory;
uses io.ebean.core.type.ExtraTypeFactory;
uses io.ebean.core.type.ScalarTypeSetFactory;
uses io.ebean.core.type.ScalarJsonMapper;
uses io.ebeanservice.docstore.api.DocStoreFactory;
uses io.ebean.migration.auto.AutoMigrationRunner;
uses io.avaje.classpath.scanner.ClassPathScannerFactory;
Expand Down
Loading

0 comments on commit 19bbe6e

Please sign in to comment.