All Downloads are FREE. Search and download functionalities are using the official Maven repository.

io.helidon.microprofile.lra.ParticipantImpl Maven / Gradle / Ivy

/*
 * Copyright (c) 2021, 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.microprofile.lra;

import java.lang.System.Logger.Level;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;
import java.net.URI;
import java.util.Arrays;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.Set;
import java.util.stream.Collectors;

import io.helidon.lra.coordinator.client.Participant;

import jakarta.ws.rs.DELETE;
import jakarta.ws.rs.GET;
import jakarta.ws.rs.POST;
import jakarta.ws.rs.PUT;
import jakarta.ws.rs.Path;
import jakarta.ws.rs.Produces;
import jakarta.ws.rs.core.UriBuilder;
import org.eclipse.microprofile.lra.annotation.AfterLRA;
import org.eclipse.microprofile.lra.annotation.Compensate;
import org.eclipse.microprofile.lra.annotation.Complete;
import org.eclipse.microprofile.lra.annotation.Forget;
import org.eclipse.microprofile.lra.annotation.Status;
import org.eclipse.microprofile.lra.annotation.ws.rs.LRA;
import org.eclipse.microprofile.lra.annotation.ws.rs.Leave;

class ParticipantImpl implements Participant {

    private static final System.Logger LOGGER = System.getLogger(ParticipantImpl.class.getName());

    static final Set> LRA_ANNOTATIONS =
            Set.of(
                    LRA.class,
                    Compensate.class,
                    Complete.class,
                    Forget.class,
                    Status.class,
                    AfterLRA.class,
                    Leave.class
            );

    static final Set> JAX_RS_ANNOTATIONS =
            Set.of(
                    Path.class,
                    GET.class,
                    PUT.class,
                    POST.class,
                    DELETE.class,
                    Produces.class
            );


    private final Map, URI> compensatorLinks = new HashMap<>();
    private final Map, Set> methodMap;

    ParticipantImpl(URI baseUri, String contextPath, Class resourceClazz) {
        methodMap = scanForLRAMethods(resourceClazz);
        methodMap.entrySet().stream()
                // Looking only for participant methods
                .filter(e -> e.getKey() != LRA.class)
                .forEach(e -> {
                    Set methods = e.getValue();
                    Method method = methods.stream().iterator().next();
                    if (methods.size() > 1) {
                        LOGGER.log(Level.WARNING,
                                "LRA participant {0} contains more then one @{1} method!",
                                new Object[] {method.getDeclaringClass().getName(),
                                        e.getKey().getSimpleName()}
                        );
                    }

                    if (Arrays.stream(method
                            .getDeclaredAnnotations())
                            .map(Annotation::annotationType)
                            .noneMatch(JAX_RS_ANNOTATIONS::contains)) {
                        //no jax-rs method
                        URI uri = UriBuilder.fromUri(baseUri)
                                .path(contextPath) //Auxiliary non Jax-Rs resource
                                .path(e.getKey().getSimpleName().toLowerCase())//@Complete -> /complete
                                .path(resourceClazz.getName())
                                .path(method.getName())
                                .build();
                        compensatorLinks.put(e.getKey(), uri);
                        return;
                    }

                    UriBuilder builder = UriBuilder.fromUri(baseUri)
                            .path(resourceClazz);

                    if (method.getAnnotation(Path.class) != null) {
                        builder.path(resourceClazz, method.getName());
                    }

                    URI uri = builder.build();
                    compensatorLinks.put(e.getKey(), uri);
                });
    }

    boolean isLraMethod(Method m) {
        return methodMap.values().stream().flatMap(Collection::stream).anyMatch(m::equals);
    }

    public Optional compensate() {
        return Optional.ofNullable(compensatorLinks.get(Compensate.class));
    }

    public Optional complete() {
        return Optional.ofNullable(compensatorLinks.get(Complete.class));
    }

    public Optional forget() {
        return Optional.ofNullable(compensatorLinks.get(Forget.class));
    }

    public Optional leave() {
        return Optional.ofNullable(compensatorLinks.get(Leave.class));
    }

    public Optional after() {
        return Optional.ofNullable(compensatorLinks.get(AfterLRA.class));
    }

    public Optional status() {
        return Optional.ofNullable(compensatorLinks.get(Status.class));
    }

    static Optional getLRAAnnotation(Method m) {
        List found = Arrays.stream(m.getDeclaredAnnotations())
                .filter(a -> LRA_ANNOTATIONS.contains(a.annotationType()))
                .collect(Collectors.toList());

        if (found.size() == 0) {
            // LRA can be inherited from class or its predecessors
            var clazz = m.getDeclaringClass();
            do {
                LRA clazzLraAnnotation = clazz.getAnnotation(LRA.class);
                if (clazzLraAnnotation != null) {
                    return Optional.of(clazzLraAnnotation);
                }
                clazz = clazz.getSuperclass();
            } while (clazz != null);
        }

        return found.stream().findFirst();
    }

    static Map, Set> scanForLRAMethods(Class clazz) {
        Map, Set> methods = new HashMap<>();
        do {
            for (Method m : clazz.getDeclaredMethods()) {
                Optional annotation = getLRAAnnotation(m);
                if (annotation.isPresent()) {
                    var annotationType = annotation.get().annotationType();
                    methods.putIfAbsent(annotationType, new HashSet<>());
                    methods.get(annotationType).add(m);
                }
            }
            clazz = clazz.getSuperclass();
        } while (clazz != null);
        return methods;
    }

    @Override
    public String toString() {
        return "ParticipantImpl{"
                + this.complete()
                .or(this::compensate)
                .or(this::after)
                .map(URI::toASCIIString)
                .orElse(null)
                + "}";
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy