Repository: quarkusio/quarkus
Issue: IllegalAccessError invoking non-public @Scheduled method 

## Describe the bug
An IllegalAccessError is thrown when invoking a non-public @Scheduled method, e.g:

@Scheduled(every = "PT10S", concurrentExecution = ConcurrentExecution.SKIP)
void myMethod() {...}

All is fine if the method is public.

Call stack looks like so:

2021-05-27 15:58:27,006 ERROR (executor-thread-2) [SimpleScheduler] Error occured while executing task for trigger IntervalTrigger [id=1_my.redacted.Service_ScheduledInvoker_myMethod_8d0ea617bdcf7cab33987d918fbaeabfd51940d4, interval=10000]
java.lang.IllegalAccessError: class my.redacted.Service_ScheduledInvoker_myMethod_8d0ea617bdcf7cab33987d918fbaeabfd51940d4 tried to access method my.redacted.Service.myMethod()V (my.redacted.Service_ScheduledInvoker_myMethod_8d0ea617bdcf7cab33987d918fbaeabfd51940d4 is in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @451b1c01; my.redacted.Service is in unnamed module of loader io.quarkus.bootstrap.classloading.QuarkusClassLoader @6f45da6b)
	at my.redacted.Service_ScheduledInvoker_myMethod_8d0ea617bdcf7cab33987d918fbaeabfd51940d4.invokeBean(Service_ScheduledInvoker_myMethod_8d0ea617bdcf7cab33987d918fbaeabfd51940d4.zig:46)
	at io.quarkus.arc.runtime.BeanInvoker.invoke(BeanInvoker.java:20)
	at io.quarkus.scheduler.runtime.SkipConcurrentExecutionInvoker.invoke(SkipConcurrentExecutionInvoker.java:37)
	at io.quarkus.scheduler.runtime.SkipConcurrentExecutionInvoker.invoke(SkipConcurrentExecutionInvoker.java:19)
	at io.quarkus.scheduler.runtime.SimpleScheduler$ScheduledTask$1.run(SimpleScheduler.java:251)
	at io.quarkus.vertx.core.runtime.VertxCoreRecorder$14.runWith(VertxCoreRecorder.java:486)
	at org.jboss.threads.EnhancedQueueExecutor$Task.run(EnhancedQueueExecutor.java:2442)
	at org.jboss.threads.EnhancedQueueExecutor$ThreadBody.run(EnhancedQueueExecutor.java:1450)
	at org.jboss.threads.DelegatingRunnable.run(DelegatingRunnable.java:29)
	at org.jboss.threads.ThreadLocalResettingRunnable.run(ThreadLocalResettingRunnable.java:29)
	at io.netty.util.concurrent.FastThreadLocalRunnable.run(FastThreadLocalRunnable.java:30)
	at java.base/java.lang.Thread.run(Thread.java:834)

### Expected behavior

Expect it to work ok with default access per other Quarkus/CDI methods.

### Actual behavior

Fails with IllegalAccessError.

### Quarkus version or git rev

2.0.0.CR1


Changed files:
extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduled.java
extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java

diff --git a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java
index 5f85c176a1a..ad34c15f837 100644
--- a/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java
+++ b/extensions/scheduler/deployment/src/main/java/io/quarkus/scheduler/deployment/SchedulerProcessor.java
@@ -3,6 +3,7 @@
 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;
@@ -10,6 +11,7 @@
 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;
@@ -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) {
@@ -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
@@ -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();
@@ -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());
         }
diff --git a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduled.java b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduled.java
index 1bfff45c537..e3d6873b99c 100644
--- a/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduled.java
+++ b/extensions/scheduler/runtime/src/main/java/io/quarkus/scheduler/Scheduled.java
@@ -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.
  *