Skip to content

Commit

Permalink
Scheduler - use an application class predicate when generating invokers
Browse files Browse the repository at this point in the history
- until now all invokers were considered application classes, i.e. they
were always loaded by the Runtime Class Loader in the dev mode and so a
package-private scheduled method defined in a dependency resulted in an
IllegalAccessError
- resolves #17492

(cherry picked from commit a5acdc9)
  • Loading branch information
mkouba authored and gsmet committed May 27, 2021
1 parent b535442 commit 582e7cb
Show file tree
Hide file tree
Showing 2 changed files with 28 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,15 @@
import static io.quarkus.deployment.annotations.ExecutionTime.RUNTIME_INIT;
import static io.quarkus.deployment.annotations.ExecutionTime.STATIC_INIT;

import java.lang.reflect.Modifier;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

import org.jboss.jandex.AnnotationInstance;
import org.jboss.jandex.AnnotationValue;
Expand Down Expand Up @@ -88,6 +90,7 @@ public class SchedulerProcessor {
Kind.CLASS);

static final String INVOKER_SUFFIX = "_ScheduledInvoker";
static final String NESTED_SEPARATOR = "$_";

@BuildStep
void beans(Capabilities capabilities, BuildProducer<AdditionalBeanBuildItem> additionalBeans) {
Expand Down Expand Up @@ -159,6 +162,12 @@ void validateScheduledBusinessMethods(SchedulerConfig config, List<ScheduledBusi
for (ScheduledBusinessMethodItem scheduledMethod : scheduledMethods) {
MethodInfo method = scheduledMethod.getMethod();

if (Modifier.isPrivate(method.flags()) || Modifier.isStatic(method.flags())) {
errors.add(new IllegalStateException("@Scheduled method must be non-private and non-static: "
+ method.declaringClass().name() + "#" + method.name() + "()"));
continue;
}

// Validate method params and return type
List<Type> params = method.parameters();
if (params.size() > 1
Expand Down Expand Up @@ -198,11 +207,24 @@ public List<UnremovableBeanBuildItem> unremovableBeans() {
@Record(RUNTIME_INIT)
public FeatureBuildItem build(SchedulerConfig config, BuildProducer<SyntheticBeanBuildItem> syntheticBeans,
SchedulerRecorder recorder, List<ScheduledBusinessMethodItem> scheduledMethods,
BuildProducer<GeneratedClassBuildItem> generatedClass, BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
BuildProducer<GeneratedClassBuildItem> generatedClasses, BuildProducer<ReflectiveClassBuildItem> reflectiveClass,
AnnotationProxyBuildItem annotationProxy, ExecutorBuildItem executor) {

List<ScheduledMethodMetadata> scheduledMetadata = new ArrayList<>();
ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClass, true);
ClassOutput classOutput = new GeneratedClassGizmoAdaptor(generatedClasses, new Function<String, String>() {
@Override
public String apply(String name) {
// org/acme/Foo_ScheduledInvoker_run_0000 -> org.acme.Foo
int idx = name.indexOf(INVOKER_SUFFIX);
if (idx != -1) {
name = name.substring(0, idx);
}
if (name.contains(NESTED_SEPARATOR)) {
name = name.replace(NESTED_SEPARATOR, "$");
}
return name;
}
});

for (ScheduledBusinessMethodItem scheduledMethod : scheduledMethods) {
ScheduledMethodMetadata metadata = new ScheduledMethodMetadata();
Expand Down Expand Up @@ -247,8 +269,8 @@ private String generateInvoker(ScheduledBusinessMethodItem scheduledMethod, Clas

String baseName;
if (bean.getImplClazz().enclosingClass() != null) {
baseName = DotNames.simpleName(bean.getImplClazz().enclosingClass()) + "_"
+ DotNames.simpleName(bean.getImplClazz().name());
baseName = DotNames.simpleName(bean.getImplClazz().enclosingClass()) + NESTED_SEPARATOR
+ DotNames.simpleName(bean.getImplClazz());
} else {
baseName = DotNames.simpleName(bean.getImplClazz().name());
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@
/**
* Marks a business method to be automatically scheduled and invoked by the container.
* <p>
* The target business method must be non-private and non-static.
* <p>
* The schedule is defined either by {@link #cron()} or by {@link #every()} attribute. If both are specified, the cron
* expression takes precedence.
*
Expand Down

0 comments on commit 582e7cb

Please sign in to comment.