Skip to content

Commit aa3f2ab

Browse files
committed
Merge branch 'auditing-gosling' of https://github.com/vitolimandibhrata/spring-data-dynamodb into vitolimandibhrata-auditing-gosling
2 parents c8a16ff + c238d39 commit aa3f2ab

File tree

17 files changed

+910
-2
lines changed

17 files changed

+910
-2
lines changed

pom.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -338,6 +338,7 @@
338338
<table>src/test/resources/feeduser_table.json</table>
339339
<table>src/test/resources/customerhistory_table.json</table>
340340
<table>src/test/resources/installation_table.json</table>
341+
<table>src/test/resources/auditable_user_table.json</table>
341342
</tables>
342343
<port>${dynamodblocal.port}</port>
343344
<dist>${project.build.directory}/dynamodb-dist</dist>
Lines changed: 114 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,114 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.socialsignin.spring.data.dynamodb.config;
17+
18+
import com.amazonaws.auth.AWSCredentials;
19+
import com.amazonaws.services.dynamodbv2.AmazonDynamoDB;
20+
import com.amazonaws.services.dynamodbv2.datamodeling.DynamoDBTable;
21+
import org.slf4j.Logger;
22+
import org.slf4j.LoggerFactory;
23+
import org.socialsignin.spring.data.dynamodb.mapping.DynamoDBMappingContext;
24+
import org.springframework.beans.factory.config.BeanDefinition;
25+
import org.springframework.context.annotation.Bean;
26+
import org.springframework.context.annotation.ClassPathScanningCandidateComponentProvider;
27+
import org.springframework.context.annotation.Configuration;
28+
import org.springframework.core.type.filter.AnnotationTypeFilter;
29+
import org.springframework.util.ClassUtils;
30+
import org.springframework.util.StringUtils;
31+
32+
import java.util.HashSet;
33+
import java.util.Set;
34+
35+
/**
36+
* Base class for Spring Data DynamoDB configuration using JavaConfig.
37+
*
38+
* @author Vito Limandibhrata
39+
*/
40+
@Configuration
41+
public abstract class AbstractDynamoDBConfiguration {
42+
43+
private static final Logger LOGGER = LoggerFactory.getLogger(AbstractDynamoDBConfiguration.class);
44+
45+
public abstract AmazonDynamoDB amazonDynamoDB();
46+
47+
public abstract AWSCredentials amazonAWSCredentials();
48+
49+
/**
50+
* Return the base packages to scan for mapped {@link DynamoDBTable}s. Will return the package name of the configuration
51+
* class' (the concrete class, not this one here) by default. So if you have a {@code com.acme.AppConfig} extending
52+
* {@link AbstractDynamoDBConfiguration} the base package will be considered {@code com.acme} unless the method is
53+
* overriden to implement alternate behaviour.
54+
*
55+
* @return the base package to scan for mapped {@link DynamoDBTable} classes or {@literal null} to not enable scanning for
56+
* entities.
57+
*/
58+
protected String[] getMappingBasePackages() {
59+
60+
Package mappingBasePackage = getClass().getPackage();
61+
String basePackage = mappingBasePackage == null ? null : mappingBasePackage.getName();
62+
63+
return new String[]{basePackage};
64+
}
65+
66+
/**
67+
* Creates a {@link DynamoDBMappingContext} equipped with entity classes scanned from the mapping base package.
68+
*
69+
* @see #getMappingBasePackages()
70+
* @return
71+
* @throws ClassNotFoundException
72+
*/
73+
@Bean
74+
public DynamoDBMappingContext dynamoDBMappingContext() throws ClassNotFoundException {
75+
76+
DynamoDBMappingContext mappingContext = new DynamoDBMappingContext();
77+
mappingContext.setInitialEntitySet(getInitialEntitySet());
78+
79+
return mappingContext;
80+
}
81+
82+
/**
83+
* Scans the mapping base package for classes annotated with {@link DynamoDBTable}.
84+
*
85+
* @see #getMappingBasePackages()
86+
* @return
87+
* @throws ClassNotFoundException
88+
*/
89+
protected Set<Class<?>> getInitialEntitySet() throws ClassNotFoundException {
90+
91+
Set<Class<?>> initialEntitySet = new HashSet<Class<?>>();
92+
93+
String[] basePackages = getMappingBasePackages();
94+
95+
for (String basePackage:basePackages) {
96+
LOGGER.trace("getInitialEntitySet. basePackage: {}", basePackage);
97+
98+
if (StringUtils.hasText(basePackage)) {
99+
ClassPathScanningCandidateComponentProvider componentProvider = new ClassPathScanningCandidateComponentProvider(
100+
false);
101+
componentProvider.addIncludeFilter(new AnnotationTypeFilter(DynamoDBTable.class));
102+
103+
for (BeanDefinition candidate : componentProvider.findCandidateComponents(basePackage)) {
104+
LOGGER.trace("getInitialEntitySet. candidate: {}", candidate.getBeanClassName());
105+
initialEntitySet.add(ClassUtils.forName(candidate.getBeanClassName(),
106+
AbstractDynamoDBConfiguration.class.getClassLoader()));
107+
}
108+
}
109+
}
110+
111+
return initialEntitySet;
112+
}
113+
114+
}
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.socialsignin.spring.data.dynamodb.config;
17+
18+
/**
19+
* Constants to declare bean names used by the namespace configuration.
20+
*
21+
* @author Vito Limandibhrata
22+
*/
23+
public abstract class BeanNames {
24+
25+
public static final String MAPPING_CONTEXT_BEAN_NAME = "dynamoDBMappingContext";
26+
27+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.socialsignin.spring.data.dynamodb.config;
17+
18+
import org.socialsignin.spring.data.dynamodb.mapping.DynamoDBMappingContext;
19+
import org.socialsignin.spring.data.dynamodb.mapping.event.AuditingEventListener;
20+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
21+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
22+
import org.springframework.beans.factory.support.RootBeanDefinition;
23+
import org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser;
24+
import org.springframework.beans.factory.xml.ParserContext;
25+
import org.springframework.data.auditing.config.IsNewAwareAuditingHandlerBeanDefinitionParser;
26+
import org.springframework.util.StringUtils;
27+
import org.w3c.dom.Element;
28+
29+
import static org.socialsignin.spring.data.dynamodb.config.BeanNames.MAPPING_CONTEXT_BEAN_NAME;
30+
import static org.springframework.data.config.ParsingUtils.getObjectFactoryBeanDefinition;
31+
32+
/**
33+
* {@link org.springframework.beans.factory.xml.BeanDefinitionParser} to register a {@link AuditingEventListener} to transparently set auditing information on
34+
* an entity.
35+
*
36+
* @author Vito Limandibhrata
37+
*/
38+
public class DynamoDBAuditingBeanDefinitionParser extends AbstractSingleBeanDefinitionParser {
39+
40+
/*
41+
* (non-Javadoc)
42+
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#getBeanClass(org.w3c.dom.Element)
43+
*/
44+
@Override
45+
protected Class<?> getBeanClass(Element element) {
46+
return AuditingEventListener.class;
47+
}
48+
49+
/*
50+
* (non-Javadoc)
51+
* @see org.springframework.beans.factory.xml.AbstractBeanDefinitionParser#shouldGenerateId()
52+
*/
53+
@Override
54+
protected boolean shouldGenerateId() {
55+
return true;
56+
}
57+
58+
/*
59+
* (non-Javadoc)
60+
* @see org.springframework.beans.factory.xml.AbstractSingleBeanDefinitionParser#doParse(org.w3c.dom.Element, org.springframework.beans.factory.xml.ParserContext, org.springframework.beans.factory.support.BeanDefinitionBuilder)
61+
*/
62+
@Override
63+
protected void doParse(Element element, ParserContext parserContext, BeanDefinitionBuilder builder) {
64+
65+
String mappingContextRef = element.getAttribute("mapping-context-ref");
66+
67+
if (!StringUtils.hasText(mappingContextRef)) {
68+
69+
BeanDefinitionRegistry registry = parserContext.getRegistry();
70+
71+
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
72+
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, new RootBeanDefinition(DynamoDBMappingContext.class));
73+
}
74+
75+
mappingContextRef = MAPPING_CONTEXT_BEAN_NAME;
76+
}
77+
78+
IsNewAwareAuditingHandlerBeanDefinitionParser parser = new IsNewAwareAuditingHandlerBeanDefinitionParser(
79+
mappingContextRef);
80+
parser.parse(element, parserContext);
81+
82+
builder.addConstructorArgValue(getObjectFactoryBeanDefinition(parser.getResolvedBeanName(),
83+
parserContext.extractSource(element)));
84+
}
85+
}
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
/*
2+
* Copyright 2016 the original author or authors.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
package org.socialsignin.spring.data.dynamodb.config;
17+
18+
import org.slf4j.Logger;
19+
import org.slf4j.LoggerFactory;
20+
import org.springframework.beans.factory.config.BeanDefinition;
21+
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
22+
import org.springframework.beans.factory.support.BeanDefinitionRegistry;
23+
import org.springframework.beans.factory.support.RootBeanDefinition;
24+
import org.springframework.core.type.AnnotationMetadata;
25+
import org.springframework.data.auditing.IsNewAwareAuditingHandler;
26+
import org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport;
27+
import org.springframework.data.auditing.config.AuditingConfiguration;
28+
import org.springframework.data.config.ParsingUtils;
29+
import org.socialsignin.spring.data.dynamodb.mapping.DynamoDBMappingContext;
30+
import org.socialsignin.spring.data.dynamodb.mapping.event.AuditingEventListener;
31+
import org.springframework.util.Assert;
32+
33+
import java.lang.annotation.Annotation;
34+
35+
import static org.springframework.beans.factory.config.BeanDefinition.ROLE_INFRASTRUCTURE;
36+
import static org.socialsignin.spring.data.dynamodb.config.BeanNames.MAPPING_CONTEXT_BEAN_NAME;
37+
38+
/**
39+
* {@link org.springframework.context.annotation.ImportBeanDefinitionRegistrar} to enable {@link EnableDynamoDBAuditing} annotation.
40+
*
41+
* @author Vito Limandibhrata
42+
*/
43+
class DynamoDBAuditingRegistrar extends AuditingBeanDefinitionRegistrarSupport {
44+
45+
private static Logger LOGGER = LoggerFactory.getLogger(DynamoDBAuditingRegistrar.class);
46+
47+
/*
48+
* (non-Javadoc)
49+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAnnotation()
50+
*/
51+
@Override
52+
protected Class<? extends Annotation> getAnnotation() {
53+
return EnableDynamoDBAuditing.class;
54+
}
55+
56+
/*
57+
* (non-Javadoc)
58+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditingHandlerBeanName()
59+
*/
60+
@Override
61+
protected String getAuditingHandlerBeanName() {
62+
return "dynamoDBAuditingHandler";
63+
}
64+
65+
/*
66+
* (non-Javadoc)
67+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerBeanDefinitions(org.springframework.core.type.AnnotationMetadata, org.springframework.beans.factory.support.BeanDefinitionRegistry)
68+
*/
69+
@Override
70+
public void registerBeanDefinitions(AnnotationMetadata annotationMetadata, BeanDefinitionRegistry registry) {
71+
LOGGER.trace("registerBeanDefinitions");
72+
Assert.notNull(annotationMetadata, "AnnotationMetadata must not be null!");
73+
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
74+
75+
defaultDependenciesIfNecessary(registry, annotationMetadata);
76+
super.registerBeanDefinitions(annotationMetadata, registry);
77+
}
78+
79+
/*
80+
* (non-Javadoc)
81+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#getAuditHandlerBeanDefinitionBuilder(org.springframework.data.auditing.config.AuditingConfiguration)
82+
*/
83+
@Override
84+
protected BeanDefinitionBuilder getAuditHandlerBeanDefinitionBuilder(AuditingConfiguration configuration) {
85+
LOGGER.trace("getAuditHandlerBeanDefinitionBuilder");
86+
Assert.notNull(configuration, "AuditingConfiguration must not be null!");
87+
88+
BeanDefinitionBuilder builder = BeanDefinitionBuilder.rootBeanDefinition(IsNewAwareAuditingHandler.class);
89+
builder.addConstructorArgReference(MAPPING_CONTEXT_BEAN_NAME);
90+
return configureDefaultAuditHandlerAttributes(configuration, builder);
91+
}
92+
93+
/*
94+
* (non-Javadoc)
95+
* @see org.springframework.data.auditing.config.AuditingBeanDefinitionRegistrarSupport#registerAuditListener(org.springframework.beans.factory.config.BeanDefinition, org.springframework.beans.factory.support.BeanDefinitionRegistry)
96+
*/
97+
@Override
98+
protected void registerAuditListenerBeanDefinition(BeanDefinition auditingHandlerDefinition,
99+
BeanDefinitionRegistry registry) {
100+
LOGGER.trace("registerAuditListenerBeanDefinition");
101+
Assert.notNull(auditingHandlerDefinition, "BeanDefinition must not be null!");
102+
Assert.notNull(registry, "BeanDefinitionRegistry must not be null!");
103+
104+
BeanDefinitionBuilder listenerBeanDefinitionBuilder = BeanDefinitionBuilder
105+
.rootBeanDefinition(AuditingEventListener.class);
106+
listenerBeanDefinitionBuilder.addConstructorArgValue(ParsingUtils.getObjectFactoryBeanDefinition(
107+
getAuditingHandlerBeanName(), registry));
108+
109+
registerInfrastructureBeanWithId(listenerBeanDefinitionBuilder.getBeanDefinition(),
110+
AuditingEventListener.class.getName(), registry);
111+
}
112+
113+
/**
114+
* Register default bean definitions for a {@link DynamoDBMappingContext} and an {@link org.springframework.data.support.IsNewStrategyFactory} in case we
115+
* don't find beans with the assumed names in the registry.
116+
*
117+
* @param registry the {@link BeanDefinitionRegistry} to use to register the components into.
118+
* @param source the source which the registered components shall be registered with
119+
*/
120+
private void defaultDependenciesIfNecessary(BeanDefinitionRegistry registry, Object source) {
121+
LOGGER.trace("defaultDependenciesIfNecessary. source:{}", source);
122+
LOGGER.trace("is registry.containsBeanDefinition {} {}", MAPPING_CONTEXT_BEAN_NAME, registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME));
123+
if (!registry.containsBeanDefinition(MAPPING_CONTEXT_BEAN_NAME)) {
124+
125+
RootBeanDefinition definition = new RootBeanDefinition(DynamoDBMappingContext.class);
126+
definition.setRole(ROLE_INFRASTRUCTURE);
127+
definition.setSource(source);
128+
129+
registry.registerBeanDefinition(MAPPING_CONTEXT_BEAN_NAME, definition);
130+
}
131+
}
132+
}

0 commit comments

Comments
 (0)