Skip to content

Commit

Permalink
Adds support for Provider-specializing injection points in OciExtension
Browse files Browse the repository at this point in the history
Signed-off-by: Laird Nelson <[email protected]>
  • Loading branch information
ljnelson committed Nov 15, 2023
1 parent ea20930 commit e5e6cdb
Show file tree
Hide file tree
Showing 2 changed files with 169 additions and 38 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@
import jakarta.enterprise.inject.spi.InjectionPoint;
import jakarta.enterprise.inject.spi.ProcessInjectionPoint;
import jakarta.enterprise.util.TypeLiteral;
import jakarta.inject.Provider;
import jakarta.inject.Singleton;
import org.eclipse.microprofile.config.Config;
import org.eclipse.microprofile.config.ConfigProvider;
Expand Down Expand Up @@ -631,37 +632,16 @@ private void beforeBeanDiscovery(@Observes BeforeBeanDiscovery event) {

private void processInjectionPoint(@Observes ProcessInjectionPoint<?, ?> event) {
InjectionPoint ip = event.getInjectionPoint();
Type baseType = ip.getAnnotated().getBaseType();
if (!(baseType instanceof Class)) {
// Optimization: all OCI constructs we're interested in
// are non-generic classes (and not therefore
// ParameterizedTypes or GenericArrayTypes).
return;
}
Class<?> baseClass = (Class<?>) baseType;
String baseClassName = baseClass.getName();
if (!baseClassName.startsWith(OCI_PACKAGE_PREFIX)) {
// Optimization: the set of classes we're interested in is
// a subset of general OCI-related classes.
return;
}
Set<Annotation> qualifiers = ip.getQualifiers();
if (AbstractAuthenticationDetailsProvider.class.isAssignableFrom(baseClass)
|| ADP_BUILDER_CLASSES.contains(baseClass)) {
// Use an "empty" ServiceTaqs as an indicator of demand
// for some kind of AbstractAuthenticationDetailsProvider
// (or a relevant builder).
this.serviceTaqs.add(new ServiceTaqs(qualifiers.toArray(EMPTY_ANNOTATION_ARRAY)));
return;
}
Matcher m = SERVICE_CLIENT_CLASS_NAME_SUBSTRING_PATTERN.matcher(baseClassName.substring(OCI_PACKAGE_PREFIX.length()));
if (!m.matches() || this.isVetoed(baseClass)) {
return;
}
this.processServiceClientInjectionPoint(event::addDefinitionError,
baseClass,
qualifiers,
OCI_PACKAGE_PREFIX + m.group(1));
processInjectionPoint(ip.getAnnotated().getBaseType(),
ip.getQualifiers(),
event::addDefinitionError);
}

private void processProviderInjectionPoint(@Observes ProcessInjectionPoint<?, ? extends Provider<?>> event) {
InjectionPoint ip = event.getInjectionPoint();
processInjectionPoint(((ParameterizedType) ip.getAnnotated().getBaseType()).getActualTypeArguments()[0],
ip.getQualifiers(),
event::addDefinitionError);
}

private void afterBeanDiscovery(@Observes AfterBeanDiscovery event, BeanManager bm) {
Expand Down Expand Up @@ -735,8 +715,38 @@ private boolean isVetoed(Class<?> c) {
return false;
}

private void processInjectionPoint(Type type,
Set<Annotation> qualifiers,
Consumer<? super ClassNotFoundException> errorHandler) {
if (!(type instanceof Class)) {
// Optimization: all OCI constructs we're interested in are non-generic classes (and not therefore
// ParameterizedTypes or GenericArrayTypes).
return;
}
Class<?> c = (Class<?>) type;
String className = c.getName();
if (!className.startsWith(OCI_PACKAGE_PREFIX)) {
// Optimization: the set of classes we're interested in is a subset of general OCI-related classes.
return;
}
if (AbstractAuthenticationDetailsProvider.class.isAssignableFrom(c) || ADP_BUILDER_CLASSES.contains(c)) {
// Register an "empty" ServiceTaqs as an indicator of demand for some kind of
// AbstractAuthenticationDetailsProvider (or a relevant builder).
this.serviceTaqs.add(new ServiceTaqs(qualifiers.toArray(EMPTY_ANNOTATION_ARRAY)));
return;
}
Matcher m = SERVICE_CLIENT_CLASS_NAME_SUBSTRING_PATTERN.matcher(className.substring(OCI_PACKAGE_PREFIX.length()));
if (!m.matches() || this.isVetoed(c)) {
return;
}
this.processServiceClientInjectionPoint(errorHandler,
c,
qualifiers,
OCI_PACKAGE_PREFIX + m.group(1));
}

private void processServiceClientInjectionPoint(Consumer<? super ClassNotFoundException> errorHandler,
Class<?> baseClass,
Class<?> c,
Set<Annotation> qualifiers,
String serviceInterfaceName) {
Annotation[] qualifiersArray = qualifiers.toArray(EMPTY_ANNOTATION_ARRAY);
Expand All @@ -746,12 +756,12 @@ private void processServiceClientInjectionPoint(Consumer<? super ClassNotFoundEx
// ....example.Example
// ....example.ExampleClient
// ....example.ExampleClient$Builder
Class<?> serviceInterfaceClass = toClassUnresolved(errorHandler, baseClass, serviceInterfaceName, lenient);
Class<?> serviceInterfaceClass = toClassUnresolved(errorHandler, c, serviceInterfaceName, lenient);
if (serviceInterfaceClass != null && serviceInterfaceClass.isInterface()) {
String serviceClient = serviceInterfaceName + "Client";
Class<?> serviceClientClass = toClassUnresolved(errorHandler, baseClass, serviceClient, lenient);
Class<?> serviceClientClass = toClassUnresolved(errorHandler, c, serviceClient, lenient);
if (serviceClientClass != null && serviceInterfaceClass.isAssignableFrom(serviceClientClass)) {
Class<?> serviceClientBuilderClass = toClassUnresolved(errorHandler, baseClass, serviceClient + "$Builder", true);
Class<?> serviceClientBuilderClass = toClassUnresolved(errorHandler, c, serviceClient + "$Builder", true);
if (serviceClientBuilderClass == null) {
serviceClientBuilderClass = toClassUnresolved(errorHandler, serviceClient + "Builder", lenient);
}
Expand All @@ -775,14 +785,14 @@ private void processServiceClientInjectionPoint(Consumer<? super ClassNotFoundEx
// ....example.ExampleAsyncClient
// ....example.ExampleAsyncClient$Builder
String serviceAsyncInterface = serviceInterfaceName + "Async";
Class<?> serviceAsyncInterfaceClass = toClassUnresolved(errorHandler, baseClass, serviceAsyncInterface, lenient);
Class<?> serviceAsyncInterfaceClass = toClassUnresolved(errorHandler, c, serviceAsyncInterface, lenient);
if (serviceAsyncInterfaceClass != null && serviceAsyncInterfaceClass.isInterface()) {
String serviceAsyncClient = serviceAsyncInterface + "Client";
Class<?> serviceAsyncClientClass = toClassUnresolved(errorHandler, baseClass, serviceAsyncClient, lenient);
Class<?> serviceAsyncClientClass = toClassUnresolved(errorHandler, c, serviceAsyncClient, lenient);
if (serviceAsyncClientClass != null
&& serviceAsyncInterfaceClass.isAssignableFrom(serviceAsyncClientClass)) {
Class<?> serviceAsyncClientBuilderClass =
toClassUnresolved(errorHandler, baseClass, serviceAsyncClient + "$Builder", true);
toClassUnresolved(errorHandler, c, serviceAsyncClient + "$Builder", true);
if (serviceAsyncClientBuilderClass == null) {
serviceAsyncClientBuilderClass = toClassUnresolved(errorHandler, serviceAsyncClient + "Builder", lenient);
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*
* 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.integrations.oci.sdk.cdi;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.net.InetAddress;

import io.helidon.microprofile.config.ConfigCdiExtension;
import io.helidon.microprofile.testing.junit5.AddBean;
import io.helidon.microprofile.testing.junit5.AddExtension;
import io.helidon.microprofile.testing.junit5.DisableDiscovery;
import io.helidon.microprofile.testing.junit5.HelidonTest;

import com.oracle.bmc.ConfigFileReader;
import com.oracle.bmc.ailanguage.AIServiceLanguage;
import jakarta.enterprise.context.Dependent;
import jakarta.inject.Inject;
import jakarta.inject.Provider;
import org.junit.jupiter.api.Test;

import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.not;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.hamcrest.MatcherAssert.assertThat;
import static org.junit.jupiter.api.Assumptions.assumeTrue;

@AddBean(TestProcessProviderInjectionPoint.ExampleBean.class)
@AddExtension(ConfigCdiExtension.class)
@AddExtension(OciExtension.class)
@DisableDiscovery
@HelidonTest
class TestProcessProviderInjectionPoint {


/*
* Instance fields.
*/


@Inject
private Provider<ExampleBean> exampleBeanProvider;


/*
* Test methods.
*/


@Test
void testProcessProviderInjectionPoint() throws IOException {
// Don't run this test if there's NO ADP anywhere; it will show up as "skipped" in the test run.
assumeTrue(imdsAvailable() || configFileExists());

assertThat(this.exampleBeanProvider.get(), is(not(nullValue())));
}


/*
* Static methods.
*/


private static final boolean configFileExists() throws IOException {
try {
return
ConfigFileReader.parse(System.getProperty("oci.config.file", "~/.oci/config"),
System.getProperty("oci.auth.profile")) != null;
} catch (final FileNotFoundException ignored) {
return false;
}
}

private static final boolean imdsAvailable() {
try {
return InetAddress.getByName(System.getProperty("oci.imds.hostname", "169.254.169.254"))
.isReachable(Integer.getInteger("oci.imds.timeout", 100).intValue());
} catch (final IOException ignored) {
return false;
}
}


/*
* Inner and nested classes.
*/


@Dependent
static class ExampleBean {

// Required by the CDI specification.
@Deprecated // For CDI use only.
ExampleBean() {
super();
}

@Inject
private ExampleBean(Provider<AIServiceLanguage> p) {
super();
assertThat(p, is(not(nullValue())));
assertThat(p.get(), is(not(nullValue())));
}

}

}

0 comments on commit e5e6cdb

Please sign in to comment.