Search in sources :

Example 6 with ClassCreator

use of io.quarkus.gizmo.ClassCreator in project quarkus-github-app by quarkiverse.

the class GitHubAppProcessor method generateMultiplexers.

/**
 * Multiplexers listen to the async events emitted by the dispatcher.
 * <p>
 * They are subclasses of the application classes listening to GitHub events through our annotations.
 * <p>
 * They are useful for several purposes:
 * <ul>
 * <li>A single application method can listen to multiple event types: the event types are qualifiers and CDI wouldn't allow
 * that (only events matching all the qualifiers would be received by the application method). That's why this class is
 * called a multiplexer: it will generate one method per event type and each generated method will delegate to the original
 * method.</li>
 * <li>The multiplexer also handles the resolution of config files.</li>
 * <li>We can inject a properly configured instance of GitHub or DynamicGraphQLClient into the method.</li>
 * </ul>
 */
private static void generateMultiplexers(ClassOutput beanClassOutput, DispatchingConfiguration dispatchingConfiguration, BuildProducer<ReflectiveClassBuildItem> reflectiveClasses) {
    for (Entry<DotName, TreeSet<EventDispatchingMethod>> eventDispatchingMethodsEntry : dispatchingConfiguration.getMethods().entrySet()) {
        DotName declaringClassName = eventDispatchingMethodsEntry.getKey();
        TreeSet<EventDispatchingMethod> eventDispatchingMethods = eventDispatchingMethodsEntry.getValue();
        ClassInfo declaringClass = eventDispatchingMethods.iterator().next().getMethod().declaringClass();
        reflectiveClasses.produce(new ReflectiveClassBuildItem(true, true, declaringClassName.toString()));
        String multiplexerClassName = declaringClassName + "_Multiplexer";
        reflectiveClasses.produce(new ReflectiveClassBuildItem(true, true, multiplexerClassName));
        ClassCreator multiplexerClassCreator = ClassCreator.builder().classOutput(beanClassOutput).className(multiplexerClassName).superClass(declaringClassName.toString()).build();
        multiplexerClassCreator.addAnnotation(Multiplexer.class);
        if (!BuiltinScope.isDeclaredOn(declaringClass)) {
            multiplexerClassCreator.addAnnotation(Singleton.class);
        }
        for (AnnotationInstance classAnnotation : declaringClass.classAnnotations()) {
            multiplexerClassCreator.addAnnotation(classAnnotation);
        }
        // Copy the constructors
        for (MethodInfo originalConstructor : declaringClass.constructors()) {
            MethodCreator constructorCreator = multiplexerClassCreator.getMethodCreator(MethodDescriptor.ofConstructor(multiplexerClassName, originalConstructor.parameters().stream().map(t -> t.name().toString()).toArray(String[]::new)));
            List<AnnotationInstance> originalMethodAnnotations = originalConstructor.annotations().stream().filter(ai -> ai.target().kind() == Kind.METHOD).collect(Collectors.toList());
            for (AnnotationInstance originalMethodAnnotation : originalMethodAnnotations) {
                constructorCreator.addAnnotation(originalMethodAnnotation);
            }
            Map<Short, List<AnnotationInstance>> originalConstructorParameterAnnotationMapping = originalConstructor.annotations().stream().filter(ai -> ai.target().kind() == Kind.METHOD_PARAMETER).collect(Collectors.groupingBy(ai -> ai.target().asMethodParameter().position()));
            List<ResultHandle> parametersRh = new ArrayList<>();
            for (short i = 0; i < originalConstructor.parameters().size(); i++) {
                parametersRh.add(constructorCreator.getMethodParam(i));
                AnnotatedElement parameterAnnotations = constructorCreator.getParameterAnnotations(i);
                List<AnnotationInstance> originalConstructorParameterAnnotations = originalConstructorParameterAnnotationMapping.getOrDefault(i, Collections.emptyList());
                for (AnnotationInstance originalConstructorParameterAnnotation : originalConstructorParameterAnnotations) {
                    parameterAnnotations.addAnnotation(originalConstructorParameterAnnotation);
                }
            }
            constructorCreator.invokeSpecialMethod(MethodDescriptor.of(originalConstructor), constructorCreator.getThis(), parametersRh.toArray(ResultHandle[]::new));
            constructorCreator.returnValue(null);
        }
        // Generate the multiplexed event dispatching methods
        for (EventDispatchingMethod eventDispatchingMethod : eventDispatchingMethods) {
            AnnotationInstance eventSubscriberInstance = eventDispatchingMethod.getEventSubscriberInstance();
            MethodInfo originalMethod = eventDispatchingMethod.getMethod();
            Map<Short, List<AnnotationInstance>> originalMethodParameterAnnotationMapping = originalMethod.annotations().stream().filter(ai -> ai.target().kind() == Kind.METHOD_PARAMETER).collect(Collectors.groupingBy(ai -> ai.target().asMethodParameter().position()));
            // if the method already has an @Observes or @ObservesAsync annotation
            if (originalMethod.hasAnnotation(DotNames.OBSERVES) || originalMethod.hasAnnotation(DotNames.OBSERVES_ASYNC)) {
                LOG.warn("Methods listening to GitHub events may not be annotated with @Observes or @ObservesAsync. Offending method: " + originalMethod.declaringClass().name() + "#" + originalMethod);
            }
            List<String> parameterTypes = new ArrayList<>();
            List<Type> originalMethodParameterTypes = originalMethod.parameters();
            // detect the parameter that is a payload
            short payloadParameterPosition = 0;
            for (short i = 0; i < originalMethodParameterTypes.size(); i++) {
                List<AnnotationInstance> parameterAnnotations = originalMethodParameterAnnotationMapping.getOrDefault(i, Collections.emptyList());
                if (parameterAnnotations.stream().anyMatch(ai -> ai.name().equals(eventSubscriberInstance.name()))) {
                    payloadParameterPosition = i;
                    break;
                }
            }
            short j = 0;
            Map<Short, Short> parameterMapping = new HashMap<>();
            for (short i = 0; i < originalMethodParameterTypes.size(); i++) {
                List<AnnotationInstance> originalMethodAnnotations = originalMethodParameterAnnotationMapping.getOrDefault(i, Collections.emptyList());
                if (originalMethodAnnotations.stream().anyMatch(ai -> CONFIG_FILE.equals(ai.name())) || GITHUB.equals(originalMethodParameterTypes.get(i).name()) || DYNAMIC_GRAPHQL_CLIENT.equals(originalMethodParameterTypes.get(i).name())) {
                    // if the parameter is annotated with @ConfigFile or is of type GitHub or DynamicGraphQLClient, we skip it
                    continue;
                }
                String parameterType;
                if (i == payloadParameterPosition) {
                    parameterType = MultiplexedEvent.class.getName();
                } else {
                    parameterType = originalMethodParameterTypes.get(i).name().toString();
                }
                parameterTypes.add(parameterType);
                parameterMapping.put(i, j);
                j++;
            }
            if (originalMethod.hasAnnotation(CONFIG_FILE)) {
                parameterTypes.add(ConfigFileReader.class.getName());
            }
            MethodCreator methodCreator = multiplexerClassCreator.getMethodCreator(originalMethod.name() + "_" + HashUtil.sha1(eventSubscriberInstance.toString()), originalMethod.returnType().name().toString(), parameterTypes.toArray());
            for (Type exceptionType : originalMethod.exceptions()) {
                methodCreator.addException(exceptionType.name().toString());
            }
            ResultHandle[] parameterValues = new ResultHandle[originalMethod.parameters().size()];
            // copy annotations except for @ConfigFile
            for (short i = 0; i < originalMethodParameterTypes.size(); i++) {
                List<AnnotationInstance> parameterAnnotations = originalMethodParameterAnnotationMapping.getOrDefault(i, Collections.emptyList());
                if (parameterAnnotations.isEmpty()) {
                    continue;
                }
                // @ConfigFile elements are not in the mapping
                Short generatedParameterIndex = parameterMapping.get(i);
                if (generatedParameterIndex == null) {
                    continue;
                }
                AnnotatedElement generatedParameterAnnotations = methodCreator.getParameterAnnotations(generatedParameterIndex);
                if (parameterAnnotations.stream().anyMatch(ai -> ai.name().equals(eventSubscriberInstance.name()))) {
                    generatedParameterAnnotations.addAnnotation(DotNames.OBSERVES_ASYNC.toString());
                    generatedParameterAnnotations.addAnnotation(eventSubscriberInstance);
                } else {
                    for (AnnotationInstance annotationInstance : parameterAnnotations) {
                        generatedParameterAnnotations.addAnnotation(annotationInstance);
                    }
                }
            }
            ResultHandle payloadRh = methodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(MultiplexedEvent.class, "getPayload", GHEventPayload.class), methodCreator.getMethodParam(parameterMapping.get(payloadParameterPosition)));
            // generate the code of the method
            for (short originalMethodParameterIndex = 0; originalMethodParameterIndex < originalMethodParameterTypes.size(); originalMethodParameterIndex++) {
                List<AnnotationInstance> parameterAnnotations = originalMethodParameterAnnotationMapping.getOrDefault(originalMethodParameterIndex, Collections.emptyList());
                Short multiplexerMethodParameterIndex = parameterMapping.get(originalMethodParameterIndex);
                if (originalMethodParameterIndex == payloadParameterPosition) {
                    parameterValues[originalMethodParameterIndex] = payloadRh;
                } else if (GITHUB.equals(originalMethodParameterTypes.get(originalMethodParameterIndex).name())) {
                    parameterValues[originalMethodParameterIndex] = methodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(MultiplexedEvent.class, "getGitHub", GitHub.class), methodCreator.getMethodParam(parameterMapping.get(payloadParameterPosition)));
                } else if (DYNAMIC_GRAPHQL_CLIENT.equals(originalMethodParameterTypes.get(originalMethodParameterIndex).name())) {
                    parameterValues[originalMethodParameterIndex] = methodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(MultiplexedEvent.class, "getGitHubGraphQLClient", DynamicGraphQLClient.class), methodCreator.getMethodParam(parameterMapping.get(payloadParameterPosition)));
                } else if (parameterAnnotations.stream().anyMatch(ai -> ai.name().equals(CONFIG_FILE))) {
                    AnnotationInstance configFileAnnotationInstance = parameterAnnotations.stream().filter(ai -> ai.name().equals(CONFIG_FILE)).findFirst().get();
                    String configObjectType = originalMethodParameterTypes.get(originalMethodParameterIndex).name().toString();
                    boolean isOptional = false;
                    if (Optional.class.getName().equals(configObjectType)) {
                        if (originalMethodParameterTypes.get(originalMethodParameterIndex).kind() != Type.Kind.PARAMETERIZED_TYPE) {
                            throw new IllegalStateException("Optional is used but not parameterized for method " + originalMethod.declaringClass().name() + "#" + originalMethod);
                        }
                        isOptional = true;
                        configObjectType = originalMethodParameterTypes.get(originalMethodParameterIndex).asParameterizedType().arguments().get(0).name().toString();
                    }
                    // it's a config file, we will use the ConfigFileReader (last parameter of the method) and inject the result
                    ResultHandle configFileReaderRh = methodCreator.getMethodParam(parameterTypes.size() - 1);
                    ResultHandle ghRepositoryRh = methodCreator.invokeStaticMethod(MethodDescriptor.ofMethod(PayloadHelper.class, "getRepository", GHRepository.class, GHEventPayload.class), payloadRh);
                    ResultHandle configObject = methodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(ConfigFileReader.class, "getConfigObject", Object.class, GHRepository.class, String.class, Class.class), configFileReaderRh, ghRepositoryRh, methodCreator.load(configFileAnnotationInstance.value().asString()), methodCreator.loadClass(configObjectType));
                    configObject = methodCreator.checkCast(configObject, configObjectType);
                    if (isOptional) {
                        configObject = methodCreator.invokeStaticMethod(MethodDescriptor.ofMethod(Optional.class, "ofNullable", Optional.class, Object.class), configObject);
                    }
                    parameterValues[originalMethodParameterIndex] = configObject;
                } else {
                    parameterValues[originalMethodParameterIndex] = methodCreator.getMethodParam(multiplexerMethodParameterIndex);
                }
            }
            ResultHandle returnValue = methodCreator.invokeVirtualMethod(originalMethod, methodCreator.getThis(), parameterValues);
            methodCreator.returnValue(returnValue);
        }
        multiplexerClassCreator.close();
    }
}
Also used : GitHubService(io.quarkiverse.githubapp.runtime.github.GitHubService) DotNames(io.quarkus.arc.processor.DotNames) GeneratedClassGizmoAdaptor(io.quarkus.deployment.GeneratedClassGizmoAdaptor) Arrays(java.util.Arrays) GHRepository(org.kohsuke.github.GHRepository) ClassOutput(io.quarkus.gizmo.ClassOutput) ShutdownContextBuildItem(io.quarkus.deployment.builditem.ShutdownContextBuildItem) AdditionalBeanBuildItem(io.quarkus.arc.deployment.AdditionalBeanBuildItem) AnnotationLiteral(javax.enterprise.util.AnnotationLiteral) Map(java.util.Map) BytecodeCreator(io.quarkus.gizmo.BytecodeCreator) GHEventPayload(org.kohsuke.github.GHEventPayload) PayloadHelper(io.quarkiverse.githubapp.runtime.github.PayloadHelper) EVENT(io.quarkiverse.githubapp.deployment.GitHubAppDotNames.EVENT) MethodParameterInfo(org.jboss.jandex.MethodParameterInfo) AnnotationsTransformerBuildItem(io.quarkus.arc.deployment.AnnotationsTransformerBuildItem) AnnotationValue(org.jboss.jandex.AnnotationValue) ReflectiveHierarchyBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveHierarchyBuildItem) EventDispatchingMethod(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventDispatchingMethod) Set(java.util.Set) EventAnnotationLiteral(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventAnnotationLiteral) Reader(java.io.Reader) BuiltinScope(io.quarkus.arc.processor.BuiltinScope) CompletionStage(java.util.concurrent.CompletionStage) AnnotationInstance(org.jboss.jandex.AnnotationInstance) HashUtil(io.quarkus.runtime.util.HashUtil) ResultHandle(io.quarkus.gizmo.ResultHandle) TryBlock(io.quarkus.gizmo.TryBlock) Record(io.quarkus.deployment.annotations.Record) CatchBlockCreator(io.quarkus.gizmo.CatchBlockCreator) DotName(org.jboss.jandex.DotName) TreeSet(java.util.TreeSet) ArrayList(java.util.ArrayList) MultiplexedEvent(io.quarkiverse.githubapp.runtime.MultiplexedEvent) ConfigFileReader(io.quarkiverse.githubapp.runtime.ConfigFileReader) CONFIG_FILE(io.quarkiverse.githubapp.deployment.GitHubAppDotNames.CONFIG_FILE) Gizmo(io.quarkus.gizmo.Gizmo) Opcodes(org.objectweb.asm.Opcodes) GitHubAppRecorder(io.quarkiverse.githubapp.runtime.GitHubAppRecorder) IOException(java.io.IOException) FieldDescriptor(io.quarkus.gizmo.FieldDescriptor) GeneratedBeanGizmoAdaptor(io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor) StringReader(java.io.StringReader) GeneratedClassBuildItem(io.quarkus.deployment.builditem.GeneratedClassBuildItem) LaunchModeBuildItem(io.quarkus.deployment.builditem.LaunchModeBuildItem) FieldCreator(io.quarkus.gizmo.FieldCreator) BytecodeTransformerBuildItem(io.quarkus.deployment.builditem.BytecodeTransformerBuildItem) GitHubEvent(io.quarkiverse.githubapp.GitHubEvent) WebJarResultsBuildItem(io.quarkus.vertx.http.deployment.webjar.WebJarResultsBuildItem) MethodVisitor(org.objectweb.asm.MethodVisitor) DefaultErrorHandler(io.quarkiverse.githubapp.runtime.error.DefaultErrorHandler) ClassInfo(org.jboss.jandex.ClassInfo) RoutingContext(io.vertx.ext.web.RoutingContext) CombinedIndexBuildItem(io.quarkus.deployment.builditem.CombinedIndexBuildItem) BuildProducer(io.quarkus.deployment.annotations.BuildProducer) Multiplexer(io.quarkiverse.githubapp.runtime.Multiplexer) GeneratedBeanBuildItem(io.quarkus.arc.deployment.GeneratedBeanBuildItem) MethodInfo(org.jboss.jandex.MethodInfo) FeatureBuildItem(io.quarkus.deployment.builditem.FeatureBuildItem) NotFoundPageDisplayableEndpointBuildItem(io.quarkus.vertx.http.deployment.devmode.NotFoundPageDisplayableEndpointBuildItem) ClassVisitor(org.objectweb.asm.ClassVisitor) GitHub(org.kohsuke.github.GitHub) Collection(java.util.Collection) ExecutionTime(io.quarkus.deployment.annotations.ExecutionTime) Collectors(java.util.stream.Collectors) MethodDescriptors(io.quarkus.arc.processor.MethodDescriptors) List(java.util.List) GitHubApiClassWithBridgeMethodsBuildItem(io.quarkiverse.githubapi.deployment.GitHubApiClassWithBridgeMethodsBuildItem) Actions(io.quarkiverse.githubapp.event.Actions) GACT(io.quarkus.maven.dependency.GACT) WebJarBuildItem(io.quarkus.vertx.http.deployment.webjar.WebJarBuildItem) Modifier(java.lang.reflect.Modifier) Annotation(java.lang.annotation.Annotation) Entry(java.util.Map.Entry) EventAnnotation(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventAnnotation) Optional(java.util.Optional) ReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem) RouteBuildItem(io.quarkus.vertx.http.deployment.RouteBuildItem) Logger(org.jboss.logging.Logger) MethodCreator(io.quarkus.gizmo.MethodCreator) Type(org.jboss.jandex.Type) AnnotatedElement(io.quarkus.gizmo.AnnotatedElement) LaunchMode(io.quarkus.runtime.LaunchMode) HashMap(java.util.HashMap) CompletableFuture(java.util.concurrent.CompletableFuture) ClassCreator(io.quarkus.gizmo.ClassCreator) Singleton(javax.inject.Singleton) ErrorHandlerBridgeFunction(io.quarkiverse.githubapp.runtime.error.ErrorHandlerBridgeFunction) Function(java.util.function.Function) HttpRootPathBuildItem(io.quarkus.vertx.http.deployment.HttpRootPathBuildItem) Inject(javax.inject.Inject) BuildStep(io.quarkus.deployment.annotations.BuildStep) Kind(org.jboss.jandex.AnnotationTarget.Kind) PrintStream(java.io.PrintStream) Event(javax.enterprise.event.Event) IndexView(org.jboss.jandex.IndexView) MethodDescriptor(io.quarkus.gizmo.MethodDescriptor) DynamicGraphQLClient(io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient) EventDispatchingConfiguration(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventDispatchingConfiguration) GITHUB(io.quarkiverse.githubapp.deployment.GitHubAppDotNames.GITHUB) DYNAMIC_GRAPHQL_CLIENT(io.quarkiverse.githubapp.deployment.GitHubAppDotNames.DYNAMIC_GRAPHQL_CLIENT) Handler(io.vertx.core.Handler) Collections(java.util.Collections) EventDispatchingMethod(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventDispatchingMethod) HashMap(java.util.HashMap) GitHub(org.kohsuke.github.GitHub) ArrayList(java.util.ArrayList) AnnotatedElement(io.quarkus.gizmo.AnnotatedElement) DotName(org.jboss.jandex.DotName) ClassCreator(io.quarkus.gizmo.ClassCreator) TreeSet(java.util.TreeSet) ArrayList(java.util.ArrayList) List(java.util.List) ResultHandle(io.quarkus.gizmo.ResultHandle) Optional(java.util.Optional) Type(org.jboss.jandex.Type) GHEventPayload(org.kohsuke.github.GHEventPayload) MethodCreator(io.quarkus.gizmo.MethodCreator) ConfigFileReader(io.quarkiverse.githubapp.runtime.ConfigFileReader) MethodInfo(org.jboss.jandex.MethodInfo) MultiplexedEvent(io.quarkiverse.githubapp.runtime.MultiplexedEvent) ReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem) AnnotationInstance(org.jboss.jandex.AnnotationInstance) ClassInfo(org.jboss.jandex.ClassInfo)

