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

no arg constructor support on pico interceptors #6552

Merged
merged 6 commits into from
Apr 5, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 10 additions & 0 deletions pico/api/src/main/java/io/helidon/pico/ServiceInfoCriteria.java
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,16 @@ public interface ServiceInfoCriteria {
*/
Optional<String> moduleName();

/**
* Determines whether the non-proxied, {@link Intercepted} services should be returned in any lookup operation. If this
* option is disabled then only the {@link Interceptor}-generated service will be eligible to be returned and not the service
* being intercepted.
* The default value is {@code false}.
*
* @return true if the non-proxied type intercepted services should be eligible
*/
boolean includeIntercepted();

/**
* Determines whether this service info matches the criteria for injection.
* Matches is a looser form of equality check than {@code equals()}. If a service matches criteria
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,15 @@ public interface ServiceProviderBindable<T> extends ServiceProvider<T> {
*/
void moduleName(String moduleName);

/**
* Returns true if this service provider instance is an {@link Interceptor}.
*
* @return true if this service provider is an interceptor
*/
default boolean isInterceptor() {
return false;
}

/**
* Returns {@code true} if this service provider is intercepted.
*
Expand Down
1 change: 0 additions & 1 deletion pico/configdriven/tests/configuredby-application/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,6 @@
<compilerArgs>
<arg>-Apico.debug=${pico.debug}</arg>
<arg>-Apico.autoAddNonContractInterfaces=true</arg>
<arg>-Apico.allowListedInterceptorAnnotations=jakarta.inject.Named</arg>
<arg>-Apico.application.pre.create=true</arg>
<arg>-Apico.mapApplicationToSingletonScope=true</arg>
</compilerArgs>
Expand Down
3 changes: 0 additions & 3 deletions pico/configdriven/tests/configuredby/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -112,9 +112,6 @@
<configuration>
<compilerArgs>
<arg>-Apico.autoAddNonContractInterfaces=true</arg>
<!-- <arg>-XprintProcessorInfo</arg>-->
<!-- <arg>-XprintRounds</arg>-->
<!-- <arg>-verbose</arg>-->
</compilerArgs>
<forceJavacCompilerUse>true</forceJavacCompilerUse>
<annotationProcessorPaths>
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* 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 io.helidon.pico.configdriven.interceptor.test;

import io.helidon.pico.Contract;

@Contract
public interface IZ {

String methodIZ1(String arg1);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* 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 io.helidon.pico.configdriven.interceptor.test;

import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

import io.helidon.pico.InterceptedTrigger;

@InterceptedTrigger
@Retention(RetentionPolicy.CLASS)
public @interface TestInterceptorTrigger {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* 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 io.helidon.pico.configdriven.interceptor.test;

import java.util.Optional;
import java.util.concurrent.atomic.AtomicInteger;

import io.helidon.pico.configdriven.configuredby.test.FakeWebServer;

import jakarta.annotation.PostConstruct;
import jakarta.inject.Inject;

/**
* This test case is applying {@link InterceptorBasedAnno} (an {@link io.helidon.pico.InterceptedTrigger}) on this class directly.
* Since it is a config-driven service it is forced to used the interface based approach to interceptors.
*/
// TODO: https://github.com/helidon-io/helidon/issues/6542
@TestInterceptorTrigger
//@ConfiguredBy(ZImplConfig.class)
@SuppressWarnings("ALL")
public class ZImpl implements IZ {
private final AtomicInteger postConstructCallCount = new AtomicInteger();

// @Inject
// ZImpl(ZImplConfig config/*,
// List<Provider<ASingletonServiceContract>> singletons*/) {
// assert (config != null && !config.name().isEmpty()) : Objects.toString(config);
//// assert (singletons.size() == 1) : singletons.toString();
// }

@Inject
ZImpl(Optional<FakeWebServer> fakeWebServer) {
assert (fakeWebServer.isPresent());
}

@Override
public String methodIZ1(String val) {
return "methodIZ1:" + val;
}

@PostConstruct
void postConstruct() {
postConstructCallCount.incrementAndGet();
}

int postConstructCallCount() {
return postConstructCallCount.get();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
/*
* Copyright (c) 2023 Oracle and/or its affiliates.
*
* 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 io.helidon.pico.configdriven.interceptor.test;

import io.helidon.builder.config.ConfigBean;

/**
* Drives {@link ZImpl} activation.
*/
@ConfigBean(drivesActivation = true)
public interface ZImplConfig {

/**
* For testing purposes.
*
* @return for testing purposes
*/
String name();

}
Original file line number Diff line number Diff line change
Expand Up @@ -340,6 +340,8 @@ protected void innerExecute() {
} else {
getLog().error("failed to process", res.error().orElse(null));
}
} catch (Exception e) {
throw new ToolsException("An error occurred creating the " + PicoServicesConfig.NAME + " Application", e);
} finally {
Thread.currentThread().setContextClassLoader(prev);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
import java.util.LinkedHashSet;
import java.util.List;
import java.util.Map;
import java.util.Objects;
import java.util.Optional;
import java.util.Set;
import java.util.Stack;
Expand Down Expand Up @@ -170,11 +169,10 @@ public boolean process(Set<? extends TypeElement> annotations,
if (!roundEnv.processingOver()) {
for (String annoType : annoTypes()) {
// annotation may not be on the classpath, in such a case just ignore it
DefaultTypeName annoName = DefaultTypeName.createFromTypeName(annoType);

if (available(annoName)) {
Set<? extends Element> typesToProcess = roundEnv.getElementsAnnotatedWith(toTypeElement(annoName));

TypeName annoName = DefaultTypeName.createFromTypeName(annoType);
Optional<TypeElement> annoTypeElement = toTypeElement(annoName);
if (annoTypeElement.isPresent()) {
Set<? extends Element> typesToProcess = roundEnv.getElementsAnnotatedWith(annoTypeElement.get());
Set<String> contraAnnotations = contraAnnotations();
if (!contraAnnotations.isEmpty()) {
// filter out the ones we will not do...
Expand Down Expand Up @@ -362,12 +360,16 @@ protected void processServiceType(TypeName serviceTypeName,
void maybeSetInterceptorPlanForServiceType(TypeName serviceTypeName,
TypeElement ignoredType) {
if (services.hasVisitedInterceptorPlanFor(serviceTypeName)) {
// if we processed it already then there is no reason to check again
return;
}

// note: it is important to use this class' CL since maven will not give us the "right" one.
ServiceInfoBasics interceptedServiceInfo = toInterceptedServiceInfoFor(serviceTypeName);
InterceptorCreator.InterceptorProcessor processor = interceptorCreator.createInterceptorProcessor(
toInterceptedServiceInfoFor(serviceTypeName), interceptorCreator, Optional.of(processingEnv));
interceptedServiceInfo,
interceptorCreator,
Optional.of(processingEnv));
Set<String> annotationTypeTriggers = processor.allAnnotationTypeTriggers();
if (annotationTypeTriggers.isEmpty()) {
services.addInterceptorPlanFor(serviceTypeName, Optional.empty());
Expand All @@ -376,7 +378,7 @@ void maybeSetInterceptorPlanForServiceType(TypeName serviceTypeName,

Optional<InterceptionPlan> plan = processor.createInterceptorPlan(annotationTypeTriggers);
if (plan.isEmpty()) {
warn("expected to see an interception plan for: " + serviceTypeName);
warn("unable to produce an interception plan for: " + serviceTypeName);
}
services.addInterceptorPlanFor(serviceTypeName, plan);
}
Expand Down Expand Up @@ -517,14 +519,18 @@ void adjustContractsForExternals(Set<TypeName> contracts,
Set<String> externalModuleNamesRequired) {
AtomicReference<String> externalModuleName = new AtomicReference<>();
for (TypeName contract : contracts) {
if (!isInThisModule(toTypeElement(contract), externalModuleName)) {
Optional<TypeElement> typeElement = toTypeElement(contract);
if (typeElement.isPresent()
&& !isInThisModule(typeElement.get(), externalModuleName)) {
maybeAddExternalModule(externalModuleName.get(), externalModuleNamesRequired);
externalContracts.add(contract);
}
}

for (TypeName externalContract : externalContracts) {
if (isInThisModule(toTypeElement(externalContract), externalModuleName)) {
Optional<TypeElement> typeElement = toTypeElement(externalContract);
if (typeElement.isPresent()
&& isInThisModule(typeElement.get(), externalModuleName)) {
warn(externalContract + " is actually in this module and therefore should not be labelled as external.", null);
maybeAddExternalModule(externalModuleName.get(), externalModuleNamesRequired);
}
Expand Down Expand Up @@ -723,7 +729,7 @@ void gatherContractsToBeProcessed(Set<TypeMirror> processed,
});

toServiceTypeHierarchy(typeElement, false).stream()
.map(te -> toTypeElement(te).asType())
.map(te -> toTypeElement(te).orElseThrow().asType())
.forEach(tm -> {
processed.add(tm);
gatherContractsToBeProcessed(processed, TypeTools.toTypeElement(tm).orElse(null));
Expand Down Expand Up @@ -793,18 +799,8 @@ Set<TypeName> toExternalContracts(TypeElement type,
return result;
}

TypeElement toTypeElement(TypeName typeName) {
return Objects.requireNonNull(processingEnv.getElementUtils().getTypeElement(typeName.name()));
}

/**
* Check if a type is available on application classpath.
*
* @param typeName type name
* @return {@code true} if the type is available
*/
boolean available(TypeName typeName) {
return processingEnv.getElementUtils().getTypeElement(typeName.name()) != null;
Optional<TypeElement> toTypeElement(TypeName typeName) {
return Optional.ofNullable(processingEnv.getElementUtils().getTypeElement(typeName.name()));
}

System.Logger logger() {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -116,8 +116,11 @@ public boolean process(Set<? extends TypeElement> annotations,
if (!roundEnv.processingOver()) {
for (String annoType : annoTypes()) {
TypeName annoName = DefaultTypeName.createFromTypeName(annoType);
TypeElement annoElement = toTypeElement(annoName);
Set<? extends Element> typesToProcess = roundEnv.getElementsAnnotatedWith(annoElement);
Optional<TypeElement> annoElement = toTypeElement(annoName);
if (annoElement.isEmpty()) {
continue;
}
Set<? extends Element> typesToProcess = roundEnv.getElementsAnnotatedWith(annoElement.get());
doInner(annoName, typesToProcess, roundEnv);
}
}
Expand Down Expand Up @@ -184,9 +187,7 @@ void doInner(TypeName annoTypeName,
void doFiler(CustomAnnotationTemplateResponse response) {
AbstractFilerMessager filer = AbstractFilerMessager.createAnnotationBasedFiler(processingEnv, this);
CodeGenFiler codegen = CodeGenFiler.create(filer);
response.generatedSourceCode().forEach((typeName, codeBody) -> {
codegen.codegenJavaFilerOut(typeName, codeBody);
});
response.generatedSourceCode().forEach(codegen::codegenJavaFilerOut);
response.generatedResources().forEach((typedElementName, resourceBody) -> {
String fileType = typedElementName.elementName();
if (!hasValue(fileType)) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ public abstract class AbstractServiceProvider<T>
private DependenciesInfo dependencies;
private Map<String, PicoInjectionPlan> injectionPlan;
private ServiceProvider<?> interceptor;
private boolean thisIsAnInterceptor;

/**
* The default constructor.
Expand Down Expand Up @@ -245,6 +246,11 @@ public void moduleName(String moduleName) {
}
}

@Override
public boolean isInterceptor() {
return thisIsAnInterceptor;
}

@Override
public Optional<ServiceProvider<?>> interceptor() {
return Optional.ofNullable(interceptor);
Expand All @@ -257,6 +263,24 @@ public void interceptor(ServiceProvider<?> interceptor) {
throw alreadyInitialized();
}
this.interceptor = interceptor;
if (interceptor instanceof AbstractServiceProvider<?>) {
((AbstractServiceProvider) interceptor).intercepted(this);
}
}

/**
* Incorporate the intercepted qualifiers into our own qualifiers.
*
* @param intercepted the service being intercepted
*/
void intercepted(AbstractServiceProvider<?> intercepted) {
if (activationSemaphore.availablePermits() == 0 || phase != Phase.INIT) {
throw alreadyInitialized();
}
this.thisIsAnInterceptor = true;
this.serviceInfo = DefaultServiceInfo.toBuilder(this.serviceInfo)
.addQualifiers(intercepted.serviceInfo().qualifiers())
.build();
}

@Override
Expand Down
Loading