Skip to content

Commit

Permalink
Merge branch 'main' into update-config-yaml-style
Browse files Browse the repository at this point in the history
  • Loading branch information
rolfedh authored Sep 13, 2023
2 parents 3a49063 + a45d2e5 commit 63fe3db
Show file tree
Hide file tree
Showing 34 changed files with 760 additions and 63 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -1003,11 +1003,15 @@ private NativeImageSourceJarBuildItem buildNativeImageThinJar(CurateOutcomeBuild

// Remove svm and graal-sdk artifacts as they are provided by GraalVM itself
if (classLoadingConfig.removedArtifacts.isEmpty()) {
classLoadingConfig.removedArtifacts = Optional.of(new ArrayList<>(2));
classLoadingConfig.removedArtifacts = Optional.of(new ArrayList<>(6));
}
List<String> removedArtifacts = classLoadingConfig.removedArtifacts.get();
removedArtifacts.add("org.graalvm.nativeimage:svm");
removedArtifacts.add("org.graalvm.sdk:graal-sdk");
removedArtifacts.add("org.graalvm.sdk:nativeimage");
removedArtifacts.add("org.graalvm.sdk:word");
removedArtifacts.add("org.graalvm.sdk:collections");
removedArtifacts.add("org.graalvm.polyglot:polyglot");

doLegacyThinJarGeneration(curateOutcomeBuildItem, outputTargetBuildItem, transformedClasses,
applicationArchivesBuildItem, applicationInfo, packageConfig, generatedResources, libDir, allClasses,
Expand Down
58 changes: 58 additions & 0 deletions core/runtime/src/main/java/io/quarkus/runtime/Shutdown.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package io.quarkus.runtime;

import static java.lang.annotation.ElementType.METHOD;
import static java.lang.annotation.RetentionPolicy.RUNTIME;

import java.lang.annotation.Retention;
import java.lang.annotation.Target;

import jakarta.enterprise.context.Dependent;
import jakarta.enterprise.inject.spi.ObserverMethod;

/**
* This annotation is used to mark a business method of a CDI bean that should be executed during application shutdown. The
* annotated method must be non-private and non-static and declare no arguments.
* <p>
* The behavior is similar to a declaration of a {@link ShutdownEvent} observer. In fact, a synthetic observer of the
* {@link ShutdownEvent} is generated for each occurence of this annotation. Within the observer, the contextual instance of a
* bean is obtained first, and then the method is invoked.
* <p>
* Furthermore, {@link #value()} can be used to specify the priority of the generated observer method and thus affects observers
* ordering.
* <p>
* The contextual instance is destroyed immediately after the method is invoked for {@link Dependent} beans.
* <p>
* The following examples are functionally equivalent.
*
* <pre>
* &#064;ApplicationScoped
* class Bean1 {
* void onShutdown(&#064;Observes ShutdownEvent event) {
* // place the logic here
* }
* }
*
* &#064;ApplicationScoped
* class Bean2 {
*
* &#064;Shutdown
* void shutdown() {
* // place the logic here
* }
* }
* </pre>
*
* @see ShutdownEvent
*/
@Target(METHOD)
@Retention(RUNTIME)
public @interface Shutdown {

/**
*
* @return the priority
* @see jakarta.annotation.Priority
*/
int value() default ObserverMethod.DEFAULT_PRIORITY;

}
2 changes: 1 addition & 1 deletion core/runtime/src/main/java/io/quarkus/runtime/Startup.java
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@
* Furthermore, {@link #value()} can be used to specify the priority of the generated observer method and thus affects observers
* ordering.
* <p>
* The contextual instance is destroyed immediately afterwards for {@link Dependent} beans.
* The contextual instance is destroyed immediately after the method is invoked for {@link Dependent} beans.
* <p>
* The following examples are functionally equivalent.
*
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package io.quarkus.runtime.graal;

import java.awt.Graphics;
import java.awt.GraphicsEnvironment;
import java.awt.Toolkit;
import java.util.Iterator;
import java.util.function.BooleanSupplier;
Expand Down Expand Up @@ -41,7 +41,7 @@ public boolean getAsBoolean() {
@TargetClass(className = "java.awt.GraphicsEnvironment", onlyWith = AwtImageIO.IsAWTAbsent.class)
final class Target_java_awt_GraphicsEnvironment {
@Substitute
public static Graphics getLocalGraphicsEnvironment() {
public static GraphicsEnvironment getLocalGraphicsEnvironment() {
throw new UnsupportedOperationException(AwtImageIO.AWT_EXTENSION_HINT);
}

Expand Down
23 changes: 21 additions & 2 deletions docs/src/main/asciidoc/flyway.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -290,18 +290,37 @@ once and then start the actual application without Flyway. To support this use c
the generated manifests contain a Kubernetes initialization `Job` for Flyway.
The `Job` performs initialization and the actual `Pod`, will starts once the `Job` is successfully completed.

=== Disabling

The feature is enabled by default and can be globally disabled, using:

[source,properties]
----
quarkus.kubernetes.externalize-init=false
quarkus.kubernetes.init-task-defaults.enabled=false
----

or on OpenShift:

[source,properties]
----
quarkus.openshift.externalize-init=false
quarkus.openshift.init-task-defaults.enabled=false
----

=== Using a custom image that controls waiting for the Job

To change the `wait-for` image which by default is `groundnuty/k8s-wait-for:no-root-v1.7` you can use:

[source,properties]
----
quarkus.kubernetes.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

or on Openshift:


[source,properties]
----
quarkus.openshift.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

**Note**: In this context globally means `for all extensions that support init task externalization`.
23 changes: 21 additions & 2 deletions docs/src/main/asciidoc/liquibase-mongodb.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -159,18 +159,37 @@ once and then start the actual application without Liquibase. To support this us
the generated manifests contain a Kubernetes initialization `Job` for Liquibase.
The `Job` performs initialization and the actual `Pod`, will starts once the `Job` is successfully completed.

=== Disabling

The feature is enabled by default and can be globally disabled, using:

[source,properties]
----
quarkus.kubernetes.externalize-init=false
quarkus.kubernetes.init-task-defaults.enabled=false
----

or on OpenShift:

[source,properties]
----
quarkus.openshift.externalize-init=false
quarkus.openshift.init-task-defaults.enabled=false
----

=== Using a custom image that controls waiting for the Job

To change the `wait-for` image which by default is `groundnuty/k8s-wait-for:no-root-v1.7` you can use:

[source,properties]
----
quarkus.kubernetes.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

or on Openshift:


[source,properties]
----
quarkus.openshift.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

**Note**: In this context globally means `for all extensions that support init task externalization`.
Expand Down
25 changes: 23 additions & 2 deletions docs/src/main/asciidoc/liquibase.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -237,20 +237,41 @@ once and then start the actual application without Liquibase. To support this us
the generated manifests contain a Kubernetes initialization `Job` for Liquibase.
The `Job` performs initialization and the actual `Pod`, will starts once the `Job` is successfully completed.

=== Disabling

The feature is enabled by default and can be globally disabled, using:

[source,properties]
----
quarkus.kubernetes.externalize-init=false
quarkus.kubernetes.init-task-defaults.enabled=false
----

or on OpenShift:

[source,properties]
----
quarkus.openshift.externalize-init=false
quarkus.openshift.init-task-defaults.enabled=false
----

=== Using a custom image that controls waiting for the Job

To change the `wait-for` image which by default is `groundnuty/k8s-wait-for:no-root-v1.7` you can use:

[source,properties]
----
quarkus.kubernetes.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----

or on Openshift:


[source,properties]
----
quarkus.openshift.init-task-defaults.wait-for-image=my/wait-for-image:1.0
----



**Note**: In this context globally means `for all extensions that support init task externalization`.

== Configuration Reference
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,141 @@
package io.quarkus.arc.deployment;

import java.lang.reflect.Modifier;
import java.util.ArrayList;
import java.util.List;
import java.util.function.Predicate;

import jakarta.enterprise.context.spi.Contextual;
import jakarta.enterprise.inject.spi.ObserverMethod;

import org.jboss.jandex.AnnotationValue;
import org.jboss.jandex.ClassInfo;
import org.jboss.jandex.DotName;
import org.jboss.jandex.MethodInfo;
import org.jboss.logging.Logger;

import io.quarkus.arc.deployment.ObserverRegistrationPhaseBuildItem.ObserverConfiguratorBuildItem;
import io.quarkus.arc.impl.CreationalContextImpl;
import io.quarkus.arc.processor.AnnotationStore;
import io.quarkus.arc.processor.BeanInfo;
import io.quarkus.arc.processor.BuildExtension;
import io.quarkus.arc.processor.BuiltinScope;
import io.quarkus.arc.processor.ObserverConfigurator;
import io.quarkus.deployment.annotations.BuildProducer;
import io.quarkus.deployment.annotations.BuildStep;
import io.quarkus.gizmo.CatchBlockCreator;
import io.quarkus.gizmo.MethodDescriptor;
import io.quarkus.gizmo.ResultHandle;
import io.quarkus.gizmo.TryBlock;
import io.quarkus.runtime.Shutdown;
import io.quarkus.runtime.ShutdownEvent;

public class ShutdownBuildSteps {

static final DotName SHUTDOWN_NAME = DotName.createSimple(Shutdown.class.getName());

private static final Logger LOG = Logger.getLogger(ShutdownBuildSteps.class);

@BuildStep
AutoAddScopeBuildItem addScope(CustomScopeAnnotationsBuildItem customScopes) {
// Class with no built-in scope annotation but with @Shutdown annotation
return AutoAddScopeBuildItem.builder()
.defaultScope(BuiltinScope.APPLICATION)
.anyMethodMatches(new Predicate<MethodInfo>() {
@Override
public boolean test(MethodInfo m) {
return m.hasAnnotation(SHUTDOWN_NAME);
}
})
.reason("Found classes containing @Shutdown annotation.")
.build();
}

@BuildStep
UnremovableBeanBuildItem unremovableBeans() {
return new UnremovableBeanBuildItem(new Predicate<BeanInfo>() {
@Override
public boolean test(BeanInfo bean) {
if (bean.isClassBean()) {
return bean.getTarget().get().asClass().annotationsMap().containsKey(SHUTDOWN_NAME);
}
return false;
}
});
}

@BuildStep
void registerShutdownObservers(ObserverRegistrationPhaseBuildItem observerRegistration,
BuildProducer<ObserverConfiguratorBuildItem> configurators) {

AnnotationStore annotationStore = observerRegistration.getContext().get(BuildExtension.Key.ANNOTATION_STORE);

for (BeanInfo bean : observerRegistration.getContext().beans().classBeans()) {
ClassInfo beanClass = bean.getTarget().get().asClass();
List<MethodInfo> shutdownMethods = new ArrayList<>();
// Collect all non-static no-args methods annotated with @Shutdown
for (MethodInfo method : beanClass.methods()) {
if (annotationStore.hasAnnotation(method, SHUTDOWN_NAME)) {
if (!method.isSynthetic()
&& !Modifier.isPrivate(method.flags())
&& !Modifier.isStatic(method.flags())
&& method.parametersCount() == 0) {
shutdownMethods.add(method);
} else {
LOG.warnf("Ignored an invalid @Shutdown method declared on %s: %s", method.declaringClass().name(),
method);
}
}
}
if (!shutdownMethods.isEmpty()) {
for (MethodInfo method : shutdownMethods) {
AnnotationValue priority = annotationStore.getAnnotation(method, SHUTDOWN_NAME).value();
registerShutdownObserver(observerRegistration, bean,
method.declaringClass().name() + "#" + method.toString(),
priority != null ? priority.asInt() : ObserverMethod.DEFAULT_PRIORITY, method);
}
}
}
}

private void registerShutdownObserver(ObserverRegistrationPhaseBuildItem observerRegistration, BeanInfo bean, String id,
int priority, MethodInfo shutdownMethod) {
ObserverConfigurator configurator = observerRegistration.getContext().configure()
.beanClass(bean.getBeanClass())
.observedType(ShutdownEvent.class);
configurator.id(id);
configurator.priority(priority);
configurator.notify(mc -> {
// InjectableBean<Foo> bean = Arc.container().bean("bflmpsvz");
ResultHandle containerHandle = mc.invokeStaticMethod(StartupBuildSteps.ARC_CONTAINER);
ResultHandle beanHandle = mc.invokeInterfaceMethod(StartupBuildSteps.ARC_CONTAINER_BEAN, containerHandle,
mc.load(bean.getIdentifier()));
if (BuiltinScope.DEPENDENT.is(bean.getScope())) {
ResultHandle creationalContext = mc.newInstance(
MethodDescriptor.ofConstructor(CreationalContextImpl.class, Contextual.class),
beanHandle);
// Create a dependent instance
ResultHandle instance = mc.invokeInterfaceMethod(StartupBuildSteps.CONTEXTUAL_CREATE, beanHandle,
creationalContext);
TryBlock tryBlock = mc.tryBlock();
tryBlock.invokeVirtualMethod(MethodDescriptor.of(shutdownMethod), instance);
CatchBlockCreator catchBlock = tryBlock.addCatch(Exception.class);
catchBlock.invokeInterfaceMethod(StartupBuildSteps.CONTEXTUAL_DESTROY, beanHandle, instance, creationalContext);
catchBlock.throwException(RuntimeException.class, "Error destroying bean with @Shutdown method",
catchBlock.getCaughtException());
// Destroy the instance immediately
mc.invokeInterfaceMethod(StartupBuildSteps.CONTEXTUAL_DESTROY, beanHandle, instance, creationalContext);
} else {
// Obtains the instance from the context
// InstanceHandle<Foo> handle = Arc.container().instance(bean);
ResultHandle instanceHandle = mc.invokeInterfaceMethod(StartupBuildSteps.ARC_CONTAINER_INSTANCE,
containerHandle,
beanHandle);
ResultHandle instance = mc.invokeInterfaceMethod(StartupBuildSteps.INSTANCE_HANDLE_GET, instanceHandle);
mc.invokeVirtualMethod(MethodDescriptor.of(shutdownMethod), instance);
}
mc.returnValue(null);
});
configurator.done();
}
}
Loading

0 comments on commit 63fe3db

Please sign in to comment.