Skip to content

Commit

Permalink
Merge branch 'release/1.0.2'
Browse files Browse the repository at this point in the history
  • Loading branch information
vegegoku committed Jan 14, 2024
2 parents 9442664 + 4797257 commit 41adb78
Show file tree
Hide file tree
Showing 9 changed files with 210 additions and 84 deletions.
66 changes: 55 additions & 11 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,13 +42,22 @@ Or as s processor path in the compiler plugin :

```xml

<annotationProcessorPaths>
<path>
<groupId>org.dominokit</groupId>
<artifactId>domino-auto-processor</artifactId>
<version>[version]</version>
</path>
</annotationProcessorPaths>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<annotationProcessorPaths>
<!-- path to your annotation processor -->
<path>
<groupId>org.dominokit</groupId>
<artifactId>domino-auto-processor</artifactId>
<version>[version]</version>
</path>
</annotationProcessorPaths>
</configuration>
</plugin>

```

### Usage
Expand All @@ -59,12 +68,47 @@ for all services defined in the classpath.
The generated service loader class name will follow the convention `[Service name]_ServiceLoader`, and will provide a
single method `load` that returns a list of that service implementations.

The user can set up a lit of black-listed service to avoid generating service loaders for them, where
the `javax.annotation.processing.Processor` [The annotation processors] are always black-listed.
to add a new entry to the black list create a package-info class or any other class and annotate it with `DominoAuto` and specify the list of black-listed service classes.

The user needs to specify an include list of packages that will be included in the generation, the provided list
represent the package of the implemented service class not the implementations. the include list can be configured using a
compiler argument `dominoAutoInclude` or using `@DominoAuto` annotation on a type or package-info.
### Example

- Make sure the service package is added to the include parameter of the annotation :

```xml

<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-compiler-plugin</artifactId>
<version>3.8.1</version>
<configuration>
<annotationProcessorPaths>
<!-- path to your annotation processor -->
<path>
<groupId>org.dominokit</groupId>
<artifactId>domino-auto-processor</artifactId>
<version>[version]</version>
</path>
</annotationProcessorPaths>
<compilerArgs>
<arg>-AdominoAutoInclude=com.dominokit.samples</arg>
</compilerArgs>
</configuration>
</plugin>
```

> Using this method, make sure in case you are building from the ide to setup the compiler arguments in the ide or make
> the ide delegate the build to maven
or

```java
@DominoAuto(include = {"com.dominokit.samples"})
package com.dominokit.samples;

import org.dominokit.auto.DominoAuto;
```

Lets define the desired service interface

