Skip to content
Permalink
Browse files
GERONIMO-6582 Adding validation of method signatures via MicroProfile.
Upgrading test dependencies.
  • Loading branch information
johnament committed Dec 27, 2017
1 parent 5b7ccac commit 27626ffdfed0f609294b95d790e767e0a1f10daa
Showing 6 changed files with 170 additions and 23 deletions.
@@ -55,8 +55,8 @@
<maven.compiler.target>1.8</maven.compiler.target>
<microprofile-fault-tolerance.version>1.0</microprofile-fault-tolerance.version>
<owb.version>2.0.1</owb.version>
<arquillian.version>1.1.13.Final</arquillian.version>
<arquillian-weld-embedded.version>2.0.0.Beta5</arquillian-weld-embedded.version>
<arquillian.version>1.1.14.Final</arquillian.version>
<arquillian-weld-embedded.version>2.0.0.Final</arquillian-weld-embedded.version>
<cdi2-api.version>2.0</cdi2-api.version>
<weld.version>3.0.1.Final</weld.version>
</properties>
@@ -0,0 +1,143 @@
/*
* 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);
}
}
@@ -26,6 +26,7 @@
import org.eclipse.microprofile.faulttolerance.Timeout;

import javax.enterprise.event.Observes;
import javax.enterprise.inject.spi.AfterBeanDiscovery;
import javax.enterprise.inject.spi.AnnotatedConstructor;
import javax.enterprise.inject.spi.AnnotatedField;
import javax.enterprise.inject.spi.AnnotatedMethod;
@@ -42,12 +43,17 @@
import static org.apache.safeguard.api.SafeguardEnabled.INSTANCE;

public class SafeguardExtension implements Extension {
public void findFaultTolerantBeans(@Observes @WithAnnotations({Retry.class, CircuitBreaker.class, Timeout.class,
Bulkhead.class})
ProcessAnnotatedType<?> pat) {
private MicroProfileValidator microProfileValidator = new MicroProfileValidator();
public <X> void findFaultTolerantBeans(@Observes @WithAnnotations({Retry.class, CircuitBreaker.class, Timeout.class,
Bulkhead.class}) ProcessAnnotatedType<X> pat) {
if (!pat.getAnnotatedType().isAnnotationPresent(SafeguardEnabled.class)) {
pat.setAnnotatedType(new SafeguardAnnotatedTypeWrapper(pat.getAnnotatedType()));
pat.setAnnotatedType(new SafeguardAnnotatedTypeWrapper<>(pat.getAnnotatedType()));
}
microProfileValidator.parse(pat.getAnnotatedType());
}

public void throwExceptions(@Observes AfterBeanDiscovery afterBeanDiscovery) {
microProfileValidator.forThrowable(afterBeanDiscovery::addDefinitionError);
}

private static class SafeguardAnnotatedTypeWrapper<X> implements AnnotatedType<X> {
@@ -19,6 +19,7 @@

package org.apache.safeguard.impl.executionPlans;

import org.apache.safeguard.exception.SafeguardException;
import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerBuilder;
import org.apache.safeguard.impl.circuitbreaker.FailsafeCircuitBreakerDefinition;
import org.apache.safeguard.impl.retry.FailsafeRetryBuilder;
@@ -30,6 +31,8 @@
import java.util.concurrent.TimeoutException;

class MicroprofileAnnotationMapper {
private static final String RETRY_FORMAT = "%s/Retry/%s";
private static final String CIRCUIT_BREAKER_FORMAT = "%s/CircuitBreaker/%s";
static FailsafeRetryDefinition mapRetry(Retry retry, FailsafeRetryBuilder retryBuilder) {
retryBuilder.withMaxRetries(retry.maxRetries())
.withRetryOn(retry.retryOn())
@@ -19,6 +19,8 @@

package org.apache.safeguard.impl.util;

import javax.enterprise.inject.spi.AnnotatedMethod;
import javax.enterprise.inject.spi.AnnotatedType;
import java.lang.annotation.Annotation;
import java.lang.reflect.Method;

@@ -35,4 +37,14 @@ public static <T extends Annotation> T getAnnotation(Method method, Class<T> cla
return method.getDeclaringClass().getAnnotation(clazz);
}
}

public static <T extends Annotation> T getAnnotation(AnnotatedMethod<?> method,
AnnotatedType<?> type,
Class<T> clazz) {
T annotation = method.getAnnotation(clazz);
if(annotation != null) {
return annotation;
}
return type.getAnnotation(clazz);
}
}
@@ -72,23 +72,6 @@
</dependenciesToScan>
<excludes>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.ConfigTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.illegalConfig.IncompatibleFallbackMethodTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.illegalConfig.IncompatibleFallbackMethodWithArgsTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.illegalConfig.IncompatibleFallbackTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidBulkheadAsynchQueueTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerFailureRatioPosTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerFailureReqVol0Test</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidRetryJitterTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerFailureSuccess0Test</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerDelayTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidRetryDelayTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerFailureRatioNegTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidBulkheadValueTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerFailureReqVolNegTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidRetryDelayDurationTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidTimeoutValueTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidCircuitBreakerFailureSuccessNegTest</exclude>
<exclude>org.eclipse.microprofile.fault.tolerance.tck.invalidParameters.InvalidRetryMaxRetriesTest</exclude>
</excludes>
</configuration>
</plugin>

0 comments on commit 27626ff

Please sign in to comment.