Skip to content

Commit

Permalink
Merge pull request #307 from restx/fix-jongo-string-id-backward-compat
Browse files Browse the repository at this point in the history
Fix jongo string id backward compat (fixes #285)
  • Loading branch information
xhanin committed Apr 27, 2020
2 parents 87bd8d8 + 18e5a50 commit 80e0c45
Show file tree
Hide file tree
Showing 5 changed files with 177 additions and 11 deletions.
@@ -0,0 +1,98 @@
/*
* Copyright (C) 2011 Benoit GUEROUT <bguerout at gmail dot com> and Yves AMSELLEM <amsellem dot yves at gmail dot com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/

package restx.jongo;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.introspect.AnnotatedMember;
import com.fasterxml.jackson.databind.introspect.BasicBeanDescription;
import com.fasterxml.jackson.databind.introspect.BasicClassIntrospector;
import com.fasterxml.jackson.databind.introspect.BeanPropertyDefinition;
import org.jongo.ObjectIdUpdater;
import org.jongo.marshall.jackson.IdSelector;
import org.bson.types.ObjectId;

// see org.jongo.marshall.jackson.JacksonObjectIdUpdater
public class BackwardCompatibleJacksonObjectIdUpdater implements ObjectIdUpdater {

private final ObjectMapper mapper;
private final IdSelector<BeanPropertyDefinition> idSelector;

public BackwardCompatibleJacksonObjectIdUpdater(ObjectMapper mapper) {
this(mapper, new org.jongo.marshall.jackson.JacksonObjectIdUpdater.BeanPropertyDefinitionIdSelector());
}

public BackwardCompatibleJacksonObjectIdUpdater(ObjectMapper mapper, IdSelector<BeanPropertyDefinition> idSelector) {
this.mapper = mapper;
this.idSelector = idSelector;
}

public boolean mustGenerateObjectId(Object pojo) {
for (BeanPropertyDefinition def : beanDescription(pojo.getClass()).findProperties()) {
if (idSelector.isId(def)) {
AnnotatedMember accessor = def.getAccessor();
accessor.fixAccess(true);
return (ObjectId.class.isAssignableFrom(accessor.getRawType())
|| String.class.isAssignableFrom(accessor.getRawType()))
&& accessor.getValue(pojo) == null;
}
}
return false;
}

public Object getId(Object pojo) {
BasicBeanDescription beanDescription = beanDescription(pojo.getClass());
for (BeanPropertyDefinition def : beanDescription.findProperties()) {
if (idSelector.isId(def)) {
AnnotatedMember accessor = def.getAccessor();
accessor.fixAccess(true);
Object id = accessor.getValue(pojo);
if (id instanceof String && idSelector.isObjectId(def)) {
return new ObjectId(id.toString());
} else {
return id;
}
}
}
return null;
}

public void setObjectId(Object target, ObjectId id) {
for (BeanPropertyDefinition def : beanDescription(target.getClass()).findProperties()) {
if (idSelector.isId(def)) {
AnnotatedMember accessor = def.getAccessor();
accessor.fixAccess(true);
if (accessor.getValue(target) != null) {
throw new IllegalArgumentException("Unable to set objectid on class: " + target.getClass());
}
AnnotatedMember field = def.getField();
field.fixAccess(true);
Class<?> type = field.getRawType();
if (ObjectId.class.isAssignableFrom(type)) {
field.setValue(target, id);
} else if (type.equals(String.class)) {
field.setValue(target, id.toString());
}
return;
}
}
}

private BasicBeanDescription beanDescription(Class<?> cls) {
BasicClassIntrospector bci = new BasicClassIntrospector();
return bci.forSerialization(mapper.getSerializationConfig(), mapper.constructType(cls), mapper.getSerializationConfig());
}
}
46 changes: 42 additions & 4 deletions restx-jongo/src/main/java/restx/jongo/JongoModule.java
@@ -1,9 +1,15 @@
package restx.jongo;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.MongoClient;
import org.jongo.Jongo;
import org.jongo.Mapper;
import org.jongo.ObjectIdUpdater;
import org.jongo.marshall.Marshaller;
import org.jongo.marshall.Unmarshaller;
import org.jongo.marshall.jackson.JacksonEngine;
import org.jongo.marshall.jackson.JacksonMapper;
import org.jongo.query.QueryFactory;
import restx.factory.Module;
import restx.factory.Name;
import restx.factory.Provides;
Expand All @@ -25,10 +31,42 @@ public class JongoModule {

@Provides @Named("Mapper")
public Mapper mapper() {
return new JacksonMapper.Builder()
.registerModule(new BsonJodaTimeModule())
.withView(Views.Private.class)
.build();
final Mapper mapper = new JacksonMapper.Builder()
.registerModule(new BsonJodaTimeModule())
.withView(Views.Private.class)
.build();
return fixJacksonMapper(mapper);
}

public Mapper fixJacksonMapper(Mapper mapper) {
// the object id updater used by default in jongo does not handle string id properties
// in a backward compatible way - we are fixing it here
JacksonEngine jacksonEngine = (JacksonEngine) mapper.getMarshaller();
ObjectMapper objectMapper = jacksonEngine.getObjectMapper();

BackwardCompatibleJacksonObjectIdUpdater jacksonObjectIdUpdater
= new BackwardCompatibleJacksonObjectIdUpdater(objectMapper);
return new Mapper() {
@Override
public Marshaller getMarshaller() {
return mapper.getMarshaller();
}

@Override
public Unmarshaller getUnmarshaller() {
return mapper.getUnmarshaller();
}

@Override
public ObjectIdUpdater getObjectIdUpdater() {
return jacksonObjectIdUpdater;
}

@Override
public QueryFactory getQueryFactory() {
return mapper.getQueryFactory();
}
};
}

@Provides @Named("Jongo")
Expand Down
29 changes: 24 additions & 5 deletions restx-samplest/src/main/java/samplest/jongo/MongoResource.java
Expand Up @@ -24,10 +24,19 @@ public static class ObjectWithNewJongoAnnotations {
public String getLabel() { return label; }
public void setLabel(String label) { this.label = label; }
}
public static class ObjectWithMongoIdAnnotations {
@MongoId
private String id;
private String label;

public String getId() { return id; }
public void setId(String id) { this.id = id; }
public String getLabel() { return label; }
public void setLabel(String label) { this.label = label; }
}
public static class ObjectWithIdAnnotation {
// WARN: using only @Id was enough in jongo 1.1, but 1.4 requires to use @MongoObjectId also
// (and deprecates @Id in favor of @MongoObjectId)
@Id @MongoObjectId
// using deprecated jongo annotations
@Id
private String id;
private String label;

Expand All @@ -53,23 +62,26 @@ public static class ObjectWithObjectIdType {
private ObjectId id;
private String label;

public String getId() { return id.toString(); }
public String getId() { return id == null ? null : id.toString(); }
public void setId(ObjectId id) { this.id = id; }
public String getLabel() { return label; }
public void setLabel(String label) { this.label = label; }
}

private final JongoCollection objectWithIdAnnotationCollection;
private final JongoCollection objectWithNewJongoAnnotationCollection;
private final JongoCollection objectWithMongoIdAnnotationCollection;
private final JongoCollection objectWithObjectIdAnnotationCollection;
private final JongoCollection objectWithObjectIdTypeCollection;

public MongoResource(
@Named("objectWithIdAnnotationCollection") JongoCollection objectWithIdAnnotationCollection,
@Named("objectWithNewJongoAnnotationCollection") JongoCollection objectWithNewJongoAnnotationCollection,
@Named("objectWithMongoIdAnnotationCollection") JongoCollection objectWithMongoIdAnnotationCollection,
@Named("objectWithObjectIdAnnotationCollection") JongoCollection objectWithObjectIdAnnotationCollection,
@Named("objectWithObjectIdTypeCollection") JongoCollection objectWithObjectIdTypeCollection) {
this.objectWithIdAnnotationCollection = objectWithIdAnnotationCollection;
this.objectWithNewJongoAnnotationCollection = objectWithNewJongoAnnotationCollection;
this.objectWithMongoIdAnnotationCollection = objectWithMongoIdAnnotationCollection;
this.objectWithObjectIdAnnotationCollection = objectWithObjectIdAnnotationCollection;
this.objectWithObjectIdTypeCollection = objectWithObjectIdTypeCollection;
Expand All @@ -84,11 +96,18 @@ public ObjectWithIdAnnotation createFoo(ObjectWithIdAnnotation foo) {

@POST("/mongo/objectsWithMongoIdAnnotation")
@PermitAll
public ObjectWithNewJongoAnnotations createObjectWithMongoIdAnnotation(ObjectWithNewJongoAnnotations o) {
public ObjectWithMongoIdAnnotations createObjectWithMongoIdAnnotation(ObjectWithMongoIdAnnotations o) {
this.objectWithMongoIdAnnotationCollection.get().insert(o);
return o;
}

@POST("/mongo/objectsWithNewJongoAnnotation")
@PermitAll
public ObjectWithNewJongoAnnotations createObjectWithNewJongoAnnotation(ObjectWithNewJongoAnnotations o) {
this.objectWithNewJongoAnnotationCollection.get().insert(o);
return o;
}

@POST("/mongo/objectsWithObjectIdAnnotation")
@PermitAll
public ObjectWithObjectIdAnnotation createObjectWithObjectIdAnnotation(ObjectWithObjectIdAnnotation objectWithObjectIdAnnotation) {
Expand Down
@@ -0,0 +1,11 @@
title: objectsWithMongoIdAnnotation persistence
given:
- time: 2018-02-07T17:01:21.795+02:00
- collection: objectWithMongoIdAnnotationCollection
sequence: 5167cec5856107c479739654
wts:
- when: |
POST mongo/objectsWithMongoIdAnnotation
{"label": "BAR"}
then: |
{"id":"5167cec5856107c479739654","label": "BAR"}
@@ -1,11 +1,11 @@
title: objectsWithNewJongoAnnotations persistence
given:
- time: 2018-02-07T17:01:21.795+02:00
- collection: objectWithMongoIdAnnotationCollection
- collection: objectWithNewJongoAnnotationCollection
sequence: 5167cec5856107c479739654
wts:
- when: |
POST mongo/objectsWithMongoIdAnnotation
POST mongo/objectsWithNewJongoAnnotation
{"label": "BAR"}
then: |
{"id":"5167cec5856107c479739654","label": "BAR"}

0 comments on commit 80e0c45

Please sign in to comment.