+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.seedstack.seed.business.mongodb.internal;
+
+import javax.inject.Inject;
+
+import org.seedstack.business.api.domain.AggregateRoot;
+import org.seedstack.business.spi.GenericImplementation;
+import org.seedstack.seed.business.mongodb.BaseMongodbRepository;
+import org.seedstack.seed.core.utils.SeedCheckUtils;
+import org.seedstack.seed.persistence.mongodb.api.Morphia;
+
+import com.google.inject.assistedinject.Assisted;
+
+/**
+ * Default Morphia implementation for Repository. Used only when no implementation is provided for an aggregate.
+ *
+ * To inject this implementation you have to use {@link org.seedstack.business.api.domain.Repository} as follows:
+ *
+ * {@literal @}Inject
+ * Repository{@literal } myAggregateRepository;
+ *
+ *
+ * @param the aggregate root
+ * @param the aggregate key
+ * @author redouane.loulou@ext.mpsa.com
+ * @see org.seedstack.business.api.domain.Repository
+ * @see org.seedstack.business.mongodb.BaseMongodbRepository
+ */
+@Morphia
+@GenericImplementation
+public class DefaultMongodbRepository, KEY> extends BaseMongodbRepository {
+
+ /**
+ * Constructs a DefaultMongodbRepository.
+ *
+ * @param genericClasses the resolved generics for the aggregate root class and the key class
+ */
+ @SuppressWarnings("unchecked")
+ @Inject
+ public DefaultMongodbRepository(@Assisted Object[] genericClasses) {
+ Object[] clonedClasses = genericClasses.clone();
+ SeedCheckUtils.checkIfNotNull(clonedClasses);
+ SeedCheckUtils.checkIf(clonedClasses.length == 2);
+ this.aggregateRootClass = (Class) clonedClasses[0];
+ this.keyClass = (Class) clonedClasses[1];
+ }
+}
\ No newline at end of file
diff --git a/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/Morphia.java b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/Morphia.java
new file mode 100644
index 0000000..9c974e0
--- /dev/null
+++ b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/Morphia.java
@@ -0,0 +1,25 @@
+/**
+ * Copyright (c) 2013-2015, The SeedStack authors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.seedstack.seed.persistence.mongodb.api;
+
+import javax.inject.Qualifier;
+import java.lang.annotation.Documented;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+
+/**
+ * This qualifier marks the use of the Morphia persistence.
+ *
+ * @author redouane.loulou@ext.mpsa.com
+ * Date: 20/10/2015
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Qualifier
+public @interface Morphia {
+}
diff --git a/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/MorphiaDatastore.java b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/MorphiaDatastore.java
new file mode 100644
index 0000000..2b952d7
--- /dev/null
+++ b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/MorphiaDatastore.java
@@ -0,0 +1,31 @@
+/**
+ * Copyright (c) 2013-2015, The SeedStack authors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.seedstack.seed.persistence.mongodb.api;
+
+import java.lang.annotation.Documented;
+import java.lang.annotation.ElementType;
+import java.lang.annotation.Retention;
+import java.lang.annotation.RetentionPolicy;
+import java.lang.annotation.Target;
+
+import com.google.inject.BindingAnnotation;
+
+/**
+ * This qualifier marks the use of the Mongodb persistence.
+ *
+ * @author redouane.loulou@ext.mpsa.com
+ * Date: 20/10/2015
+ */
+@Documented
+@Retention(RetentionPolicy.RUNTIME)
+@Target({ ElementType.FIELD, ElementType.PARAMETER, ElementType.METHOD })
+@BindingAnnotation
+public @interface MorphiaDatastore {
+ String clientName();
+ String dbName();
+}
\ No newline at end of file
diff --git a/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/MorphiaErrorCodes.java b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/MorphiaErrorCodes.java
new file mode 100644
index 0000000..8cf9f04
--- /dev/null
+++ b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/api/MorphiaErrorCodes.java
@@ -0,0 +1,23 @@
+/**
+ * Copyright (c) 2013-2015, The SeedStack authors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.seedstack.seed.persistence.mongodb.api;
+
+import org.seedstack.seed.core.api.ErrorCode;
+
+/**
+ *
+ * @author redouane.loulou@ext.mpsa.com
+ *
+ */
+public enum MorphiaErrorCodes implements ErrorCode {
+ UNKNOW_DATASTORE_CONFIGURATION,
+ UNKNOW_DATASTORE_CLIENT,
+ UNKNOW_DATASTORE_DATABASE,
+ UNKNOW_DATABASE_NAME,
+ ERROR_ASYNC_CLIENT
+}
diff --git a/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/DatastoreProvider.java b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/DatastoreProvider.java
new file mode 100644
index 0000000..a560103
--- /dev/null
+++ b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/DatastoreProvider.java
@@ -0,0 +1,53 @@
+/**
+ * Copyright (c) 2013-2015, The SeedStack authors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.seedstack.seed.persistence.mongodb.internal;
+
+import org.mongodb.morphia.Datastore;
+import org.mongodb.morphia.Morphia;
+import org.seedstack.seed.core.api.Application;
+import org.seedstack.seed.persistence.mongodb.api.MorphiaDatastore;
+
+import com.google.inject.Inject;
+import com.google.inject.Injector;
+import com.google.inject.Key;
+import com.google.inject.Provider;
+import com.google.inject.name.Names;
+import com.mongodb.MongoClient;
+/**
+ *
+ * @author redouane.loulou@ext.mpsa.com
+ *
+ */
+class DatastoreProvider implements Provider {
+
+ @Inject
+ private Injector injector;
+
+ @Inject
+ private Application application;
+
+ private Class> mappedclass;
+
+ private MorphiaDatastore morphiaDatastore;
+
+ private final Morphia morphia;
+
+ @Override
+ public Datastore get() {
+ MongoClient mongoClient = injector
+ .getInstance(Key.get(MongoClient.class, Names.named(morphiaDatastore.clientName())));
+ return morphia.createDatastore(mongoClient, morphiaDatastore.dbName());
+ }
+
+ public DatastoreProvider(MorphiaDatastore morphiaDatastore, Morphia morphia) {
+ super();
+ this.morphiaDatastore = morphiaDatastore;
+ this.morphia = morphia;
+ }
+
+}
diff --git a/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaDatastoreImpl.java b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaDatastoreImpl.java
new file mode 100644
index 0000000..3eb2f54
--- /dev/null
+++ b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaDatastoreImpl.java
@@ -0,0 +1,74 @@
+/**
+ * Copyright (c) 2013-2015, The SeedStack authors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.seedstack.seed.persistence.mongodb.internal;
+
+import java.io.Serializable;
+import java.lang.annotation.Annotation;
+
+import org.seedstack.seed.persistence.mongodb.api.MorphiaDatastore;
+/**
+ *
+ * @author redouane.loulou@ext.mpsa.com
+ *
+ */
+class MorphiaDatastoreImpl implements MorphiaDatastore, Serializable {
+
+ private static final long serialVersionUID = 3861460142806494075L;
+ private String clientName;
+ private String dbName;
+
+ public MorphiaDatastoreImpl(String clientName, String dbName) {
+ this.clientName = clientName;
+ this.dbName = dbName;
+ }
+
+ @Override
+ public Class extends Annotation> annotationType() {
+ return MorphiaDatastore.class;
+ }
+
+ @Override
+ public String dbName() {
+ return dbName;
+ }
+
+ @Override
+ public String clientName() {
+ return clientName;
+ }
+
+ @Override
+ public int hashCode() {
+ return ((127 * "clientName".hashCode()) ^ clientName.hashCode())
+ + ((127 * "dbName".hashCode()) ^ dbName.hashCode());
+ }
+
+ @Override
+ public boolean equals(Object obj) {
+ if (this == obj)
+ return true;
+ if (obj == null)
+ return false;
+ if (!(obj instanceof MorphiaDatastore))
+ return false;
+ MorphiaDatastoreImpl other = (MorphiaDatastoreImpl) obj;
+ if (clientName == null) {
+ if (other.clientName != null)
+ return false;
+ } else if (!clientName.equals(other.clientName))
+ return false;
+ if (dbName == null) {
+ if (other.dbName != null)
+ return false;
+ } else if (!dbName.equals(other.dbName))
+ return false;
+ return true;
+ }
+
+
+}
\ No newline at end of file
diff --git a/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaModule.java b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaModule.java
new file mode 100644
index 0000000..73075e2
--- /dev/null
+++ b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaModule.java
@@ -0,0 +1,50 @@
+/**
+ * Copyright (c) 2013-2015, The SeedStack authors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+/**
+ *
+ */
+package org.seedstack.seed.persistence.mongodb.internal;
+
+import java.util.Collection;
+
+import org.mongodb.morphia.Datastore;
+import org.mongodb.morphia.Morphia;
+import org.seedstack.seed.persistence.mongodb.api.MorphiaDatastore;
+
+import com.google.inject.AbstractModule;
+import com.google.inject.Key;
+
+/**
+ * @author redouane.loulou@ext.mpsa.com
+ *
+ */
+class MorphiaModule extends AbstractModule{
+
+ private Collection morphiaDatastoresAnnotation;
+ private final Morphia morphia;
+
+ public MorphiaModule(Collection morphiaDatastoresAnnotation, Morphia morphia) {
+ super();
+ this.morphiaDatastoresAnnotation = morphiaDatastoresAnnotation;
+ this.morphia = morphia;
+ }
+
+ @Override
+ protected void configure() {
+ if(morphiaDatastoresAnnotation!=null && !morphiaDatastoresAnnotation.isEmpty()){
+ for (MorphiaDatastore morphiaDatastore : morphiaDatastoresAnnotation) {
+ DatastoreProvider datastoreProvider = new DatastoreProvider(morphiaDatastore, morphia);
+ requestInjection(datastoreProvider);
+ bind(Key.get(Datastore.class, morphiaDatastore)).toProvider(datastoreProvider);
+ }
+ }
+ }
+
+
+
+}
diff --git a/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaPlugin.java b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaPlugin.java
new file mode 100644
index 0000000..b658421
--- /dev/null
+++ b/morphia/src/main/java/org/seedstack/seed/persistence/mongodb/internal/MorphiaPlugin.java
@@ -0,0 +1,167 @@
+/**
+ * Copyright (c) 2013-2015, The SeedStack authors
+ *
+ * This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/.
+ */
+package org.seedstack.seed.persistence.mongodb.internal;
+
+import static org.seedstack.seed.core.utils.BaseClassSpecifications.classIsAbstract;
+
+import java.util.ArrayList;
+import java.util.Arrays;
+import java.util.Collection;
+import java.util.HashSet;
+
+import org.apache.commons.configuration.Configuration;
+import org.kametic.specifications.Specification;
+import org.mongodb.morphia.Morphia;
+import org.mongodb.morphia.annotations.Embedded;
+import org.mongodb.morphia.annotations.Entity;
+import org.seedstack.seed.core.api.Application;
+import org.seedstack.seed.core.api.SeedException;
+import org.seedstack.seed.core.internal.application.ApplicationPlugin;
+import org.seedstack.seed.core.utils.BaseClassSpecifications;
+import org.seedstack.seed.persistence.mongodb.api.MongoDbErrorCodes;
+import org.seedstack.seed.persistence.mongodb.api.MorphiaDatastore;
+import org.seedstack.seed.persistence.mongodb.api.MorphiaErrorCodes;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+import io.nuun.kernel.api.Plugin;
+import io.nuun.kernel.api.plugin.InitState;
+import io.nuun.kernel.api.plugin.PluginException;
+import io.nuun.kernel.api.plugin.context.InitContext;
+import io.nuun.kernel.api.plugin.request.ClasspathScanRequest;
+import io.nuun.kernel.core.AbstractPlugin;
+
+/**
+ *
+ * @author redouane.loulou@ext.mpsa.com
+ *
+ */
+public class MorphiaPlugin extends AbstractPlugin {
+ public static final String MONGO_PLUGIN_CONFIGURATION_PREFIX = "org.seedstack.seed.persistence.mongodb";
+
+ private static final Logger LOGGER = LoggerFactory.getLogger(MorphiaPlugin.class);
+
+ private static final Specification> MORPHIA_MAPPED_CLASSES_SPECS = morphiaSpecification();
+
+ private Collection> morphiaScannedClasses;
+
+ private Collection morphiaDatastores = new HashSet();
+
+ private final Morphia morphia = new Morphia();
+
+
+ @Override
+ public String name() {
+ return "seed-persistence-morphia-plugin";
+ }
+
+ @Override
+ public InitState init(InitContext initContext) {
+ Application application = null;
+ for (Plugin plugin : initContext.pluginsRequired()) {
+ if (plugin instanceof ApplicationPlugin) {
+ application = ((ApplicationPlugin) plugin).getApplication();
+ }
+ }
+
+ if (application == null) {
+ throw new PluginException("Unable to find application plugin");
+ }
+
+ if(MORPHIA_MAPPED_CLASSES_SPECS!=null){
+ morphiaScannedClasses = initContext.scannedTypesBySpecification().get(MORPHIA_MAPPED_CLASSES_SPECS);
+
+ if(morphiaScannedClasses != null && !morphiaScannedClasses.isEmpty()){
+ morphia.map(new HashSet(morphiaScannedClasses));
+ for (Class> morphiaClass : morphiaScannedClasses) {
+ MorphiaDatastore morphiaDatastore = getMongoDatastore(application, morphiaClass);
+ if(!morphiaDatastores.contains(morphiaDatastore)){
+ morphiaDatastores.add(morphiaDatastore);
+ }
+ }
+ }
+ }
+ return InitState.INITIALIZED;
+ }
+
+ /**
+ * if the morphia configuration is ok, Return an instance of the annatation MorphiaDatastore
+ * @param application Application
+ * @param morphiaClass persistent morphia object
+ * @return MorphiaDatastore
+ */
+ public static MorphiaDatastore getMongoDatastore(Application application, Class> morphiaClass) {
+ Configuration morphiaEntityConfiguration = application.getConfiguration(morphiaClass).subset("morphia");
+ if (morphiaEntityConfiguration.isEmpty()) {
+ throw SeedException.createNew(MorphiaErrorCodes.UNKNOW_DATASTORE_CONFIGURATION).put("aggregate",
+ morphiaClass.getName());
+ }
+ String clientName = morphiaEntityConfiguration.getString("clientName");
+ String dbName = morphiaEntityConfiguration.getString("dbName");
+ if (clientName == null) {
+ throw SeedException.createNew(MorphiaErrorCodes.UNKNOW_DATASTORE_CLIENT)
+ .put("aggregate", morphiaClass.getName()).put("clientName", clientName);
+ }
+ if (dbName == null) {
+ throw SeedException.createNew(MorphiaErrorCodes.UNKNOW_DATASTORE_DATABASE)
+ .put("aggregate", morphiaClass.getName()).put("clientName", clientName).put("dbName", dbName);
+ }
+ checkMongoClient(application, morphiaClass, clientName, dbName);
+ MorphiaDatastore morphiaDatastore = new MorphiaDatastoreImpl(clientName, dbName);
+ return morphiaDatastore;
+ }
+
+ private static void checkMongoClient(Application application, Class> mappedClass, String clientName, String dbName) {
+ Configuration configurationClientMongodb = application.getConfiguration().subset(MongoDbPlugin.MONGO_PLUGIN_CONFIGURATION_PREFIX + ".client." + clientName);
+ if (configurationClientMongodb.isEmpty()) {
+ throw SeedException.createNew(MongoDbErrorCodes.UNKNOWN_CLIENT_SPECIFIED)
+ .put("aggregate", mappedClass.getName()).put("clientName", clientName).put("dbName", dbName);
+ }
+ boolean async = configurationClientMongodb.getBoolean("async", false);
+ if(async){
+ throw SeedException.createNew(MorphiaErrorCodes.ERROR_ASYNC_CLIENT)
+ .put("aggregate", mappedClass.getName()).put("clientName", clientName).put("dbName", dbName);
+ }
+ String[] dbNames = configurationClientMongodb.getStringArray("databases");
+ if (dbNames != null && dbNames.length>0 && !Arrays.asList(dbNames).contains(dbName)) {
+ throw SeedException.createNew(MorphiaErrorCodes.UNKNOW_DATABASE_NAME)
+ .put("aggregate", mappedClass.getName()).put("clientName", clientName).put("dbName", dbName);
+ }
+ }
+
+ @Override
+ public Collection> requiredPlugins() {
+ Collection> plugins = new ArrayList>();
+ plugins.add(ApplicationPlugin.class);
+ plugins.add(MongoDbPlugin.class);
+ return plugins;
+ }
+
+ @Override
+ public Collection classpathScanRequests() {
+ return classpathScanRequestBuilder().specification(MORPHIA_MAPPED_CLASSES_SPECS).build();
+ }
+
+ @SuppressWarnings("unchecked")
+ private static Specification> morphiaSpecification() {
+ Specification> specification = null;
+ specification = BaseClassSpecifications.and(
+ BaseClassSpecifications.or(BaseClassSpecifications.classAnnotatedWith(Entity.class),
+ BaseClassSpecifications.classAnnotatedWith(Embedded.class)),
+ BaseClassSpecifications.not(classIsAbstract()));
+
+ return specification;
+ }
+
+ @Override
+ public Object nativeUnitModule() {
+ return new MorphiaModule(morphiaDatastores, morphia);
+ }
+
+
+}
\ No newline at end of file
diff --git a/morphia/src/main/resources/META-INF/errors/org.seedstack.seed.persistence.mongodb.api.MorphiaErrorCodes.properties b/morphia/src/main/resources/META-INF/errors/org.seedstack.seed.persistence.mongodb.api.MorphiaErrorCodes.properties
new file mode 100644
index 0000000..622c7ce
--- /dev/null
+++ b/morphia/src/main/resources/META-INF/errors/org.seedstack.seed.persistence.mongodb.api.MorphiaErrorCodes.properties
@@ -0,0 +1,18 @@
+#
+# Copyright (c) 2013-2015, The SeedStack authors
+#
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+#
+
+UNKNOW_DATASTORE_CONFIGURATION.message = Unable to instantiate Repository datastore, unknown morphia configuration for aggregate "${aggregate}"
+UNKNOW_DATASTORE_CONFIGURATION.fix = Please refer Seed documentation for linking domains to specifics Mongodb configurations
+UNKNOW_DATASTORE_CLIENT.message = Unable to instantiate Repository datastore, no clientName configuration for aggregate "${aggregate}"
+UNKNOW_DATASTORE_CLIENT.fix = Please refer to Seed documentation for linking domains to specifics Mongodb clients
+UNKNOW_DATASTORE_DATABASE.message = Unable to instantiate Repository datastore, no dbName for aggregate "${aggregate}"
+UNKNOW_DATASTORE_DATABASE.fix = Please refer to Seed documentation for linking domains to specifics Mongodb databases
+UNKNOW_DATABASE_NAME.message = Unable to instantiate Repository datastore, unfound database "${dbName}" for Mongodb client "${clientName}"
+UNKNOW_DATABASE_NAME.fix = Please refer to Seed documentation for specifying a Mongodb client
+ERROR_ASYNC_CLIENT = Mongodb Repositories does not support asynchronous clients, please check client "${clientName}" for aggregate "${aggregate}"
+
diff --git a/morphia/src/main/resources/META-INF/services/io.nuun.kernel.api.Plugin b/morphia/src/main/resources/META-INF/services/io.nuun.kernel.api.Plugin
new file mode 100644
index 0000000..9be6f1e
--- /dev/null
+++ b/morphia/src/main/resources/META-INF/services/io.nuun.kernel.api.Plugin
@@ -0,0 +1 @@
+org.seedstack.seed.persistence.mongodb.internal.MorphiaPlugin
\ No newline at end of file
diff --git a/morphia/src/test/resources/logback-test.xml b/morphia/src/test/resources/logback-test.xml
new file mode 100644
index 0000000..206a61c
--- /dev/null
+++ b/morphia/src/test/resources/logback-test.xml
@@ -0,0 +1,20 @@
+
+
+
+
+
+ %d{HH:mm:ss.SSS} [%thread] %-5level %logger{36} - %msg%n
+
+
+
+
+
+
\ No newline at end of file
diff --git a/pom.xml b/pom.xml
index 2b7ec17..476aeec 100644
--- a/pom.xml
+++ b/pom.xml
@@ -1,8 +1,6 @@