Example 7 with ClassCreator

use of io.quarkus.gizmo.ClassCreator in project quarkus-github-app by quarkiverse.

the class GitHubAppProcessor method generateDispatcher.

/**
 * The role of the dispatcher is to receive the CDI events emitted by the reactive route.
 * <p>
 * It parses the raw payload into the appropriate {@link GHEventPayload} and then emit
 * an async CDI event with a MultiplexedEvent containing the payload instance,
 * the GitHub instance and the DynamicGraphQLClient instance if needed.
 * <p>
 * It only generates code for the GitHub events actually listened to by the application.
 */
private static void generateDispatcher(ClassOutput beanClassOutput, CombinedIndexBuildItem combinedIndex, LaunchModeBuildItem launchMode, DispatchingConfiguration dispatchingConfiguration, BuildProducer<ReflectiveClassBuildItem> reflectiveClasses) {
    String dispatcherClassName = GitHubEvent.class.getName() + "DispatcherImpl";
    reflectiveClasses.produce(new ReflectiveClassBuildItem(true, true, dispatcherClassName));
    ClassCreator dispatcherClassCreator = ClassCreator.builder().classOutput(beanClassOutput).className(dispatcherClassName).build();
    dispatcherClassCreator.addAnnotation(Singleton.class);
    FieldCreator eventFieldCreator = dispatcherClassCreator.getFieldCreator(EVENT_EMITTER_FIELD, Event.class);
    eventFieldCreator.addAnnotation(Inject.class);
    eventFieldCreator.setModifiers(Modifier.PROTECTED);
    FieldCreator gitHubServiceFieldCreator = dispatcherClassCreator.getFieldCreator(GITHUB_SERVICE_FIELD, GitHubService.class);
    gitHubServiceFieldCreator.addAnnotation(Inject.class);
    gitHubServiceFieldCreator.setModifiers(Modifier.PROTECTED);
    MethodCreator dispatchMethodCreator = dispatcherClassCreator.getMethodCreator("dispatch", void.class, GitHubEvent.class);
    dispatchMethodCreator.setModifiers(Modifier.PUBLIC);
    dispatchMethodCreator.getParameterAnnotations(0).addAnnotation(DotNames.OBSERVES.toString());
    ResultHandle gitHubEventRh = dispatchMethodCreator.getMethodParam(0);
    ResultHandle installationIdRh = dispatchMethodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(GitHubEvent.class, "getInstallationId", Long.class), gitHubEventRh);
    ResultHandle dispatchedEventRh = dispatchMethodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(GitHubEvent.class, "getEvent", String.class), gitHubEventRh);
    ResultHandle dispatchedActionRh = dispatchMethodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(GitHubEvent.class, "getAction", String.class), gitHubEventRh);
    ResultHandle dispatchedPayloadRh = dispatchMethodCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(GitHubEvent.class, "getPayload", String.class), gitHubEventRh);
    TryBlock tryBlock = dispatchMethodCreator.tryBlock();
    ResultHandle gitHubRh = tryBlock.invokeVirtualMethod(MethodDescriptor.ofMethod(GitHubService.class, "getInstallationClient", GitHub.class, Long.class), tryBlock.readInstanceField(FieldDescriptor.of(dispatcherClassCreator.getClassName(), GITHUB_SERVICE_FIELD, GitHubService.class), tryBlock.getThis()), installationIdRh);
    ResultHandle gitHubGraphQLClientRh = tryBlock.loadNull();
    if (dispatchingConfiguration.requiresGraphQLClient()) {
        gitHubGraphQLClientRh = tryBlock.invokeVirtualMethod(MethodDescriptor.ofMethod(GitHubService.class, "getInstallationGraphQLClient", DynamicGraphQLClient.class, Long.class), tryBlock.readInstanceField(FieldDescriptor.of(dispatcherClassCreator.getClassName(), GITHUB_SERVICE_FIELD, GitHubService.class), tryBlock.getThis()), installationIdRh);
    }
    for (EventDispatchingConfiguration eventDispatchingConfiguration : dispatchingConfiguration.getEventConfigurations().values()) {
        ResultHandle eventRh = tryBlock.load(eventDispatchingConfiguration.getEvent());
        String payloadType = eventDispatchingConfiguration.getPayloadType();
        BytecodeCreator eventMatchesCreator = tryBlock.ifTrue(tryBlock.invokeVirtualMethod(MethodDescriptors.OBJECT_EQUALS, eventRh, dispatchedEventRh)).trueBranch();
        ResultHandle payloadInstanceRh = eventMatchesCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(GitHub.class, "parseEventPayload", GHEventPayload.class, Reader.class, Class.class), gitHubRh, eventMatchesCreator.newInstance(MethodDescriptor.ofConstructor(StringReader.class, String.class), dispatchedPayloadRh), eventMatchesCreator.loadClass(payloadType));
        ResultHandle multiplexedEventRh = eventMatchesCreator.newInstance(MethodDescriptor.ofConstructor(MultiplexedEvent.class, GHEventPayload.class, GitHub.class, DynamicGraphQLClient.class), payloadInstanceRh, gitHubRh, gitHubGraphQLClientRh);
        for (Entry<String, EventAnnotation> eventAnnotationEntry : eventDispatchingConfiguration.getEventAnnotations().entrySet()) {
            String action = eventAnnotationEntry.getKey();
            EventAnnotation eventAnnotation = eventAnnotationEntry.getValue();
            Class<?>[] literalParameterTypes = new Class<?>[eventAnnotation.getValues().size()];
            Arrays.fill(literalParameterTypes, String.class);
            List<ResultHandle> literalParameters = new ArrayList<>();
            ResultHandle annotationLiteralRh = eventMatchesCreator.newInstance(MethodDescriptor.ofConstructor(getLiteralClassName(eventAnnotation.getName()), (Object[]) literalParameterTypes), literalParameters.toArray(ResultHandle[]::new));
            ResultHandle annotationLiteralArrayRh = eventMatchesCreator.newArray(Annotation.class, 1);
            eventMatchesCreator.writeArrayValue(annotationLiteralArrayRh, 0, annotationLiteralRh);
            if (Actions.ALL.equals(action)) {
                fireAsyncAction(eventMatchesCreator, launchMode.getLaunchMode(), dispatcherClassCreator.getClassName(), gitHubEventRh, multiplexedEventRh, annotationLiteralArrayRh);
            } else {
                BytecodeCreator actionMatchesCreator = eventMatchesCreator.ifTrue(eventMatchesCreator.invokeVirtualMethod(MethodDescriptors.OBJECT_EQUALS, eventMatchesCreator.load(action), dispatchedActionRh)).trueBranch();
                fireAsyncAction(actionMatchesCreator, launchMode.getLaunchMode(), dispatcherClassCreator.getClassName(), gitHubEventRh, multiplexedEventRh, annotationLiteralArrayRh);
            }
        }
    }
    CatchBlockCreator catchBlockCreator = tryBlock.addCatch(Throwable.class);
    catchBlockCreator.invokeVirtualMethod(MethodDescriptor.ofMethod(ErrorHandlerBridgeFunction.class, "apply", Void.class, Throwable.class), catchBlockCreator.newInstance(MethodDescriptor.ofConstructor(ErrorHandlerBridgeFunction.class, GitHubEvent.class), gitHubEventRh), catchBlockCreator.getCaughtException());
    dispatchMethodCreator.returnValue(null);
    dispatcherClassCreator.close();
}
Also used : GitHub(org.kohsuke.github.GitHub) FieldCreator(io.quarkus.gizmo.FieldCreator) ArrayList(java.util.ArrayList) Reader(java.io.Reader) ConfigFileReader(io.quarkiverse.githubapp.runtime.ConfigFileReader) StringReader(java.io.StringReader) TryBlock(io.quarkus.gizmo.TryBlock) ClassCreator(io.quarkus.gizmo.ClassCreator) ErrorHandlerBridgeFunction(io.quarkiverse.githubapp.runtime.error.ErrorHandlerBridgeFunction) ResultHandle(io.quarkus.gizmo.ResultHandle) GitHubService(io.quarkiverse.githubapp.runtime.github.GitHubService) BytecodeCreator(io.quarkus.gizmo.BytecodeCreator) GitHubEvent(io.quarkiverse.githubapp.GitHubEvent) EventAnnotation(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventAnnotation) GHEventPayload(org.kohsuke.github.GHEventPayload) MethodCreator(io.quarkus.gizmo.MethodCreator) EventDispatchingConfiguration(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventDispatchingConfiguration) MultiplexedEvent(io.quarkiverse.githubapp.runtime.MultiplexedEvent) CatchBlockCreator(io.quarkus.gizmo.CatchBlockCreator) DynamicGraphQLClient(io.smallrye.graphql.client.dynamic.api.DynamicGraphQLClient) ReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem)

