diff --git a/.idea/compiler.xml b/.idea/compiler.xml index 708f857c..572e8fdf 100644 --- a/.idea/compiler.xml +++ b/.idea/compiler.xml @@ -21,6 +21,7 @@ + @@ -36,23 +37,22 @@ + + + - - - - diff --git a/.idea/encodings.xml b/.idea/encodings.xml index ae125419..719ab113 100644 --- a/.idea/encodings.xml +++ b/.idea/encodings.xml @@ -70,7 +70,9 @@ - + + + diff --git a/.idea/misc.xml b/.idea/misc.xml index b6d66ff0..458b082b 100644 --- a/.idea/misc.xml +++ b/.idea/misc.xml @@ -1,3 +1,4 @@ + @@ -27,6 +28,9 @@ + + diff --git a/.idea/modules.xml b/.idea/modules.xml index 9b7378c9..97349d16 100644 --- a/.idea/modules.xml +++ b/.idea/modules.xml @@ -15,9 +15,8 @@ - - + @@ -25,9 +24,13 @@ + + + + diff --git a/.idea/vcs.xml b/.idea/vcs.xml index bc06916b..94a25f7f 100644 --- a/.idea/vcs.xml +++ b/.idea/vcs.xml @@ -1,7 +1,6 @@ - \ No newline at end of file diff --git a/archetypes/dvalin-archetype-parent.iml b/archetypes/dvalin-archetype-parent.iml index 39e95b68..30466db0 100644 --- a/archetypes/dvalin-archetype-parent.iml +++ b/archetypes/dvalin-archetype-parent.iml @@ -1,11 +1,8 @@ - + - - \ No newline at end of file diff --git a/cloud/dvalin-cloud-parent.iml b/cloud/dvalin-cloud-parent.iml index a7d45923..30466db0 100644 --- a/cloud/dvalin-cloud-parent.iml +++ b/cloud/dvalin-cloud-parent.iml @@ -1,11 +1,8 @@ - + - - \ No newline at end of file diff --git a/cluster/dvalin-cluster-parent.iml b/cluster/dvalin-cluster-parent.iml index 28cca6c0..30466db0 100644 --- a/cluster/dvalin-cluster-parent.iml +++ b/cluster/dvalin-cluster-parent.iml @@ -1,11 +1,8 @@ - + - - \ No newline at end of file diff --git a/dvalin-parent.iml b/dvalin-parent.iml index cd7fb257..30466db0 100644 --- a/dvalin-parent.iml +++ b/dvalin-parent.iml @@ -1,15 +1,8 @@ - + - - - - - - \ No newline at end of file diff --git a/interconnect/dvalin-interconnect-parent.iml b/interconnect/dvalin-interconnect-parent.iml index 4df0bd8c..30466db0 100644 --- a/interconnect/dvalin-interconnect-parent.iml +++ b/interconnect/dvalin-interconnect-parent.iml @@ -1,11 +1,8 @@ - + - - \ No newline at end of file diff --git a/mongodb-legacy/README.md b/mongodb-legacy/README.md new file mode 100644 index 00000000..aaa361ac --- /dev/null +++ b/mongodb-legacy/README.md @@ -0,0 +1,57 @@ +## mongodb + +The mongodb library adds support for the MongoDB document store. By adding the dependency you get the +full support to interact with MongoDB databases including an in-memory database for tests. + +### Connection properties + +The following settings are possible: + +* `mongodb.type` - {fake|real} connect to real MongoDB database or in-memory version using `Mongo Java Server` +* `mongodb.name` - the name of the database to use for data storage +* `mongobee.enabled` - {true|false} use mongobee for database migration +* `mongobee.basepackage` - the base package of the Mongobee changesets +* `mongodb.demodata` - {true|false} load demodata on startup from ND-JSON files + +For connections to real MongoDB databases, these extra properties can be set: + +* `mongodb.host` - the host of the MongoDB instance (default: localhost) +* `mongodb.port` - the port of the MongoDB instance (default: 27017) +* `mongodb.uri` - instead of host and port you can specify the complete connection string +* `mongodb.sockettimeout` - the socket timeout of the connection (default: 10 seconds) +* `mongodb.connecttimeout` - the connection timeout of the connection attempt (default: 10 seconds) + +### Access to database + +To get access to the database inject the `MongoDBDataAccess` for your entity class into your bean. +You can then call several methods to query and write data. + +### Abstract entity and DAO interface + +The library provides a general purpose DAO interface (`ICrudDAO`) and an abstract implementation +(`AbstractMongoDAO`) with many helper methods to ease the development of the data layer. For this to +work your entities have to extend the `AEntity` superclass. The DAOs created have integrated support +for JodaTime classes. If you want to use polymorphic types in your entities make sure to implement +`@IMappedSupertype` on the super class. This advises the Jackson mapper to include type information +into the created JSON for deserialization. + +### Changesets + +For database migration purpose the mongobee library is included and is configured as denoted above +using system properties. See the mongobee documentation on how to implement changesets. +For Index creation take a look at the `ChangelogUtil` helper class. + +### MongoDBInit + +To prefill the database with startup data or test data for integration tests put file on your classpath +into the package `mongodb` and name them using the following pattern: `.ndjson` +If you set the system property `mongodb.demodata`to `true` dvalin will populate the given collections +with the objects contained in this new-line delimited files. Just put one JSON object per line. + +### DocumentLinks + +Another feature of dvalin's MongoDB support are DocumentLinks. These allow for references between your +documents. To include a reference in one of your entities just add a field of the generic type +`DocumentLink` and let your referenced entity implement `AReferenceableEntity`. +Dvalin will then include a reference to the given document in your JSON which you can resolve +by injecting the `IDlinkDAO` wherever you want. diff --git a/mongodb-legacy/pom.xml b/mongodb-legacy/pom.xml new file mode 100644 index 00000000..097183ef --- /dev/null +++ b/mongodb-legacy/pom.xml @@ -0,0 +1,100 @@ + + 4.0.0 + + de.taimos + dvalin-parent + 1.36-SNAPSHOT + + dvalin-mongodb-legacy + Legacy MongoDB support for dvalin + 2015 + + 1.5.1 + + + + + io.mongock + mongock-bom + ${mongock.version} + pom + import + + + + + + de.taimos + dvalin-daemon + ${project.version} + + + + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + + + de.undercouch + bson4jackson + ${bson.version} + + + + + org.springframework + spring-beans + + + org.springframework + spring-context + + + org.mongodb + mongodb-driver-legacy + ${mongo.version} + + + org.jongo + jongo + ${jongo.version} + + + jackson-databind + com.fasterxml.jackson.core + + + jackson-core + com.fasterxml.jackson.core + + + bson4jackson + de.undercouch + + + jackson-annotations + com.fasterxml.jackson.core + + + + + io.mongock + mongock-standalone + + + io.mongock + mongodb-sync-v4-driver + + + de.bwaldvogel + mongo-java-server + ${mongo-java-server.version} + true + + + diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AAuditedEntity.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AAuditedEntity.java new file mode 100644 index 00000000..fe378833 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AAuditedEntity.java @@ -0,0 +1,62 @@ +package de.taimos.dvalin.mongo; + +/*- + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 - 2017 Taimos GmbH + * %% + * 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. + * #L% + */ + +import org.joda.time.DateTime; + +/** + * Copyright 2015 Cinovo AG
+ *
+ * + * @author psigloch + * + */ +public abstract class AAuditedEntity extends AEntity { + + private Integer version; + private DateTime lastChange; + private String lastChangeUser; + + public Integer getVersion() { + return this.version; + } + + public void setVersion(Integer version) { + this.version = version; + } + + public DateTime getLastChange() { + return this.lastChange; + } + + public void setLastChange(DateTime lastChange) { + this.lastChange = lastChange; + } + + public String getLastChangeUser() { + return this.lastChangeUser; + } + + public void setLastChangeUser(String lastChangeUser) { + this.lastChangeUser = lastChangeUser; + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AEntity.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AEntity.java new file mode 100644 index 00000000..19df4ca0 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AEntity.java @@ -0,0 +1,57 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.io.Serializable; + +import org.bson.types.ObjectId; +import org.jongo.marshall.jackson.oid.MongoId; +import org.jongo.marshall.jackson.oid.MongoObjectId; + +/** + * Copyright 2015 Hoegernet
+ *
+ * superclass to implement for Elements to be stored into the MongoDB. It provides an _id field which is pre-filled with a new + * {@link ObjectId} + * + * @author Thorsten Hoeger + */ +public abstract class AEntity implements Serializable { + + private static final long serialVersionUID = 6328501276339927785L; + + @MongoId + @MongoObjectId + protected String id = ObjectId.get().toString(); + + + /** + * @return the unique id of the element + */ + public String getId() { + return this.id; + } + + public void setId(final String id) { + this.id = id; + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AbstractAuditedMongoDAO.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AbstractAuditedMongoDAO.java new file mode 100644 index 00000000..fe0e9b53 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AbstractAuditedMongoDAO.java @@ -0,0 +1,95 @@ +package de.taimos.dvalin.mongo; + +/*- + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 - 2017 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.util.List; + +import org.bson.Document; +import org.bson.types.ObjectId; +import org.joda.time.DateTime; +import org.jongo.Jongo; +import org.jongo.Mapper; +import org.jongo.MongoCollection; +import org.jongo.MongoCursor; +import org.jongo.bson.BsonDocument; + +import com.mongodb.BasicDBObject; +import com.mongodb.client.MongoDatabase; + +public abstract class AbstractAuditedMongoDAO extends AbstractMongoDAO implements ICrudAuditedDAO { + + protected MongoCollection jongoHistoryCollection; + private com.mongodb.client.MongoCollection historyCollection; + private Mapper jongoMapper; + + @Override + protected void customInit(MongoDatabase db, Jongo jongo) { + String collectionName = this.dataAccess.getCollectionName() + "_history"; + this.jongoHistoryCollection = jongo.getCollection(collectionName); + this.historyCollection = db.getCollection(collectionName); + this.jongoMapper = jongo.getMapper(); + } + + @Override + public T findVersion(String id, Integer version) { + if (version == null) { + return this.findById(id); + } + + MongoCursor as = this.jongoHistoryCollection.find("{originalId:#, version:#}", new ObjectId(id), version).as(this.dataAccess.getEntityClass()); + if (as.hasNext()) { + return as.next(); + } + return null; + } + + @Override + public List findHistoryElements(String id) { + Iterable as = this.jongoHistoryCollection.find("{originalId : #}", new ObjectId(id)).sort("{version: -1}").as(this.dataAccess.getEntityClass()); + return this.dataAccess.convertIterable(as); + } + + @Override + protected void beforeSave(T object) { + Integer oldVersion = object.getVersion(); + if (oldVersion == null) { + object.setVersion(0); + } else { + object.setVersion(oldVersion + 1); + } + object.setLastChange(DateTime.now()); + } + + @Override + protected void afterSave(T object) { + try { + BsonDocument bsonDocument = this.jongoMapper.getMarshaller().marshall(object); + BasicDBObject dbObject = new BasicDBObject(bsonDocument.toDBObject().toMap()); + dbObject.removeField("_id"); + dbObject.put("originalId", new ObjectId(object.getId())); + Document doc = Document.parse(dbObject.toString()); + this.historyCollection.insertOne(doc); + } catch (Exception e) { + String message = String.format("Unable to save object %s due to a marshalling error", object); + throw new IllegalArgumentException(message, e); + } + } +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AbstractMongoDAO.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AbstractMongoDAO.java new file mode 100644 index 00000000..778a6237 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/AbstractMongoDAO.java @@ -0,0 +1,304 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.mongodb.DBObject; +import com.mongodb.MongoClient; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.gridfs.GridFSBucket; +import org.jongo.Jongo; +import org.jongo.MongoCollection; +import org.jongo.ResultHandler; +import org.springframework.beans.factory.annotation.Autowired; + +import javax.annotation.PostConstruct; +import java.util.List; +import java.util.Map; + +/** + * Copyright 2015 Hoegernet
+ *
+ * abstract class to derive to implement Mongo DAOs. The system property mongodb.name has to be set and it denotes the database + * used for this DAO. + * + * @param the entity this DAO is used for + * @author Thorsten Hoeger + */ +public abstract class AbstractMongoDAO implements ICrudDAO { + + @Autowired + @Deprecated + protected MongoClient mongo; + + @Autowired + private Jongo jongo; + @Autowired + private MongoDatabase db; + + @Deprecated + protected MongoCollection collection; + + @Autowired + protected MongoDBDataAccess dataAccess; + + @PostConstruct + public final void init() { + this.collection = this.dataAccess.getCollection(); + this.customInit(this.db, this.jongo); + } + + protected void customInit(MongoDatabase db, Jongo jongo) { + // implement if needed + } + + /** + * runs a map-reduce-job on the collection. same as {@link #mapReduce(String, DBObject, DBObject, Map, MapReduceResultHandler) + * mapReduce(name, null, null, null, conv)} + * + * @param the type of the result class + * @param name the name of the map-reduce functions + * @param conv the converter to convert the result + * @return an {@link Iterable} with the result entries + */ + protected final Iterable mapReduce(String name, final MapReduceResultHandler conv) { + return this.dataAccess.mapReduce(name, conv); + } + + /** + * runs a map-reduce-job on the collection. The functions are read from the classpath in the folder mongodb. The systems reads them from + * files called <name>.map.js, <name>.reduce.js and optionally <name>.finalize.js. After this the result is converted + * using the given {@link MapReduceResultHandler} + * + * @param the type of the result class + * @param name the name of the map-reduce functions + * @param query the query to filter the elements used for the map-reduce + * @param sort sort query to sort elements before running map-reduce + * @param scope the global scope for the JavaScript run + * @param conv the converter to convert the result + * @return an {@link Iterable} with the result entries + * @throws RuntimeException if resources cannot be read + */ + protected final Iterable mapReduce(String name, DBObject query, DBObject sort, Map scope, final MapReduceResultHandler conv) { + return this.dataAccess.mapReduce(name, query, sort, scope, conv); + } + + @Override + public final List findList() { + return this.dataAccess.findList(); + } + + @Override + public List findList(String sortProp, Integer sortDirection, Integer limit, Integer skip) { + return this.dataAccess.findList(sortProp, sortDirection, limit, skip); + } + + /** + * finds all elements matching the given query + * + * @param query the query to search for + * @param params the parameters to replace # symbols + * @return the list of elements found + */ + protected final List findByQuery(String query, Object... params) { + return this.dataAccess.findByQuery(query, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly + * + * @param query the query to search for + * @param sort the sort query to apply + * @param params the parameters to replace # symbols + * @return the list of elements found + */ + protected final List findSortedByQuery(String query, String sort, Object... params) { + return this.dataAccess.findSortedByQuery(query, sort, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to + * rename or filter fields in the result elements. Instead of returning typed objects it returns objects of type + * as + * + * @param query the query to search for + * @param sort the sort query to apply + * @param projection the projection of fields to use + * @param as the target to convert result elements to + * @param params the parameters to replace # symbols + * @param

the element type + * @return the list of elements found + */ + protected final

List

findSortedByQuery(String query, String sort, String projection, Class

as, Object... params) { + return this.dataAccess.findSortedByQuery(query, sort, projection, as, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to + * rename or filter fields in the result elements. Instead of returning typed objects it returns objects converted + * by the given {@link ResultHandler} + * + * @param query the query to search for + * @param sort the sort query to apply + * @param projection the projection of fields to use + * @param handler the handler to convert result elements with + * @param params the parameters to replace # symbols + * @param

the element type + * @return the list of elements found + */ + protected final

List

findSortedByQuery(String query, String sort, String projection, ResultHandler

handler, Object... params) { + return this.dataAccess.findSortedByQuery(query, sort, projection, handler, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly + * + * @param query the query to search for + * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch + * @param params the parameters to replace # symbols + * @return the list of elements found + */ + protected final List findSortedByQuery(String query, String sort, Integer skip, Integer limit, Object... params) { + return this.dataAccess.findSortedByQuery(query, sort, skip, limit, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to + * rename or filter fields in the result elements. Instead of returning typed objects it returns objects of type + * as + * + * @param query the query to search for + * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch + * @param projection the projection of fields to use + * @param as the target to convert result elements to + * @param params the parameters to replace # symbols + * @param

the element type + * @return the list of elements found + */ + protected final

List

findSortedByQuery(String query, String sort, Integer skip, Integer limit, String projection, Class

as, Object... params) { + return this.dataAccess.findSortedByQuery(query, sort, skip, limit, projection, as, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to + * rename or filter fields in the result elements. Instead of returning typed objects it returns objects converted + * by the given {@link ResultHandler} + * + * @param query the query to search for + * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch + * @param projection the projection of fields to use + * @param handler the handler to convert result elements with + * @param params the parameters to replace # symbols + * @param

the element type + * @return the list of elements found + */ + protected final

List

findSortedByQuery(String query, String sort, Integer skip, Integer limit, String projection, ResultHandler

handler, Object... params) { + return this.dataAccess.findSortedByQuery(query, sort, skip, limit, projection, handler, params); + } + + /** + * finds all elements containing the given searchString in any text field and sorts them accordingly. + * + * @param searchString the searchString to search for + * @param sort the sort query to apply + * @return the list of elements found + */ + protected final List searchSorted(String searchString, String sort) { + return this.dataAccess.searchSorted(searchString, sort); + } + + /** + * queries with the given string, sorts the result and returns the first element. null is returned if no element is found. + * + * @param query the query string + * @param sort the sort string + * @param params the parameters to replace # symbols + * @return the first element found or null if none is found + */ + protected final T findFirstByQuery(String query, String sort, Object... params) { + return this.dataAccess.findFirstByQuery(query, sort, params).orElse(null); + } + + @Override + public final T findById(String id) { + return this.dataAccess.findByObjectId(id).orElse(null); + } + + @Override + public final T save(T object) { + this.beforeSave(object); + T saved = this.dataAccess.save(object); + this.afterSave(saved); + return saved; + } + + /** + * override this to do something after the object was saved + * + * @param object the saved object + */ + @SuppressWarnings("unused") + protected void afterSave(T object) { + // + } + + /** + * override this to do something before the object was saved + * + * @param object the object to be saved + */ + @SuppressWarnings("unused") + protected void beforeSave(T object) { + // + } + + @Override + public final void delete(T object) { + this.delete(object.getId()); + } + + @Override + public final void delete(String id) { + this.beforeDelete(id); + this.dataAccess.deleteByObjectId(id); + this.afterDelete(id); + } + + @SuppressWarnings("unused") + protected void beforeDelete(String id) { + // + } + + @SuppressWarnings("unused") + protected void afterDelete(String id) { + // + } + + protected GridFSBucket getGridFSBucket(String bucket) { + return this.dataAccess.getGridFSBucket(bucket); + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ChangelogUtil.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ChangelogUtil.java new file mode 100644 index 00000000..46abc789 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ChangelogUtil.java @@ -0,0 +1,76 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.mongodb.BasicDBObject; +import com.mongodb.DBCollection; + +/** + * Copyright 2015 Hoegernet
+ *
+ * Utils for changelog creation + * + * @author Thorsten Hoeger + */ +public final class ChangelogUtil { + + private ChangelogUtil() { + // private utility class constructor + } + + /** + * adds a TTL index to the given collection. The TTL must be a positive integer. + * + * @param collection the collection to use for the TTL index + * @param field the field to use for the TTL index + * @param ttl the TTL to set on the given field + * @throws IllegalArgumentException if the TTL is less or equal 0 + */ + public static void addTTLIndex(DBCollection collection, String field, int ttl) { + if (ttl <= 0) { + throw new IllegalArgumentException("TTL must be positive"); + } + collection.createIndex(new BasicDBObject(field, 1), new BasicDBObject("expireAfterSeconds", ttl)); + } + + /** + * Add an index on the given collection and field + * + * @param collection the collection to use for the index + * @param field the field to use for the index + * @param asc the sorting direction. true to sort ascending; false to sort descending + * @param background iff true the index is created in the background + */ + public static void addIndex(DBCollection collection, String field, boolean asc, boolean background) { + int dir = (asc) ? 1 : -1; + collection.createIndex(new BasicDBObject(field, dir), new BasicDBObject("background", background)); + } + + /** + * Add a text index on the given collection + * + * @param collection the collection to use for the index + */ + public static void addTextIndex(DBCollection collection) { + collection.createIndex(new BasicDBObject("$**", "text")); + } + +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/ConverterIterable.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ConverterIterable.java similarity index 100% rename from mongodb/src/main/java/de/taimos/dvalin/mongo/ConverterIterable.java rename to mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ConverterIterable.java diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ICrudAuditedDAO.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ICrudAuditedDAO.java new file mode 100644 index 00000000..e54d264a --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ICrudAuditedDAO.java @@ -0,0 +1,48 @@ +package de.taimos.dvalin.mongo; + +/*- + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 - 2017 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.util.List; + +/** + * Copyright 2015 Cinovo AG
+ *
+ * + * @author psigloch + * + * @param an {@link AEntity} + */ +public interface ICrudAuditedDAO extends ICrudDAO { + + /** + * @param id the id of the document to get the history elements for + * @return the history elements for a document + */ + public List findHistoryElements(String id); + + /** + * @param id the id of the document to get the element for + * @param version the version of the document to get + * @return the correct version of a document + */ + public E findVersion(String id, Integer version); + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ICrudDAO.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ICrudDAO.java new file mode 100644 index 00000000..52f9b1d9 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/ICrudDAO.java @@ -0,0 +1,80 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.util.List; + +/** + * Copyright 2015 Hoegernet
+ *
+ * DAO with default CRUD operations + * + * @param type of the managed {@link AEntity} + * @author Thorsten Hoeger + */ +public interface ICrudDAO { + + /** + * find the element with the given id + * + * @param id the id to find + * @return the obejct with the given id or null if no element exists with this id + */ + T findById(String id); + + /** + * saves the given element + * + * @param object the element to save + * @return the saved element + */ + T save(T object); + + /** + * deletes the given object using its id. + * + * @param object the object to delete + */ + void delete(T object); + + /** + * deletes the given object using its id. + * + * @param id the id of the object to delete + */ + void delete(String id); + + /** + * @return a list of all elements in this collection + */ + List findList(); + + /** + * @param sortProp the sort parameter + * @param sortDirection the sort direction + * @param limit the limit + * @param skip the number of documents to skip + * @return list of sorted stored contracts + */ + List findList(String sortProp, Integer sortDirection, Integer limit, Integer skip); + + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/IMappedSupertype.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/IMappedSupertype.java new file mode 100644 index 00000000..b4547f1f --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/IMappedSupertype.java @@ -0,0 +1,36 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonTypeInfo; + +/** + * Copyright 2015 Hoegernet
+ *
+ * denotes that the class is used as a superclass meant to be sub-classes. It advises Jongo to include type information into the JSON string + * into the field @class to allow deserialization into the correct Java class + * + * @author Thorsten Hoeger + */ +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, include = JsonTypeInfo.As.PROPERTY, property = "@class") +public interface IMappedSupertype { + // +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/JodaMapping.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/JodaMapping.java similarity index 100% rename from mongodb/src/main/java/de/taimos/dvalin/mongo/JodaMapping.java rename to mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/JodaMapping.java diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/JongoFactory.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/JongoFactory.java similarity index 100% rename from mongodb/src/main/java/de/taimos/dvalin/mongo/JongoFactory.java rename to mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/JongoFactory.java diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/MapReduceResultHandler.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MapReduceResultHandler.java similarity index 100% rename from mongodb/src/main/java/de/taimos/dvalin/mongo/MapReduceResultHandler.java rename to mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MapReduceResultHandler.java diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoClientOptionsFactory.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoClientOptionsFactory.java new file mode 100644 index 00000000..eae4c0e1 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoClientOptionsFactory.java @@ -0,0 +1,74 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + + +import org.springframework.beans.factory.FactoryBean; + +import com.mongodb.MongoClientOptions; +import com.mongodb.MongoClientOptions.Builder; + +/** + * Copyright 2014 Hoegernet
+ *
+ * Special factory for MongoClientOptions which is setting timeouts + * + * @author Thorsten Hoeger + */ +public class MongoClientOptionsFactory implements FactoryBean { + + private int socketTimeout; + private int connectTimeout; + + + @Override + public MongoClientOptions.Builder getObject() throws Exception { + Builder builder = MongoClientOptions.builder(); + builder.socketTimeout(this.socketTimeout); + builder.connectTimeout(this.connectTimeout); + return builder; + } + + @Override + public Class getObjectType() { + return MongoClientOptions.Builder.class; + } + + @Override + public boolean isSingleton() { + return false; + } + + /** + * @param socketTimeout the socketTimeout to set + */ + public void setSocketTimeout(int socketTimeout) { + this.socketTimeout = socketTimeout; + } + + /** + * @param connectTimeout the connectTimeout to set + */ + public void setConnectTimeout(int connectTimeout) { + this.connectTimeout = connectTimeout; + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoDBDataAccess.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoDBDataAccess.java new file mode 100644 index 00000000..9b2992d0 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoDBDataAccess.java @@ -0,0 +1,382 @@ +package de.taimos.dvalin.mongo; + +/*- + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 - 2018 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.io.IOException; +import java.io.InputStream; +import java.nio.charset.Charset; +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Optional; + +import org.bson.types.ObjectId; +import org.jongo.Find; +import org.jongo.Jongo; +import org.jongo.MongoCollection; +import org.jongo.ResultHandler; +import org.jongo.Update; +import org.springframework.beans.factory.InjectionPoint; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.beans.factory.config.ConfigurableBeanFactory; +import org.springframework.context.annotation.Scope; +import org.springframework.stereotype.Repository; +import org.springframework.util.StreamUtils; + +import com.mongodb.DBObject; +import com.mongodb.MapReduceCommand; +import com.mongodb.MapReduceOutput; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.gridfs.GridFSBucket; +import com.mongodb.client.gridfs.GridFSBuckets; + +import de.taimos.dvalin.daemon.spring.InjectionUtils; + +@Repository +@Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) +public class MongoDBDataAccess { + + /** + * Query to perform a full text search + */ + public static final String FULLTEXT_QUERY = "{\"$text\" : {\"$search\" : #}}"; + + private final MongoDatabase db; + private final Class entityClass; + private final MongoCollection collection; + + @Autowired + public MongoDBDataAccess(Jongo jongo, MongoDatabase db, InjectionPoint ip) { + this.db = db; + this.entityClass = (Class) InjectionUtils.getGenericType(ip); + this.collection = jongo.getCollection(this.getCollectionName()); + } + + public String getCollectionName() { + return this.entityClass.getSimpleName(); + } + + public Class getEntityClass() { + return this.entityClass; + } + + /** + * runs a map-reduce-job on the collection. same as {@link #mapReduce(String, DBObject, DBObject, Map, MapReduceResultHandler) + * mapReduce(name, null, null, null, conv)} + * + * @param the type of the result class + * @param name the name of the map-reduce functions + * @param conv the converter to convert the result + * @return an {@link Iterable} with the result entries + */ + public final Iterable mapReduce(String name, final MapReduceResultHandler conv) { + return this.mapReduce(name, null, null, null, conv); + } + + /** + * runs a map-reduce-job on the collection. The functions are read from the classpath in the folder mongodb. The systems reads them from + * files called <name>.map.js, <name>.reduce.js and optionally <name>.finalize.js. After this the result is converted + * using the given {@link MapReduceResultHandler} + * + * @param the type of the result class + * @param name the name of the map-reduce functions + * @param query the query to filter the elements used for the map-reduce + * @param sort sort query to sort elements before running map-reduce + * @param scope the global scope for the JavaScript run + * @param conv the converter to convert the result + * @return an {@link Iterable} with the result entries + * @throws RuntimeException if resources cannot be read + */ + public final Iterable mapReduce(String name, DBObject query, DBObject sort, Map scope, final MapReduceResultHandler conv) { + String map = this.getMRFunction(name, "map"); + String reduce = this.getMRFunction(name, "reduce"); + + MapReduceCommand mrc = new MapReduceCommand(this.collection.getDBCollection(), map, reduce, null, MapReduceCommand.OutputType.INLINE, query); + String finalizeFunction = this.getMRFunction(name, "finalize"); + if(finalizeFunction != null) { + mrc.setFinalize(finalizeFunction); + } + if(sort != null) { + mrc.setSort(sort); + } + if(scope != null) { + mrc.setScope(scope); + } + MapReduceOutput mr = this.collection.getDBCollection().mapReduce(mrc); + return new ConverterIterable<>(mr.results().iterator(), conv); + } + + private String getMRFunction(String name, String type) { + try { + InputStream stream = this.getClass().getResourceAsStream("/mongodb/" + name + "." + type + ".js"); + if(stream != null) { + return StreamUtils.copyToString(stream, Charset.defaultCharset()); + } + return null; + } catch(IOException e) { + throw new RuntimeException("Failed to read resource", e); + } + } + + public final List findList() { + Iterable as = this.collection.find().sort("{_id:1}").as(this.entityClass); + return this.convertIterable(as); + } + + public List findList(String sortProp, Integer sortDirection, Integer limit, Integer skip) { + return this.findSortedByQuery("{}", "{" + sortProp + ":" + sortDirection + "}", skip, limit); + } + + /** + * converts the given {@link Iterable} to a {@link List} + * + * @param

the element type + * @param as the {@link Iterable} + * @return the converted {@link List} + */ + public final

List

convertIterable(Iterable

as) { + List

objects = new ArrayList<>(); + for(P mp : as) { + objects.add(mp); + } + return objects; + } + + /** + * finds all elements matching the given query + * + * @param query the query to search for + * @param params the parameters to replace # symbols + * @return the list of elements found + */ + public final List findByQuery(String query, Object... params) { + return this.findSortedByQuery(query, null, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly + * + * @param query the query to search for + * @param sort the sort query to apply + * @param params the parameters to replace # symbols + * @return the list of elements found + */ + public final List findSortedByQuery(String query, String sort, Object... params) { + return this.findSortedByQuery(query, sort, null, (Integer) null, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to + * rename or filter fields in the result elements. Instead of returning objects it returns objects of type + * as + * + * @param query the query to search for + * @param sort the sort query to apply + * @param projection the projection of fields to use + * @param as the target to convert result elements to + * @param params the parameters to replace # symbols + * @param

the element type + * @return the list of elements found + */ + public final

List

findSortedByQuery(String query, String sort, String projection, Class

as, Object... params) { + return this.findSortedByQuery(query, sort, null, null, projection, as, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to + * rename or filter fields in the result elements. Instead of returning objects it returns objects converted + * by the given {@link ResultHandler} + * + * @param query the query to search for + * @param sort the sort query to apply + * @param projection the projection of fields to use + * @param handler the handler to convert result elements with + * @param params the parameters to replace # symbols + * @param

the element type + * @return the list of elements found + */ + protected final

List

findSortedByQuery(String query, String sort, String projection, ResultHandler

handler, Object... params) { + return this.findSortedByQuery(query, sort, null, null, projection, handler, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly + * + * @param query the query to search for + * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch + * @param params the parameters to replace # symbols + * @return the list of elements found + */ + public final List findSortedByQuery(String query, String sort, Integer skip, Integer limit, Object... params) { + return this.findSortedByQuery(query, sort, skip, limit, null, this.entityClass, params); + } + + /** + * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to + * rename or filter fields in the result elements. Instead of returning objects it returns objects of type + * as + * + * @param query the query to search for + * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch + * @param projection the projection of fields to use + * @param as the target to convert result elements to + * @param params the parameters to replace # symbols + * @param

the element type + * @return the list of elements found + */ + public final

List

findSortedByQuery(String query, String sort, Integer skip, Integer limit, String projection, Class

as, Object... params) { + Find find = this.createFind(query, sort, skip, limit, projection, params); + return this.convertIterable(find.as(as)); + } + + /** + * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to + * rename or filter fields in the result elements. Instead of returning objects it returns objects converted + * by the given {@link ResultHandler} + * + * @param query the query to search for + * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch + * @param projection the projection of fields to use + * @param handler the handler to convert result elements with + * @param params the parameters to replace # symbols + * @param

the element type + * @return the list of elements found + */ + public final

List

findSortedByQuery(String query, String sort, Integer skip, Integer limit, String projection, ResultHandler

handler, Object... params) { + Find find = this.createFind(query, sort, skip, limit, projection, params); + return this.convertIterable(find.map(handler)); + } + + private Find createFind(String query, String sort, Integer skip, Integer limit, String projection, Object... params) { + Find find = this.collection.find(query, params); + if((sort != null) && !sort.isEmpty()) { + find.sort(sort); + } + if((projection != null) && !projection.isEmpty()) { + find.projection(projection); + } + if(skip != null) { + find.skip(skip); + } + if(limit != null) { + find.limit(limit); + } + return find; + } + + /** + * finds all elements containing the given searchString in any text field and sorts them accordingly. + * + * @param searchString the searchString to search for + * @param sort the sort query to apply + * @return the list of elements found + */ + public final List searchSorted(String searchString, String sort) { + return this.findSortedByQuery(MongoDBDataAccess.FULLTEXT_QUERY, sort, searchString); + } + + /** + * queries with the given string, sorts the result and returns the first element. null is returned if no element is found. + * + * @param query the query string + * @param sort the sort string + * @param params the parameters to replace # symbols + * @return the first element found or null if none is found + */ + public final Optional findFirstByQuery(String query, String sort, Object... params) { + Find find = this.collection.find(query, params); + if((sort != null) && !sort.isEmpty()) { + find.sort(sort); + } + Iterable as = find.limit(1).as(this.entityClass); + Iterator iterator = as.iterator(); + if(iterator.hasNext()) { + return Optional.of(iterator.next()); + } + return Optional.empty(); + } + + public final Optional findByObjectId(String id) { + return Optional.ofNullable(this.collection.findOne(new ObjectId(id)).as(this.entityClass)); + } + + public final Optional findByStringId(String id) { + return Optional.ofNullable(this.collection.findOne("{\"_id\":#}", id).as(this.entityClass)); + } + + /** + * @param query the query string + * @param parameter the parameters to replace # symbols + * @return the number of elements matching the query + */ + public final long count(String query, Object... parameter) { + return this.collection.count(query, parameter); + } + + public final T save(T object) { + this.collection.save(object); + return object; + } + + public final void deleteByObjectId(String id) { + this.collection.remove(new ObjectId(id)); + } + + public final void deleteByStringId(String id) { + this.collection.remove("{\"_id\":#}", id); + } + + public final void delete(String query, Object... parameter) { + this.collection.remove(query, parameter); + } + + public final Update update(ObjectId id) { + return this.collection.update(id); + } + + public final Update update(String query) { + return this.collection.update(query); + } + + public final Update update(String query, Object... parameter) { + return this.collection.update(query, parameter); + } + + @Deprecated + public MongoCollection getCollection() { + return this.collection; + } + + public GridFSBucket getGridFSBucket(String bucket) { + if(bucket == null || bucket.isEmpty()) { + throw new IllegalArgumentException(); + } + return GridFSBuckets.create(this.db, bucket); + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoDBInit.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoDBInit.java new file mode 100644 index 00000000..4209af1e --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/MongoDBInit.java @@ -0,0 +1,103 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.io.IOException; +import java.util.Scanner; + +import javax.annotation.PostConstruct; + +import com.mongodb.BasicDBObject; +import com.mongodb.DBObject; +import com.mongodb.MongoClient; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import de.taimos.daemon.spring.conditional.OnSystemProperty; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.core.io.Resource; +import org.springframework.core.io.support.PathMatchingResourcePatternResolver; +import org.springframework.stereotype.Component; + + +/** + * Copyright 2014 Taimos GmbH
+ *
+ *

+ * Initialized the database with data found in the classpath. It searches for files in the folder mongodb ending with .ndjson. The filename + * is used as name of the collection. The contents of this file has to be valid ND-JSON which means that it contains one JSON Object per + * line. + * + * @author Thorsten Hoeger + */ +@Component +@OnSystemProperty(propertyName = "mongodb.demodata", propertyValue = "true") +public class MongoDBInit { + + private static final Logger LOGGER = LoggerFactory.getLogger(MongoDBInit.class); + + @Autowired + private MongoClient mongo; + + + /** + * init database with demo data + */ + @PostConstruct + public void initDatabase() { + MongoDBInit.LOGGER.info("initializing MongoDB"); + String dbName = System.getProperty("mongodb.name"); + if (dbName == null) { + throw new RuntimeException("Missing database name; Set system property 'mongodb.name'"); + } + MongoDatabase db = this.mongo.getDatabase(dbName); + + try { + PathMatchingResourcePatternResolver resolver = new PathMatchingResourcePatternResolver(); + Resource[] resources = resolver.getResources("classpath*:mongodb/*.ndjson"); + MongoDBInit.LOGGER.info("Scanning for collection data"); + for (Resource res : resources) { + String filename = res.getFilename(); + if(filename != null) { + String collection = filename.substring(0, filename.length() - 7); + MongoDBInit.LOGGER.info("Found collection file: {}", collection); + MongoCollection dbCollection = db.getCollection(collection, DBObject.class); + try (Scanner scan = new Scanner(res.getInputStream(), "UTF-8")) { + int lines = 0; + while (scan.hasNextLine()) { + String json = scan.nextLine(); + DBObject parse = BasicDBObject.parse(json); + dbCollection.insertOne(parse); + lines++; + } + MongoDBInit.LOGGER.info("Imported {} objects into collection {}", lines, collection); + } + } else { + MongoDBInit.LOGGER.error("Failure loading resource {}", res); + } + } + } catch (IOException e) { + throw new RuntimeException("Error importing objects", e); + } + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/FakeClientConfig.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/FakeClientConfig.java new file mode 100644 index 00000000..05377059 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/FakeClientConfig.java @@ -0,0 +1,54 @@ +package de.taimos.dvalin.mongo.config; + +/*- + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 - 2017 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.mongodb.MongoClient; +import com.mongodb.ServerAddress; +import com.mongodb.client.MongoClients; +import de.bwaldvogel.mongo.MongoServer; +import de.bwaldvogel.mongo.backend.memory.MemoryBackend; +import de.taimos.daemon.spring.conditional.OnSystemProperty; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@OnSystemProperty(propertyName = "mongodb.type", propertyValue = "fake") +@Configuration +public class FakeClientConfig { + + + @Bean + public MongoServer mongoServer() { + return new MongoServer(new MemoryBackend()); + } + + + @Bean + public MongoClient mongoClient(MongoServer mongoServer) { + return new MongoClient(new ServerAddress(mongoServer.bind())); + } + + @Bean + public com.mongodb.client.MongoClient mongoClient2(MongoServer mongoServer) { + return MongoClients.create(mongoServer.bindAndGetConnectionString()); + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/MongoDBConfig.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/MongoDBConfig.java new file mode 100644 index 00000000..116b0eea --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/MongoDBConfig.java @@ -0,0 +1,96 @@ +package de.taimos.dvalin.mongo.config; + +/*- + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 - 2017 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.util.concurrent.TimeUnit; + +import com.mongodb.DB; +import com.mongodb.MongoClient; +import com.mongodb.ReadConcern; +import com.mongodb.ReadPreference; +import com.mongodb.WriteConcern; +import com.mongodb.client.MongoDatabase; +import de.taimos.dvalin.mongo.JongoFactory; +import io.mongock.api.config.LegacyMigration; +import io.mongock.driver.mongodb.sync.v4.driver.MongoSync4Driver; +import io.mongock.runner.standalone.MongockStandalone; +import io.mongock.runner.standalone.RunnerStandaloneBuilder; +import org.jongo.Jongo; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +@Configuration +public class MongoDBConfig { + + @Value("${mongock.enabled:false}") + private boolean mongockEnabled; + + @Value("${mongock.legacyMigration.enabled:true}") + private boolean mongockLegacyMigrationEnabled; + + @Value("${mongock.legacyMigration.table:dbchangelog}") + private String mongockLegacyTable; + + @Value("${mongodb.name}") + private String dbName; + + @Value("${mongock.basepackage:${mongock.basePackage:}}") + private String basePackage; + + @Bean + public RunnerStandaloneBuilder mongockRunner(com.mongodb.client.MongoClient mongoClient, Jongo jongo, DB legacyDB) { + + MongoSync4Driver driver = MongoSync4Driver.withDefaultLock(mongoClient, this.dbName); + driver.setWriteConcern(WriteConcern.MAJORITY.withJournal(true).withWTimeout(1000, TimeUnit.MILLISECONDS)); + driver.setReadConcern(ReadConcern.MAJORITY); + driver.setReadPreference(ReadPreference.primary()); + driver.disableTransaction(); + + RunnerStandaloneBuilder runnerStandaloneBuilder = MongockStandalone.builder().setDriver(driver).setTransactionEnabled(false); + if (this.basePackage == null || this.basePackage.isEmpty()){ + throw new RuntimeException("LegacyMigration basePackage must be set!"); + } + runnerStandaloneBuilder.addMigrationScanPackage(this.basePackage); + if(mongockLegacyMigrationEnabled) { + LegacyMigration legacyMigration = new LegacyMigration(); + legacyMigration.setOrigin(this.mongockLegacyTable); + runnerStandaloneBuilder.setLegacyMigration(legacyMigration); + } + runnerStandaloneBuilder.addDependency(jongo).addDependency(legacyDB).buildRunner().execute(); + return runnerStandaloneBuilder; + } + + @Bean + public MongoDatabase mongoDatabase(MongoClient mongoClient) { + return mongoClient.getDatabase(this.dbName); + } + + @Bean + public Jongo jongo(MongoClient mongoClient) { + return JongoFactory.createDefault(mongoClient.getDB(this.dbName)); + } + + @Bean + public DB mongoDB(MongoClient mongoClient){ + return mongoClient.getDB(this.dbName); + } +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/RealClientConfig.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/RealClientConfig.java new file mode 100644 index 00000000..207b228d --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/config/RealClientConfig.java @@ -0,0 +1,66 @@ +package de.taimos.dvalin.mongo.config; + +/*- + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 - 2017 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.mongodb.MongoClient; +import com.mongodb.MongoClientOptions; +import com.mongodb.MongoClientURI; +import com.mongodb.client.MongoClients; +import de.taimos.daemon.spring.conditional.OnSystemProperty; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + + +@OnSystemProperty(propertyName = "mongodb.type", propertyValue = "real") +@Configuration +public class RealClientConfig { + + @Value("${mongodb.uri:mongodb://${mongodb.host:localhost}:${mongodb.port:27017}}") + private String mongoURI; + + @Value("${mongodb.sockettimeout:${mongodb.socketTimeout:10000}}") + private int socketTimeout; + + @Value("${mongodb.connecttimeout:${mongodb.connectTimeout:10000}}") + private int connectTimeout; + + + @Bean + public MongoClient mongoClient() { + MongoClientOptions.Builder builder = MongoClientOptions.builder(); + builder.socketTimeout(this.socketTimeout); + builder.connectTimeout(this.connectTimeout); + + return new MongoClient(new MongoClientURI(this.mongoURI, builder)); + } + + @Bean + public com.mongodb.client.MongoClient mongoClient2() { + MongoClientOptions.Builder builder = MongoClientOptions.builder(); + builder.socketTimeout(this.socketTimeout); + builder.connectTimeout(this.connectTimeout); + return MongoClients.create(new MongoClientURI(this.mongoURI, builder).getURI()); + } + + + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/AReferenceableEntity.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/AReferenceableEntity.java new file mode 100644 index 00000000..5ddb9bc3 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/AReferenceableEntity.java @@ -0,0 +1,44 @@ +package de.taimos.dvalin.mongo.links; + +/*- + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 - 2018 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonIgnore; + +/** + * referenceable via DocumentLink + * + * @param generic link to myself + * @author Thorsten Hoeger + */ +public interface AReferenceableEntity> { + + @JsonIgnore + @SuppressWarnings("unchecked") + default DocumentLink asLink() { + return new DocumentLink<>((T) this); + } + + @JsonIgnore + String getLabel(); + + String getId(); + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DLinkDAO.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DLinkDAO.java new file mode 100644 index 00000000..b9a44ee4 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DLinkDAO.java @@ -0,0 +1,68 @@ +package de.taimos.dvalin.mongo.links; + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.bson.types.ObjectId; +import org.jongo.Jongo; +import org.jongo.MongoCollection; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Repository; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + + +@Repository +public class DLinkDAO implements IDLinkDAO { + + private final Jongo jongo; + + @Autowired + public DLinkDAO(Jongo jongo) { + this.jongo = jongo; + } + + @Override + public > T resolve(DocumentLink link) { + MongoCollection collection = this.jongo.getCollection(link.getTargetClass().getSimpleName()); + return collection.findOne(new ObjectId(link.getObjectId())).as(link.getTargetClass()); + } + + @Override + public > List resolve(List> links, Class targetClass) { + MongoCollection collection = this.jongo.getCollection(targetClass.getSimpleName()); + List ids = new ArrayList<>(); + for (DocumentLink link : links) { + if (!link.getTargetClass().equals(targetClass)) { + throw new IllegalArgumentException("Invalid link in collection"); + } + ids.add(new ObjectId(link.getObjectId())); + } + Iterator it = collection.find("{\"_id\" : {\"$in\" : #}}", ids).as(targetClass).iterator(); + List resolved = new ArrayList<>(); + while (it.hasNext()) { + resolved.add(it.next()); + } + return resolved; + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DLinkQuery.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DLinkQuery.java new file mode 100644 index 00000000..ce063aa4 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DLinkQuery.java @@ -0,0 +1,80 @@ +package de.taimos.dvalin.mongo.links; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.util.ArrayList; +import java.util.Iterator; +import java.util.List; + +import org.jongo.MongoCollection; + +import com.mongodb.DBObject; + +import de.taimos.dvalin.mongo.MongoDBDataAccess; + +/** + * QueryHelper to convert query result to a list of DLinks. It only queries the fields necessary to construct links. + * + * @param the {@link AReferenceableEntity} this link uses + * @author Thorsten Hoeger + */ +public class DLinkQuery> { + + private final Class targetClass; + + private final String labelField; + + + /** + * @param targetClass the links target class + * @param labelField the name of the label field + */ + public DLinkQuery(Class targetClass, String labelField) { + this.targetClass = targetClass; + this.labelField = labelField; + } + + public List> find(MongoDBDataAccess dataAccess, String query, Object... parameter) { + return dataAccess.findSortedByQuery(query, null, null, null, String.format("{%s:1}", this.labelField), this::convert, parameter); + } + + @Deprecated + public List> find(MongoCollection collection, String query, Object... parameter) { + Iterator> it = collection.find(query, parameter).projection(String.format("{%s:1}", this.labelField)).map(this::convert).iterator(); + + List> objects = new ArrayList<>(); + while (it.hasNext()) { + DocumentLink link = it.next(); + objects.add(link); + } + return objects; + } + + private DocumentLink convert(DBObject result) { + if (!result.containsField("_id") || !result.containsField(DLinkQuery.this.labelField)) { + throw new RuntimeException("Fields missing to construct DocumentLink"); + } + String id = result.get("_id").toString(); + String label = result.get(DLinkQuery.this.labelField).toString(); + return new DocumentLink<>(DLinkQuery.this.targetClass, id, label); + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DocumentLink.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DocumentLink.java new file mode 100644 index 00000000..591a0115 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/DocumentLink.java @@ -0,0 +1,123 @@ +package de.taimos.dvalin.mongo.links; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.io.Serializable; + +/** + * Link to another document of a {@link AReferenceableEntity}
+ * It is stored as an object containing the target class, the objectId of the target and a label to avoid joining the document for display + * purpose. + * + * @param the target type + * @author Thorsten Hoeger + */ +public class DocumentLink> implements Serializable { + + private Class targetClass; + private String objectId; + private String label; + + + public DocumentLink() { + // + } + + @SuppressWarnings("unchecked") + public DocumentLink(T object) { + this((Class) object.getClass(), object.getId(), object.getLabel()); + } + + public DocumentLink(Class targetClass, String objectId, String label) { + this.targetClass = targetClass; + this.objectId = objectId; + this.label = label; + } + + public Class getTargetClass() { + return this.targetClass; + } + + public void setTargetClass(Class targetClass) { + this.targetClass = targetClass; + } + + public String getObjectId() { + return this.objectId; + } + + public void setObjectId(String objectId) { + this.objectId = objectId; + } + + public String getLabel() { + return this.label; + } + + public void setLabel(String label) { + this.label = label; + } + + @Override + public String toString() { + return String.format("%s [%s@%s]", this.label, this.objectId, this.targetClass.getSimpleName()); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = (prime * result) + ((this.objectId == null) ? 0 : this.objectId.hashCode()); + result = (prime * result) + ((this.targetClass == null) ? 0 : this.targetClass.hashCode()); + return result; + } + + @Override + @SuppressWarnings("rawtypes") + public boolean equals(Object obj) { + if (this == obj) { + return true; + } + if (obj == null) { + return false; + } + if (!(obj instanceof DocumentLink)) { + return false; + } + DocumentLink other = (DocumentLink) obj; + if (this.objectId == null) { + if (other.objectId != null) { + return false; + } + } else if (!this.objectId.equals(other.objectId)) { + return false; + } + if (this.targetClass == null) { + if (other.targetClass != null) { + return false; + } + } else if (!this.targetClass.equals(other.targetClass)) { + return false; + } + return true; + } + +} diff --git a/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/IDLinkDAO.java b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/IDLinkDAO.java new file mode 100644 index 00000000..bf02aef1 --- /dev/null +++ b/mongodb-legacy/src/main/java/de/taimos/dvalin/mongo/links/IDLinkDAO.java @@ -0,0 +1,34 @@ +package de.taimos.dvalin.mongo.links; + +import java.util.List; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +/** + * DAO to generically resolve DLinks + */ +public interface IDLinkDAO { + + > T resolve(DocumentLink link); + + > List resolve(List> links, Class targetClass); + +} diff --git a/mongodb-legacy/src/main/resources/.gitkeep b/mongodb-legacy/src/main/resources/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/ABaseTest.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/ABaseTest.java new file mode 100644 index 00000000..7aa7f075 --- /dev/null +++ b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/ABaseTest.java @@ -0,0 +1,79 @@ +/** + * + */ +package de.taimos.dvalin.mongo; + +/* + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.math.BigDecimal; + +import com.mongodb.ConnectionString; +import com.mongodb.DB; +import com.mongodb.ServerAddress; +import com.mongodb.client.MongoClient; +import com.mongodb.client.MongoClients; +import com.mongodb.client.MongoDatabase; +import de.bwaldvogel.mongo.MongoServer; +import de.bwaldvogel.mongo.ServerVersion; +import de.bwaldvogel.mongo.backend.memory.MemoryBackend; +import de.taimos.daemon.log4j.Log4jLoggingConfigurer; +import org.jongo.Jongo; +import org.junit.Assert; + +/** + * Copyright 2015 Taimos GmbH
+ *
+ * + * @author thoeger + */ +public class ABaseTest { + + protected static final String dbName = "dvalin-mongo"; + private static final ServerAddress serverAddress = new ServerAddress(new MongoServer(new MemoryBackend().version(ServerVersion.MONGO_3_6)).bind()); + + public static final MongoClient mongo = MongoClients.create(new ConnectionString(String.format("mongodb://%s:%d", ABaseTest.serverAddress.getHost(), ABaseTest.serverAddress.getPort()))); + public static final com.mongodb.MongoClient oldMongo = new com.mongodb.MongoClient(new ServerAddress(new MongoServer(new MemoryBackend().version(ServerVersion.MONGO_3_6)).bind()));; + + public static final DB oldDB = ABaseTest.oldMongo.getDB(ABaseTest.dbName); + public static final Jongo jongo = JongoFactory.createDefault(ABaseTest.oldMongo.getDB(ABaseTest.dbName)); + public static final MongoDatabase database = ABaseTest.mongo.getDatabase(ABaseTest.dbName); + + static { + try { + new Log4jLoggingConfigurer().simpleLogging(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + + protected static void assertEquals(BigDecimal bd1, BigDecimal bd2) { + Assert.assertEquals(bd1.doubleValue(), bd2.doubleValue(), 0); + } + + /** + * + */ + public ABaseTest() { + super(); + } + +} diff --git a/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkDAO.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkDAO.java new file mode 100644 index 00000000..439b43d4 --- /dev/null +++ b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkDAO.java @@ -0,0 +1,29 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +public class LinkDAO extends AbstractMongoDAO { + + public LinkObject findByName(String name) { + return this.findFirstByQuery("{name:#}", null, name); + } + +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkObject.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkObject.java similarity index 100% rename from mongodb/src/test/java/de/taimos/dvalin/mongo/LinkObject.java rename to mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkObject.java diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkTester.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkTester.java similarity index 100% rename from mongodb/src/test/java/de/taimos/dvalin/mongo/LinkTester.java rename to mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkTester.java diff --git a/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkedDAO.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkedDAO.java new file mode 100644 index 00000000..a16a6bc8 --- /dev/null +++ b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkedDAO.java @@ -0,0 +1,26 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +public class LinkedDAO extends AbstractMongoDAO { + + // +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkedObject.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkedObject.java similarity index 100% rename from mongodb/src/test/java/de/taimos/dvalin/mongo/LinkedObject.java rename to mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/LinkedObject.java diff --git a/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/TestDAO.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/TestDAO.java new file mode 100644 index 00000000..ef1cd34a --- /dev/null +++ b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/TestDAO.java @@ -0,0 +1,28 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +public class TestDAO extends AbstractMongoDAO { + + public TestObject findByName(String name) { + return this.findFirstByQuery("{name:#}", null, name); + } +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/TestObject.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/TestObject.java similarity index 100% rename from mongodb/src/test/java/de/taimos/dvalin/mongo/TestObject.java rename to mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/TestObject.java diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/Tester.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/Tester.java similarity index 90% rename from mongodb/src/test/java/de/taimos/dvalin/mongo/Tester.java rename to mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/Tester.java index 2f84a770..16ccdef8 100644 --- a/mongodb/src/test/java/de/taimos/dvalin/mongo/Tester.java +++ b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/Tester.java @@ -23,6 +23,7 @@ import java.lang.reflect.Field; import java.math.BigDecimal; +import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.DeserializationFeature; import com.fasterxml.jackson.databind.MapperFeature; import com.fasterxml.jackson.databind.SerializationFeature; @@ -33,6 +34,7 @@ import io.mongock.driver.mongodb.sync.v4.driver.MongoSync4Driver; import io.mongock.runner.standalone.MongockStandalone; import org.bson.Document; +import org.bson.types.ObjectId; import org.joda.time.DateTime; import org.jongo.Mapper; import org.jongo.marshall.jackson.JacksonMapper; @@ -40,6 +42,7 @@ import org.junit.Assert; import org.junit.BeforeClass; import org.junit.Test; +import org.junit.jupiter.api.Assertions; public class Tester extends ABaseTest { @@ -73,6 +76,20 @@ public static void init() { } } + @Test + public void testObjectId() throws JsonProcessingException { + TestObject to = new TestObject(); + Tester.dao.dataAccess.save(to); + + Document result = Tester.dao.dataAccess.getCollection().findOne().as(Document.class); + + Assertions.assertNotNull(result); + +// TestObject mappedObject = mapper.readValue(result.toJson(), TestObject.class); + Object id = result.get("_id"); + Assertions.assertEquals(new ObjectId(to.getId()), id); + } + @Test public void testUpdate() { TestObject o = new TestObject(); diff --git a/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/changelog/TestChangelog.java b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/changelog/TestChangelog.java new file mode 100644 index 00000000..fbd5b541 --- /dev/null +++ b/mongodb-legacy/src/test/java/de/taimos/dvalin/mongo/changelog/TestChangelog.java @@ -0,0 +1,39 @@ +package de.taimos.dvalin.mongo.changelog; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import de.taimos.dvalin.mongo.ABaseTest; +import de.taimos.dvalin.mongo.ChangelogUtil; +import io.mongock.api.annotations.ChangeUnit; +import io.mongock.api.annotations.Execution; +import io.mongock.api.annotations.RollbackExecution; + +@ChangeUnit(order = "001", id = "index1", author = "thoeger", transactional = false) +public class TestChangelog { + + @Execution + public void index1() { + ChangelogUtil.addIndex(ABaseTest.oldDB.getCollection("TestObject"), "name", true, true); + } + + @RollbackExecution + public void index1Rollback(){} +} diff --git a/mongodb-legacy/src/test/resources/.gitkeep b/mongodb-legacy/src/test/resources/.gitkeep new file mode 100644 index 00000000..e69de29b diff --git a/mongodb/pom.xml b/mongodb/pom.xml index c7628211..26e0b9c3 100644 --- a/mongodb/pom.xml +++ b/mongodb/pom.xml @@ -1,15 +1,14 @@ - - 4.0.0 - - de.taimos - dvalin-parent - 1.36-SNAPSHOT - - dvalin-mongodb - MongoDB support for dvalin - 2015 - - + + 4.0.0 + + de.taimos + dvalin-parent + 1.36-SNAPSHOT + + dvalin-mongodb + MongoDB support for dvalin + 2015 @@ -21,7 +20,7 @@ - + de.taimos dvalin-daemon @@ -29,56 +28,39 @@ - com.fasterxml.jackson.core - jackson-databind - - - com.fasterxml.jackson.jaxrs - jackson-jaxrs-json-provider - - - de.undercouch - bson4jackson - ${bson.version} - + com.fasterxml.jackson.core + jackson-databind + + + com.fasterxml.jackson.datatype + jackson-datatype-joda + + + com.fasterxml.jackson.jaxrs + jackson-jaxrs-json-provider + + + de.undercouch + bson4jackson + ${bson.version} + + + + + org.springframework + spring-beans + + + org.springframework + spring-context + + + org.mongodb + mongodb-driver-sync + ${mongo.version} + - - - org.springframework - spring-beans - - - org.springframework - spring-context - - - org.mongodb - mongodb-driver-legacy - - - org.jongo - jongo - ${jongo.version} - - - jackson-databind - com.fasterxml.jackson.core - - - jackson-core - com.fasterxml.jackson.core - - - bson4jackson - de.undercouch - - - jackson-annotations - com.fasterxml.jackson.core - - - io.mongock mongock-standalone @@ -93,5 +75,5 @@ ${mongo-java-server.version} true - + diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/AEntity.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/AEntity.java index 19df4ca0..a94680b1 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/AEntity.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/AEntity.java @@ -9,9 +9,9 @@ * 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. @@ -20,11 +20,14 @@ * #L% */ -import java.io.Serializable; - +import com.fasterxml.jackson.annotation.JsonProperty; +import com.fasterxml.jackson.databind.annotation.JsonDeserialize; +import com.fasterxml.jackson.databind.annotation.JsonSerialize; +import de.taimos.dvalin.mongo.id.IdEntity; +import de.taimos.dvalin.mongo.mapper.ObjectIdMapping; import org.bson.types.ObjectId; -import org.jongo.marshall.jackson.oid.MongoId; -import org.jongo.marshall.jackson.oid.MongoObjectId; + +import java.io.Serializable; /** * Copyright 2015 Hoegernet
@@ -34,18 +37,16 @@ * * @author Thorsten Hoeger */ -public abstract class AEntity implements Serializable { +public abstract class AEntity extends IdEntity implements Serializable { private static final long serialVersionUID = 6328501276339927785L; - @MongoId - @MongoObjectId + @JsonProperty("_id") + @JsonSerialize(using = ObjectIdMapping.ObjectIdSerializer.class) + @JsonDeserialize(using = ObjectIdMapping.ObjectIdDeserializer.class) protected String id = ObjectId.get().toString(); - - /** - * @return the unique id of the element - */ + @Override public String getId() { return this.id; } @@ -53,5 +54,4 @@ public String getId() { public void setId(final String id) { this.id = id; } - } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/AbstractAuditedMongoDAO.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/AbstractAuditedMongoDAO.java index fe0e9b53..214b8571 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/AbstractAuditedMongoDAO.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/AbstractAuditedMongoDAO.java @@ -20,32 +20,39 @@ * #L% */ -import java.util.List; - +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.Sorts; +import org.bson.BsonDocument; +import org.bson.BsonObjectId; import org.bson.Document; +import org.bson.conversions.Bson; import org.bson.types.ObjectId; import org.joda.time.DateTime; -import org.jongo.Jongo; -import org.jongo.Mapper; -import org.jongo.MongoCollection; -import org.jongo.MongoCursor; -import org.jongo.bson.BsonDocument; -import com.mongodb.BasicDBObject; -import com.mongodb.client.MongoDatabase; +import java.util.ArrayList; +import java.util.List; -public abstract class AbstractAuditedMongoDAO extends AbstractMongoDAO implements ICrudAuditedDAO { +import static com.mongodb.client.model.Filters.and; +import static com.mongodb.client.model.Filters.eq; - protected MongoCollection jongoHistoryCollection; - private com.mongodb.client.MongoCollection historyCollection; - private Mapper jongoMapper; +/** + * @param the entity this DAO is used for + * @author Thorsten Hoeger + */ +public abstract class AbstractAuditedMongoDAO extends AbstractMongoDAO + implements ICrudAuditedDAO { + + private static final String HISTORY_OBJECT_ID = "originalId"; + + private MongoCollection historyCollection; + private ObjectMapper mapper; @Override - protected void customInit(MongoDatabase db, Jongo jongo) { + protected void customInit() { + this.mapper = this.dataAccess.getMapper(); String collectionName = this.dataAccess.getCollectionName() + "_history"; - this.jongoHistoryCollection = jongo.getCollection(collectionName); - this.historyCollection = db.getCollection(collectionName); - this.jongoMapper = jongo.getMapper(); + this.historyCollection = this.dataAccess.getDb().getCollection(collectionName); } @Override @@ -54,17 +61,22 @@ public T findVersion(String id, Integer version) { return this.findById(id); } - MongoCursor as = this.jongoHistoryCollection.find("{originalId:#, version:#}", new ObjectId(id), version).as(this.dataAccess.getEntityClass()); - if (as.hasNext()) { - return as.next(); - } - return null; + return this.historyCollection.find(and(eq(AbstractAuditedMongoDAO.getIdFilter(id)), eq("version", version))) + .map(this::mapToEntity).first(); } @Override public List findHistoryElements(String id) { - Iterable as = this.jongoHistoryCollection.find("{originalId : #}", new ObjectId(id)).sort("{version: -1}").as(this.dataAccess.getEntityClass()); - return this.dataAccess.convertIterable(as); + return this.historyCollection.find(AbstractAuditedMongoDAO.getIdFilter(id)).sort(Sorts.descending("version")) + .map(this::mapToEntity).into(new ArrayList<>()); + } + + private T mapToEntity(Document document) { + return this.dataAccess.mapToEntity(document); + } + + private static Bson getIdFilter(String id) { + return eq(AbstractAuditedMongoDAO.HISTORY_OBJECT_ID, new ObjectId(id)); } @Override @@ -81,15 +93,17 @@ protected void beforeSave(T object) { @Override protected void afterSave(T object) { try { - BsonDocument bsonDocument = this.jongoMapper.getMarshaller().marshall(object); - BasicDBObject dbObject = new BasicDBObject(bsonDocument.toDBObject().toMap()); - dbObject.removeField("_id"); - dbObject.put("originalId", new ObjectId(object.getId())); - Document doc = Document.parse(dbObject.toString()); - this.historyCollection.insertOne(doc); + String json = this.mapper.writeValueAsString(object); + BsonDocument bsonDocument = BsonDocument.parse(json); + bsonDocument.remove(this.dataAccess.idField()); + bsonDocument.append(AbstractAuditedMongoDAO.HISTORY_OBJECT_ID, + new BsonObjectId(new ObjectId(object.getId()))); + + this.historyCollection.insertOne(this.bsonToDocument(bsonDocument)); } catch (Exception e) { String message = String.format("Unable to save object %s due to a marshalling error", object); throw new IllegalArgumentException(message, e); } } + } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/AbstractMongoDAO.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/AbstractMongoDAO.java index 547f707c..53c23022 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/AbstractMongoDAO.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/AbstractMongoDAO.java @@ -20,20 +20,18 @@ * #L% */ -import java.util.List; -import java.util.Map; - -import javax.annotation.PostConstruct; - -import org.jongo.Jongo; -import org.jongo.MongoCollection; -import org.jongo.ResultHandler; +import com.mongodb.Function; +import com.mongodb.client.gridfs.GridFSBucket; +import org.bson.BsonDocument; +import org.bson.BsonDocumentReader; +import org.bson.Document; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.DocumentCodec; +import org.bson.conversions.Bson; import org.springframework.beans.factory.annotation.Autowired; -import com.mongodb.DBObject; -import com.mongodb.MongoClient; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.gridfs.GridFSBucket; +import javax.annotation.PostConstruct; +import java.util.List; /** * Copyright 2015 Hoegernet
@@ -46,60 +44,26 @@ */ public abstract class AbstractMongoDAO implements ICrudDAO { - @Autowired - @Deprecated - protected MongoClient mongo; - - @Autowired - private Jongo jongo; - @Autowired - private MongoDatabase db; - - @Deprecated - protected MongoCollection collection; + protected DocumentCodec codec; + protected DecoderContext decoderContext; @Autowired protected MongoDBDataAccess dataAccess; @PostConstruct public final void init() { - this.collection = this.dataAccess.getCollection(); - this.customInit(this.db, this.jongo); - } - - protected void customInit(MongoDatabase db, Jongo jongo) { - // implement if needed + this.customInit(); + this.codecInit(); } - /** - * runs a map-reduce-job on the collection. same as {@link #mapReduce(String, DBObject, DBObject, Map, MapReduceResultHandler) - * mapReduce(name, null, null, null, conv)} - * - * @param the type of the result class - * @param name the name of the map-reduce functions - * @param conv the converter to convert the result - * @return an {@link Iterable} with the result entries - */ - protected final Iterable mapReduce(String name, final MapReduceResultHandler conv) { - return this.dataAccess.mapReduce(name, conv); + protected void codecInit() { + // override if needed + this.codec = new DocumentCodec(); + this.decoderContext = DecoderContext.builder().build(); } - /** - * runs a map-reduce-job on the collection. The functions are read from the classpath in the folder mongodb. The systems reads them from - * files called <name>.map.js, <name>.reduce.js and optionally <name>.finalize.js. After this the result is converted - * using the given {@link MapReduceResultHandler} - * - * @param the type of the result class - * @param name the name of the map-reduce functions - * @param query the query to filter the elements used for the map-reduce - * @param sort sort query to sort elements before running map-reduce - * @param scope the global scope for the JavaScript run - * @param conv the converter to convert the result - * @return an {@link Iterable} with the result entries - * @throws RuntimeException if resources cannot be read - */ - protected final Iterable mapReduce(String name, DBObject query, DBObject sort, Map scope, final MapReduceResultHandler conv) { - return this.dataAccess.mapReduce(name, query, sort, scope, conv); + protected void customInit() { + // implement if needed } @Override @@ -115,24 +79,22 @@ public List findList(String sortProp, Integer sortDirection, Integer limit, I /** * finds all elements matching the given query * - * @param query the query to search for - * @param params the parameters to replace # symbols + * @param query the query to search for * @return the list of elements found */ - protected final List findByQuery(String query, Object... params) { - return this.dataAccess.findByQuery(query, params); + protected final List findByQuery(Bson query) { + return this.dataAccess.findSortedByQuery(query, null, null, null, null); } /** * finds all elements matching the given query and sorts them accordingly * - * @param query the query to search for - * @param sort the sort query to apply - * @param params the parameters to replace # symbols + * @param query the query to search for + * @param sort the sort query to apply * @return the list of elements found */ - protected final List findSortedByQuery(String query, String sort, Object... params) { - return this.dataAccess.findSortedByQuery(query, sort, params); + protected final List findSortedByQuery(Bson query, Bson sort) { + return this.dataAccess.findSortedByQuery(query, sort, null, null, null); } /** @@ -143,44 +105,39 @@ protected final List findSortedByQuery(String query, String sort, Object... p * @param query the query to search for * @param sort the sort query to apply * @param projection the projection of fields to use - * @param as the target to convert result elements to - * @param params the parameters to replace # symbols - * @param

the element type * @return the list of elements found */ - protected final

List

findSortedByQuery(String query, String sort, String projection, Class

as, Object... params) { - return this.dataAccess.findSortedByQuery(query, sort, projection, as, params); + protected final List findSortedByQuery(Bson query, Bson sort, Bson projection) { + return this.dataAccess.findSortedByQuery(query, sort, null, null, projection); } /** * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to * rename or filter fields in the result elements. Instead of returning typed objects it returns objects converted - * by the given {@link ResultHandler} + * by the given {@link Function} * * @param query the query to search for * @param sort the sort query to apply * @param projection the projection of fields to use * @param handler the handler to convert result elements with - * @param params the parameters to replace # symbols * @param

the element type * @return the list of elements found */ - protected final

List

findSortedByQuery(String query, String sort, String projection, ResultHandler

handler, Object... params) { - return this.dataAccess.findSortedByQuery(query, sort, projection, handler, params); + protected final

List

findSortedByQuery(Bson query, Bson sort, Bson projection, Function handler) { + return this.dataAccess.findSortedByQuery(query, sort, null, null, projection, handler); } /** * finds all elements matching the given query and sorts them accordingly * - * @param query the query to search for - * @param sort the sort query to apply - * @param skip the number of elements to skip - * @param limit the number of elements to fetch - * @param params the parameters to replace # symbols + * @param query the query to search for + * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch * @return the list of elements found */ - protected final List findSortedByQuery(String query, String sort, Integer skip, Integer limit, Object... params) { - return this.dataAccess.findSortedByQuery(query, sort, skip, limit, params); + protected final List findSortedByQuery(Bson query, Bson sort, Integer skip, Integer limit) { + return this.dataAccess.findSortedByQuery(query, sort, skip, limit, null); } /** @@ -193,19 +150,16 @@ protected final List findSortedByQuery(String query, String sort, Integer ski * @param skip the number of elements to skip * @param limit the number of elements to fetch * @param projection the projection of fields to use - * @param as the target to convert result elements to - * @param params the parameters to replace # symbols - * @param

the element type * @return the list of elements found */ - protected final

List

findSortedByQuery(String query, String sort, Integer skip, Integer limit, String projection, Class

as, Object... params) { - return this.dataAccess.findSortedByQuery(query, sort, skip, limit, projection, as, params); + protected final List findSortedByQuery(Bson query, Bson sort, Integer skip, Integer limit, Bson projection) { + return this.dataAccess.findSortedByQuery(query, sort, skip, limit, projection); } /** * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to * rename or filter fields in the result elements. Instead of returning typed objects it returns objects converted - * by the given {@link ResultHandler} + * by the given {@link Function} * * @param query the query to search for * @param sort the sort query to apply @@ -213,40 +167,38 @@ protected final

List

findSortedByQuery(String query, String sort, Integer * @param limit the number of elements to fetch * @param projection the projection of fields to use * @param handler the handler to convert result elements with - * @param params the parameters to replace # symbols - * @param

the element type + * @param the element type * @return the list of elements found */ - protected final

List

findSortedByQuery(String query, String sort, Integer skip, Integer limit, String projection, ResultHandler

handler, Object... params) { - return this.dataAccess.findSortedByQuery(query, sort, skip, limit, projection, handler, params); + protected final List findSortedByQuery(Bson query, Bson sort, Integer skip, Integer limit, Bson projection, Function handler) { + return this.dataAccess.findSortedByQuery(query, sort, skip, limit, projection, handler); } /** * finds all elements containing the given searchString in any text field and sorts them accordingly. * - * @param searchString the searchString to search for - * @param sort the sort query to apply + * @param searchString the searchString to search for + * @param sort the sort query to apply * @return the list of elements found */ - protected final List searchSorted(String searchString, String sort) { + protected final List searchSorted(String searchString, Bson sort) { return this.dataAccess.searchSorted(searchString, sort); } /** * queries with the given string, sorts the result and returns the first element. null is returned if no element is found. * - * @param query the query string - * @param sort the sort string - * @param params the parameters to replace # symbols + * @param query the query + * @param sort the sort * @return the first element found or null if none is found */ - protected final T findFirstByQuery(String query, String sort, Object... params) { - return this.dataAccess.findFirstByQuery(query, sort, params).orElse(null); + protected final T findFirstByQuery(Bson query, Bson sort) { + return this.dataAccess.findFirstByQuery(query, sort).orElse(null); } @Override public final T findById(String id) { - return this.dataAccess.findByObjectId(id).orElse(null); + return this.dataAccess.findById(id).orElse(null); } @Override @@ -285,7 +237,7 @@ public final void delete(T object) { @Override public final void delete(String id) { this.beforeDelete(id); - this.dataAccess.deleteByObjectId(id); + this.dataAccess.deleteById(id); this.afterDelete(id); } @@ -303,4 +255,7 @@ protected GridFSBucket getGridFSBucket(String bucket) { return this.dataAccess.getGridFSBucket(bucket); } + protected Document bsonToDocument(BsonDocument bsonDocument) { + return this.codec.decode(new BsonDocumentReader(bsonDocument), this.decoderContext); + } } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/ChangelogUtil.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/ChangelogUtil.java index 46abc789..a5bda9e1 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/ChangelogUtil.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/ChangelogUtil.java @@ -21,7 +21,10 @@ */ import com.mongodb.BasicDBObject; -import com.mongodb.DBCollection; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.model.IndexOptions; + +import java.util.concurrent.TimeUnit; /** * Copyright 2015 Hoegernet
@@ -44,11 +47,13 @@ private ChangelogUtil() { * @param ttl the TTL to set on the given field * @throws IllegalArgumentException if the TTL is less or equal 0 */ - public static void addTTLIndex(DBCollection collection, String field, int ttl) { + public static void addTTLIndex(MongoCollection collection, String field, int ttl) { if (ttl <= 0) { throw new IllegalArgumentException("TTL must be positive"); } - collection.createIndex(new BasicDBObject(field, 1), new BasicDBObject("expireAfterSeconds", ttl)); + IndexOptions indexOptions = new IndexOptions(); + indexOptions.expireAfter((long) ttl, TimeUnit.SECONDS); + collection.createIndex(new BasicDBObject(field, 1), indexOptions); } /** @@ -59,9 +64,11 @@ public static void addTTLIndex(DBCollection collection, String field, int ttl) { * @param asc the sorting direction. true to sort ascending; false to sort descending * @param background iff true the index is created in the background */ - public static void addIndex(DBCollection collection, String field, boolean asc, boolean background) { + public static void addIndex(MongoCollection collection, String field, boolean asc, boolean background) { int dir = (asc) ? 1 : -1; - collection.createIndex(new BasicDBObject(field, dir), new BasicDBObject("background", background)); + IndexOptions indexOptions = new IndexOptions(); + indexOptions.background(background); + collection.createIndex(new BasicDBObject(field, dir), indexOptions); } /** @@ -69,8 +76,8 @@ public static void addIndex(DBCollection collection, String field, boolean asc, * * @param collection the collection to use for the index */ - public static void addTextIndex(DBCollection collection) { - collection.createIndex(new BasicDBObject("$**", "text")); + public static void addTextIndex(MongoCollection collection) { + collection.createIndex(new BasicDBObject("$**", "text")); } } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/JodaCodec.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/JodaCodec.java new file mode 100644 index 00000000..ad37de4d --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/JodaCodec.java @@ -0,0 +1,33 @@ +package de.taimos.dvalin.mongo; + +import org.bson.BsonReader; +import org.bson.BsonWriter; +import org.bson.codecs.Codec; +import org.bson.codecs.DecoderContext; +import org.bson.codecs.EncoderContext; +import org.joda.time.DateTime; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +public class JodaCodec implements Codec { + @Override + public DateTime decode(BsonReader reader, DecoderContext decoderContext) { + return new DateTime(reader.readDateTime()); + } + + @Override + public void encode(BsonWriter writer, DateTime value, EncoderContext encoderContext) { + if (value != null) { + writer.writeDateTime(value.getMillis()); + } + } + + @Override + public Class getEncoderClass() { + return DateTime.class; + } +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoClientOptionsFactory.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoClientOptionsFactory.java index eae4c0e1..2a36e534 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoClientOptionsFactory.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoClientOptionsFactory.java @@ -9,9 +9,9 @@ * 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. @@ -21,10 +21,12 @@ */ +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoClientSettings.Builder; import org.springframework.beans.factory.FactoryBean; -import com.mongodb.MongoClientOptions; -import com.mongodb.MongoClientOptions.Builder; +import java.util.concurrent.TimeUnit; + /** * Copyright 2014 Hoegernet
@@ -33,23 +35,25 @@ * * @author Thorsten Hoeger */ -public class MongoClientOptionsFactory implements FactoryBean { +public class MongoClientOptionsFactory implements FactoryBean { private int socketTimeout; private int connectTimeout; @Override - public MongoClientOptions.Builder getObject() throws Exception { - Builder builder = MongoClientOptions.builder(); - builder.socketTimeout(this.socketTimeout); - builder.connectTimeout(this.connectTimeout); - return builder; + public MongoClientSettings.Builder getObject() throws Exception { + Builder settingsBuilder = MongoClientSettings.builder(); + settingsBuilder.applyToSocketSettings(builder -> { + builder.connectTimeout(MongoClientOptionsFactory.this.connectTimeout, TimeUnit.MILLISECONDS); + builder.readTimeout(MongoClientOptionsFactory.this.socketTimeout, TimeUnit.MILLISECONDS); + }); + return settingsBuilder; } @Override public Class getObjectType() { - return MongoClientOptions.Builder.class; + return MongoClientSettings.Builder.class; } @Override diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoDBDataAccess.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoDBDataAccess.java index 9b2992d0..df618daa 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoDBDataAccess.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoDBDataAccess.java @@ -20,360 +20,343 @@ * #L% */ -import java.io.IOException; -import java.io.InputStream; -import java.nio.charset.Charset; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; -import java.util.Map; -import java.util.Optional; - +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.mongodb.Function; +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.gridfs.GridFSBucket; +import com.mongodb.client.gridfs.GridFSBuckets; +import com.mongodb.client.model.ReplaceOptions; +import com.mongodb.client.model.Sorts; +import de.taimos.dvalin.daemon.spring.InjectionUtils; +import de.taimos.dvalin.mongo.id.IdEntity; +import de.taimos.dvalin.mongo.mapper.JacksonConfig; +import org.bson.BsonDocument; +import org.bson.Document; +import org.bson.conversions.Bson; import org.bson.types.ObjectId; -import org.jongo.Find; -import org.jongo.Jongo; -import org.jongo.MongoCollection; -import org.jongo.ResultHandler; -import org.jongo.Update; import org.springframework.beans.factory.InjectionPoint; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.beans.factory.config.ConfigurableBeanFactory; import org.springframework.context.annotation.Scope; import org.springframework.stereotype.Repository; -import org.springframework.util.StreamUtils; -import com.mongodb.DBObject; -import com.mongodb.MapReduceCommand; -import com.mongodb.MapReduceOutput; -import com.mongodb.client.MongoDatabase; -import com.mongodb.client.gridfs.GridFSBucket; -import com.mongodb.client.gridfs.GridFSBuckets; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; -import de.taimos.dvalin.daemon.spring.InjectionUtils; +import static com.mongodb.client.model.Filters.eq; +/** + * @param document class for which this will be the DB access + * @author hoegertn + * @author fzwirn + */ @Repository @Scope(ConfigurableBeanFactory.SCOPE_PROTOTYPE) -public class MongoDBDataAccess { +@SuppressWarnings("unused") +public class MongoDBDataAccess { /** * Query to perform a full text search */ public static final String FULLTEXT_QUERY = "{\"$text\" : {\"$search\" : #}}"; + public static final String DEFAULT_ID = "_id"; private final MongoDatabase db; private final Class entityClass; - private final MongoCollection collection; + private final MongoCollection collection; + private ObjectMapper mapper; + /** + * @param db database + * @param ip injection point + */ @Autowired - public MongoDBDataAccess(Jongo jongo, MongoDatabase db, InjectionPoint ip) { + public MongoDBDataAccess(MongoDatabase db, InjectionPoint ip) { this.db = db; + //noinspection unchecked this.entityClass = (Class) InjectionUtils.getGenericType(ip); - this.collection = jongo.getCollection(this.getCollectionName()); + this.collection = db.getCollection(this.getCollectionName()); + this.initMapper(); } - public String getCollectionName() { - return this.entityClass.getSimpleName(); - } - - public Class getEntityClass() { - return this.entityClass; + protected void initMapper() { + this.mapper = JacksonConfig.createObjectMapper(); } /** - * runs a map-reduce-job on the collection. same as {@link #mapReduce(String, DBObject, DBObject, Map, MapReduceResultHandler) - * mapReduce(name, null, null, null, conv)} - * - * @param the type of the result class - * @param name the name of the map-reduce functions - * @param conv the converter to convert the result - * @return an {@link Iterable} with the result entries + * @return the mapper */ - public final Iterable mapReduce(String name, final MapReduceResultHandler conv) { - return this.mapReduce(name, null, null, null, conv); + public ObjectMapper getMapper() { + return this.mapper; } /** - * runs a map-reduce-job on the collection. The functions are read from the classpath in the folder mongodb. The systems reads them from - * files called <name>.map.js, <name>.reduce.js and optionally <name>.finalize.js. After this the result is converted - * using the given {@link MapReduceResultHandler} - * - * @param the type of the result class - * @param name the name of the map-reduce functions - * @param query the query to filter the elements used for the map-reduce - * @param sort sort query to sort elements before running map-reduce - * @param scope the global scope for the JavaScript run - * @param conv the converter to convert the result - * @return an {@link Iterable} with the result entries - * @throws RuntimeException if resources cannot be read + * @param mapper the mapper to set */ - public final Iterable mapReduce(String name, DBObject query, DBObject sort, Map scope, final MapReduceResultHandler conv) { - String map = this.getMRFunction(name, "map"); - String reduce = this.getMRFunction(name, "reduce"); - - MapReduceCommand mrc = new MapReduceCommand(this.collection.getDBCollection(), map, reduce, null, MapReduceCommand.OutputType.INLINE, query); - String finalizeFunction = this.getMRFunction(name, "finalize"); - if(finalizeFunction != null) { - mrc.setFinalize(finalizeFunction); - } - if(sort != null) { - mrc.setSort(sort); - } - if(scope != null) { - mrc.setScope(scope); - } - MapReduceOutput mr = this.collection.getDBCollection().mapReduce(mrc); - return new ConverterIterable<>(mr.results().iterator(), conv); - } - - private String getMRFunction(String name, String type) { - try { - InputStream stream = this.getClass().getResourceAsStream("/mongodb/" + name + "." + type + ".js"); - if(stream != null) { - return StreamUtils.copyToString(stream, Charset.defaultCharset()); - } - return null; - } catch(IOException e) { - throw new RuntimeException("Failed to read resource", e); - } + public void setMapper(ObjectMapper mapper) { + this.mapper = mapper; } - public final List findList() { - Iterable as = this.collection.find().sort("{_id:1}").as(this.entityClass); - return this.convertIterable(as); + /** + * @return the id Field used by the entity + */ + public String idField() { + return MongoDBDataAccess.DEFAULT_ID; } - public List findList(String sortProp, Integer sortDirection, Integer limit, Integer skip) { - return this.findSortedByQuery("{}", "{" + sortProp + ":" + sortDirection + "}", skip, limit); + /** + * @return the name of the collection + */ + public String getCollectionName() { + return this.entityClass.getSimpleName(); } /** - * converts the given {@link Iterable} to a {@link List} - * - * @param

the element type - * @param as the {@link Iterable} - * @return the converted {@link List} + * @return the class of the entity */ - public final

List

convertIterable(Iterable

as) { - List

objects = new ArrayList<>(); - for(P mp : as) { - objects.add(mp); - } - return objects; + public Class getEntityClass() { + return this.entityClass; } /** - * finds all elements matching the given query - * - * @param query the query to search for - * @param params the parameters to replace # symbols - * @return the list of elements found + * @return all elements sorted by id field */ - public final List findByQuery(String query, Object... params) { - return this.findSortedByQuery(query, null, params); + public final List findList() { + return this.collection.find().sort(Sorts.ascending(this.idField())).map(this::mapToEntity) + .into(new ArrayList<>()); } /** - * finds all elements matching the given query and sorts them accordingly - * - * @param query the query to search for - * @param sort the sort query to apply - * @param params the parameters to replace # symbols + * @param sortProp property to sort by + * @param sortDirection direction to sort by + * @param skip the number of elements to skip + * @param limit the number of elements to fetch * @return the list of elements found */ - public final List findSortedByQuery(String query, String sort, Object... params) { - return this.findSortedByQuery(query, sort, null, (Integer) null, params); + public List findList(String sortProp, Integer sortDirection, Integer limit, Integer skip) { + return this.collection.find().sort(sortDirection == -1 ? Sorts.descending(sortProp) : Sorts.ascending(sortProp)) + .skip(skip).limit(limit).map(this::mapToEntity).into(new ArrayList<>()); } /** * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to - * rename or filter fields in the result elements. Instead of returning objects it returns objects of type - * as + * rename or filter fields in the result elements. * * @param query the query to search for * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch * @param projection the projection of fields to use - * @param as the target to convert result elements to - * @param params the parameters to replace # symbols - * @param

the element type * @return the list of elements found */ - public final

List

findSortedByQuery(String query, String sort, String projection, Class

as, Object... params) { - return this.findSortedByQuery(query, sort, null, null, projection, as, params); + public final List findSortedByQuery(Bson query, Bson sort, Integer skip, Integer limit, Bson projection) { + return this.innerFindSortedByQuery(query, sort, skip, limit, projection).map(this::mapToEntity) + .into(new ArrayList<>()); + } + + private FindIterable innerFindSortedByQuery(Bson query, Bson sort, Integer skip, Integer limit, Bson projection) { + FindIterable result = this.collection.find(query).sort(sort).limit(limit != null ? limit : 0) + .projection(projection); + if (skip != null) { + result = result.skip(skip); + } + return result; } + /** * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to * rename or filter fields in the result elements. Instead of returning objects it returns objects converted - * by the given {@link ResultHandler} + * by the given {@link Function} * - * @param query the query to search for - * @param sort the sort query to apply - * @param projection the projection of fields to use - * @param handler the handler to convert result elements with - * @param params the parameters to replace # symbols - * @param

the element type + * @param query the query to search for + * @param sort the sort query to apply + * @param skip the number of elements to skip + * @param limit the number of elements to fetch + * @param projection the projection of fields to use + * @param mappingFunction the handler to convert result elements with + * @param the element type * @return the list of elements found */ - protected final

List

findSortedByQuery(String query, String sort, String projection, ResultHandler

handler, Object... params) { - return this.findSortedByQuery(query, sort, null, null, projection, handler, params); + public final List findSortedByQuery(Bson query, Bson sort, Integer skip, Integer limit, Bson projection, Function mappingFunction) { + return this.innerFindSortedByQuery(query, sort, skip, limit, projection).map(this::mapToEntity) + .map(mappingFunction).into(new ArrayList<>()); } /** - * finds all elements matching the given query and sorts them accordingly + * finds all elements containing the given searchString in any text field and sorts them accordingly. * - * @param query the query to search for - * @param sort the sort query to apply - * @param skip the number of elements to skip - * @param limit the number of elements to fetch - * @param params the parameters to replace # symbols + * @param searchString the searchString to search for + * @param sort the sort query to apply * @return the list of elements found */ - public final List findSortedByQuery(String query, String sort, Integer skip, Integer limit, Object... params) { - return this.findSortedByQuery(query, sort, skip, limit, null, this.entityClass, params); + public final List searchSorted(String searchString, Bson sort) { + return this.findSortedByQuery(MongoDBDataAccess.createQuery(MongoDBDataAccess.FULLTEXT_QUERY, searchString), + sort, null, null, null); } + /** - * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to - * rename or filter fields in the result elements. Instead of returning objects it returns objects of type - * as + * queries with the given query, sorts the result and returns the first element. Empty Optional is returned if no element is found. * - * @param query the query to search for - * @param sort the sort query to apply - * @param skip the number of elements to skip - * @param limit the number of elements to fetch - * @param projection the projection of fields to use - * @param as the target to convert result elements to - * @param params the parameters to replace # symbols - * @param

the element type - * @return the list of elements found + * @param query the query + * @param sort the sort + * @return optional of the first element found or null if none is found */ - public final

List

findSortedByQuery(String query, String sort, Integer skip, Integer limit, String projection, Class

as, Object... params) { - Find find = this.createFind(query, sort, skip, limit, projection, params); - return this.convertIterable(find.as(as)); + public final Optional findFirstByQuery(Bson query, Bson sort) { + return Optional.ofNullable(this.collection.find(query).sort(sort).map(this::mapToEntity).first()); } /** - * finds all elements matching the given query and sorts them accordingly. With this method it is possible to specify a projection to - * rename or filter fields in the result elements. Instead of returning objects it returns objects converted - * by the given {@link ResultHandler} - * - * @param query the query to search for - * @param sort the sort query to apply - * @param skip the number of elements to skip - * @param limit the number of elements to fetch - * @param projection the projection of fields to use - * @param handler the handler to convert result elements with - * @param params the parameters to replace # symbols - * @param

the element type - * @return the list of elements found + * @param id of the object + * @return optional with the object or empty optional */ - public final

List

findSortedByQuery(String query, String sort, Integer skip, Integer limit, String projection, ResultHandler

handler, Object... params) { - Find find = this.createFind(query, sort, skip, limit, projection, params); - return this.convertIterable(find.map(handler)); + public final Optional findById(String id) { + return Optional.ofNullable( + this.collection.find(MongoDBDataAccess.getIdFilter(id)).map(this::mapToEntity).first()); } - private Find createFind(String query, String sort, Integer skip, Integer limit, String projection, Object... params) { - Find find = this.collection.find(query, params); - if((sort != null) && !sort.isEmpty()) { - find.sort(sort); - } - if((projection != null) && !projection.isEmpty()) { - find.projection(projection); - } - if(skip != null) { - find.skip(skip); - } - if(limit != null) { - find.limit(limit); - } - return find; + private static Bson getIdFilter(String id) { + return eq(new ObjectId(id)); } /** - * finds all elements containing the given searchString in any text field and sorts them accordingly. - * - * @param searchString the searchString to search for - * @param sort the sort query to apply - * @return the list of elements found + * @param id the id + * @deprecated use findById - this one will be deleted with next version */ - public final List searchSorted(String searchString, String sort) { - return this.findSortedByQuery(MongoDBDataAccess.FULLTEXT_QUERY, sort, searchString); + @Deprecated + public final Optional findByObjectId(String id) { + return this.findById(id); } /** - * queries with the given string, sorts the result and returns the first element. null is returned if no element is found. - * - * @param query the query string - * @param sort the sort string - * @param params the parameters to replace # symbols - * @return the first element found or null if none is found + * @param id the id + * @deprecated use findById - this one will be deleted with next version */ - public final Optional findFirstByQuery(String query, String sort, Object... params) { - Find find = this.collection.find(query, params); - if((sort != null) && !sort.isEmpty()) { - find.sort(sort); - } - Iterable as = find.limit(1).as(this.entityClass); - Iterator iterator = as.iterator(); - if(iterator.hasNext()) { - return Optional.of(iterator.next()); - } - return Optional.empty(); + @Deprecated + public final Optional findByStringId(String id) { + return this.findById(id); } - public final Optional findByObjectId(String id) { - return Optional.ofNullable(this.collection.findOne(new ObjectId(id)).as(this.entityClass)); + /** + * @param query the query as Bson + * @return the number of elements matching the query + */ + public final long count(Bson query) { + return this.collection.countDocuments(query); } - public final Optional findByStringId(String id) { - return Optional.ofNullable(this.collection.findOne("{\"_id\":#}", id).as(this.entityClass)); + private static Bson createQuery(String query, Object... params) { + String filledQuery = query; + + for (Object param : params) { + if (filledQuery.indexOf('#') != -1) { + filledQuery = filledQuery.replaceFirst("#", "\\\"" + param.toString() + "\\\""); + } + } + + return BsonDocument.parse(filledQuery); + } + + + protected T mapToEntity(Document document) { + try { + return this.mapper.readValue(document.toJson(), this.entityClass); + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } /** - * @param query the query string - * @param parameter the parameters to replace # symbols - * @return the number of elements matching the query + * @param object to save + * @return saved object */ - public final long count(String query, Object... parameter) { - return this.collection.count(query, parameter); + public final T save(T object) { + try { + Document document = Document.parse(this.mapper.writeValueAsString(object)); + if (this.collection.countDocuments(MongoDBDataAccess.getIdFilter(object.getId())) == 0) { + this.collection.insertOne(document); + } else { + this.collection.replaceOne(MongoDBDataAccess.getIdFilter(object.getId()), document); + } + return object; + } catch (JsonProcessingException e) { + throw new RuntimeException(e); + } } - public final T save(T object) { - this.collection.save(object); - return object; + /** + * @param id of the object, that will be deleted + */ + public final void deleteById(String id) { + this.collection.deleteOne(MongoDBDataAccess.getIdFilter(id)); } + /** + * @param id the di + * @deprecated use deleteById - this one will be deleted with next version + */ + @Deprecated public final void deleteByObjectId(String id) { - this.collection.remove(new ObjectId(id)); + this.deleteById(id); } + /** + * @param id the di + * @deprecated use deleteById - this one will be deleted with next version + */ + @Deprecated public final void deleteByStringId(String id) { - this.collection.remove("{\"_id\":#}", id); + this.deleteById(id); } - public final void delete(String query, Object... parameter) { - this.collection.remove(query, parameter); + /** + * @param query to select the objects that will be deleted + */ + public final void delete(String query) { + this.collection.deleteOne(BsonDocument.parse(query)); } - public final Update update(ObjectId id) { - return this.collection.update(id); + /** + * @param id of the object + * @param update list of updates to perfom + */ + public final void update(ObjectId id, List update) { + this.collection.updateOne(eq(id), update); } - public final Update update(String query) { - return this.collection.update(query); + /** + * @param query to select the objects that will be updated + * @param update list of updates to perfom + */ + public final void update(String query, List update) { + this.collection.updateOne(BsonDocument.parse(query), update); } - public final Update update(String query, Object... parameter) { - return this.collection.update(query, parameter); + /** + * @return the MongoCollection + */ + public MongoCollection getCollection() { + return this.collection; } - @Deprecated - public MongoCollection getCollection() { - return this.collection; + /** + * @return the db + */ + public MongoDatabase getDb() { + return this.db; } + /** + * @param bucket name of the bucket + * @return the GridFS bucket + */ public GridFSBucket getGridFSBucket(String bucket) { - if(bucket == null || bucket.isEmpty()) { + if (bucket == null || bucket.isEmpty()) { throw new IllegalArgumentException(); } return GridFSBuckets.create(this.db, bucket); diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoDBInit.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoDBInit.java index 4209af1e..b10ff39e 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoDBInit.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/MongoDBInit.java @@ -20,14 +20,9 @@ * #L% */ -import java.io.IOException; -import java.util.Scanner; - -import javax.annotation.PostConstruct; - import com.mongodb.BasicDBObject; import com.mongodb.DBObject; -import com.mongodb.MongoClient; +import com.mongodb.client.MongoClient; import com.mongodb.client.MongoCollection; import com.mongodb.client.MongoDatabase; import de.taimos.daemon.spring.conditional.OnSystemProperty; @@ -38,6 +33,10 @@ import org.springframework.core.io.support.PathMatchingResourcePatternResolver; import org.springframework.stereotype.Component; +import javax.annotation.PostConstruct; +import java.io.IOException; +import java.util.Scanner; + /** * Copyright 2014 Taimos GmbH
@@ -77,7 +76,7 @@ public void initDatabase() { MongoDBInit.LOGGER.info("Scanning for collection data"); for (Resource res : resources) { String filename = res.getFilename(); - if(filename != null) { + if (filename != null) { String collection = filename.substring(0, filename.length() - 7); MongoDBInit.LOGGER.info("Found collection file: {}", collection); MongoCollection dbCollection = db.getCollection(collection, DBObject.class); diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/config/FakeClientConfig.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/config/FakeClientConfig.java index ed1b14a2..657f8f55 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/config/FakeClientConfig.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/config/FakeClientConfig.java @@ -20,9 +20,7 @@ * #L% */ -import com.mongodb.ConnectionString; -import com.mongodb.MongoClient; -import com.mongodb.ServerAddress; +import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; import de.bwaldvogel.mongo.MongoServer; import de.bwaldvogel.mongo.backend.memory.MemoryBackend; @@ -44,12 +42,8 @@ public MongoServer mongoServer() { @Bean public MongoClient mongoClient(MongoServer mongoServer) { - return new MongoClient(new ServerAddress(mongoServer.bind())); - } - - @Bean - public com.mongodb.client.MongoClient mongoClient2(MongoServer mongoServer) { return MongoClients.create(mongoServer.bindAndGetConnectionString()); } + } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/config/MongoDBConfig.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/config/MongoDBConfig.java index 116b0eea..bebcc219 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/config/MongoDBConfig.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/config/MongoDBConfig.java @@ -20,24 +20,28 @@ * #L% */ -import java.util.concurrent.TimeUnit; - -import com.mongodb.DB; -import com.mongodb.MongoClient; +import com.mongodb.MongoClientSettings; import com.mongodb.ReadConcern; import com.mongodb.ReadPreference; import com.mongodb.WriteConcern; +import com.mongodb.client.MongoClient; import com.mongodb.client.MongoDatabase; -import de.taimos.dvalin.mongo.JongoFactory; +import de.taimos.dvalin.mongo.JodaCodec; import io.mongock.api.config.LegacyMigration; import io.mongock.driver.mongodb.sync.v4.driver.MongoSync4Driver; import io.mongock.runner.standalone.MongockStandalone; import io.mongock.runner.standalone.RunnerStandaloneBuilder; -import org.jongo.Jongo; +import org.bson.codecs.configuration.CodecRegistries; +import org.bson.codecs.configuration.CodecRegistry; +import org.bson.codecs.pojo.PojoCodecProvider; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.concurrent.TimeUnit; + +import static org.bson.codecs.pojo.Conventions.DEFAULT_CONVENTIONS; + @Configuration public class MongoDBConfig { @@ -57,7 +61,7 @@ public class MongoDBConfig { private String basePackage; @Bean - public RunnerStandaloneBuilder mongockRunner(com.mongodb.client.MongoClient mongoClient, Jongo jongo, DB legacyDB) { + public RunnerStandaloneBuilder mongockRunner(MongoClient mongoClient, MongoDatabase mongoDatabase) { MongoSync4Driver driver = MongoSync4Driver.withDefaultLock(mongoClient, this.dbName); driver.setWriteConcern(WriteConcern.MAJORITY.withJournal(true).withWTimeout(1000, TimeUnit.MILLISECONDS)); @@ -65,32 +69,30 @@ public RunnerStandaloneBuilder mongockRunner(com.mongodb.client.MongoClient mong driver.setReadPreference(ReadPreference.primary()); driver.disableTransaction(); - RunnerStandaloneBuilder runnerStandaloneBuilder = MongockStandalone.builder().setDriver(driver).setTransactionEnabled(false); - if (this.basePackage == null || this.basePackage.isEmpty()){ + RunnerStandaloneBuilder runnerStandaloneBuilder = MongockStandalone.builder().setDriver(driver) + .setTransactionEnabled(false); + if (this.basePackage == null || this.basePackage.isEmpty()) { throw new RuntimeException("LegacyMigration basePackage must be set!"); } runnerStandaloneBuilder.addMigrationScanPackage(this.basePackage); - if(mongockLegacyMigrationEnabled) { + if (mongockLegacyMigrationEnabled) { LegacyMigration legacyMigration = new LegacyMigration(); legacyMigration.setOrigin(this.mongockLegacyTable); runnerStandaloneBuilder.setLegacyMigration(legacyMigration); } - runnerStandaloneBuilder.addDependency(jongo).addDependency(legacyDB).buildRunner().execute(); + runnerStandaloneBuilder.addDependency(mongoDatabase).buildRunner().execute(); return runnerStandaloneBuilder; } @Bean public MongoDatabase mongoDatabase(MongoClient mongoClient) { - return mongoClient.getDatabase(this.dbName); - } + MongoDatabase database = mongoClient.getDatabase(this.dbName); - @Bean - public Jongo jongo(MongoClient mongoClient) { - return JongoFactory.createDefault(mongoClient.getDB(this.dbName)); - } + CodecRegistry codecReg = CodecRegistries.fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), + CodecRegistries.fromCodecs(new JodaCodec()), CodecRegistries.fromProviders( + PojoCodecProvider.builder().conventions(DEFAULT_CONVENTIONS).automatic(true).build())); + database.withCodecRegistry(codecReg); - @Bean - public DB mongoDB(MongoClient mongoClient){ - return mongoClient.getDB(this.dbName); + return database; } } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/config/RealClientConfig.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/config/RealClientConfig.java index 207b228d..3673fc03 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/config/RealClientConfig.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/config/RealClientConfig.java @@ -20,15 +20,18 @@ * #L% */ -import com.mongodb.MongoClient; -import com.mongodb.MongoClientOptions; -import com.mongodb.MongoClientURI; +import com.mongodb.ConnectionString; +import com.mongodb.MongoClientSettings; +import com.mongodb.MongoClientSettings.Builder; import com.mongodb.client.MongoClients; import de.taimos.daemon.spring.conditional.OnSystemProperty; +import de.taimos.dvalin.mongo.MongoClientOptionsFactory; import org.springframework.beans.factory.annotation.Value; import org.springframework.context.annotation.Bean; import org.springframework.context.annotation.Configuration; +import java.util.concurrent.TimeUnit; + @OnSystemProperty(propertyName = "mongodb.type", propertyValue = "real") @Configuration @@ -45,22 +48,15 @@ public class RealClientConfig { @Bean - public MongoClient mongoClient() { - MongoClientOptions.Builder builder = MongoClientOptions.builder(); - builder.socketTimeout(this.socketTimeout); - builder.connectTimeout(this.connectTimeout); - - return new MongoClient(new MongoClientURI(this.mongoURI, builder)); - } - - @Bean - public com.mongodb.client.MongoClient mongoClient2() { - MongoClientOptions.Builder builder = MongoClientOptions.builder(); - builder.socketTimeout(this.socketTimeout); - builder.connectTimeout(this.connectTimeout); - return MongoClients.create(new MongoClientURI(this.mongoURI, builder).getURI()); + public com.mongodb.client.MongoClient mongoClient() { + Builder settingsBuilder = MongoClientSettings.builder(); + settingsBuilder.applyConnectionString(new ConnectionString(this.mongoURI)); + settingsBuilder.applyToSocketSettings(builder -> { + builder.connectTimeout(RealClientConfig.this.connectTimeout, TimeUnit.MILLISECONDS); + builder.readTimeout(RealClientConfig.this.socketTimeout, TimeUnit.MILLISECONDS); + }); + return MongoClients.create(settingsBuilder.build()); } - } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/id/IdEntity.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/id/IdEntity.java new file mode 100644 index 00000000..5af54ceb --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/id/IdEntity.java @@ -0,0 +1,14 @@ +package de.taimos.dvalin.mongo.id; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +public abstract class IdEntity { + /** + * @return the unique id of the element + */ + abstract public String getId(); +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/id/MongoId.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/id/MongoId.java new file mode 100644 index 00000000..6dbae176 --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/id/MongoId.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 Benoît GUÉROUT and Yves AMSELLEM + * + * 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 de.taimos.dvalin.mongo.id; + +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +public @interface MongoId { +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/id/MongoObjectId.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/id/MongoObjectId.java new file mode 100644 index 00000000..1361dfef --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/id/MongoObjectId.java @@ -0,0 +1,25 @@ +/* + * Copyright (C) 2011 Benoît GUÉROUT and Yves AMSELLEM + * + * 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 de.taimos.dvalin.mongo.id; + +import java.lang.annotation.Retention; + +import static java.lang.annotation.RetentionPolicy.RUNTIME; + +@Retention(RUNTIME) +public @interface MongoObjectId { +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/links/DLinkDAO.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/links/DLinkDAO.java index b9a44ee4..1201f181 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/links/DLinkDAO.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/links/DLinkDAO.java @@ -1,15 +1,17 @@ package de.taimos.dvalin.mongo.links; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - +import com.mongodb.client.FindIterable; +import com.mongodb.client.MongoCollection; +import com.mongodb.client.MongoDatabase; +import com.mongodb.client.model.Filters; +import org.bson.Document; import org.bson.types.ObjectId; -import org.jongo.Jongo; -import org.jongo.MongoCollection; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Repository; +import java.util.ArrayList; +import java.util.List; + /* * #%L * Spring DAO Mongo @@ -34,22 +36,23 @@ @Repository public class DLinkDAO implements IDLinkDAO { - private final Jongo jongo; + private final MongoDatabase mongoDatabase; @Autowired - public DLinkDAO(Jongo jongo) { - this.jongo = jongo; + public DLinkDAO(MongoDatabase mongoDatabase) { + this.mongoDatabase = mongoDatabase; } @Override public > T resolve(DocumentLink link) { - MongoCollection collection = this.jongo.getCollection(link.getTargetClass().getSimpleName()); - return collection.findOne(new ObjectId(link.getObjectId())).as(link.getTargetClass()); + MongoCollection collection = this.mongoDatabase.getCollection(link.getTargetClass().getSimpleName(), + link.getTargetClass()); + return (T) collection.find(new Document("_id", link.getObjectId()), link.getTargetClass()).first(); } @Override public > List resolve(List> links, Class targetClass) { - MongoCollection collection = this.jongo.getCollection(targetClass.getSimpleName()); + MongoCollection collection = this.mongoDatabase.getCollection(targetClass.getSimpleName(), targetClass); List ids = new ArrayList<>(); for (DocumentLink link : links) { if (!link.getTargetClass().equals(targetClass)) { @@ -57,10 +60,10 @@ public > List resolve(List> } ids.add(new ObjectId(link.getObjectId())); } - Iterator it = collection.find("{\"_id\" : {\"$in\" : #}}", ids).as(targetClass).iterator(); + FindIterable it = collection.find(Filters.in("_id", ids), targetClass); List resolved = new ArrayList<>(); - while (it.hasNext()) { - resolved.add(it.next()); + for (T t : it) { + resolved.add(t); } return resolved; } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/links/DLinkQuery.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/links/DLinkQuery.java index ce063aa4..f766ca42 100644 --- a/mongodb/src/main/java/de/taimos/dvalin/mongo/links/DLinkQuery.java +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/links/DLinkQuery.java @@ -20,15 +20,12 @@ * #L% */ -import java.util.ArrayList; -import java.util.Iterator; -import java.util.List; - -import org.jongo.MongoCollection; - import com.mongodb.DBObject; - +import com.mongodb.client.model.Projections; import de.taimos.dvalin.mongo.MongoDBDataAccess; +import org.bson.conversions.Bson; + +import java.util.List; /** * QueryHelper to convert query result to a list of DLinks. It only queries the fields necessary to construct links. @@ -52,28 +49,20 @@ public DLinkQuery(Class targetClass, String labelField) { this.labelField = labelField; } - public List> find(MongoDBDataAccess dataAccess, String query, Object... parameter) { - return dataAccess.findSortedByQuery(query, null, null, null, String.format("{%s:1}", this.labelField), this::convert, parameter); + public List> find(MongoDBDataAccess dataAccess, Bson query) { + return dataAccess.findSortedByQuery(query, null, null, null, Projections.include(this.labelField), this::convert); } - @Deprecated - public List> find(MongoCollection collection, String query, Object... parameter) { - Iterator> it = collection.find(query, parameter).projection(String.format("{%s:1}", this.labelField)).map(this::convert).iterator(); - - List> objects = new ArrayList<>(); - while (it.hasNext()) { - DocumentLink link = it.next(); - objects.add(link); + private DocumentLink convert(Object result) { + if (!(result instanceof DBObject)) { + throw new RuntimeException("Wront response for DocumentLink"); } - return objects; - } - - private DocumentLink convert(DBObject result) { - if (!result.containsField("_id") || !result.containsField(DLinkQuery.this.labelField)) { + if (!((DBObject) result).containsField("_id") || + !((DBObject) result).containsField(DLinkQuery.this.labelField)) { throw new RuntimeException("Fields missing to construct DocumentLink"); } - String id = result.get("_id").toString(); - String label = result.get(DLinkQuery.this.labelField).toString(); + String id = ((DBObject) result).get("_id").toString(); + String label = ((DBObject) result).get(DLinkQuery.this.labelField).toString(); return new DocumentLink<>(DLinkQuery.this.targetClass, id, label); } diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/DvalinJodaModule.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/DvalinJodaModule.java new file mode 100644 index 00000000..fbc640d2 --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/DvalinJodaModule.java @@ -0,0 +1,22 @@ +package de.taimos.dvalin.mongo.mapper; + +import com.fasterxml.jackson.core.Version; +import com.fasterxml.jackson.databind.module.SimpleModule; +import org.joda.time.DateTime; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +public class DvalinJodaModule extends SimpleModule { + + private static final long serialVersionUID = 232046413074427246L; + + public DvalinJodaModule() { + super(new Version(1, 0, 0, null, "de.taimos.dvalin", "mongodb")); + this.addSerializer(DateTime.class, new JodaMapping.MongoDateTimeSerializer()); + this.addDeserializer(DateTime.class, new JodaMapping.MongoDateTimeDeserializer()); + } +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/IdSelector.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/IdSelector.java new file mode 100644 index 00000000..96993af7 --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/IdSelector.java @@ -0,0 +1,12 @@ +package de.taimos.dvalin.mongo.mapper; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +public interface IdSelector { + boolean isId(T a); + boolean isObjectId(T a); +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/JacksonConfig.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/JacksonConfig.java new file mode 100644 index 00000000..617b0442 --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/JacksonConfig.java @@ -0,0 +1,34 @@ +package de.taimos.dvalin.mongo.mapper; + +import com.fasterxml.jackson.databind.DeserializationFeature; +import com.fasterxml.jackson.databind.MapperFeature; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.fasterxml.jackson.databind.json.JsonMapper; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +public class JacksonConfig { + /** + * @return configured Jackson object mapper + */ + public static ObjectMapper createObjectMapper() { + return JsonMapper.builder() // + .enable(MapperFeature.AUTO_DETECT_GETTERS) // + // enable SerializationFeatures + .enable(SerializationFeature.FAIL_ON_EMPTY_BEANS)// + // enable DeserializationFeature + .enable(DeserializationFeature.USE_BIG_DECIMAL_FOR_FLOATS)// + // disable SerializationFeature + .disable(SerializationFeature.WRITE_DATES_AS_TIMESTAMPS)// + // disable DeserializationFeature + .disable(DeserializationFeature.ADJUST_DATES_TO_CONTEXT_TIME_ZONE)// + .disable(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES)// + // modules + .addModule(new DvalinJodaModule()).build(); + } +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/JodaMapping.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/JodaMapping.java new file mode 100644 index 00000000..ed2bc9b7 --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/JodaMapping.java @@ -0,0 +1,65 @@ +package de.taimos.dvalin.mongo.mapper; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 Taimos GmbH + * %% + * 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. + * #L% + */ + +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.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.joda.time.format.ISODateTimeFormat; + +import java.io.IOException; + +/** + * Copyright 2015 Hoegernet
+ *
+ * (De-)Serializer for Jodatime + * + * @author Thorsten Hoeger + */ +public class JodaMapping { + + public static class MongoDateTimeSerializer extends JsonSerializer { + + @Override + public void serialize(DateTime value, JsonGenerator jgen, SerializerProvider provider) throws IOException { + jgen.writeStartObject(); + jgen.writeStringField("$date", value.withZone(DateTimeZone.UTC).toString(ISODateTimeFormat.dateTime())); + jgen.writeEndObject(); + } + + } + + public static class MongoDateTimeDeserializer extends JsonDeserializer { + + @Override + public DateTime deserialize(JsonParser jp, DeserializationContext ctxt) throws IOException { + JsonNode node = jp.getCodec().readTree(jp); + String date = node.get("$date").asText(); + return ISODateTimeFormat.dateTimeParser().withZoneUTC().parseDateTime(date); + } + } +} diff --git a/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/ObjectIdMapping.java b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/ObjectIdMapping.java new file mode 100644 index 00000000..b79ada08 --- /dev/null +++ b/mongodb/src/main/java/de/taimos/dvalin/mongo/mapper/ObjectIdMapping.java @@ -0,0 +1,40 @@ +package de.taimos.dvalin.mongo.mapper; + +import com.fasterxml.jackson.core.JacksonException; +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.JsonNode; +import com.fasterxml.jackson.databind.JsonSerializer; +import com.fasterxml.jackson.databind.SerializerProvider; + +import java.io.IOException; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +public class ObjectIdMapping { + public static class ObjectIdSerializer extends JsonSerializer { + + @Override + public void serialize(String value, JsonGenerator gen, SerializerProvider serializers) throws IOException { + gen.writeStartObject(); + gen.writeFieldName("$oid"); + gen.writeString(value); + gen.writeEndObject(); + } + } + + public static class ObjectIdDeserializer extends JsonDeserializer { + @Override + public String deserialize(JsonParser p, DeserializationContext ctxt) throws IOException, JacksonException { + JsonNode node = p.getCodec().readTree(p); + return node.get("$oid").textValue(); + } + + } +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/ABaseTest.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/ABaseTest.java index 7aa7f075..1f30915c 100644 --- a/mongodb/src/test/java/de/taimos/dvalin/mongo/ABaseTest.java +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/ABaseTest.java @@ -23,10 +23,8 @@ * #L% */ -import java.math.BigDecimal; - import com.mongodb.ConnectionString; -import com.mongodb.DB; +import com.mongodb.MongoClientSettings; import com.mongodb.ServerAddress; import com.mongodb.client.MongoClient; import com.mongodb.client.MongoClients; @@ -35,8 +33,14 @@ import de.bwaldvogel.mongo.ServerVersion; import de.bwaldvogel.mongo.backend.memory.MemoryBackend; import de.taimos.daemon.log4j.Log4jLoggingConfigurer; -import org.jongo.Jongo; -import org.junit.Assert; +import org.bson.codecs.configuration.CodecRegistries; +import org.bson.codecs.pojo.PojoCodecProvider; +import org.junit.jupiter.api.Assertions; + +import java.math.BigDecimal; +import java.util.Arrays; + +import static org.bson.codecs.pojo.Conventions.DEFAULT_CONVENTIONS; /** * Copyright 2015 Taimos GmbH
@@ -46,27 +50,31 @@ */ public class ABaseTest { - protected static final String dbName = "dvalin-mongo"; - private static final ServerAddress serverAddress = new ServerAddress(new MongoServer(new MemoryBackend().version(ServerVersion.MONGO_3_6)).bind()); + public static final String dbName = "dvalin-mongo"; + + private static final ServerAddress serverAddress = new ServerAddress( + new MongoServer(new MemoryBackend().version(ServerVersion.MONGO_3_6)).bind()); - public static final MongoClient mongo = MongoClients.create(new ConnectionString(String.format("mongodb://%s:%d", ABaseTest.serverAddress.getHost(), ABaseTest.serverAddress.getPort()))); - public static final com.mongodb.MongoClient oldMongo = new com.mongodb.MongoClient(new ServerAddress(new MongoServer(new MemoryBackend().version(ServerVersion.MONGO_3_6)).bind()));; + public static final MongoClient mongo = MongoClients.create(new ConnectionString( + String.format("mongodb://%s:%d", ABaseTest.serverAddress.getHost(), ABaseTest.serverAddress.getPort()))); - public static final DB oldDB = ABaseTest.oldMongo.getDB(ABaseTest.dbName); - public static final Jongo jongo = JongoFactory.createDefault(ABaseTest.oldMongo.getDB(ABaseTest.dbName)); - public static final MongoDatabase database = ABaseTest.mongo.getDatabase(ABaseTest.dbName); + public static final MongoDatabase database = ABaseTest.mongo.getDatabase(ABaseTest.dbName).withCodecRegistry( + CodecRegistries.fromRegistries(MongoClientSettings.getDefaultCodecRegistry(), + CodecRegistries.fromCodecs(new JodaCodec()), + CodecRegistries.fromProviders( + PojoCodecProvider.builder().conventions(DEFAULT_CONVENTIONS).automatic(true).build()))); static { try { new Log4jLoggingConfigurer().simpleLogging(); } catch (Exception e) { - e.printStackTrace(); + Assertions.fail(Arrays.toString(e.getStackTrace())); } } protected static void assertEquals(BigDecimal bd1, BigDecimal bd2) { - Assert.assertEquals(bd1.doubleValue(), bd2.doubleValue(), 0); + Assertions.assertEquals(bd1.doubleValue(), bd2.doubleValue(), 0); } /** diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkDAO.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkDAO.java index 439b43d4..5cbb6b16 100644 --- a/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkDAO.java +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkDAO.java @@ -20,10 +20,13 @@ * #L% */ +import com.mongodb.client.model.Filters; +import de.taimos.dvalin.mongo.model.LinkObject; + public class LinkDAO extends AbstractMongoDAO { public LinkObject findByName(String name) { - return this.findFirstByQuery("{name:#}", null, name); + return this.findFirstByQuery(Filters.eq("name", name), null); } } diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkTest.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkTest.java new file mode 100644 index 00000000..ecc72844 --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkTest.java @@ -0,0 +1,97 @@ +/** + * + */ +package de.taimos.dvalin.mongo; + +/* + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.lang.reflect.Field; +import java.util.List; + +import de.taimos.dvalin.daemon.spring.InjectionUtils; +import de.taimos.dvalin.mongo.links.DLinkDAO; +import de.taimos.dvalin.mongo.model.LinkObject; +import de.taimos.dvalin.mongo.model.LinkedObject; +import de.taimos.dvalin.mongo.model.TestObject; +import io.mongock.driver.mongodb.sync.v4.driver.MongoSync4Driver; +import io.mongock.runner.standalone.MongockStandalone; +import org.junit.Assert; +import org.junit.BeforeClass; +import org.junit.Test; + +/** + * Copyright 2015 Taimos GmbH
+ *
+ * + * @author thoeger + */ +public class LinkTest { + + private static final LinkDAO dao = new LinkDAO(); + private static final LinkedDAO ldao = new LinkedDAO(); + + private static final DLinkDAO dlinkDAO = new DLinkDAO(ABaseTest.mongo.getDatabase(ABaseTest.dbName)); + + + @BeforeClass + public static void init() { + try { + System.setProperty("mongodb.name", ABaseTest.dbName); + + Field dao2Field = AbstractMongoDAO.class.getDeclaredField("dataAccess"); + dao2Field.setAccessible(true); + dao2Field.set(LinkTest.dao, new MongoDBDataAccess(ABaseTest.database, InjectionUtils.createDependencyDescriptor(dao2Field, LinkTest.dao))); + dao2Field.set(LinkTest.ldao, new MongoDBDataAccess(ABaseTest.database, InjectionUtils.createDependencyDescriptor(dao2Field, LinkTest.ldao))); + + + MongoSync4Driver driver = MongoSync4Driver.withDefaultLock(ABaseTest.mongo, ABaseTest.dbName); + driver.disableTransaction(); + MongockStandalone.builder().setDriver(driver).addMigrationScanPackage("de.taimos.dvalin.mongo.changelog").setTransactionEnabled(false).setEnabled(true).buildRunner().execute(); + LinkTest.dao.init(); + LinkTest.ldao.init(); + } catch (Exception e) { + e.printStackTrace(); + } + } + + @Test + public void testLinks() { + LinkedObject lo1 = new LinkedObject(); + lo1.setName("LinkedObject1"); + lo1 = LinkTest.ldao.save(lo1); + + LinkedObject lo2 = new LinkedObject(); + lo2.setName("LinkedObject2"); + lo2 = LinkTest.ldao.save(lo2); + + LinkObject lo = new LinkObject(); + lo.setName("LinkObject"); + lo.getLinks().add(lo1.asLink()); + lo.getLinks().add(lo2.asLink()); + lo = LinkTest.dao.save(lo); + + Assert.assertEquals(2, lo.getLinks().size()); + + List list = LinkTest.dlinkDAO.resolve(lo.getLinks(), LinkedObject.class); + Assert.assertEquals(2, list.size()); + } + +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkedDAO.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkedDAO.java index a16a6bc8..ff4246c5 100644 --- a/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkedDAO.java +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/LinkedDAO.java @@ -20,6 +20,8 @@ * #L% */ +import de.taimos.dvalin.mongo.model.LinkedObject; + public class LinkedDAO extends AbstractMongoDAO { // diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/MongoAuditedDAOTest.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/MongoAuditedDAOTest.java new file mode 100644 index 00000000..1710a556 --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/MongoAuditedDAOTest.java @@ -0,0 +1,148 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.mongodb.client.ListIndexesIterable; +import com.mongodb.client.model.Filters; +import de.taimos.dvalin.daemon.spring.InjectionUtils; +import de.taimos.dvalin.mongo.model.AuditedTestObject; +import de.taimos.dvalin.mongo.model.TestObject; +import io.mongock.driver.mongodb.sync.v4.driver.MongoSync4Driver; +import io.mongock.runner.standalone.MongockStandalone; +import org.bson.Document; +import org.bson.types.ObjectId; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.List; + +class MongoAuditedDAOTest extends ABaseTest { + + private static final TestAuditedDAO dao = new TestAuditedDAO(); + + @BeforeAll + static void init() { + System.out.println("MongoDataAccessTest.init()"); + try { + Field daoField = AbstractMongoDAO.class.getDeclaredField("dataAccess"); + daoField.setAccessible(true); + daoField.set(MongoAuditedDAOTest.dao, new MongoDBDataAccess(ABaseTest.database, + InjectionUtils.createDependencyDescriptor(daoField, MongoAuditedDAOTest.dao))); + + MongoSync4Driver driver = MongoSync4Driver.withDefaultLock(ABaseTest.mongo, ABaseTest.dbName); + driver.disableTransaction(); + MongockStandalone.builder().setDriver(driver).addMigrationScanPackage("de.taimos.dvalin.mongo.changelog") + .setTransactionEnabled(false).setEnabled(true).buildRunner().execute(); + MongoAuditedDAOTest.dao.init(); + } catch (Exception e) { + Assertions.fail(Arrays.toString(e.getStackTrace())); + } + } + + @AfterEach + void clearDatabase() { + MongoAuditedDAOTest.dao.dataAccess.getCollection().drop(); + } + + @Test + void testUpdate() { + AuditedTestObject o = new AuditedTestObject(); + o.setName("bar"); + o.setValue(new BigDecimal("5")); + o.setDt(new DateTime(2024, 6, 7, 8, 6)); + Assertions.assertEquals("bar", o.getName()); + String id = o.getId(); + + AuditedTestObject save = MongoAuditedDAOTest.dao.save(o); + Assertions.assertEquals("bar", save.getName()); + ABaseTest.assertEquals(new BigDecimal("5"), save.getValue()); + Assertions.assertNotNull(save.getId()); + Assertions.assertNotNull(save.getDt()); + Assertions.assertEquals(o.getDt(), save.getDt()); + + AuditedTestObject find = MongoAuditedDAOTest.dao.findById(id); + Assertions.assertNotNull(find); + Assertions.assertEquals("bar", find.getName()); + ABaseTest.assertEquals(new BigDecimal("5"), find.getValue()); + Assertions.assertEquals(id, find.getId()); + Assertions.assertNotNull(find.getDt()); + + find.setName("blubb"); + + AuditedTestObject save2 = MongoAuditedDAOTest.dao.save(find); + Assertions.assertNotNull(save2); + Assertions.assertEquals("blubb", save2.getName()); + ABaseTest.assertEquals(new BigDecimal("5"), save2.getValue()); + Assertions.assertEquals(id, save2.getId()); + Assertions.assertNotNull(save2.getDt()); + + AuditedTestObject find3 = MongoAuditedDAOTest.dao.findByName("blubb"); + Assertions.assertNotNull(find3); + Assertions.assertEquals("blubb", find3.getName()); + ABaseTest.assertEquals(new BigDecimal("5"), find3.getValue()); + Assertions.assertEquals(id, find3.getId()); + Assertions.assertNotNull(find3.getDt()); + + long count = MongoAuditedDAOTest.dao.dataAccess.count(Filters.empty()); + Assertions.assertEquals(1, count); + + MongoAuditedDAOTest.dao.delete(id); + + AuditedTestObject find2 = MongoAuditedDAOTest.dao.findById(id); + Assertions.assertNull(find2); + + count = MongoAuditedDAOTest.dao.dataAccess.count(Filters.empty()); + Assertions.assertEquals(0, count); + + ListIndexesIterable listIndexes = ABaseTest.mongo.getDatabase(ABaseTest.dbName) + .getCollection("TestObject").listIndexes(); + for (Document index : listIndexes) { + System.out.println(index.toString()); + } + + List actualHistoryElements = MongoAuditedDAOTest.dao.findHistoryElements(o.getId()); + Assertions.assertEquals(2, actualHistoryElements.size()); + } + + @Test + void testObjectId() { + AuditedTestObject to = new AuditedTestObject(); + to.setId("66682ae8161422626090bad3"); + to.setDt(new DateTime(2024, 4, 3, 9, 9, 3, DateTimeZone.UTC)); + + MongoAuditedDAOTest.dao.dataAccess.save(to); + + Document result = MongoAuditedDAOTest.dao.dataAccess.getCollection().find().first(); + + Assertions.assertNotNull(result); + + Assertions.assertEquals(new ObjectId("66682ae8161422626090bad3"), result.get("_id")); + + + } +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/MongoDataAccessTest.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/MongoDataAccessTest.java new file mode 100644 index 00000000..06205480 --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/MongoDataAccessTest.java @@ -0,0 +1,171 @@ +package de.taimos.dvalin.mongo; + +/* + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.mongodb.client.ListIndexesIterable; +import com.mongodb.client.model.Filters; +import com.mongodb.client.model.Sorts; +import de.taimos.dvalin.daemon.spring.InjectionUtils; +import de.taimos.dvalin.mongo.model.PolyTestObjectA; +import de.taimos.dvalin.mongo.model.TestObject; +import io.mongock.driver.mongodb.sync.v4.driver.MongoSync4Driver; +import io.mongock.runner.standalone.MongockStandalone; +import org.bson.Document; +import org.bson.types.ObjectId; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.junit.jupiter.api.AfterEach; +import org.junit.jupiter.api.Assertions; +import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.Test; + +import java.lang.reflect.Field; +import java.math.BigDecimal; +import java.util.ArrayList; +import java.util.Arrays; + +class MongoDataAccessTest extends ABaseTest { + + private static final TestDAO dao = new TestDAO(); + + @BeforeAll + static void init() { + System.out.println("MongoDataAccessTest.init()"); + try { + Field daoField = AbstractMongoDAO.class.getDeclaredField("dataAccess"); + daoField.setAccessible(true); + daoField.set(MongoDataAccessTest.dao, new MongoDBDataAccess(ABaseTest.database, + InjectionUtils.createDependencyDescriptor(daoField, MongoDataAccessTest.dao))); + + MongoSync4Driver driver = MongoSync4Driver.withDefaultLock(ABaseTest.mongo, ABaseTest.dbName); + driver.disableTransaction(); + MongockStandalone.builder().setDriver(driver).addMigrationScanPackage("de.taimos.dvalin.mongo.changelog") + .setTransactionEnabled(false).setEnabled(true).buildRunner().execute(); + MongoDataAccessTest.dao.init(); + } catch (Exception e) { + Assertions.fail(Arrays.toString(e.getStackTrace())); + } + } + + @AfterEach + void clearDatabase() { + MongoDataAccessTest.dao.dataAccess.getCollection().drop(); + } + + @Test + void testUpdate() { + TestObject o = new TestObject(); + o.setName("bar"); + o.setValue(new BigDecimal("5")); + o.setDt(new DateTime(2024, 6, 7, 8, 6)); + Assertions.assertEquals("bar", o.getName()); + String id = o.getId(); + + TestObject save = MongoDataAccessTest.dao.save(o); + Assertions.assertEquals("bar", save.getName()); + ABaseTest.assertEquals(new BigDecimal("5"), save.getValue()); + Assertions.assertNotNull(save.getId()); + Assertions.assertNotNull(save.getDt()); + Assertions.assertEquals(o.getDt(), save.getDt()); + + TestObject find = MongoDataAccessTest.dao.findById(id); + Assertions.assertNotNull(find); + Assertions.assertEquals("bar", find.getName()); + ABaseTest.assertEquals(new BigDecimal("5"), find.getValue()); + Assertions.assertEquals(id, find.getId()); + Assertions.assertNotNull(find.getDt()); + + find.setName("blubb"); + + TestObject save2 = MongoDataAccessTest.dao.save(find); + Assertions.assertNotNull(save2); + Assertions.assertEquals("blubb", save2.getName()); + ABaseTest.assertEquals(new BigDecimal("5"), save2.getValue()); + Assertions.assertEquals(id, save2.getId()); + Assertions.assertNotNull(save2.getDt()); + + TestObject find3 = MongoDataAccessTest.dao.findByName("blubb"); + Assertions.assertNotNull(find3); + Assertions.assertEquals("blubb", find3.getName()); + ABaseTest.assertEquals(new BigDecimal("5"), find3.getValue()); + Assertions.assertEquals(id, find3.getId()); + Assertions.assertNotNull(find3.getDt()); + + long count = MongoDataAccessTest.dao.dataAccess.count(Filters.empty()); + Assertions.assertEquals(1, count); + + MongoDataAccessTest.dao.delete(id); + + TestObject find2 = MongoDataAccessTest.dao.findById(id); + Assertions.assertNull(find2); + + count = MongoDataAccessTest.dao.dataAccess.count(Filters.empty()); + Assertions.assertEquals(0, count); + + ListIndexesIterable listIndexes = ABaseTest.mongo.getDatabase(ABaseTest.dbName) + .getCollection("TestObject").listIndexes(); + for (Document index : listIndexes) { + System.out.println(index.toString()); + } + } + + @Test + void testObjectId() { + TestObject to = new TestObject(); + to.setId("66682ae8161422626090bad3"); + to.setDt(new DateTime(2024, 4, 3, 9, 9, 3, DateTimeZone.UTC)); + + MongoDataAccessTest.dao.dataAccess.save(to); + + Document result = MongoDataAccessTest.dao.dataAccess.getCollection().find().first(); + + Assertions.assertNotNull(result); + + Assertions.assertEquals(new ObjectId("66682ae8161422626090bad3"), result.get("_id")); + } + + @Test + void testPoly() { + TestObject objectA = new TestObject(); + objectA.setName("foo"); + objectA.setValue(new BigDecimal("5")); + objectA.setDt(new DateTime(2024, 6, 7, 8, 6)); + MongoDataAccessTest.dao.save(objectA); + + PolyTestObjectA objectB = new PolyTestObjectA(); + objectB.setName("bar"); + objectB.setField("foo"); + objectB.setValue(new BigDecimal("6.0")); + objectB.setDt(new DateTime(2024, 4, 7, 11, 6)); + MongoDataAccessTest.dao.save(objectB); + + long amountOfObjects = MongoDataAccessTest.dao.dataAccess.getCollection().countDocuments(); + Assertions.assertEquals(2L, amountOfObjects); + + ArrayList result = MongoDataAccessTest.dao.dataAccess.getCollection() // + .find(Document.class) // + .sort(Sorts.ascending("name")) // + .into(new ArrayList<>()); + Assertions.assertEquals(2, result.size()); + Assertions.assertEquals(TestObject.class.getCanonicalName(), result.get(1).get("clazz")); + Assertions.assertEquals(PolyTestObjectA.class.getCanonicalName(), result.get(0).get("clazz")); + } +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/TestAuditedDAO.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/TestAuditedDAO.java new file mode 100644 index 00000000..2e9c1ecb --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/TestAuditedDAO.java @@ -0,0 +1,16 @@ +package de.taimos.dvalin.mongo; + +import com.mongodb.client.model.Filters; +import de.taimos.dvalin.mongo.model.AuditedTestObject; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +public class TestAuditedDAO extends AbstractAuditedMongoDAO { + public AuditedTestObject findByName(String name) { + return this.findFirstByQuery(Filters.eq("name", name), null); + } +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/TestDAO.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/TestDAO.java index ef1cd34a..864d1466 100644 --- a/mongodb/src/test/java/de/taimos/dvalin/mongo/TestDAO.java +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/TestDAO.java @@ -20,9 +20,12 @@ * #L% */ +import com.mongodb.client.model.Filters; +import de.taimos.dvalin.mongo.model.TestObject; + public class TestDAO extends AbstractMongoDAO { - + public TestObject findByName(String name) { - return this.findFirstByQuery("{name:#}", null, name); + return this.findFirstByQuery(Filters.eq("name", name), null); } } diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/changelog/TestChangelog.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/changelog/TestChangelog.java index fbd5b541..c79d666e 100644 --- a/mongodb/src/test/java/de/taimos/dvalin/mongo/changelog/TestChangelog.java +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/changelog/TestChangelog.java @@ -31,7 +31,7 @@ public class TestChangelog { @Execution public void index1() { - ChangelogUtil.addIndex(ABaseTest.oldDB.getCollection("TestObject"), "name", true, true); + ChangelogUtil.addIndex(ABaseTest.mongo.getDatabase(ABaseTest.dbName).getCollection("TestObject"), "name", true, true); } @RollbackExecution diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/mapper/JacksonConfigTest.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/mapper/JacksonConfigTest.java new file mode 100644 index 00000000..0131616f --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/mapper/JacksonConfigTest.java @@ -0,0 +1,40 @@ +package de.taimos.dvalin.mongo.mapper; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import de.taimos.dvalin.mongo.model.TestObject; +import org.joda.time.DateTime; +import org.joda.time.DateTimeZone; +import org.junit.jupiter.api.Test; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +class JacksonConfigTest { + + private final ObjectMapper mapper = JacksonConfig.createObjectMapper(); + + + @Test + void testDateMapping() throws JsonProcessingException { + String json = "{\"$date\":\"2024-04-03T09:09:03.000Z\"}"; + DateTime date = new DateTime(2024, 4, 3, 9, 9, 3, DateTimeZone.UTC); + String toJson = this.mapper.writeValueAsString(date); + assertEquals(json, toJson); + } + + @Test + void testObjectWithDateMapping() throws JsonProcessingException { + String json = "{\"clazz\":\"de.taimos.dvalin.mongo.model.TestObject\",\"name\":null,\"value\":null,\"dt\":{\"$date\":\"2024-04-03T09:09:03.000Z\"},\"_id\":{\"$oid\":\"66682ae8161422626090bad3\"}}"; + TestObject to = new TestObject(); + to.setId("66682ae8161422626090bad3"); + to.setDt(new DateTime(2024, 4, 3, 9, 9, 3, DateTimeZone.UTC)); + String toJson = this.mapper.writeValueAsString(to); + assertEquals(json, toJson); + } +} \ No newline at end of file diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/model/AuditedTestObject.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/AuditedTestObject.java new file mode 100644 index 00000000..9c7e566b --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/AuditedTestObject.java @@ -0,0 +1,69 @@ +package de.taimos.dvalin.mongo.model; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import de.taimos.dvalin.mongo.AAuditedEntity; +import de.taimos.dvalin.mongo.AEntity; +import org.joda.time.DateTime; + +import java.math.BigDecimal; + +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "clazz") +public class AuditedTestObject extends AAuditedEntity { + + private static final long serialVersionUID = 1L; + + private String name; + + private BigDecimal value; + + private DateTime dt = DateTime.now(); + + public DateTime getDt() { + return this.dt; + } + + public void setDt(DateTime dt) { + this.dt = dt; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getValue() { + return this.value; + } + + public void setValue(BigDecimal value) { + this.value = value; + } + + @Override + public String toString() { + return "TestObject [name=" + this.name + ", value=" + this.value + ", id=" + this.id + "]"; + } +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/model/LinkObject.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/LinkObject.java new file mode 100644 index 00000000..198df7c6 --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/LinkObject.java @@ -0,0 +1,68 @@ +/** + * + */ +package de.taimos.dvalin.mongo.model; + +/* + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import java.util.ArrayList; +import java.util.List; + +import de.taimos.dvalin.mongo.AEntity; +import de.taimos.dvalin.mongo.links.DocumentLink; + +/** + * Copyright 2015 Taimos GmbH
+ *
+ * + * @author thoeger + */ +public class LinkObject extends AEntity { + + private static final long serialVersionUID = 1L; + + private String name; + + private List> links = new ArrayList<>(); + + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public List> getLinks() { + return this.links; + } + + public void setLinks(List> links) { + this.links = links; + } + + @Override + public String toString() { + return "LinkObject [name=" + this.name + ", links=" + this.links + "]"; + } + +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/model/LinkedObject.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/LinkedObject.java new file mode 100644 index 00000000..048db01e --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/LinkedObject.java @@ -0,0 +1,60 @@ +/** + * + */ +package de.taimos.dvalin.mongo.model; + +/* + * #%L + * MongoDB support for dvalin + * %% + * Copyright (C) 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import de.taimos.dvalin.mongo.AEntity; +import de.taimos.dvalin.mongo.links.AReferenceableEntity; + +/** + * Copyright 2015 Taimos GmbH
+ *
+ * + * @author thoeger + */ +public class LinkedObject extends AEntity implements AReferenceableEntity { + + private static final long serialVersionUID = 1L; + + private String name; + + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + @Override + public String getLabel() { + return this.name; + } + + @Override + public String toString() { + return "LinkedObject [name=" + this.name + "]"; + } + +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/model/PolyTestObjectA.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/PolyTestObjectA.java new file mode 100644 index 00000000..1010b156 --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/PolyTestObjectA.java @@ -0,0 +1,31 @@ +package de.taimos.dvalin.mongo.model; + +/** + * Copyright 2024 Cinovo AG
+ *
+ * + * @author fzwirn + */ +public class PolyTestObjectA extends TestObject { + + private static final long serialVersionUID = -660366321316836825L; + + public PolyTestObjectA() { + } + + private String field; + + /** + * @return the field + */ + public String getField() { + return this.field; + } + + /** + * @param field the field to set + */ + public void setField(String field) { + this.field = field; + } +} diff --git a/mongodb/src/test/java/de/taimos/dvalin/mongo/model/TestObject.java b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/TestObject.java new file mode 100644 index 00000000..bdbc48ef --- /dev/null +++ b/mongodb/src/test/java/de/taimos/dvalin/mongo/model/TestObject.java @@ -0,0 +1,68 @@ +package de.taimos.dvalin.mongo.model; + +/* + * #%L + * Spring DAO Mongo + * %% + * Copyright (C) 2013 - 2015 Taimos GmbH + * %% + * 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. + * #L% + */ + +import com.fasterxml.jackson.annotation.JsonTypeInfo; +import de.taimos.dvalin.mongo.AEntity; +import org.joda.time.DateTime; + +import java.math.BigDecimal; + +@JsonTypeInfo(use = JsonTypeInfo.Id.CLASS, property = "clazz") +public class TestObject extends AEntity { + + private static final long serialVersionUID = 1L; + + private String name; + + private BigDecimal value; + + private DateTime dt = DateTime.now(); + + public DateTime getDt() { + return this.dt; + } + + public void setDt(DateTime dt) { + this.dt = dt; + } + + public String getName() { + return this.name; + } + + public void setName(String name) { + this.name = name; + } + + public BigDecimal getValue() { + return this.value; + } + + public void setValue(BigDecimal value) { + this.value = value; + } + + @Override + public String toString() { + return "TestObject [name=" + this.name + ", value=" + this.value + ", id=" + this.id + "]"; + } +} diff --git a/monitoring/dvalin-monitoring-parent.iml b/monitoring/dvalin-monitoring-parent.iml index ae5b1115..30466db0 100644 --- a/monitoring/dvalin-monitoring-parent.iml +++ b/monitoring/dvalin-monitoring-parent.iml @@ -1,11 +1,8 @@ - + - - \ No newline at end of file diff --git a/notification/dvalin-notification-parent.iml b/notification/dvalin-notification-parent.iml index 489a4e19..30466db0 100644 --- a/notification/dvalin-notification-parent.iml +++ b/notification/dvalin-notification-parent.iml @@ -1,11 +1,8 @@ - + - - \ No newline at end of file diff --git a/pom.xml b/pom.xml index 09aaf7b2..b78ff76e 100644 --- a/pom.xml +++ b/pom.xml @@ -1,4 +1,5 @@ - + 4.0.0 de.taimos @@ -65,7 +66,6 @@ 2.2 2.3 2.15.0 - 1.5.1 4.11.1 5.3.5 1.43.0 @@ -345,11 +345,7 @@ hibernate-core ${hibernate.version} - - org.mongodb - mongodb-driver-legacy - ${mongo.version} - + com.amazonaws @@ -509,20 +505,20 @@ maven-gpg-plugin 3.1.0 @@ -587,6 +583,7 @@ jaxrs-jwtauth jaxrs-swagger mongodb + mongodb-legacy monitoring jpa dynamodb diff --git a/template/dvalin-template-parent.iml b/template/dvalin-template-parent.iml index 5f9b8644..30466db0 100644 --- a/template/dvalin-template-parent.iml +++ b/template/dvalin-template-parent.iml @@ -1,11 +1,8 @@ - + - - \ No newline at end of file