blob: 714818b5246c4848fc0e8447275ff3d870cda6c3 [file] [log] [blame]
/*
* Licensed to the Apache Software Foundation (ASF) under one
* or more contributor license agreements. See the NOTICE file
* distributed with this work for additional information
* regarding copyright ownership. The ASF licenses this file
* to you 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 org.apache.safeguard.impl.cdi;
import org.apache.safeguard.exception.SafeguardException;
import org.apache.safeguard.impl.util.AnnotationUtil;
import org.eclipse.microprofile.faulttolerance.Bulkhead;
import org.eclipse.microprofile.faulttolerance.CircuitBreaker;
import org.eclipse.microprofile.faulttolerance.ExecutionContext;
import org.eclipse.microprofile.faulttolerance.Fallback;
import org.eclipse.microprofile.faulttolerance.Retry;
import org.eclipse.microprofile.faulttolerance.Timeout;
import javax.enterprise.inject.Vetoed;
import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import java.lang.reflect.Method;
import java.time.Duration;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Consumer;
@Vetoed
final class MicroProfileValidator {
private List<Throwable> capturedThrowables = new ArrayList<>();
void parse(AnnotatedType<?> annotatedType) {
for(AnnotatedMethod<?> method : annotatedType.getMethods()) {
Retry retry = AnnotationUtil.getAnnotation(method, annotatedType, Retry.class);
if (retry != null) {
validateRetry(retry, method);
}
Bulkhead bulkhead = AnnotationUtil.getAnnotation(method, annotatedType, Bulkhead.class);
if (bulkhead != null) {
validateBulkhead(bulkhead, method);
}
Timeout timeout = AnnotationUtil.getAnnotation(method, annotatedType, Timeout.class);
if (timeout != null) {
validateTimeout(timeout, method);
}
CircuitBreaker circuitBreaker = AnnotationUtil.getAnnotation(method, annotatedType, CircuitBreaker.class);
if (circuitBreaker != null) {
validateCircuitBreaker(circuitBreaker, method);
}
Fallback fallback = AnnotationUtil.getAnnotation(method, annotatedType, Fallback.class);
if (fallback != null) {
validateFallback(fallback, method, annotatedType);
}
}
}
private void validateFallback(Fallback fallback, AnnotatedMethod<?> method, AnnotatedType<?> annotatedType) {
if(fallback.fallbackMethod().equals("") && fallback.value().equals(Fallback.DEFAULT.class)) {
capturedThrowables.add(new SafeguardException("Invalid Fallback definition on method " + method));
}
else if(!fallback.fallbackMethod().equals("") && !fallback.value().equals(Fallback.DEFAULT.class)) {
capturedThrowables.add(new SafeguardException("Invalid Fallback definition on method " + method));
}
else if(!fallback.fallbackMethod().equals("")) {
boolean found = false;
for(AnnotatedMethod<?> otherMethod : annotatedType.getMethods()) {
if(otherMethod.getJavaMember().getName().equals(fallback.fallbackMethod())) {
found = true;
if(!method.getJavaMember().getReturnType().equals(otherMethod.getJavaMember().getReturnType())) {
capturedThrowables.add(new SafeguardException("Invalid Fallback definition on method " + method +
" wrong return type"));
} else if(!Arrays.equals(method.getJavaMember().getParameterTypes(), otherMethod.getJavaMember().getParameterTypes())) {
capturedThrowables.add(new SafeguardException("Invalid Fallback definition on method " + method +
" wrong parameters"));
}
}
}
if (!found) {
capturedThrowables.add(new SafeguardException("Invalid Fallback definition on method " + method +
" fallback method not found"));
}
}
else {
try {
Method methodHandle = fallback.value().getMethod("handle", ExecutionContext.class);
if(!methodHandle.getReturnType().equals(method.getJavaMember().getReturnType())) {
capturedThrowables.add(new SafeguardException("Invalid Fallback definition on method " + method +
" wrong return type"));
}
} catch (NoSuchMethodException e) {
capturedThrowables.add(new SafeguardException("Invalid Fallback definition on method " + method +
" fallback method not found"));
}
}
}
private void validateTimeout(Timeout timeout, AnnotatedMethod<?> method) {
if(timeout.value() < 0) {
capturedThrowables.add(new SafeguardException("Invalid Timeout definition on method " + method));
}
}
private void validateCircuitBreaker(CircuitBreaker circuitBreaker, AnnotatedMethod<?> method) {
if(circuitBreaker.requestVolumeThreshold() <= 0 || circuitBreaker.delay() < 0 || circuitBreaker.failureRatio() < 0.0
|| circuitBreaker.failureRatio() > 1.0 || circuitBreaker.successThreshold() <= 0) {
capturedThrowables.add(new SafeguardException("Invalid CircuitBreaker definition on method " + method));
}
}
private void validateRetry(Retry retry, AnnotatedMethod<?> method) {
if(retry.jitter() < 0 || retry.maxDuration() < 0 || retry.delay() < 0 || retry.maxRetries() < 0) {
capturedThrowables.add(new SafeguardException("Invalid Retry definition on method " + method));
}
Duration delay = Duration.of(retry.delay(), retry.delayUnit());
Duration maxDuration = Duration.of(retry.maxDuration(), retry.durationUnit());
if(maxDuration.compareTo(delay) < 0) {
capturedThrowables.add(new SafeguardException("Invalid Retry definition on method " + method));
}
}
private void validateBulkhead(Bulkhead bulkhead, AnnotatedMethod<?> method) {
if(bulkhead.value() < 0 || bulkhead.waitingTaskQueue() < 0) {
capturedThrowables.add(new SafeguardException("Invalid Bulkhead definition on method " + method));
}
}
void forThrowable(Consumer<Throwable> consumer) {
capturedThrowables.forEach(consumer);
}
}