Example 8 with ClassCreator

use of io.quarkus.gizmo.ClassCreator in project quarkus-github-app by quarkiverse.

the class GitHubAppProcessor method generateAnnotationLiterals.

private static void generateAnnotationLiterals(ClassOutput classOutput, DispatchingConfiguration dispatchingConfiguration) {
    for (EventDispatchingConfiguration eventDispatchingConfiguration : dispatchingConfiguration.getEventConfigurations().values()) {
        for (EventAnnotationLiteral eventAnnotationLiteral : eventDispatchingConfiguration.getEventAnnotationLiterals()) {
            String literalClassName = getLiteralClassName(eventAnnotationLiteral.getName());
            String signature = String.format("Ljavax/enterprise/util/AnnotationLiteral<L%1$s;>;L%1$s;", eventAnnotationLiteral.getName().toString().replace('.', '/'));
            ClassCreator literalClassCreator = ClassCreator.builder().classOutput(classOutput).className(literalClassName).signature(signature).superClass(AnnotationLiteral.class).interfaces(eventAnnotationLiteral.getName().toString()).build();
            Class<?>[] parameterTypes = new Class<?>[eventAnnotationLiteral.getAttributes().size()];
            Arrays.fill(parameterTypes, String.class);
            MethodCreator constructorCreator = literalClassCreator.getMethodCreator("<init>", "V", (Object[]) parameterTypes);
            constructorCreator.invokeSpecialMethod(MethodDescriptor.ofConstructor(AnnotationLiteral.class), constructorCreator.getThis());
            for (int i = 0; i < eventAnnotationLiteral.getAttributes().size(); i++) {
                constructorCreator.writeInstanceField(FieldDescriptor.of(literalClassName, eventAnnotationLiteral.getAttributes().get(i), String.class), constructorCreator.getThis(), constructorCreator.getMethodParam(i));
                constructorCreator.setModifiers(Modifier.PUBLIC);
            }
            constructorCreator.returnValue(null);
            for (String attribute : eventAnnotationLiteral.getAttributes()) {
                // we only support String for now
                literalClassCreator.getFieldCreator(attribute, String.class).setModifiers(Modifier.PRIVATE);
                MethodCreator getterCreator = literalClassCreator.getMethodCreator(attribute, String.class);
                getterCreator.setModifiers(Modifier.PUBLIC);
                getterCreator.returnValue(getterCreator.readInstanceField(FieldDescriptor.of(literalClassName, attribute, String.class), getterCreator.getThis()));
            }
            literalClassCreator.close();
        }
    }
}
Also used : MethodCreator(io.quarkus.gizmo.MethodCreator) AnnotationLiteral(javax.enterprise.util.AnnotationLiteral) EventAnnotationLiteral(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventAnnotationLiteral) EventDispatchingConfiguration(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventDispatchingConfiguration) EventAnnotationLiteral(io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventAnnotationLiteral) ClassCreator(io.quarkus.gizmo.ClassCreator)

