Support for candidate components index
This commit adds a "spring-context-indexer" module that can be added to any project in order to generate an index of candidate components defined in the project. `CandidateComponentsIndexer` is a standard annotation processor that looks for source files with target annotations (typically `@Component`) and references them in a `META-INF/spring.components` generated file. Each entry in the index is the fully qualified name of a candidate component and the comma-separated list of stereotypes that apply to that candidate. A typical example of a stereotype is `@Component`. If a project has a `com.example.FooService` annotated with `@Component` the following `META-INF/spring.components` file is generated at compile time: ``` com.example.FooService=org.springframework.stereotype.Component ``` A new `@Indexed` annotation can be added on any annotation to instructs the scanner to include a source file that contains that annotation. For instance, `@Component` is meta-annotated with `@Indexed` now and adding `@Indexed` to more annotation types will transparently improve the index with additional information. This also works for interaces or parent classes: adding `@Indexed` on a `Repository` base interface means that the indexed can be queried for its implementation by using the fully qualified name of the `Repository` interface. The indexer also adds any class or interface that has a type-level annotation from the `javax` package. This includes obviously JPA (`@Entity` and related) but also CDI (`@Named`, `@ManagedBean`) and servlet annotations (i.e. `@WebFilter`). These are meant to handle cases where a component needs to identify candidates and use classpath scanning currently. If a `package-info.java` file exists, the package is registered using a "package-info" stereotype. Such files can later be reused by the `ApplicationContext` to avoid using component scan. A global `CandidateComponentsIndex` can be easily loaded from the current classpath using `CandidateComponentsIndexLoader`. The core framework uses such infrastructure in two areas: to retrieve the candidate `@Component`s and to build a default `PersistenceUnitInfo`. Rather than scanning the classpath and using ASM to identify candidates, the index is used if present. As long as the include filters refer to an annotation that is directly annotated with `@Indexed` or an assignable type that is directly annotated with `@Indexed`, the index can be used since a dedicated entry wil be present for that type. If any other unsupported include filter is specified, we fallback on classpath scanning. In case the index is incomplete or cannot be used, The `spring.index.ignore` system property can be set to `true` or, alternatively, in a "spring.properties" at the root of the classpath. Issue: SPR-11890
This commit is contained in:
@@ -0,0 +1,111 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.ArrayList;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.AbstractProcessor;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.annotation.processing.SupportedAnnotationTypes;
|
||||
import javax.annotation.processing.SupportedSourceVersion;
|
||||
import javax.lang.model.SourceVersion;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import org.springframework.context.index.metadata.CandidateComponentsMetadata;
|
||||
import org.springframework.context.index.metadata.ItemMetadata;
|
||||
|
||||
/**
|
||||
* Annotation {@link Processor} that writes {@link CandidateComponentsMetadata}
|
||||
* file for spring components.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 5.0
|
||||
*/
|
||||
@SupportedAnnotationTypes({"*"})
|
||||
@SupportedSourceVersion(SourceVersion.RELEASE_8)
|
||||
public class CandidateComponentsIndexer extends AbstractProcessor {
|
||||
|
||||
private MetadataStore metadataStore;
|
||||
|
||||
private MetadataCollector metadataCollector;
|
||||
|
||||
private TypeUtils typeUtils;
|
||||
|
||||
private List<StereotypesProvider> stereotypesProviders;
|
||||
|
||||
@Override
|
||||
public synchronized void init(ProcessingEnvironment env) {
|
||||
this.stereotypesProviders = getStereotypesProviders(env);
|
||||
this.typeUtils = new TypeUtils(env);
|
||||
this.metadataStore = new MetadataStore(env);
|
||||
this.metadataCollector = new MetadataCollector(env,
|
||||
this.metadataStore.readMetadata());
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
|
||||
this.metadataCollector.processing(roundEnv);
|
||||
roundEnv.getRootElements().forEach(this::processElement);
|
||||
|
||||
if (roundEnv.processingOver()) {
|
||||
writeMetaData();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
protected List<StereotypesProvider> getStereotypesProviders(ProcessingEnvironment env) {
|
||||
List<StereotypesProvider> result = new ArrayList<>();
|
||||
TypeUtils typeUtils = new TypeUtils(env);
|
||||
result.add(new IndexedStereotypesProvider(typeUtils));
|
||||
result.add(new StandardStereotypesProvider(typeUtils));
|
||||
result.add(new PackageInfoStereotypesProvider());
|
||||
return result;
|
||||
}
|
||||
|
||||
private void processElement(Element element) {
|
||||
Set<String> stereotypes = new LinkedHashSet<>();
|
||||
this.stereotypesProviders.forEach(p -> {
|
||||
stereotypes.addAll(p.getStereotypes(element));
|
||||
|
||||
});
|
||||
if (!stereotypes.isEmpty()) {
|
||||
this.metadataCollector.add(new ItemMetadata(
|
||||
this.typeUtils.getType(element), stereotypes));
|
||||
}
|
||||
}
|
||||
|
||||
protected CandidateComponentsMetadata writeMetaData() {
|
||||
CandidateComponentsMetadata metadata = this.metadataCollector.getMetadata();
|
||||
if (!metadata.getItems().isEmpty()) {
|
||||
try {
|
||||
this.metadataStore.writeMetadata(metadata);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
throw new IllegalStateException("Failed to write metadata", ex);
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
|
||||
/**
|
||||
* A {@link StereotypesProvider} implementation that extracts the stereotypes
|
||||
* flagged by the {@value INDEXED_ANNOTATION} annotation. This implementation
|
||||
* honors stereotypes defined this way on meta-annotations.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class IndexedStereotypesProvider implements StereotypesProvider {
|
||||
|
||||
private static final String INDEXED_ANNOTATION = "org.springframework.stereotype.Indexed";
|
||||
|
||||
private final TypeUtils typeUtils;
|
||||
|
||||
public IndexedStereotypesProvider(TypeUtils typeUtils) {
|
||||
this.typeUtils = typeUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getStereotypes(Element element) {
|
||||
Set<String> stereotypes = new LinkedHashSet<>();
|
||||
ElementKind kind = element.getKind();
|
||||
if (kind != ElementKind.CLASS && kind != ElementKind.INTERFACE) {
|
||||
return stereotypes;
|
||||
}
|
||||
Set<Element> seen = new HashSet<>();
|
||||
collectStereotypesOnAnnotations(seen, stereotypes, element);
|
||||
seen = new HashSet<>();
|
||||
collectStereotypesOnTypes(seen, stereotypes, element);
|
||||
return stereotypes;
|
||||
}
|
||||
|
||||
private void collectStereotypesOnAnnotations(Set<Element> seen, Set<String> stereotypes,
|
||||
Element element) {
|
||||
for (AnnotationMirror annotation : this.typeUtils.getAllAnnotationMirrors(element)) {
|
||||
Element next = collectStereotypes(seen, stereotypes, element, annotation);
|
||||
if (next != null) {
|
||||
collectStereotypesOnAnnotations(seen, stereotypes, next);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
private void collectStereotypesOnTypes(Set<Element> seen, Set<String> stereotypes,
|
||||
Element type) {
|
||||
if (!seen.contains(type)) {
|
||||
seen.add(type);
|
||||
if (isAnnotatedWithIndexed(type)) {
|
||||
stereotypes.add(this.typeUtils.getType(type));
|
||||
}
|
||||
Element superClass = this.typeUtils.getSuperClass(type);
|
||||
if (superClass != null) {
|
||||
collectStereotypesOnTypes(seen, stereotypes, superClass);
|
||||
}
|
||||
this.typeUtils.getDirectInterfaces(type).forEach(
|
||||
i -> collectStereotypesOnTypes(seen, stereotypes, i));
|
||||
}
|
||||
}
|
||||
|
||||
private Element collectStereotypes(Set<Element> seen, Set<String> stereotypes,
|
||||
Element element, AnnotationMirror annotation) {
|
||||
if (isIndexedAnnotation(annotation)) {
|
||||
stereotypes.add(this.typeUtils.getType(element));
|
||||
}
|
||||
return getCandidateAnnotationElement(seen, annotation);
|
||||
}
|
||||
|
||||
private Element getCandidateAnnotationElement(Set<Element> seen, AnnotationMirror annotation) {
|
||||
Element element = annotation.getAnnotationType().asElement();
|
||||
if (seen.contains(element)) {
|
||||
return null;
|
||||
}
|
||||
// We need to visit all indexed annotations.
|
||||
if (!isIndexedAnnotation(annotation)) {
|
||||
seen.add(element);
|
||||
}
|
||||
return (!element.toString().startsWith("java.lang") ? element : null);
|
||||
}
|
||||
|
||||
private boolean isAnnotatedWithIndexed(Element type) {
|
||||
for (AnnotationMirror annotation : type.getAnnotationMirrors()) {
|
||||
if (isIndexedAnnotation(annotation)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
private boolean isIndexedAnnotation(AnnotationMirror annotation) {
|
||||
return INDEXED_ANNOTATION.equals(annotation.getAnnotationType().toString());
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,107 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Set;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.annotation.processing.RoundEnvironment;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
|
||||
import org.springframework.context.index.metadata.ItemMetadata;
|
||||
import org.springframework.context.index.metadata.CandidateComponentsMetadata;
|
||||
|
||||
/**
|
||||
* Used by {@link CandidateComponentsIndexer} to collect {@link CandidateComponentsMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class MetadataCollector {
|
||||
|
||||
private final List<ItemMetadata> metadataItems = new ArrayList<ItemMetadata>();
|
||||
|
||||
private final ProcessingEnvironment processingEnvironment;
|
||||
|
||||
private final CandidateComponentsMetadata previousMetadata;
|
||||
|
||||
private final TypeUtils typeUtils;
|
||||
|
||||
private final Set<String> processedSourceTypes = new HashSet<String>();
|
||||
|
||||
/**
|
||||
* Creates a new {@code MetadataProcessor} instance.
|
||||
* @param processingEnvironment The processing environment of the build
|
||||
* @param previousMetadata Any previous metadata or {@code null}
|
||||
*/
|
||||
public MetadataCollector(ProcessingEnvironment processingEnvironment,
|
||||
CandidateComponentsMetadata previousMetadata) {
|
||||
this.processingEnvironment = processingEnvironment;
|
||||
this.previousMetadata = previousMetadata;
|
||||
this.typeUtils = new TypeUtils(processingEnvironment);
|
||||
}
|
||||
|
||||
public void processing(RoundEnvironment roundEnv) {
|
||||
for (Element element : roundEnv.getRootElements()) {
|
||||
markAsProcessed(element);
|
||||
}
|
||||
}
|
||||
|
||||
private void markAsProcessed(Element element) {
|
||||
if (element instanceof TypeElement) {
|
||||
this.processedSourceTypes.add(this.typeUtils.getType(element));
|
||||
}
|
||||
}
|
||||
|
||||
public void add(ItemMetadata metadata) {
|
||||
this.metadataItems.add(metadata);
|
||||
}
|
||||
|
||||
public CandidateComponentsMetadata getMetadata() {
|
||||
CandidateComponentsMetadata metadata = new CandidateComponentsMetadata();
|
||||
for (ItemMetadata item : this.metadataItems) {
|
||||
metadata.add(item);
|
||||
}
|
||||
if (this.previousMetadata != null) {
|
||||
List<ItemMetadata> items = this.previousMetadata.getItems();
|
||||
for (ItemMetadata item : items) {
|
||||
if (shouldBeMerged(item)) {
|
||||
metadata.add(item);
|
||||
}
|
||||
}
|
||||
}
|
||||
return metadata;
|
||||
}
|
||||
|
||||
private boolean shouldBeMerged(ItemMetadata itemMetadata) {
|
||||
String sourceType = itemMetadata.getType();
|
||||
return (sourceType != null && !deletedInCurrentBuild(sourceType)
|
||||
&& !processedInCurrentBuild(sourceType));
|
||||
}
|
||||
|
||||
private boolean deletedInCurrentBuild(String sourceType) {
|
||||
return this.processingEnvironment.getElementUtils()
|
||||
.getTypeElement(sourceType) == null;
|
||||
}
|
||||
|
||||
private boolean processedInCurrentBuild(String sourceType) {
|
||||
return this.processedSourceTypes.contains(sourceType);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,83 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.tools.FileObject;
|
||||
import javax.tools.StandardLocation;
|
||||
|
||||
import org.springframework.context.index.metadata.PropertiesMarshaller;
|
||||
import org.springframework.context.index.metadata.CandidateComponentsMetadata;
|
||||
|
||||
/**
|
||||
* Store {@link CandidateComponentsMetadata} on the filesystem.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class MetadataStore {
|
||||
|
||||
static final String METADATA_PATH = "META-INF/spring.components";
|
||||
|
||||
private final ProcessingEnvironment environment;
|
||||
|
||||
public MetadataStore(ProcessingEnvironment environment) {
|
||||
this.environment = environment;
|
||||
}
|
||||
|
||||
public CandidateComponentsMetadata readMetadata() {
|
||||
try {
|
||||
return readMetadata(getMetadataResource().openInputStream());
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public void writeMetadata(CandidateComponentsMetadata metadata) throws IOException {
|
||||
if (!metadata.getItems().isEmpty()) {
|
||||
try (OutputStream outputStream = createMetadataResource().openOutputStream()) {
|
||||
new PropertiesMarshaller().write(metadata, outputStream);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private CandidateComponentsMetadata readMetadata(InputStream in) throws IOException {
|
||||
try {
|
||||
return new PropertiesMarshaller().read(in);
|
||||
}
|
||||
catch (IOException ex) {
|
||||
return null;
|
||||
}
|
||||
finally {
|
||||
in.close();
|
||||
}
|
||||
}
|
||||
|
||||
private FileObject getMetadataResource() throws IOException {
|
||||
return this.environment.getFiler()
|
||||
.getResource(StandardLocation.CLASS_OUTPUT, "", METADATA_PATH);
|
||||
}
|
||||
|
||||
private FileObject createMetadataResource() throws IOException {
|
||||
return this.environment.getFiler()
|
||||
.createResource(StandardLocation.CLASS_OUTPUT, "", METADATA_PATH);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,43 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
|
||||
/**
|
||||
* A {@link StereotypesProvider} implementation that provides the
|
||||
* {@value STEREOTYPE} stereotype for each package-info.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class PackageInfoStereotypesProvider implements StereotypesProvider {
|
||||
|
||||
public static final String STEREOTYPE = "package-info";
|
||||
|
||||
@Override
|
||||
public Set<String> getStereotypes(Element element) {
|
||||
Set<String> stereotypes = new HashSet<>();
|
||||
if (element.getKind() == ElementKind.PACKAGE) {
|
||||
stereotypes.add(STEREOTYPE);
|
||||
}
|
||||
return stereotypes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,55 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.ElementKind;
|
||||
|
||||
/**
|
||||
* A {@link StereotypesProvider} that extract a stereotype for each
|
||||
* {@code javax.*} annotation placed on a class or interface.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class StandardStereotypesProvider implements StereotypesProvider {
|
||||
|
||||
private final TypeUtils typeUtils;
|
||||
|
||||
StandardStereotypesProvider(TypeUtils typeUtils) {
|
||||
this.typeUtils = typeUtils;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Set<String> getStereotypes(Element element) {
|
||||
Set<String> stereotypes = new LinkedHashSet<>();
|
||||
ElementKind kind = element.getKind();
|
||||
if (kind != ElementKind.CLASS && kind != ElementKind.INTERFACE) {
|
||||
return stereotypes;
|
||||
}
|
||||
for (AnnotationMirror annotation : this.typeUtils.getAllAnnotationMirrors(element)) {
|
||||
String type = this.typeUtils.getType(annotation);
|
||||
if (type.startsWith("javax.")) {
|
||||
stereotypes.add(type);
|
||||
}
|
||||
}
|
||||
return stereotypes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,38 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.util.Set;
|
||||
import javax.lang.model.element.Element;
|
||||
|
||||
/**
|
||||
* Provide the list of stereotypes that match an {@link Element}. If an element
|
||||
* has one more stereotypes, it is referenced in the index of candidate
|
||||
* components and each stereotype can be queried individually.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
interface StereotypesProvider {
|
||||
|
||||
/**
|
||||
* Return the stereotypes that are present on the given {@link Element}.
|
||||
* @param element the element to handle
|
||||
* @return the stereotypes or an empty set if none were found
|
||||
*/
|
||||
Set<String> getStereotypes(Element element);
|
||||
|
||||
}
|
||||
@@ -0,0 +1,113 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import javax.annotation.processing.ProcessingEnvironment;
|
||||
import javax.lang.model.element.AnnotationMirror;
|
||||
import javax.lang.model.element.Element;
|
||||
import javax.lang.model.element.QualifiedNameable;
|
||||
import javax.lang.model.element.TypeElement;
|
||||
import javax.lang.model.type.DeclaredType;
|
||||
import javax.lang.model.type.TypeMirror;
|
||||
import javax.lang.model.util.Types;
|
||||
|
||||
/**
|
||||
* Type utilities.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
class TypeUtils {
|
||||
|
||||
private final ProcessingEnvironment env;
|
||||
|
||||
private final Types types;
|
||||
|
||||
TypeUtils(ProcessingEnvironment env) {
|
||||
this.env = env;
|
||||
this.types = env.getTypeUtils();
|
||||
}
|
||||
|
||||
public String getType(Element element) {
|
||||
return getType(element != null ? element.asType() : null);
|
||||
}
|
||||
|
||||
public String getType(AnnotationMirror annotation) {
|
||||
return getType(annotation != null ? annotation.getAnnotationType() : null);
|
||||
}
|
||||
|
||||
public String getType(TypeMirror type) {
|
||||
if (type == null) {
|
||||
return null;
|
||||
}
|
||||
if (type instanceof DeclaredType) {
|
||||
DeclaredType declaredType = (DeclaredType) type;
|
||||
Element enclosingElement = declaredType.asElement().getEnclosingElement();
|
||||
if (enclosingElement != null && enclosingElement instanceof TypeElement) {
|
||||
return getQualifiedName(enclosingElement) + "$"
|
||||
+ declaredType.asElement().getSimpleName().toString();
|
||||
} else {
|
||||
return getQualifiedName(declaredType.asElement());
|
||||
}
|
||||
}
|
||||
return type.toString();
|
||||
}
|
||||
|
||||
private String getQualifiedName(Element element) {
|
||||
if (element instanceof QualifiedNameable) {
|
||||
return ((QualifiedNameable) element).getQualifiedName().toString();
|
||||
}
|
||||
return element.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the super class of the specified {@link Element} or null if this
|
||||
* {@code element} represents {@link Object}.
|
||||
*/
|
||||
public Element getSuperClass(Element element) {
|
||||
List<? extends TypeMirror> superTypes = this.types.directSupertypes(element.asType());
|
||||
if (superTypes.isEmpty()) {
|
||||
return null; // reached java.lang.Object
|
||||
}
|
||||
return this.types.asElement(superTypes.get(0));
|
||||
}
|
||||
|
||||
/**
|
||||
* Return the interfaces that are <strong>directly</strong> implemented by the
|
||||
* specified {@link Element} or an empty list if this {@code element} does not
|
||||
* implement any interface.
|
||||
*/
|
||||
public List<Element> getDirectInterfaces(Element element) {
|
||||
List<? extends TypeMirror> superTypes = this.types.directSupertypes(element.asType());
|
||||
List<Element> directInterfaces = new ArrayList<>();
|
||||
if (superTypes.size() > 1) { // index 0 is the super class
|
||||
for (int i = 1; i < superTypes.size(); i++) {
|
||||
Element e = this.types.asElement(superTypes.get(i));
|
||||
if (e != null) {
|
||||
directInterfaces.add(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
return directInterfaces;
|
||||
}
|
||||
|
||||
public List<? extends AnnotationMirror> getAllAnnotationMirrors(Element e) {
|
||||
return this.env.getElementUtils().getAllAnnotationMirrors(e);
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.metadata;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Meta-data for candidate components.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 5.0
|
||||
*/
|
||||
public class CandidateComponentsMetadata {
|
||||
|
||||
private final List<ItemMetadata> items;
|
||||
|
||||
public CandidateComponentsMetadata() {
|
||||
this.items = new ArrayList<>();
|
||||
}
|
||||
|
||||
public void add(ItemMetadata item) {
|
||||
this.items.add(item);
|
||||
}
|
||||
|
||||
public List<ItemMetadata> getItems() {
|
||||
return Collections.unmodifiableList(this.items);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "CandidateComponentsMetadata{" + "items=" + this.items + '}';
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,50 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.metadata;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Represents one entry in the index. The type defines the identify of the target
|
||||
* candidate (usually fully qualified name) and the stereotypes are "markers" that can
|
||||
* be used to retrieve the candidates. A typical use case is the presence of a given
|
||||
* annotation on the candidate.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 5.0
|
||||
*/
|
||||
public class ItemMetadata {
|
||||
|
||||
private final String type;
|
||||
|
||||
private final Set<String> stereotypes;
|
||||
|
||||
public ItemMetadata(String type, Set<String> stereotypes) {
|
||||
this.type = type;
|
||||
this.stereotypes = new HashSet<>(stereotypes);
|
||||
}
|
||||
|
||||
public String getType() {
|
||||
return this.type;
|
||||
}
|
||||
|
||||
public Set<String> getStereotypes() {
|
||||
return this.stereotypes;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,56 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.metadata;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.InputStream;
|
||||
import java.io.OutputStream;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
import java.util.Map;
|
||||
import java.util.Properties;
|
||||
import java.util.Set;
|
||||
|
||||
/**
|
||||
* Marshaller to write {@link CandidateComponentsMetadata} as properties.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
* @since 5.0
|
||||
*/
|
||||
public class PropertiesMarshaller {
|
||||
|
||||
public void write(CandidateComponentsMetadata metadata, OutputStream out)
|
||||
throws IOException {
|
||||
|
||||
Properties props = new Properties();
|
||||
metadata.getItems().forEach(m -> props.put(m.getType(), String.join(",", m.getStereotypes())));
|
||||
props.store(out, "");
|
||||
}
|
||||
|
||||
public CandidateComponentsMetadata read(InputStream in) throws IOException {
|
||||
CandidateComponentsMetadata result = new CandidateComponentsMetadata();
|
||||
Properties props = new Properties();
|
||||
props.load(in);
|
||||
for (Map.Entry<Object, Object> entry : props.entrySet()) {
|
||||
String type = (String) entry.getKey();
|
||||
Set<String> candidates = new HashSet<>(Arrays.asList(((String) entry.getValue()).split(",")));
|
||||
result.add(new ItemMetadata(type, candidates));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,20 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Support package for defining and storing the metadata that forms the index.
|
||||
*/
|
||||
package org.springframework.context.index.metadata;
|
||||
@@ -0,0 +1,21 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Generates a 'META-INF/spring.candidates' at compilation time with all
|
||||
* the component candidates detected in the module.
|
||||
*/
|
||||
package org.springframework.context.index;
|
||||
@@ -0,0 +1 @@
|
||||
org.springframework.context.index.CandidateComponentsIndexer
|
||||
@@ -0,0 +1,246 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.FileInputStream;
|
||||
import java.io.IOException;
|
||||
import javax.annotation.ManagedBean;
|
||||
import javax.inject.Named;
|
||||
import javax.persistence.Converter;
|
||||
import javax.persistence.Embeddable;
|
||||
import javax.persistence.Entity;
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Rule;
|
||||
import org.junit.Test;
|
||||
import org.junit.rules.ExpectedException;
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
import org.springframework.context.index.metadata.CandidateComponentsMetadata;
|
||||
import org.springframework.context.index.metadata.PropertiesMarshaller;
|
||||
import org.springframework.context.index.sample.AbstractController;
|
||||
import org.springframework.context.index.sample.MetaControllerIndexed;
|
||||
import org.springframework.context.index.sample.SampleComponent;
|
||||
import org.springframework.context.index.sample.SampleController;
|
||||
import org.springframework.context.index.sample.SampleMetaController;
|
||||
import org.springframework.context.index.sample.SampleMetaIndexedController;
|
||||
import org.springframework.context.index.sample.SampleNone;
|
||||
import org.springframework.context.index.sample.SampleRepository;
|
||||
import org.springframework.context.index.sample.SampleService;
|
||||
import org.springframework.context.index.sample.cdi.SampleManagedBean;
|
||||
import org.springframework.context.index.sample.cdi.SampleNamed;
|
||||
import org.springframework.context.index.sample.jpa.SampleConverter;
|
||||
import org.springframework.context.index.sample.jpa.SampleEmbeddable;
|
||||
import org.springframework.context.index.sample.jpa.SampleEntity;
|
||||
import org.springframework.context.index.sample.jpa.SampleMappedSuperClass;
|
||||
import org.springframework.context.index.sample.type.SampleRepo;
|
||||
import org.springframework.context.index.sample.type.SampleSmartRepo;
|
||||
import org.springframework.context.index.sample.type.SampleSpecializedRepo;
|
||||
import org.springframework.context.index.sample.type.Repo;
|
||||
import org.springframework.context.index.sample.type.SmartRepo;
|
||||
import org.springframework.context.index.sample.type.SpecializedRepo;
|
||||
import org.springframework.context.index.test.TestCompiler;
|
||||
import org.springframework.stereotype.Component;
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.context.index.test.Metadata.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link CandidateComponentsIndexer}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class CandidateComponentsIndexerTests {
|
||||
|
||||
@Rule
|
||||
public TemporaryFolder temporaryFolder = new TemporaryFolder();
|
||||
|
||||
@Rule
|
||||
public ExpectedException thrown = ExpectedException.none();
|
||||
|
||||
private TestCompiler compiler;
|
||||
|
||||
@Before
|
||||
public void createCompiler() throws IOException {
|
||||
this.compiler = new TestCompiler(this.temporaryFolder);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noCandidate() throws IOException {
|
||||
CandidateComponentsMetadata metadata = compile(SampleNone.class);
|
||||
assertThat(metadata.getItems(), hasSize(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void noAnnotation() throws IOException {
|
||||
CandidateComponentsMetadata metadata = compile(CandidateComponentsIndexerTests.class);
|
||||
assertThat(metadata.getItems(), hasSize(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stereotypeComponent() throws IOException {
|
||||
testComponent(SampleComponent.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stereotypeService() throws IOException {
|
||||
testComponent(SampleService.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stereotypeController() throws IOException {
|
||||
testComponent(SampleController.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stereotypeControllerMetaAnnotation() throws IOException {
|
||||
testComponent(SampleMetaController.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stereotypeRepository() throws IOException {
|
||||
testSingleComponent(SampleRepository.class, Component.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stereotypeControllerMetaIndex() throws IOException {
|
||||
testSingleComponent(SampleMetaIndexedController.class,
|
||||
Component.class, MetaControllerIndexed.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void stereotypeOnAbstractClass() throws IOException {
|
||||
testComponent(AbstractController.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cdiManagedBean() throws IOException {
|
||||
testSingleComponent(SampleManagedBean.class, ManagedBean.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void cdiNamed() throws IOException {
|
||||
testSingleComponent(SampleNamed.class, Named.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistenceEntity() throws IOException {
|
||||
testSingleComponent(SampleEntity.class, Entity.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistenceMappedSuperClass() throws IOException {
|
||||
testSingleComponent(SampleMappedSuperClass.class, MappedSuperclass.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistenceEmbeddable() throws IOException {
|
||||
testSingleComponent(SampleEmbeddable.class, Embeddable.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void persistenceConverter() throws IOException {
|
||||
testSingleComponent(SampleConverter.class, Converter.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void packageInfo() throws IOException {
|
||||
CandidateComponentsMetadata metadata = compile(
|
||||
"org/springframework/context/index/sample/jpa/package-info");
|
||||
assertThat(metadata, hasComponent(
|
||||
"org.springframework.context.index.sample.jpa", "package-info"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeStereotypeFromMetaInterface() throws IOException {
|
||||
testSingleComponent(SampleSpecializedRepo.class, Repo.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeStereotypeFromInterfaceFromSuperClass() throws IOException {
|
||||
testSingleComponent(SampleRepo.class, Repo.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeStereotypeFromSeveralInterfaces() throws IOException {
|
||||
testSingleComponent(SampleSmartRepo.class, Repo.class, SmartRepo.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeStereotypeOnInterface() throws IOException {
|
||||
testSingleComponent(SpecializedRepo.class, Repo.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeStereotypeOnInterfaceFromSeveralInterfaces() throws IOException {
|
||||
testSingleComponent(SmartRepo.class, Repo.class, SmartRepo.class);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void typeStereotypeOnIndexedInterface() throws IOException {
|
||||
testSingleComponent(Repo.class, Repo.class);
|
||||
}
|
||||
|
||||
|
||||
private void testComponent(Class<?>... classes) throws IOException {
|
||||
CandidateComponentsMetadata metadata = compile(classes);
|
||||
for (Class<?> c : classes) {
|
||||
assertThat(metadata, hasComponent(c, Component.class));
|
||||
}
|
||||
assertThat(metadata.getItems(), hasSize(classes.length));
|
||||
}
|
||||
|
||||
private void testSingleComponent(Class<?> target, Class<?>... stereotypes) throws IOException {
|
||||
CandidateComponentsMetadata metadata = compile(target);
|
||||
assertThat(metadata, hasComponent(target, stereotypes));
|
||||
assertThat(metadata.getItems(), hasSize(1));
|
||||
}
|
||||
|
||||
private CandidateComponentsMetadata compile(Class<?>... types) throws IOException {
|
||||
CandidateComponentsIndexer processor = new CandidateComponentsIndexer();
|
||||
this.compiler.getTask(types).call(processor);
|
||||
return readGeneratedMetadata(this.compiler.getOutputLocation());
|
||||
}
|
||||
|
||||
private CandidateComponentsMetadata compile(String... types) throws IOException {
|
||||
CandidateComponentsIndexer processor = new CandidateComponentsIndexer();
|
||||
this.compiler.getTask(types).call(processor);
|
||||
return readGeneratedMetadata(this.compiler.getOutputLocation());
|
||||
}
|
||||
|
||||
private CandidateComponentsMetadata readGeneratedMetadata(File outputLocation) {
|
||||
try {
|
||||
File metadataFile = new File(outputLocation,
|
||||
MetadataStore.METADATA_PATH);
|
||||
if (metadataFile.isFile()) {
|
||||
return new PropertiesMarshaller()
|
||||
.read(new FileInputStream(metadataFile));
|
||||
}
|
||||
else {
|
||||
return new CandidateComponentsMetadata();
|
||||
}
|
||||
}
|
||||
catch (IOException e) {
|
||||
throw new RuntimeException("Failed to read metadata from disk", e);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,58 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.metadata;
|
||||
|
||||
import java.io.ByteArrayInputStream;
|
||||
import java.io.ByteArrayOutputStream;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.HashSet;
|
||||
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.hamcrest.Matchers.*;
|
||||
import static org.junit.Assert.*;
|
||||
import static org.springframework.context.index.test.Metadata.*;
|
||||
|
||||
/**
|
||||
* Tests for {@link PropertiesMarshaller}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class PropertiesMarshallerTests {
|
||||
|
||||
@Test
|
||||
public void readWrite() throws IOException {
|
||||
CandidateComponentsMetadata metadata = new CandidateComponentsMetadata();
|
||||
metadata.add(createItem("com.foo", "first", "second"));
|
||||
metadata.add(createItem("com.bar", "first"));
|
||||
|
||||
ByteArrayOutputStream outputStream = new ByteArrayOutputStream();
|
||||
PropertiesMarshaller marshaller = new PropertiesMarshaller();
|
||||
marshaller.write(metadata, outputStream);
|
||||
CandidateComponentsMetadata readMetadata = marshaller.read(
|
||||
new ByteArrayInputStream(outputStream.toByteArray()));
|
||||
assertThat(readMetadata, hasComponent("com.foo", "first", "second"));
|
||||
assertThat(readMetadata, hasComponent("com.bar", "first"));
|
||||
assertThat(readMetadata.getItems(), hasSize(2));
|
||||
}
|
||||
|
||||
private static ItemMetadata createItem(String type, String... stereotypes) {
|
||||
return new ItemMetadata(type, new HashSet<>(Arrays.asList(stereotypes)));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Abstract {@link Component} that shouldn't be registered.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Component
|
||||
public abstract class AbstractController {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
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 org.springframework.stereotype.Controller;
|
||||
|
||||
/**
|
||||
* Sample meta-annotation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Controller
|
||||
public @interface MetaController {
|
||||
}
|
||||
@@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
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 org.springframework.stereotype.Controller;
|
||||
import org.springframework.stereotype.Indexed;
|
||||
|
||||
/**
|
||||
* A test annotation that triggers a dedicated entry in the index.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Target(ElementType.TYPE)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Documented
|
||||
@Controller
|
||||
@Indexed
|
||||
public @interface MetaControllerIndexed {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
import org.springframework.stereotype.Component;
|
||||
|
||||
/**
|
||||
* Test candidate for {@link Component}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Component
|
||||
public class SampleComponent {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
import org.springframework.stereotype.Controller;
|
||||
|
||||
/**
|
||||
* Test candidate for {@link Controller}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Controller
|
||||
public class SampleController {
|
||||
}
|
||||
@@ -0,0 +1,26 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
/**
|
||||
* Test candidate for a {@code Controller} defined using a meta-annotation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@MetaController
|
||||
public class SampleMetaController {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
/**
|
||||
* Test candidate for a {@code Controller} that adds both the
|
||||
* {@code Component} and {@code MetaControllerIndexed} stereotypes.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@MetaControllerIndexed
|
||||
public class SampleMetaIndexedController {
|
||||
}
|
||||
@@ -0,0 +1,30 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
import org.springframework.beans.factory.annotation.Qualifier;
|
||||
import org.springframework.core.type.Scope;
|
||||
|
||||
/**
|
||||
* Candidate with no matching annotation.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Scope("None")
|
||||
@Qualifier("None")
|
||||
public class SampleNone {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
import org.springframework.stereotype.Repository;
|
||||
|
||||
/**
|
||||
* Test candidate for {@link Repository}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Repository
|
||||
public class SampleRepository {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample;
|
||||
|
||||
import org.springframework.stereotype.Service;
|
||||
|
||||
/**
|
||||
* Test candidate for {@link Service}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Service
|
||||
public class SampleService {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.cdi;
|
||||
|
||||
import javax.annotation.ManagedBean;
|
||||
|
||||
/**
|
||||
* Test candidate for a CDI {@link ManagedBean}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@ManagedBean
|
||||
public class SampleManagedBean {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.cdi;
|
||||
|
||||
import javax.inject.Named;
|
||||
|
||||
/**
|
||||
* Test candidate for a CDI {@link Named} bean.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Named
|
||||
public class SampleNamed {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.jpa;
|
||||
|
||||
import javax.persistence.Converter;
|
||||
|
||||
/**
|
||||
* Test candidate for {@link Converter}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Converter
|
||||
public class SampleConverter {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.jpa;
|
||||
|
||||
import javax.persistence.Embeddable;
|
||||
|
||||
/**
|
||||
* Test candidate for {@link Embeddable}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Embeddable
|
||||
public class SampleEmbeddable {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.jpa;
|
||||
|
||||
import javax.persistence.Entity;
|
||||
|
||||
/**
|
||||
* Test candidate for {@link Entity}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Entity
|
||||
public class SampleEntity {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.jpa;
|
||||
|
||||
import javax.persistence.MappedSuperclass;
|
||||
|
||||
/**
|
||||
* Test candidate for {@link MappedSuperclass}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@MappedSuperclass
|
||||
public abstract class SampleMappedSuperClass {
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Test candidate for {@code package-info}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
package org.springframework.context.index.sample.jpa;
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.type;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public abstract class AbstractRepo<T, I> implements Repo<T, I> {
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.type;
|
||||
|
||||
import org.springframework.stereotype.Indexed;
|
||||
|
||||
/**
|
||||
* A sample interface flagged with {@link Indexed} to indicate that a stereotype
|
||||
* for all implementations should be added to the index.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Indexed
|
||||
public interface Repo<T, I> {
|
||||
}
|
||||
@@ -0,0 +1,23 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.type;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class SampleEntity {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.type;
|
||||
|
||||
/**
|
||||
* A sample that gets its stereotype via an abstract class.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class SampleRepo extends AbstractRepo<SampleEntity, Long> {
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.type;
|
||||
|
||||
/**
|
||||
* A sample that implements both interface used to demonstrate that no
|
||||
* duplicate stereotypes are generated.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class SampleSmartRepo
|
||||
implements SmartRepo<SampleEntity, Long>, Repo<SampleEntity, Long> {
|
||||
}
|
||||
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.type;
|
||||
|
||||
/**
|
||||
* A sample that does not directly implement the {@link Repo} interface.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class SampleSpecializedRepo implements SpecializedRepo<SampleEntity> {
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.type;
|
||||
|
||||
import org.springframework.stereotype.Indexed;
|
||||
|
||||
/**
|
||||
* A {@link Repo} that requires an extra stereotype.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
@Indexed
|
||||
public interface SmartRepo<T, I> extends Repo<T, I> {
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.sample.type;
|
||||
|
||||
/**
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public interface SpecializedRepo<T> extends Repo<T, Long> {
|
||||
|
||||
}
|
||||
@@ -0,0 +1,105 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.test;
|
||||
|
||||
import java.util.Arrays;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
import org.hamcrest.BaseMatcher;
|
||||
import org.hamcrest.Description;
|
||||
|
||||
import org.springframework.context.index.metadata.ItemMetadata;
|
||||
import org.springframework.context.index.metadata.CandidateComponentsMetadata;
|
||||
|
||||
/**
|
||||
* Hamcrest {@link org.hamcrest.Matcher Matcher} to help test {@link CandidateComponentsMetadata}.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class Metadata {
|
||||
|
||||
public static ItemMetadataMatcher hasComponent(Class<?> type, Class<?>... stereotypes) {
|
||||
return new ItemMetadataMatcher(type.getName(), stereotypes);
|
||||
}
|
||||
|
||||
public static ItemMetadataMatcher hasComponent(String type, String... stereotypes) {
|
||||
return new ItemMetadataMatcher(type, stereotypes);
|
||||
}
|
||||
|
||||
private static class ItemMetadataMatcher extends BaseMatcher<CandidateComponentsMetadata> {
|
||||
|
||||
private final String type;
|
||||
|
||||
private final List<String> stereotypes;
|
||||
|
||||
private ItemMetadataMatcher(String type, List<String> stereotypes) {
|
||||
this.type = type;
|
||||
this.stereotypes = stereotypes;
|
||||
}
|
||||
|
||||
public ItemMetadataMatcher(String type, String... stereotypes) {
|
||||
this(type, Arrays.asList(stereotypes));
|
||||
}
|
||||
|
||||
public ItemMetadataMatcher(String type, Class<?>... stereotypes) {
|
||||
this(type, Arrays.stream(stereotypes)
|
||||
.map(Class::getName).collect(Collectors.toList()));
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean matches(Object value) {
|
||||
if (!(value instanceof CandidateComponentsMetadata)) {
|
||||
return false;
|
||||
}
|
||||
ItemMetadata itemMetadata = getFirstItemWithType((CandidateComponentsMetadata) value, this.type);
|
||||
if (itemMetadata == null) {
|
||||
return false;
|
||||
}
|
||||
if (this.type != null && !this.type.equals(itemMetadata.getType())) {
|
||||
return false;
|
||||
}
|
||||
if (this.stereotypes != null) {
|
||||
for (String stereotype : this.stereotypes) {
|
||||
if (!itemMetadata.getStereotypes().contains(stereotype)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (this.stereotypes.size() != itemMetadata.getStereotypes().size()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
private ItemMetadata getFirstItemWithType(CandidateComponentsMetadata metadata, String type) {
|
||||
for (ItemMetadata item : metadata.getItems()) {
|
||||
if (item.getType().equals(type)) {
|
||||
return item;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void describeTo(Description description) {
|
||||
description.appendText("Candidates with type ").appendValue(this.type);
|
||||
description.appendText(" and stereotypes ").appendValue(this.stereotypes);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,124 @@
|
||||
/*
|
||||
* Copyright 2002-2016 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.context.index.test;
|
||||
|
||||
import java.io.File;
|
||||
import java.io.IOException;
|
||||
import java.util.Arrays;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
import javax.annotation.processing.Processor;
|
||||
import javax.tools.JavaCompiler;
|
||||
import javax.tools.JavaFileObject;
|
||||
import javax.tools.StandardJavaFileManager;
|
||||
import javax.tools.StandardLocation;
|
||||
import javax.tools.ToolProvider;
|
||||
|
||||
import org.junit.rules.TemporaryFolder;
|
||||
|
||||
/**
|
||||
* Wrapper to make the {@link JavaCompiler} easier to use in tests.
|
||||
*
|
||||
* @author Stephane Nicoll
|
||||
*/
|
||||
public class TestCompiler {
|
||||
|
||||
public static final File ORIGINAL_SOURCE_FOLDER = new File("src/test/java");
|
||||
|
||||
private final JavaCompiler compiler;
|
||||
|
||||
private final StandardJavaFileManager fileManager;
|
||||
|
||||
private final File outputLocation;
|
||||
|
||||
public TestCompiler(TemporaryFolder temporaryFolder) throws IOException {
|
||||
this(ToolProvider.getSystemJavaCompiler(), temporaryFolder);
|
||||
}
|
||||
|
||||
public TestCompiler(JavaCompiler compiler, TemporaryFolder temporaryFolder)
|
||||
throws IOException {
|
||||
this.compiler = compiler;
|
||||
this.fileManager = compiler.getStandardFileManager(null, null, null);
|
||||
this.outputLocation = temporaryFolder.newFolder();
|
||||
Iterable<? extends File> temp = Collections.singletonList(this.outputLocation);
|
||||
this.fileManager.setLocation(StandardLocation.CLASS_OUTPUT, temp);
|
||||
this.fileManager.setLocation(StandardLocation.SOURCE_OUTPUT, temp);
|
||||
}
|
||||
|
||||
public TestCompilationTask getTask(Class<?>... types) {
|
||||
List<String> names = Arrays.stream(types).map(Class::getName)
|
||||
.collect(Collectors.toList());
|
||||
return getTask(names.toArray(new String[names.size()]));
|
||||
}
|
||||
|
||||
public TestCompilationTask getTask(String... types) {
|
||||
Iterable<? extends JavaFileObject> javaFileObjects = getJavaFileObjects(types);
|
||||
return getTask(javaFileObjects);
|
||||
}
|
||||
|
||||
private TestCompilationTask getTask(
|
||||
Iterable<? extends JavaFileObject> javaFileObjects) {
|
||||
return new TestCompilationTask(this.compiler.getTask(null, this.fileManager, null,
|
||||
null, null, javaFileObjects));
|
||||
}
|
||||
|
||||
public File getOutputLocation() {
|
||||
return this.outputLocation;
|
||||
}
|
||||
|
||||
private Iterable<? extends JavaFileObject> getJavaFileObjects(String... types) {
|
||||
File[] files = new File[types.length];
|
||||
for (int i = 0; i < types.length; i++) {
|
||||
files[i] = getFile(types[i]);
|
||||
}
|
||||
return this.fileManager.getJavaFileObjects(files);
|
||||
}
|
||||
|
||||
private File getFile(String type) {
|
||||
return new File(getSourceFolder(), sourcePathFor(type));
|
||||
}
|
||||
|
||||
private static String sourcePathFor(String type) {
|
||||
return type.replace(".", "/") + ".java";
|
||||
}
|
||||
|
||||
private File getSourceFolder() {
|
||||
return ORIGINAL_SOURCE_FOLDER;
|
||||
}
|
||||
|
||||
/**
|
||||
* A compilation task.
|
||||
*/
|
||||
public static class TestCompilationTask {
|
||||
|
||||
private final JavaCompiler.CompilationTask task;
|
||||
|
||||
public TestCompilationTask(JavaCompiler.CompilationTask task) {
|
||||
this.task = task;
|
||||
}
|
||||
|
||||
public void call(Processor... processors) {
|
||||
this.task.setProcessors(Arrays.asList(processors));
|
||||
if (!this.task.call()) {
|
||||
throw new IllegalStateException("Compilation failed");
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
Reference in New Issue
Block a user