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)
+ "}";
}
}