Example 9 with ClassCreator

use of io.quarkus.gizmo.ClassCreator in project quarkus-github-action by quarkiverse.

the class GitHubActionProcessor method generateActionMain.

/**
 * This method generates the <code>@QuarkusMain</code> class.
 * <p>
 * It emits the GitHub events as CDI events that will then be caught by the multiplexers.
 */
private static void generateActionMain(ClassOutput beanClassOutput, CombinedIndexBuildItem combinedIndex, LaunchModeBuildItem launchMode, DispatchingConfiguration dispatchingConfiguration, BuildProducer<ReflectiveClassBuildItem> reflectiveClasses) {
    String gitHubEventHandlerClassName = GitHubEventHandler.class.getName() + "Impl";
    reflectiveClasses.produce(new ReflectiveClassBuildItem(true, true, gitHubEventHandlerClassName));
    ClassCreator gitHubEventHandlerClassCreator = ClassCreator.builder().classOutput(beanClassOutput).className(gitHubEventHandlerClassName).interfaces(GitHubEventHandler.class).build();
    gitHubEventHandlerClassCreator.addAnnotation(Singleton.class);
    FieldCreator eventFieldCreator = gitHubEventHandlerClassCreator.getFieldCreator(EVENT_EMITTER_FIELD, Event.class);
    eventFieldCreator.addAnnotation(Inject.class);
    eventFieldCreator.setModifiers(Modifier.PROTECTED);
    MethodCreator handleMethodCreator = gitHubEventHandlerClassCreator.getMethodCreator("handle", void.class, GitHubEvent.class);
    handleMethodCreator.setModifiers(Modifier.PUBLIC);
    ResultHandle gitHubEventRh = handleMethodCreator.getMethodParam(0);
    ResultHandle dispatchedNameRh = handleMethodCreator.invokeVirtualMethod(GITHUB_EVENT_GET_NAME, gitHubEventRh);
    ResultHandle dispatchedEventRh = handleMethodCreator.invokeVirtualMethod(GITHUB_EVENT_GET_EVENT, gitHubEventRh);
    ResultHandle dispatchedActionRh = handleMethodCreator.invokeVirtualMethod(GITHUB_EVENT_GET_EVENT_ACTION, gitHubEventRh);
    for (Entry<String, Map<String, ActionDispatchingConfiguration>> actionConfigurationEntry : dispatchingConfiguration.getActionConfigurations().entrySet()) {
        String name = actionConfigurationEntry.getKey();
        Map<String, ActionDispatchingConfiguration> actionConfiguration = actionConfigurationEntry.getValue();
        BytecodeCreator nameMatchesCreator = handleMethodCreator.ifTrue(handleMethodCreator.invokeVirtualMethod(MethodDescriptors.OBJECT_EQUALS, handleMethodCreator.load(name), dispatchedNameRh)).trueBranch();
        ResultHandle actionAnnotationLiteralRh = nameMatchesCreator.newInstance(MethodDescriptor.ofConstructor(ActionLiteral.class, String.class), new ResultHandle[] { nameMatchesCreator.load(name) });
        for (Entry<String, ActionDispatchingConfiguration> eventConfigurationEntry : actionConfiguration.entrySet()) {
            String event = eventConfigurationEntry.getKey();
            ActionDispatchingConfiguration eventDispatchingConfiguration = eventConfigurationEntry.getValue();
            if (EventDefinition.ALL.equals(event)) {
                ResultHandle annotationLiteralArrayRh = nameMatchesCreator.newArray(Annotation.class, 1);
                nameMatchesCreator.writeArrayValue(annotationLiteralArrayRh, 0, actionAnnotationLiteralRh);
                fireEvent(nameMatchesCreator, gitHubEventHandlerClassCreator.getClassName(), gitHubEventRh, annotationLiteralArrayRh);
                continue;
            }
            BytecodeCreator eventMatchesCreator = nameMatchesCreator.ifTrue(nameMatchesCreator.invokeVirtualMethod(MethodDescriptors.OBJECT_EQUALS, nameMatchesCreator.load(event), dispatchedEventRh)).trueBranch();
            for (Entry<String, EventAnnotation> eventAnnotationEntry : eventDispatchingConfiguration.getEventAnnotations().entrySet()) {
                String action = eventAnnotationEntry.getKey();
                EventAnnotation eventAnnotation = eventAnnotationEntry.getValue();
                Class<?>[] literalParameterTypes = new Class<?>[eventAnnotation.getValues().size()];
                Arrays.fill(literalParameterTypes, String.class);
                List<ResultHandle> literalParameters = new ArrayList<>();
                ResultHandle eventAnnotationLiteralRh = eventMatchesCreator.newInstance(MethodDescriptor.ofConstructor(getLiteralClassName(eventAnnotation.getName()), (Object[]) literalParameterTypes), literalParameters.toArray(ResultHandle[]::new));
                ResultHandle annotationLiteralArrayRh = eventMatchesCreator.newArray(Annotation.class, 2);
                eventMatchesCreator.writeArrayValue(annotationLiteralArrayRh, 0, actionAnnotationLiteralRh);
                eventMatchesCreator.writeArrayValue(annotationLiteralArrayRh, 1, eventAnnotationLiteralRh);
                if (Actions.ALL.equals(action)) {
                    fireEvent(eventMatchesCreator, gitHubEventHandlerClassCreator.getClassName(), gitHubEventRh, annotationLiteralArrayRh);
                } else {
                    BytecodeCreator actionMatchesCreator = eventMatchesCreator.ifTrue(eventMatchesCreator.invokeVirtualMethod(MethodDescriptors.OBJECT_EQUALS, eventMatchesCreator.load(action), dispatchedActionRh)).trueBranch();
                    fireEvent(actionMatchesCreator, gitHubEventHandlerClassCreator.getClassName(), gitHubEventRh, annotationLiteralArrayRh);
                }
            }
        }
    }
    handleMethodCreator.returnValue(null);
    gitHubEventHandlerClassCreator.close();
}
Also used : FieldCreator(io.quarkus.gizmo.FieldCreator) ActionLiteral(io.quarkiverse.githubaction.Action.ActionLiteral) ArrayList(java.util.ArrayList) ActionDispatchingConfiguration(io.quarkiverse.githubaction.deployment.DispatchingConfiguration.ActionDispatchingConfiguration) BytecodeCreator(io.quarkus.gizmo.BytecodeCreator) ClassCreator(io.quarkus.gizmo.ClassCreator) EventAnnotation(io.quarkiverse.githubaction.deployment.DispatchingConfiguration.EventAnnotation) MethodCreator(io.quarkus.gizmo.MethodCreator) ResultHandle(io.quarkus.gizmo.ResultHandle) GitHubEventHandler(io.quarkiverse.githubaction.runtime.GitHubEventHandler) Map(java.util.Map) HashMap(java.util.HashMap) ReflectiveClassBuildItem(io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem)

