Skip to content
New issue

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

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

Already on GitHub? Sign in to your account

Maven plugin for Helidon Service Inject #9461

Open
wants to merge 9 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions all/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1140,6 +1140,10 @@
<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject-maven-plugin</artifactId>
</dependency>
<dependency>
<groupId>io.helidon.metadata</groupId>
<artifactId>helidon-metadata-hson</artifactId>
Expand Down
5 changes: 5 additions & 0 deletions bom/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -1506,6 +1506,11 @@
<artifactId>helidon-service-inject</artifactId>
<version>${helidon.version}</version>
</dependency>
<dependency>
Copy link
Contributor

Choose a reason for hiding this comment

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

We need a pluginManagement in the application parent pom.

Copy link
Member Author

Choose a reason for hiding this comment

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

I would prefer to leave the application updates to Helidon declarative, as this requires actual applications using it to verify it is done correctly, and that seemed like a bit bigger task.

Copy link
Contributor

Choose a reason for hiding this comment

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

This plugin will be usable in 4.2.0, we should provide proper version management for it.

<groupId>io.helidon.service.inject</groupId>
<artifactId>helidon-service-inject-maven-plugin</artifactId>
<version>${helidon.version}</version>
</dependency>

<!-- Metadata -->
<dependency>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -697,7 +697,8 @@ private static boolean isTypeInThisModule(AptContext ctx,
moduleName.set(null);

ModuleElement module = ctx.aptEnv().getElementUtils().getModuleOf(type);
if (!module.isUnnamed()) {

if (module != null && !module.isUnnamed()) {
String name = module.getQualifiedName().toString();
if (hasValue(name)) {
moduleName.set(name);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@

import java.nio.file.Path;
import java.util.List;
import java.util.Optional;

import io.helidon.builder.api.Option;
import io.helidon.builder.api.Prototype;
Expand Down Expand Up @@ -77,6 +78,14 @@ interface CompilerOptionsBlueprint {
@Option.Default("21")
String target();

/**
* The compiler release.
* If not specified, {@link #source()} and {@link #target()} would be used.
*
* @return release for compilation
*/
Optional<String> release();

/**
* Target directory to generate class files to.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,7 @@ class JavaC {
private final List<String> commandLineArgs;
private final String source;
private final String target;
private final String release;
private final Path outputDirectory;
private final CodegenLogger logger;

Expand All @@ -54,6 +55,7 @@ private JavaC(CompilerOptions options) {
this.commandLineArgs = options.commandLineArguments();
this.source = options.source();
this.target = options.target();
this.release = options.release().orElse(null);
this.outputDirectory = options.outputDirectory();
this.logger = options.logger();
}
Expand Down Expand Up @@ -123,14 +125,20 @@ private void doCompile(Result result, Path[] sourceFilesToCompile) {
optionList.add("--source-path");
optionList.add(toSourcepath());
}
if (source != null) {
optionList.add("--source");
optionList.add(source);
}
if (target != null) {
optionList.add("--target");
optionList.add(target);
if (release == null) {
if (source != null) {
optionList.add("--source");
optionList.add(source);
}
if (target != null) {
optionList.add("--target");
optionList.add(target);
}
} else {
optionList.add("--release");
optionList.add(release);
}

optionList.addAll(commandLineArgs);
if (outputDirectory != null) {
optionList.add("-d");
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,39 @@ public sealed interface ElementSignature permits ElementSignatures.FieldSignatur
ElementSignatures.MethodSignature,
ElementSignatures.ParameterSignature,
ElementSignatures.NoSignature {
/**
* A field signature.
*
* @param type type of the field
* @param name name of the field
* @return a new field signature
*/
static ElementSignature createField(TypeName type, String name) {
return ElementSignatures.createField(type, name);
}

/**
* A constructor signature.
*
* @param parameters list of types of parameters
* @return a new constructor signature
*/
static ElementSignature createConstructor(List<TypeName> parameters) {
return ElementSignatures.createConstructor(parameters);
}

/**
* A method signature.
*
* @param returnType return type of the method
* @param name name of the method
* @param parameters parameter types of the method
* @return a new method signature
*/
static ElementSignature createMethod(TypeName returnType, String name, List<TypeName> parameters) {
return ElementSignatures.createMethod(returnType, name, parameters);
}

/**
* Type of the element. Resolves as follows:
* <ul>
Expand Down
88 changes: 88 additions & 0 deletions pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,13 @@
<version.lib.wiremock>2.26.3</version.lib.wiremock>
<version.lib.commons-lang3>3.10</version.lib.commons-lang3>
<version.lib.classgraph>4.8.165</version.lib.classgraph>
<version.lib.plexus.classworlds>2.7.0</version.lib.plexus.classworlds>
<version.lib.plexus.utils>3.3.0</version.lib.plexus.utils>
<version.lib.maven.plugin.annotations>3.9.0</version.lib.maven.plugin.annotations>
<version.lib.maven.plugin.api>3.9.3</version.lib.maven.plugin.api>
<version.lib.maven.plugin.project>2.2.1</version.lib.maven.plugin.project>
<version.lib.apiguardian.api>1.1.2</version.lib.apiguardian.api>
<version.lib.groovy>2.4.16</version.lib.groovy>
<!--
!Version statement! - end
-->
Expand All @@ -120,13 +127,15 @@
<version.plugin.helidon-build-tools>4.0.14</version.plugin.helidon-build-tools>
<version.plugin.hibernate-enhance>${version.lib.hibernate}</version.plugin.hibernate-enhance>
<version.plugin.install>3.1.2</version.plugin.install>
<version.plugin.invoker>3.2.2</version.plugin.invoker>
<version.plugin.jacoco>0.8.5</version.plugin.jacoco>
<version.plugin.jandex>3.1.2</version.plugin.jandex>
<version.plugin.jar>3.3.0</version.plugin.jar>
<version.plugin.javadoc>3.6.2</version.plugin.javadoc>
<version.plugin.jaxb>2.5.0</version.plugin.jaxb>
<version.plugin.license>1.16</version.plugin.license>
<version.plugin.os>1.5.0.Final</version.plugin.os>
<version.plugin.plugin>3.9.0</version.plugin.plugin>
<version.plugin.project-info-reports>3.0.0</version.plugin.project-info-reports>
<version.plugin.protobuf>0.6.1</version.plugin.protobuf>
<version.plugin.resources>3.3.1</version.plugin.resources>
Expand Down Expand Up @@ -853,6 +862,39 @@
<artifactId>maven-antrun-plugin</artifactId>
<version>${version.plugin.ant}</version>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-invoker-plugin</artifactId>
<version>${version.plugin.invoker}</version>
<configuration>
<addTestClassPath>true</addTestClassPath>
<settingsFile>src/it/settings.xml</settingsFile>
<mergeUserSettings>true</mergeUserSettings>
<localRepositoryPath>${project.build.directory}/it-repo</localRepositoryPath>
<!--suppress MavenModelInspection -->
<ignoreFailures>${maven.test.failure.ignore}</ignoreFailures>
<showErrors>true</showErrors>
<!--suppress UnresolvedMavenProperty, MavenModelInspection -->
<skipInvocation>${skipTests}</skipInvocation>
<!--suppress UnresolvedMavenProperty, MavenModelInspection -->
<skipInstallation>${skipTests}</skipInstallation>
<pomIncludes>
<pomInclude>projects/*/pom.xml</pomInclude>
</pomIncludes>
<cloneProjectsTo>${project.build.directory}/it</cloneProjectsTo>
<goals>
<goal>clean</goal>
<goal>install</goal>
</goals>
</configuration>
<dependencies>
<dependency>
<groupId>org.codehaus.groovy</groupId>
<artifactId>groovy</artifactId>
<version>${version.lib.groovy}</version>
</dependency>
</dependencies>
</plugin>
</plugins>
</pluginManagement>
<plugins>
Expand Down Expand Up @@ -944,6 +986,15 @@
</execution>
</executions>
</plugin>
<plugin>
<groupId>org.apache.maven.plugins</groupId>
<artifactId>maven-plugin-plugin</artifactId>
<version>${version.plugin.plugin}</version>
<configuration>
<requiredMavenVersion>[3.6.1,)</requiredMavenVersion>
<requiredJavaVersion>[${version.java}.0,)</requiredJavaVersion>
</configuration>
</plugin>
</plugins>
</build>

Expand Down Expand Up @@ -1314,6 +1365,43 @@
<artifactId>jakarta.inject-tck</artifactId>
<version>${version.lib.jakarta.inject}</version>
</dependency>
<!-- Maven plugin -->
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-artifact</artifactId>
<version>${version.lib.maven.plugin.api}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-model</artifactId>
<version>${version.lib.maven.plugin.api}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-plugin-api</artifactId>
<version>${version.lib.maven.plugin.api}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven.plugin-tools</groupId>
<artifactId>maven-plugin-annotations</artifactId>
<version>${version.lib.maven.plugin.annotations}</version>
<scope>provided</scope>
</dependency>
<dependency>
<groupId>org.apache.maven</groupId>
<artifactId>maven-project</artifactId>
<version>${version.lib.maven.plugin.project}</version>
<scope>provided</scope>
</dependency>
<dependency>
<!-- dependency convergence -->
<groupId>org.apiguardian</groupId>
<artifactId>apiguardian-api</artifactId>
<version>${version.lib.apiguardian.api}</version>
</dependency>
</dependencies>
</dependencyManagement>

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,28 @@
* New service descriptor metadata with its class code.
*/
public interface DescriptorClassCode {
/**
* Create a new instance.
*
* @param classCode class code that contains necessary information for the generated class.
* @param registryType type of registry that generates the descriptor (core, inject)
* @param weight weight of the service this descriptor describes
* @param contracts contracts of the service (i.e. {@code MyContract})
* @param factoryContracts factory contracts of this service (i.e. {@code Supplier<MyContract>})
* @return a new class code of service descriptor
*/
static DescriptorClassCode create(ClassCode classCode,
String registryType,
double weight,
Set<ResolvedType> contracts,
Set<ResolvedType> factoryContracts) {
return new DescriptorClassCodeImpl(classCode,
registryType,
weight,
contracts,
factoryContracts);
}

/**
* New source code information.
*
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
/**
* Generates a service descriptor.
*/
class GenerateServiceDescriptor {
public class GenerateServiceDescriptor {
static final TypeName SET_OF_RESOLVED_TYPES = TypeName.builder(TypeNames.SET)
.addTypeArgument(TypeNames.RESOLVED_TYPE_NAME)
.build();
Expand Down Expand Up @@ -89,11 +89,11 @@ private GenerateServiceDescriptor(TypeName generator,
* @param service service to create a descriptor for
* @return class model builder of the service descriptor
*/
static ClassModel.Builder generate(TypeName generator,
RegistryCodegenContext ctx,
RegistryRoundContext roundContext,
Collection<TypeInfo> allServices,
TypeInfo service) {
public static ClassModel.Builder generate(TypeName generator,
RegistryCodegenContext ctx,
RegistryRoundContext roundContext,
Collection<TypeInfo> allServices,
TypeInfo service) {
return new GenerateServiceDescriptor(generator,
ctx,
roundContext,
Expand Down
24 changes: 24 additions & 0 deletions service/inject/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ Helidon Inject includes:
- [Aspect Oriented Programming (interceptors)](#interceptors)
- [Events](events)
- [Programmatic Lookup](#programmatic-lookup)
- [Startup](#startup)
- [Other](#other)
- [Glossary](#glossary)

Expand Down Expand Up @@ -361,6 +362,29 @@ Lookup parameter options:
- `TypeName` - the same, but using Helidon abstraction of type names (may have type arguments)
- `Lookup` - a full search criteria for a registry lookup

# Startup

The following options are available to start a service registry (and the application):

1. Use API to create an `io.helidon.service.inject.InjectRegistryManager`
2. Use the Helidon startup class `io.helidon.Main`, which will use the injection main class through service loader
3. Use a generated main class, by default named `ApplicationMain` in the main package of the application (supports customization)

## Generated Main Class

To generate a main class, the Helidon Service Inject Maven plugin must be configured.
This is expected to be configured only for an application (i.e. not for library modules) - this is the reason we do not generate it automatically.

The generated main class will contain full, reflection less configuration of the service registry. It registers all services directly through API, and disables service discovery from classpath.

The Main class can also be customized; to do this:
1. Create a custom class (let's call it `CustomMain` as an example)
2. The class must extend the injection main class (`public abstract class CustomMain extends InjectionMain`)
3. The class must be annotated with `@Injection.Main`, so it is discovered by annotation processor
4. Implement any desired methods; the generated class will only implement `serviceDescriptors(InjectConfig.Builder configBuilder)` (always), and `discoverServices()` (if created from the Maven plugin)

For details on how to configure your build, see [Maven Plugin](../maven-plugin/README.md).

# Other

## API types quick reference
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -311,6 +311,26 @@ private Injection() {
Class<? extends Annotation> value() default Singleton.class;
}

/**
* Marks a custom main class (should be declared as {@code abstract}).
* This is a required annotation to make sure the generated main class extends the user's main class.
* Note that the custom main class MUST extend the {@code io.helidon.service.inject.InjectionMain} to customize
* startup sequence.
* The name {@code ApplicationMain} is reserved by service registry, and cannot be used for your custom main class;
* the generated name can be modified through {@code Maven} plugin configuration and annotation processor configuration.
* <p>
* The generated main class adds registration of all available services to injection config,
* and disables service discovery to prevent any and all reflection done by service registry itself.
* <p>
* To have correct startup of your application, use the {@code ApplicationMain} as your application entry point
* (such as a {@code mainClass} configured in {@code pom.xml} when using Maven and Helidon application parents).
*/
@Documented
@Retention(RetentionPolicy.CLASS)
@Target(ElementType.TYPE)
public @interface Main {
}

/**
* Provides an ability to create more than one service instance from a single service definition.
* This is useful when the cardinality can only be determined at runtime.
Expand Down
Loading
Loading