```java
Expand Down
2 changes: 1 addition & 1 deletion domino-auto-api/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.dominokit</groupId>
<artifactId>domino-auto</artifactId>
<version>1.0.1</version>
<version>1.0.2</version>
</parent>

<artifactId>domino-auto-api</artifactId>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -20,8 +20,10 @@
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Target(ElementType.TYPE)
@Target({ElementType.TYPE, ElementType.PACKAGE})
@Retention(RetentionPolicy.RUNTIME)
public @interface DominoAuto {
Class<?>[] blackList();
String[] exclude() default {};

String[] include() default {};
}
16 changes: 15 additions & 1 deletion domino-auto-processor/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
<parent>
<groupId>org.dominokit</groupId>
<artifactId>domino-auto</artifactId>
<version>1.0.1</version>
<version>1.0.2</version>
</parent>

<artifactId>domino-auto-processor</artifactId>
Expand All @@ -33,6 +33,12 @@
<artifactId>javapoet</artifactId>
<version>1.13.0</version>
</dependency>
<!-- https://mvnrepository.com/artifact/io.github.classgraph/classgraph -->
<dependency>
<groupId>io.github.classgraph</groupId>
<artifactId>classgraph</artifactId>
<version>4.8.165</version>
</dependency>

</dependencies>

Expand Down Expand Up @@ -80,6 +86,14 @@
<pattern>org.checkerframework</pattern>
<shadedPattern>dominoauto.shaded.org.checkerframework</shadedPattern>
</relocation>
<relocation>
<pattern>io.github.classgraph</pattern>
<shadedPattern>dominoauto.shaded.io.github.classgraph</shadedPattern>
</relocation>
<relocation>
<pattern>nonapi.io.github.classgraph</pattern>
<shadedPattern>dominoauto.shaded.nonapi.io.github.classgraph</shadedPattern>
</relocation>
</relocations>
<transformers>
<transformer
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,31 +15,35 @@
*/
package org.dominokit.auto;

import static java.util.Objects.nonNull;

import com.google.auto.service.AutoService;
import com.squareup.javapoet.ClassName;
import com.squareup.javapoet.CodeBlock;
import com.squareup.javapoet.JavaFile;
import com.squareup.javapoet.MethodSpec;
import com.squareup.javapoet.ParameterizedTypeName;
import com.squareup.javapoet.TypeSpec;
import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import io.github.classgraph.ClassGraph;
import io.github.classgraph.Resource;
import io.github.classgraph.ScanResult;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;
import java.util.stream.Stream;
import javax.annotation.processing.AbstractProcessor;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.annotation.processing.ProcessingEnvironment;
import javax.annotation.processing.Processor;
import javax.annotation.processing.RoundEnvironment;
import javax.lang.model.SourceVersion;
import javax.lang.model.element.Element;
import javax.lang.model.element.Modifier;
import javax.lang.model.element.TypeElement;
Expand All @@ -65,80 +69,87 @@ public Set<String> getSupportedAnnotationTypes() {
return new HashSet<>(Arrays.asList("*"));
}

@Override
public SourceVersion getSupportedSourceVersion() {
return SourceVersion.latestSupported();
}

@Override
public boolean process(Set<? extends TypeElement> annotations, RoundEnvironment roundEnv) {
try {
if (roundEnv.processingOver()) {

Set<? extends Element> dominoAutoElements =
roundEnv.getElementsAnnotatedWith(DominoAuto.class);

Set<String> blackList = getBlackListedServices(dominoAutoElements);

Map<String, Set<String>> services = new HashMap<>();

// Load META-INF/services entries from the classpath
ClassLoader loader = Thread.currentThread().getContextClassLoader();
Enumeration<URL> resources = loader.getResources("META-INF/services");

while (resources.hasMoreElements()) {
URL url = resources.nextElement();

try (InputStream is = url.openStream();
BufferedReader reader = new BufferedReader(new InputStreamReader(is))) {
String line;
while ((line = reader.readLine()) != null) {
if (!blackList.contains(line)) {
URL service = loader.getResource("META-INF/services/" + line);
assert service != null;
try (InputStream serviceStream = service.openStream();
BufferedReader serviceReader =
new BufferedReader(new InputStreamReader(serviceStream))) {
String serviceLine;
while ((serviceLine = serviceReader.readLine()) != null) {
if (!services.containsKey(line)) {
services.put(line, new HashSet<>());

Set<? extends Element> dominoAutoElements =
roundEnv.getElementsAnnotatedWith(DominoAuto.class);

Set<String> includes = new HashSet<>();
Set<String> exclude = new HashSet<>();

if (options().containsKey("dominoAutoInclude")) {
includes.addAll(
Arrays.stream(options().get("dominoAutoInclude").split(","))
.collect(Collectors.toSet()));
}

if (options().containsKey("dominoAutoExclude")) {
exclude.addAll(
Arrays.stream(options().get("dominoAutoExclude").split(","))
.collect(Collectors.toSet()));
}

dominoAutoElements.forEach(
element -> {
includes.addAll(Arrays.asList(element.getAnnotation(DominoAuto.class).include()));
exclude.addAll(Arrays.asList(element.getAnnotation(DominoAuto.class).exclude()));
});

Map<String, Set<String>> services = new HashMap<>();

try (ScanResult scanResult =
new ClassGraph().acceptPathsNonRecursive("META-INF/services").scan()) {
scanResult
.getAllResources()
.forEachByteArrayThrowingIOException(
(Resource res, byte[] content) -> {
String serviceName = res.getPath().replace("META-INF/services/", "");

if (includes.stream().anyMatch(serviceName::startsWith)
&& exclude.stream().noneMatch(serviceName::startsWith)) {
if (!services.containsKey(serviceName)) {
services.put(serviceName, new HashSet<>());
}
services.get(line).add(serviceLine);
Stream<String> impls = new String(content, StandardCharsets.UTF_8).lines();
impls
.filter(
impl ->
nonNull(impl) && !impl.trim().isEmpty() && !impl.startsWith("#"))
.forEach(
impl -> {
services.get(serviceName).add(impl);
});
}
}
}
}
}
}

writeServiceLoaders(services);
});
}
writeServiceLoaders(services);
} catch (Exception ex) {
SourceUtil.errorStackTrace(env.getMessager(), ex);
env.getMessager().printMessage(Diagnostic.Kind.ERROR, "Failed to generate service loaders.");
}
return false;
}

private Set<String> getBlackListedServices(Set<? extends Element> dominoAutoElements) {
Set<String> blackList = new HashSet<>();
blackList.add(Processor.class.getCanonicalName());

dominoAutoElements.forEach(
element ->
sourceUtil
.getClassArrayValueFromAnnotation(element, DominoAuto.class, "blackList")
.forEach(
typeMirror ->
blackList.add(env.getTypeUtils().erasure(typeMirror).toString())));
return blackList;
}

private void writeServiceLoaders(Map<String, Set<String>> services) {
services.forEach(
(key, impls) -> {
CodeBlock.Builder bodyBuilder = CodeBlock.builder();
bodyBuilder.addStatement(
"$T<$T> services = new $T()", List.class, ClassName.bestGuess(key), ArrayList.class);
impls.forEach(
impl -> {
bodyBuilder.addStatement("services.add(new $T())", ClassName.bestGuess(impl));
});
impls.stream()
.forEach(
impl -> {
env.getMessager()
.printMessage(Diagnostic.Kind.WARNING, "Adding service entry : " + impl);
bodyBuilder.addStatement("services.add(new $T())", ClassName.bestGuess(impl));
});

bodyBuilder.addStatement("return services");
TypeSpec classSpec =
Expand All @@ -158,12 +169,10 @@ private void writeServiceLoaders(Map<String, Set<String>> services) {
JavaFile.builder(getPackageName(key), classSpec)
.build()
.writeTo(processingEnv.getFiler());
} catch (IOException e) {
processingEnv
.getMessager()
} catch (Exception e) {
messager()
.printMessage(
Diagnostic.Kind.ERROR,
"Failed to write service loader for service : [" + key + "]");
Diagnostic.Kind.WARNING, "Failed to write service loader : " + e.getMessage());
}
});
}
Expand Down Expand Up @@ -200,4 +209,14 @@ public Elements elements() {
public Messager messager() {
return env.getMessager();
}

@Override
public Filer getFiler() {
return env.getFiler();
}

@Override
public Map<String, String> options() {
return env.getOptions();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
*/
package org.dominokit.auto;

import java.util.Map;
import javax.annotation.processing.Filer;
import javax.annotation.processing.Messager;
import javax.lang.model.util.Elements;
import javax.lang.model.util.Types;
Expand All @@ -25,4 +27,8 @@ public interface HasProcessorEnv {
Elements elements();

Messager messager();

Filer getFiler();

Map<String, String> options();
}
Loading

0 comments on commit 41adb78

Please sign in to comment.