Example 10 with ClassCreator

use of io.quarkus.gizmo.ClassCreator in project quarkus-github-action by quarkiverse.

the class GitHubActionProcessor method generateAnnotationLiterals.

private static void generateAnnotationLiterals(ClassOutput classOutput, DispatchingConfiguration dispatchingConfiguration) {
    for (ActionDispatchingConfiguration eventDispatchingConfiguration : dispatchingConfiguration.getActionDispatchingConfigurations()) {
        for (EventAnnotationLiteral eventAnnotationLiteral : eventDispatchingConfiguration.getEventAnnotationLiterals()) {
            String literalClassName = getLiteralClassName(eventAnnotationLiteral.getName());
            String signature = String.format("Ljavax/enterprise/util/AnnotationLiteral<L%1$s;>;L%1$s;", eventAnnotationLiteral.getName().toString().replace('.', '/'));
            ClassCreator literalClassCreator = ClassCreator.builder().classOutput(classOutput).className(literalClassName).signature(signature).superClass(AnnotationLiteral.class).interfaces(eventAnnotationLiteral.getName().toString()).build();
            Class<?>[] parameterTypes = new Class<?>[eventAnnotationLiteral.getAttributes().size()];
            Arrays.fill(parameterTypes, String.class);
            MethodCreator constructorCreator = literalClassCreator.getMethodCreator("<init>", "V", (Object[]) parameterTypes);
            constructorCreator.invokeSpecialMethod(MethodDescriptor.ofConstructor(AnnotationLiteral.class), constructorCreator.getThis());
            for (int i = 0; i < eventAnnotationLiteral.getAttributes().size(); i++) {
                constructorCreator.writeInstanceField(FieldDescriptor.of(literalClassName, eventAnnotationLiteral.getAttributes().get(i), String.class), constructorCreator.getThis(), constructorCreator.getMethodParam(i));
                constructorCreator.setModifiers(Modifier.PUBLIC);
            }
            constructorCreator.returnValue(null);
            for (String attribute : eventAnnotationLiteral.getAttributes()) {
                // we only support String for now
                literalClassCreator.getFieldCreator(attribute, String.class).setModifiers(Modifier.PRIVATE);
                MethodCreator getterCreator = literalClassCreator.getMethodCreator(attribute, String.class);
                getterCreator.setModifiers(Modifier.PUBLIC);
                getterCreator.returnValue(getterCreator.readInstanceField(FieldDescriptor.of(literalClassName, attribute, String.class), getterCreator.getThis()));
            }
            literalClassCreator.close();
        }
    }
}
Also used : MethodCreator(io.quarkus.gizmo.MethodCreator) AnnotationLiteral(javax.enterprise.util.AnnotationLiteral) EventAnnotationLiteral(io.quarkiverse.githubaction.deployment.DispatchingConfiguration.EventAnnotationLiteral) ActionDispatchingConfiguration(io.quarkiverse.githubaction.deployment.DispatchingConfiguration.ActionDispatchingConfiguration) EventAnnotationLiteral(io.quarkiverse.githubaction.deployment.DispatchingConfiguration.EventAnnotationLiteral) ClassCreator(io.quarkus.gizmo.ClassCreator)

