From 2da3840334144ef8ee48c0d26e4d3c895409e611 Mon Sep 17 00:00:00 2001 From: Mark Paluch Date: Thu, 11 Sep 2014 10:31:31 +0200 Subject: [PATCH] DATACOUCH-109 - Provide CDI support for Spring Data Couchbase repositories Initial integration to enable CDI usage of Spring Data Couchbase repositories along with custom repository implementations. --- pom.xml | 23 ++++ .../cdi/CouchbaseRepositoryBean.java | 71 ++++++++++++ .../cdi/CouchbaseRepositoryExtension.java | 100 +++++++++++++++++ .../javax.enterprise.inject.spi.Extension | 1 + .../repository/cdi/CdiPersonRepository.java | 27 +++++ .../cdi/CdiPersonRepositoryCustom.java | 26 +++++ .../cdi/CdiPersonRepositoryImpl.java | 28 +++++ .../repository/cdi/CdiRepositoryClient.java | 42 ++++++++ .../repository/cdi/CdiRepositoryTests.java | 102 ++++++++++++++++++ .../cdi/CouchbaseClientProducer.java | 45 ++++++++ .../cdi/CouchbaseOperationsProducer.java | 40 +++++++ .../data/couchbase/repository/cdi/Person.java | 53 +++++++++ src/test/resources/META-INF/beans.xml | 6 ++ template.mf | 1 + 14 files changed, 565 insertions(+) create mode 100644 src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryBean.java create mode 100644 src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryExtension.java create mode 100644 src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension create mode 100644 src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepository.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepositoryCustom.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepositoryImpl.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/cdi/CdiRepositoryClient.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/cdi/CdiRepositoryTests.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/cdi/CouchbaseClientProducer.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/cdi/CouchbaseOperationsProducer.java create mode 100644 src/test/java/org/springframework/data/couchbase/repository/cdi/Person.java create mode 100644 src/test/resources/META-INF/beans.xml diff --git a/pom.xml b/pom.xml index d58542f5..b9158b46 100644 --- a/pom.xml +++ b/pom.xml @@ -96,6 +96,29 @@ true + + + javax.enterprise + cdi-api + ${cdi} + provided + true + + + + org.apache.openwebbeans.test + cditest-owb + ${webbeans} + test + + + + javax.servlet + servlet-api + 3.0-alpha-1 + test + + diff --git a/src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryBean.java b/src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryBean.java new file mode 100644 index 00000000..e3ca2ab9 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryBean.java @@ -0,0 +1,71 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +import javax.enterprise.context.spi.CreationalContext; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import java.lang.annotation.Annotation; +import java.util.Set; + +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.repository.support.CouchbaseRepositoryFactory; +import org.springframework.data.repository.cdi.CdiRepositoryBean; +import org.springframework.data.repository.config.CustomRepositoryImplementationDetector; +import org.springframework.util.Assert; + +/** + * A bean which represents a Couchbase repository. + * @author Mark Paluch + */ +public class CouchbaseRepositoryBean extends CdiRepositoryBean { + + private final Bean couchbaseOperationsBean; + + /** + * Creates a new {@link CouchbaseRepositoryBean}. + * + * @param operations must not be {@literal null}. + * @param qualifiers must not be {@literal null}. + * @param repositoryType must not be {@literal null}. + * @param beanManager must not be {@literal null}. + * @param detector detector for the custom {@link org.springframework.data.repository.Repository} implementations + * {@link org.springframework.data.repository.config.CustomRepositoryImplementationDetector}, can be {@literal null}. + */ + public CouchbaseRepositoryBean(Bean operations, Set qualifiers, Class repositoryType, + BeanManager beanManager, CustomRepositoryImplementationDetector detector) { + super(qualifiers, repositoryType, beanManager, detector); + + Assert.notNull(operations, "Cannot create repository with 'null' for CouchbaseOperations."); + this.couchbaseOperationsBean = operations; + } + + /* + * (non-Javadoc) + * @see org.springframework.data.repository.cdi.CdiRepositoryBean#create(javax.enterprise.context.spi.CreationalContext, java.lang.Class, java.lang.Object) + */ + @Override + protected T create(CreationalContext creationalContext, Class repositoryType, Object customImplementation) { + CouchbaseOperations couchbaseOperations = getDependencyInstance(couchbaseOperationsBean, CouchbaseOperations.class); + return new CouchbaseRepositoryFactory(couchbaseOperations).getRepository(repositoryType, customImplementation); + } + + @Override + public Class getScope() { + return couchbaseOperationsBean.getScope(); + } +} diff --git a/src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryExtension.java b/src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryExtension.java new file mode 100644 index 00000000..eab8fb20 --- /dev/null +++ b/src/main/java/org/springframework/data/couchbase/repository/cdi/CouchbaseRepositoryExtension.java @@ -0,0 +1,100 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +import javax.enterprise.event.Observes; +import javax.enterprise.inject.UnsatisfiedResolutionException; +import javax.enterprise.inject.spi.AfterBeanDiscovery; +import javax.enterprise.inject.spi.Bean; +import javax.enterprise.inject.spi.BeanManager; +import javax.enterprise.inject.spi.ProcessBean; +import java.lang.annotation.Annotation; +import java.lang.reflect.Type; +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.repository.cdi.CdiRepositoryBean; +import org.springframework.data.repository.cdi.CdiRepositoryExtensionSupport; + +/** + * A portable CDI extension which registers beans for Spring Data Couchbase repositories. + * @author Mark Paluch + */ +public class CouchbaseRepositoryExtension extends CdiRepositoryExtensionSupport{ + + private final Map> couchbaseOperationsMap = new HashMap>(); + + /** + * Implementation of a an observer which checks for CouchbaseOperations beans and stores them in {@link #couchbaseOperationsMap} for + * later association with corresponding repository beans. + * + * @param The type. + * @param processBean The annotated type as defined by CDI. + */ + @SuppressWarnings("unchecked") + void processBean(@Observes ProcessBean processBean) { + Bean bean = processBean.getBean(); + for (Type type : bean.getTypes()) { + if (type instanceof Class && CouchbaseOperations.class.isAssignableFrom((Class) type)) { + couchbaseOperationsMap.put(bean.getQualifiers().toString(), ((Bean) bean)); + } + } + } + + /** + * Implementation of a an observer which registers beans to the CDI container for the detected Spring Data + * repositories. + *

+ * The repository beans are associated to the CouchbaseOperations using their qualifiers. + * + * @param beanManager The BeanManager instance. + */ + void afterBeanDiscovery(@Observes AfterBeanDiscovery afterBeanDiscovery, BeanManager beanManager) { + for (Map.Entry, Set> entry : getRepositoryTypes()) { + + Class repositoryType = entry.getKey(); + Set qualifiers = entry.getValue(); + + CdiRepositoryBean repositoryBean = createRepositoryBean(repositoryType, qualifiers, beanManager); + afterBeanDiscovery.addBean(repositoryBean); + registerBean(repositoryBean); + } + } + + /** + * Creates a {@link Bean}. + * + * @param The type of the repository. + * @param repositoryType The class representing the repository. + * @param beanManager The BeanManager instance. + * @return The bean. + */ + private CdiRepositoryBean createRepositoryBean(Class repositoryType, Set qualifiers, BeanManager beanManager) { + + Bean couchbaseOperationsBean = this.couchbaseOperationsMap.get(qualifiers + .toString()); + + if (couchbaseOperationsBean == null) { + throw new UnsatisfiedResolutionException(String.format("Unable to resolve a bean for '%s' with qualifiers %s.", + CouchbaseOperations.class.getName(), qualifiers)); + } + + return new CouchbaseRepositoryBean(couchbaseOperationsBean, qualifiers, repositoryType, beanManager, getCustomImplementationDetector()); + } +} diff --git a/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension b/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension new file mode 100644 index 00000000..6645e6b1 --- /dev/null +++ b/src/main/resources/META-INF/services/javax.enterprise.inject.spi.Extension @@ -0,0 +1 @@ +org.springframework.data.couchbase.repository.cdi.CouchbaseRepositoryExtension \ No newline at end of file diff --git a/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepository.java b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepository.java new file mode 100644 index 00000000..4e29e7d0 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepository.java @@ -0,0 +1,27 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +import org.springframework.data.couchbase.repository.CouchbaseRepository; +import org.springframework.data.couchbase.repository.User; + +/** + * @author Mark Paluch + */ +public interface CdiPersonRepository extends CouchbaseRepository, CdiPersonRepositoryCustom { + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepositoryCustom.java b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepositoryCustom.java new file mode 100644 index 00000000..ddf5d76b --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepositoryCustom.java @@ -0,0 +1,26 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +/** + * @author Mark Paluch + */ +public interface CdiPersonRepositoryCustom { + + int returnTwo(); + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepositoryImpl.java b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepositoryImpl.java new file mode 100644 index 00000000..d963bc71 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiPersonRepositoryImpl.java @@ -0,0 +1,28 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +/** + * @author Mark Paluch + */ +public class CdiPersonRepositoryImpl implements CdiPersonRepositoryCustom { + + @Override + public int returnTwo() { + return 2; + } +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiRepositoryClient.java b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiRepositoryClient.java new file mode 100644 index 00000000..9fab73d0 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiRepositoryClient.java @@ -0,0 +1,42 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +import javax.inject.Inject; + +import com.couchbase.client.CouchbaseClient; +import org.springframework.data.couchbase.repository.UserRepository; + +/** + * @author Mark Paluch + */ +class CdiRepositoryClient { + + @Inject + private CdiPersonRepository cdiPersonRepository; + + @Inject + private CouchbaseClient couchbaseClient; + + public CdiPersonRepository getCdiPersonRepository() { + return cdiPersonRepository; + } + + public CouchbaseClient getCouchbaseClient() { + return couchbaseClient; + } +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiRepositoryTests.java b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiRepositoryTests.java new file mode 100644 index 00000000..b4c91043 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/cdi/CdiRepositoryTests.java @@ -0,0 +1,102 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +import static org.junit.Assert.*; + +import org.apache.webbeans.cditest.CdiTestContainer; +import org.apache.webbeans.cditest.CdiTestContainerLoader; +import org.junit.AfterClass; +import org.junit.Before; +import org.junit.BeforeClass; +import org.junit.Test; + +import com.couchbase.client.CouchbaseClient; +import com.couchbase.client.protocol.views.DesignDocument; +import com.couchbase.client.protocol.views.ViewDesign; + +/** + * @author Mark Paluch + */ +public class CdiRepositoryTests { + + private static CdiTestContainer cdiContainer; + private CdiPersonRepository repository; + private CouchbaseClient couchbaseClient; + + @BeforeClass + public static void init() throws Exception { + cdiContainer = CdiTestContainerLoader.getCdiContainer(); + cdiContainer.startApplicationScope(); + cdiContainer.bootContainer(); + } + + @AfterClass + public static void shutdown() throws Exception { + cdiContainer.stopContexts(); + cdiContainer.shutdownContainer(); + } + + @Before + public void setUp() { + CdiRepositoryClient repositoryClient = cdiContainer.getInstance(CdiRepositoryClient.class); + repository = repositoryClient.getCdiPersonRepository(); + + couchbaseClient = repositoryClient.getCouchbaseClient(); + createAndWaitForDesignDocs(couchbaseClient); + + } + + private void createAndWaitForDesignDocs(CouchbaseClient client) { + DesignDocument designDoc = new DesignDocument("person"); + String mapFunction = "function (doc, meta) { if(doc._class == \"" + Person.class.getName() + + "\") { emit(null, null); } }"; + designDoc.setView(new ViewDesign("all", mapFunction, "_count")); + client.createDesignDoc(designDoc); + } + + /** + * @see DATACOUCH-109 + */ + @Test + public void testCdiRepository() { + assertNotNull(repository); + repository.deleteAll(); + + Person bean = new Person("key", "username"); + + repository.save(bean); + + assertTrue(repository.exists(bean.getId())); + + Person retrieved = repository.findOne(bean.getId()); + assertNotNull(retrieved); + assertEquals(bean.getName(), retrieved.getName()); + assertEquals(bean.getId(), retrieved.getId()); + + } + + /** + * @see DATACOUCH-109 + */ + @Test + public void testCustomRepository() { + + assertEquals(2, repository.returnTwo()); + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/cdi/CouchbaseClientProducer.java b/src/test/java/org/springframework/data/couchbase/repository/cdi/CouchbaseClientProducer.java new file mode 100644 index 00000000..1c1f2c9a --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/cdi/CouchbaseClientProducer.java @@ -0,0 +1,45 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +import javax.enterprise.inject.Disposes; +import javax.enterprise.inject.Produces; + +import org.springframework.data.couchbase.core.CouchbaseFactoryBean; + +import com.couchbase.client.CouchbaseClient; + +/** + * Producer for {@link CouchbaseClient}. Defaults from {@link CouchbaseFactoryBean} are sufficient for our test. + * + * @author Mark Paluch + */ +class CouchbaseClientProducer { + + @Produces + public CouchbaseClient createCouchbaseClient() throws Exception { + + CouchbaseFactoryBean couchbaseFactoryBean = new CouchbaseFactoryBean(); + couchbaseFactoryBean.setBucket("default"); + couchbaseFactoryBean.afterPropertiesSet(); + return couchbaseFactoryBean.getObject(); + } + + public void close(@Disposes CouchbaseClient couchbaseClient) { + couchbaseClient.shutdown(); + } +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/cdi/CouchbaseOperationsProducer.java b/src/test/java/org/springframework/data/couchbase/repository/cdi/CouchbaseOperationsProducer.java new file mode 100644 index 00000000..bf3f1d84 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/cdi/CouchbaseOperationsProducer.java @@ -0,0 +1,40 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +import javax.enterprise.inject.Produces; + +import org.springframework.data.couchbase.core.CouchbaseOperations; +import org.springframework.data.couchbase.core.CouchbaseTemplate; + +import com.couchbase.client.CouchbaseClient; + +/** + * Produces a {@link CouchbaseOperations} instance for test usage. + * @author Mark Paluch + */ +class CouchbaseOperationsProducer { + + @Produces + public CouchbaseOperations createCouchbaseOperations(CouchbaseClient couchbaseClient) throws Exception { + + CouchbaseTemplate couchbaseTemplate = new CouchbaseTemplate(couchbaseClient); + + return couchbaseTemplate; + } + +} diff --git a/src/test/java/org/springframework/data/couchbase/repository/cdi/Person.java b/src/test/java/org/springframework/data/couchbase/repository/cdi/Person.java new file mode 100644 index 00000000..a726d414 --- /dev/null +++ b/src/test/java/org/springframework/data/couchbase/repository/cdi/Person.java @@ -0,0 +1,53 @@ +/* + * Copyright 2014 the original author or authors. + * + * 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 org.springframework.data.couchbase.repository.cdi; + +import org.springframework.data.annotation.Id; +import org.springframework.data.couchbase.core.mapping.Field; + +/** + * @author Mark Paluch + */ +public class Person { + + @Id private String id; + + @Field private String name; + + public Person() {} + + public Person(String id, String name) { + this.id = id; + this.name = name; + } + + public String getId() { + return id; + } + + public void setId(String id) { + this.id = id; + } + + public String getName() { + return name; + } + + public void setName(String name) { + this.name = name; + } +} diff --git a/src/test/resources/META-INF/beans.xml b/src/test/resources/META-INF/beans.xml new file mode 100644 index 00000000..71dc6db1 --- /dev/null +++ b/src/test/resources/META-INF/beans.xml @@ -0,0 +1,6 @@ + + + + diff --git a/template.mf b/template.mf index f200af5c..2111ab1d 100644 --- a/template.mf +++ b/template.mf @@ -7,6 +7,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Export-Template: org.springframework.data.couchbase.*;version="${project.version}" Import-Template: + javax.enterprise.*;version="${cdi:[=.=.=,+1.0.0)}";resolution:=optional, com.couchbase.client.*;version="${couchbase:[=.=.=,+1.0.0)}", net.spy.memcached.*;version="[2.8.0,3.0.0)", com.fasterxml.jackson.*;version="${jackson:[=.=.=,+1.0.0)}",