Skip to content

To support jdk 16, add converters and module-info. #1167

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Aug 13, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -265,6 +265,17 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-jar-plugin</artifactId>
<configuration>
<archive>
<manifestEntries>
<Automatic-Module-Name>${java-module-name}</Automatic-Module-Name>
</manifestEntries>
</archive>
</configuration>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-assembly-plugin</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -133,7 +133,7 @@ public long getCas(final Object entity) {

long cas = 0;
if (versionProperty != null) {
Object casObject = (Number) accessor.getProperty(versionProperty);
Object casObject = accessor.getProperty(versionProperty);
if (casObject instanceof Number) {
cas = ((Number) casObject).longValue();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ public Long getCas(final Object entity) {

long cas = 0;
if (versionProperty != null) {
Object casObject = (Number) accessor.getProperty(versionProperty);
Object casObject = accessor.getProperty(versionProperty);
if (casObject instanceof Number) {
cas = ((Number) casObject).longValue();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2017-2020 the original author or authors.
* Copyright 2017-2021 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.
Expand Down Expand Up @@ -50,6 +50,7 @@ public class CouchbaseCustomConversions extends org.springframework.data.convert

converters.addAll(DateConverters.getConvertersToRegister());
converters.addAll(CouchbaseJsr310Converters.getConvertersToRegister());
converters.addAll(OtherConverters.getConvertersToRegister());

STORE_CONVERTERS = Collections.unmodifiableList(converters);
STORE_CONVERSIONS = StoreConversions.of(SimpleTypeHolder.DEFAULT, STORE_CONVERTERS);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
/*
* Copyright 2012-2020 the original author or authors
* Copyright 2012-2021 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.
Expand Down Expand Up @@ -35,6 +35,7 @@
import org.springframework.core.convert.ConversionService;
import org.springframework.core.convert.support.DefaultConversionService;
import org.springframework.data.annotation.Transient;
import org.springframework.data.convert.CustomConversions;
import org.springframework.data.convert.EntityInstantiator;
import org.springframework.data.couchbase.core.mapping.CouchbaseDocument;
import org.springframework.data.couchbase.core.mapping.CouchbaseList;
Expand Down Expand Up @@ -117,11 +118,7 @@ public class MappingCouchbaseConverter extends AbstractCouchbaseConverter implem
private @Nullable EntityCallbacks entityCallbacks;

public MappingCouchbaseConverter() {
super(new DefaultConversionService());

this.typeMapper = new DefaultCouchbaseTypeMapper(TYPEKEY_DEFAULT);
this.mappingContext = new CouchbaseMappingContext();
this.spELContext = new SpELContext(CouchbaseDocumentPropertyAccessor.INSTANCE);
this(new CouchbaseMappingContext(), null);
}

/**
Expand All @@ -131,7 +128,7 @@ public MappingCouchbaseConverter() {
*/
public MappingCouchbaseConverter(
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext) {
this(mappingContext, TYPEKEY_DEFAULT);
this(mappingContext, null);
}

/**
Expand All @@ -145,8 +142,14 @@ public MappingCouchbaseConverter(
final MappingContext<? extends CouchbasePersistentEntity<?>, CouchbasePersistentProperty> mappingContext,
final String typeKey) {
super(new DefaultConversionService());

this.mappingContext = mappingContext;
// this is how the MappingCouchbaseConverter gets the custom conversions.
// the conversions Service gets them in afterPropertiesSet()
CustomConversions customConversions = new CouchbaseCustomConversions(Collections.emptyList());
this.setCustomConversions(customConversions);
// if the mappingContext does not have the SimpleTypes, it will not know that they have converters, then it will
// try to access the fields of the type and (maybe) fail with InaccessibleObjectException
((CouchbaseMappingContext) mappingContext).setSimpleTypeHolder(customConversions.getSimpleTypeHolder());
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

While this would work, associating custom conversions should happen outside the converter. Typically, CustomConversions gets configured by users, and then it should get associated with the converter/mapping context within @Bean methods.

Copy link
Collaborator Author

@mikereiche mikereiche Aug 12, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

They were already populated in AbstractCouchbaseConverter.

	protected CustomConversions conversions = new CouchbaseCustomConversions(Collections.emptyList());

I set them again in the MappingCouchbaseConverter constructor so that the mappingContext could capture the SimpleTypes so those converters would be used.

btw - these aren't strictly user-supplied "custom" conversions. These are provided/required by couchbase (and also included in any customer-created CouchbaseCustomConversions).

Maybe the issue is with the MappingCouchbaseConverter constructors - there is no constructor that takes a CouchbaseCustomConverters parameter, therefore the caller cannot capture the SimpleTypes of the CouchbaseCustomsConverts object to populate the mappingContext.

typeMapper = new DefaultCouchbaseTypeMapper(typeKey != null ? typeKey : TYPEKEY_DEFAULT);
spELContext = new SpELContext(CouchbaseDocumentPropertyAccessor.INSTANCE);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
/*
* Copyright 2021 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
*
* https://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.core.convert;

import java.math.BigInteger;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.UUID;

import org.springframework.core.convert.converter.Converter;
import org.springframework.data.convert.ReadingConverter;
import org.springframework.data.convert.WritingConverter;

/**
* Out of the box conversions for java dates and calendars.
*
* @author Michael Reiche
*/
public final class OtherConverters {

private OtherConverters() {}

/**
* Returns all converters by this class that can be registered.
*
* @return the list of converters to register.
*/
public static Collection<Converter<?, ?>> getConvertersToRegister() {
List<Converter<?, ?>> converters = new ArrayList<Converter<?, ?>>();

converters.add(UuidToString.INSTANCE);
converters.add(StringToUuid.INSTANCE);
converters.add(BigIntegerToString.INSTANCE);
converters.add(StringToBigInteger.INSTANCE);

return converters;
}

@WritingConverter
public enum UuidToString implements Converter<UUID, String> {
INSTANCE;

@Override
public String convert(UUID source) {
return source == null ? null : source.toString();
}
}

@ReadingConverter
public enum StringToUuid implements Converter<String, UUID> {
INSTANCE;

@Override
public UUID convert(String source) {
return source == null ? null : UUID.fromString(source);
}
}

@WritingConverter
public enum BigIntegerToString implements Converter<BigInteger, String> {
INSTANCE;

@Override
public String convert(BigInteger source) {
return source == null ? null : source.toString();
}
}

@ReadingConverter
public enum StringToBigInteger implements Converter<String, BigInteger> {
INSTANCE;

@Override
public BigInteger convert(String source) {
return source == null ? null : new BigInteger(source);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public void setAutoIndexCreation(boolean autoCreateIndexes) {
/**
* override method from AbstractMappingContext as that method will not publishEvent() if it finds the entity has
* already been cached
*
*
* @param typeInformation - entity type
*/
@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
/*
* Copyright 2014-2021 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
*
* https://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 java.lang.annotation.Annotation;
import java.util.Optional;
import java.util.Set;

import javax.enterprise.context.spi.CreationalContext;
import javax.enterprise.inject.spi.Bean;
import javax.enterprise.inject.spi.BeanManager;

import org.springframework.data.couchbase.core.CouchbaseOperations;
import org.springframework.data.couchbase.repository.config.RepositoryOperationsMapping;
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<T> extends CdiRepositoryBean<T> {

private final Bean<CouchbaseOperations> 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<CouchbaseOperations> operations, Set<Annotation> qualifiers,
Class<T> repositoryType, BeanManager beanManager, CustomRepositoryImplementationDetector detector) {
super(qualifiers, repositoryType, beanManager, Optional.of(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)
*/
@Override
protected T create(CreationalContext<T> creationalContext, Class<T> repositoryType) {

CouchbaseOperations couchbaseOperations = getDependencyInstance(couchbaseOperationsBean, CouchbaseOperations.class);
RepositoryOperationsMapping couchbaseOperationsMapping = new RepositoryOperationsMapping(couchbaseOperations);

return create(() -> new CouchbaseRepositoryFactory(couchbaseOperationsMapping), repositoryType);
}

@Override
public Class<? extends Annotation> getScope() {
return couchbaseOperationsBean.getScope();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,103 @@
/*
* Copyright 2014-2021 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
*
* https://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 java.lang.annotation.Annotation;
import java.lang.reflect.Type;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;

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 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<Set<Annotation>, Bean<CouchbaseOperations>> couchbaseOperationsMap = new HashMap<Set<Annotation>, Bean<CouchbaseOperations>>();

/**
* Implementation of a an observer which checks for CouchbaseOperations beans and stores them in
* {@link #couchbaseOperationsMap} for later association with corresponding repository beans.
*
* @param <T> The type.
* @param processBean The annotated type as defined by CDI.
*/
@SuppressWarnings("unchecked")
<T> void processBean(@Observes ProcessBean<T> processBean) {
Bean<T> bean = processBean.getBean();
for (Type type : bean.getTypes()) {
if (type instanceof Class<?> && CouchbaseOperations.class.isAssignableFrom((Class<?>) type)) {
couchbaseOperationsMap.put(bean.getQualifiers(), ((Bean<CouchbaseOperations>) bean));
}
}
}

/**
* Implementation of a an observer which registers beans to the CDI container for the detected Spring Data
* repositories.
* <p>
* 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<Class<?>, Set<Annotation>> entry : getRepositoryTypes()) {

Class<?> repositoryType = entry.getKey();
Set<Annotation> qualifiers = entry.getValue();

CdiRepositoryBean<?> repositoryBean = createRepositoryBean(repositoryType, qualifiers, beanManager);
afterBeanDiscovery.addBean(repositoryBean);
registerBean(repositoryBean);
}
}

/**
* Creates a {@link Bean}.
*
* @param <T> The type of the repository.
* @param repositoryType The class representing the repository.
* @param beanManager The BeanManager instance.
* @return The bean.
*/
private <T> CdiRepositoryBean<T> createRepositoryBean(Class<T> repositoryType, Set<Annotation> qualifiers,
BeanManager beanManager) {

Bean<CouchbaseOperations> couchbaseOperationsBean = this.couchbaseOperationsMap.get(qualifiers);

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<T>(couchbaseOperationsBean, qualifiers, repositoryType, beanManager,
getCustomImplementationDetector());
}
}
Loading