Aggregations

ClassCreator (io.quarkus.gizmo.ClassCreator)10 MethodCreator (io.quarkus.gizmo.MethodCreator)10 BuildStep (io.quarkus.deployment.annotations.BuildStep)6 ReflectiveClassBuildItem (io.quarkus.deployment.builditem.nativeimage.ReflectiveClassBuildItem)6 FieldCreator (io.quarkus.gizmo.FieldCreator)6 ResultHandle (io.quarkus.gizmo.ResultHandle)6 List (java.util.List)5 ActionDispatchingConfiguration (io.quarkiverse.githubaction.deployment.DispatchingConfiguration.ActionDispatchingConfiguration)4 ActionLiteral (io.quarkiverse.githubaction.Action.ActionLiteral)3 EventAnnotation (io.quarkiverse.githubaction.deployment.DispatchingConfiguration.EventAnnotation)3 EventAnnotationLiteral (io.quarkiverse.githubaction.deployment.DispatchingConfiguration.EventAnnotationLiteral)3 GitHubEventHandler (io.quarkiverse.githubaction.runtime.GitHubEventHandler)3 GitHubApiClassWithBridgeMethodsBuildItem (io.quarkiverse.githubapi.deployment.GitHubApiClassWithBridgeMethodsBuildItem)3 EventDispatchingConfiguration (io.quarkiverse.githubapp.deployment.DispatchingConfiguration.EventDispatchingConfiguration)3 AdditionalBeanBuildItem (io.quarkus.arc.deployment.AdditionalBeanBuildItem)3 GeneratedBeanBuildItem (io.quarkus.arc.deployment.GeneratedBeanBuildItem)3 GeneratedBeanGizmoAdaptor (io.quarkus.arc.deployment.GeneratedBeanGizmoAdaptor)3 BuildProducer (io.quarkus.deployment.annotations.BuildProducer)3 CombinedIndexBuildItem (io.quarkus.deployment.builditem.CombinedIndexBuildItem)3 FeatureBuildItem (io.quarkus.deployment.builditem.FeatureBuildItem)3