Line endings
This commit is contained in:
@@ -1,83 +1,83 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A special purpose {@link Classifier} with easy configuration options for
|
||||
* mapping from one arbitrary type of object to another via a pattern matcher.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BackToBackPatternClassifier<C, T> implements Classifier<C, T> {
|
||||
|
||||
private Classifier<C, String> router;
|
||||
|
||||
private Classifier<String, T> matcher;
|
||||
|
||||
/**
|
||||
* Default constructor, provided as a convenience for people using setter
|
||||
* injection.
|
||||
*/
|
||||
public BackToBackPatternClassifier() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a classifier with input to the router and output from the matcher.
|
||||
*
|
||||
* @param router see {@link #setRouterDelegate(Object)}
|
||||
* @param matcher see {@link #setMatcherMap(Map)}
|
||||
*/
|
||||
public BackToBackPatternClassifier(Classifier<C, String> router, Classifier<String, T> matcher) {
|
||||
super();
|
||||
this.router = router;
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for creating a pattern matching classifier for the
|
||||
* matcher component.
|
||||
*
|
||||
* @param map maps pattern keys with wildcards to output values
|
||||
*/
|
||||
public void setMatcherMap(Map<String, T> map) {
|
||||
this.matcher = new PatternMatchingClassifier<T>(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method of creating a router classifier based on a plain old
|
||||
* Java Object. The object provided must have precisely one public method
|
||||
* that either has the <code>@Classifier</code> annotation or accepts a single argument
|
||||
* and outputs a String. This will be used to create an input classifier for
|
||||
* the router component.
|
||||
*
|
||||
* @param delegate the delegate object used to create a router classifier
|
||||
*/
|
||||
public void setRouterDelegate(Object delegate) {
|
||||
this.router = new ClassifierAdapter<C,String>(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify the input and map to a String, then take that and put it into a
|
||||
* pattern matcher to match to an output value.
|
||||
*/
|
||||
public T classify(C classifiable) {
|
||||
return matcher.classify(router.classify(classifiable));
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A special purpose {@link Classifier} with easy configuration options for
|
||||
* mapping from one arbitrary type of object to another via a pattern matcher.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BackToBackPatternClassifier<C, T> implements Classifier<C, T> {
|
||||
|
||||
private Classifier<C, String> router;
|
||||
|
||||
private Classifier<String, T> matcher;
|
||||
|
||||
/**
|
||||
* Default constructor, provided as a convenience for people using setter
|
||||
* injection.
|
||||
*/
|
||||
public BackToBackPatternClassifier() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Set up a classifier with input to the router and output from the matcher.
|
||||
*
|
||||
* @param router see {@link #setRouterDelegate(Object)}
|
||||
* @param matcher see {@link #setMatcherMap(Map)}
|
||||
*/
|
||||
public BackToBackPatternClassifier(Classifier<C, String> router, Classifier<String, T> matcher) {
|
||||
super();
|
||||
this.router = router;
|
||||
this.matcher = matcher;
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method for creating a pattern matching classifier for the
|
||||
* matcher component.
|
||||
*
|
||||
* @param map maps pattern keys with wildcards to output values
|
||||
*/
|
||||
public void setMatcherMap(Map<String, T> map) {
|
||||
this.matcher = new PatternMatchingClassifier<T>(map);
|
||||
}
|
||||
|
||||
/**
|
||||
* A convenience method of creating a router classifier based on a plain old
|
||||
* Java Object. The object provided must have precisely one public method
|
||||
* that either has the <code>@Classifier</code> annotation or accepts a single argument
|
||||
* and outputs a String. This will be used to create an input classifier for
|
||||
* the router component.
|
||||
*
|
||||
* @param delegate the delegate object used to create a router classifier
|
||||
*/
|
||||
public void setRouterDelegate(Object delegate) {
|
||||
this.router = new ClassifierAdapter<C,String>(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify the input and map to a String, then take that and put it into a
|
||||
* pattern matcher to match to an output value.
|
||||
*/
|
||||
public T classify(C classifiable) {
|
||||
return matcher.classify(router.classify(classifiable));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,101 +1,101 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import org.springframework.classify.util.MethodInvoker;
|
||||
import org.springframework.classify.util.MethodInvokerUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Wrapper for an object to adapt it to the {@link Classifier} interface.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ClassifierAdapter<C, T> implements Classifier<C, T> {
|
||||
|
||||
private MethodInvoker invoker;
|
||||
|
||||
private Classifier<C, T> classifier;
|
||||
|
||||
/**
|
||||
* Default constructor for use with setter injection.
|
||||
*/
|
||||
public ClassifierAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Classifier} from the delegate provided. Use the
|
||||
* constructor as an alternative to the {@link #setDelegate(Object)} method.
|
||||
*
|
||||
* @param delegate the delegate
|
||||
*/
|
||||
public ClassifierAdapter(Object delegate) {
|
||||
setDelegate(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Classifier} from the delegate provided. Use the
|
||||
* constructor as an alternative to the {@link #setDelegate(Classifier)}
|
||||
* method.
|
||||
*
|
||||
* @param delegate the classifier to delegate to
|
||||
*/
|
||||
public ClassifierAdapter(Classifier<C, T> delegate) {
|
||||
classifier = delegate;
|
||||
}
|
||||
|
||||
public void setDelegate(Classifier<C, T> delegate) {
|
||||
classifier = delegate;
|
||||
invoker = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the
|
||||
* {@link org.springframework.classify.annotation.Classifier
|
||||
* Classifier} annotation on a method in the supplied delegate and use that
|
||||
* to create a {@link Classifier} from the parameter type to the return
|
||||
* type. If the annotation is not found a unique non-void method with a
|
||||
* single parameter will be used, if it exists. The signature of the method
|
||||
* cannot be checked here, so might be a runtime exception when the method
|
||||
* is invoked if the signature doesn't match the classifier types.
|
||||
*
|
||||
* @param delegate an object with an annotated method
|
||||
*/
|
||||
public final void setDelegate(Object delegate) {
|
||||
classifier = null;
|
||||
invoker = MethodInvokerUtils.getMethodInvokerByAnnotation(
|
||||
org.springframework.classify.annotation.Classifier.class, delegate);
|
||||
if (invoker == null) {
|
||||
invoker = MethodInvokerUtils.<C, T> getMethodInvokerForSingleArgument(delegate);
|
||||
}
|
||||
Assert.state(invoker != null, "No single argument public method with or without "
|
||||
+ "@Classifier was found in delegate of type " + delegate.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T classify(C classifiable) {
|
||||
if (classifier != null) {
|
||||
return classifier.classify(classifiable);
|
||||
}
|
||||
return (T) invoker.invokeMethod(classifiable);
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import org.springframework.classify.util.MethodInvoker;
|
||||
import org.springframework.classify.util.MethodInvokerUtils;
|
||||
import org.springframework.util.Assert;
|
||||
|
||||
/**
|
||||
* Wrapper for an object to adapt it to the {@link Classifier} interface.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ClassifierAdapter<C, T> implements Classifier<C, T> {
|
||||
|
||||
private MethodInvoker invoker;
|
||||
|
||||
private Classifier<C, T> classifier;
|
||||
|
||||
/**
|
||||
* Default constructor for use with setter injection.
|
||||
*/
|
||||
public ClassifierAdapter() {
|
||||
super();
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Classifier} from the delegate provided. Use the
|
||||
* constructor as an alternative to the {@link #setDelegate(Object)} method.
|
||||
*
|
||||
* @param delegate the delegate
|
||||
*/
|
||||
public ClassifierAdapter(Object delegate) {
|
||||
setDelegate(delegate);
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a new {@link Classifier} from the delegate provided. Use the
|
||||
* constructor as an alternative to the {@link #setDelegate(Classifier)}
|
||||
* method.
|
||||
*
|
||||
* @param delegate the classifier to delegate to
|
||||
*/
|
||||
public ClassifierAdapter(Classifier<C, T> delegate) {
|
||||
classifier = delegate;
|
||||
}
|
||||
|
||||
public void setDelegate(Classifier<C, T> delegate) {
|
||||
classifier = delegate;
|
||||
invoker = null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Search for the
|
||||
* {@link org.springframework.classify.annotation.Classifier
|
||||
* Classifier} annotation on a method in the supplied delegate and use that
|
||||
* to create a {@link Classifier} from the parameter type to the return
|
||||
* type. If the annotation is not found a unique non-void method with a
|
||||
* single parameter will be used, if it exists. The signature of the method
|
||||
* cannot be checked here, so might be a runtime exception when the method
|
||||
* is invoked if the signature doesn't match the classifier types.
|
||||
*
|
||||
* @param delegate an object with an annotated method
|
||||
*/
|
||||
public final void setDelegate(Object delegate) {
|
||||
classifier = null;
|
||||
invoker = MethodInvokerUtils.getMethodInvokerByAnnotation(
|
||||
org.springframework.classify.annotation.Classifier.class, delegate);
|
||||
if (invoker == null) {
|
||||
invoker = MethodInvokerUtils.<C, T> getMethodInvokerForSingleArgument(delegate);
|
||||
}
|
||||
Assert.state(invoker != null, "No single argument public method with or without "
|
||||
+ "@Classifier was found in delegate of type " + delegate.getClass());
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritDoc}
|
||||
*/
|
||||
@SuppressWarnings("unchecked")
|
||||
public T classify(C classifiable) {
|
||||
if (classifier != null) {
|
||||
return classifier.classify(classifiable);
|
||||
}
|
||||
return (T) invoker.invokeMethod(classifiable);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@link Classifier} that maps from String patterns with wildcards to a set
|
||||
* of values of a given type. An input String is matched with the most specific
|
||||
* pattern possible to the corresponding value in an input map. A default value
|
||||
* should be specified with a pattern key of "*".
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PatternMatchingClassifier<T> implements Classifier<String, T> {
|
||||
|
||||
private PatternMatcher<T> values;
|
||||
|
||||
/**
|
||||
* Default constructor. Use the setter or the other constructor to create a
|
||||
* sensible classifier, otherwise all inputs will cause an exception.
|
||||
*/
|
||||
public PatternMatchingClassifier() {
|
||||
this(new HashMap<String, T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a classifier from the provided map. The keys are patterns, using
|
||||
* '?' as a single character and '*' as multi-character wildcard.
|
||||
*
|
||||
* @param values the values to use in the {@link PatternMatcher}
|
||||
*/
|
||||
public PatternMatchingClassifier(Map<String, T> values) {
|
||||
super();
|
||||
this.values = new PatternMatcher<T>(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* A map from pattern to value
|
||||
* @param values the pattern map to set
|
||||
*/
|
||||
public void setPatternMap(Map<String, T> values) {
|
||||
this.values = new PatternMatcher<T>(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify the input by matching it against the patterns provided in
|
||||
* {@link #setPatternMap(Map)}. The most specific pattern that matches will
|
||||
* be used to locate a value.
|
||||
*
|
||||
* @return the value matching the most specific pattern possible
|
||||
*
|
||||
* @throws IllegalStateException if no matching value is found.
|
||||
*/
|
||||
public T classify(String classifiable) {
|
||||
T value = values.match(classifiable);
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* A {@link Classifier} that maps from String patterns with wildcards to a set
|
||||
* of values of a given type. An input String is matched with the most specific
|
||||
* pattern possible to the corresponding value in an input map. A default value
|
||||
* should be specified with a pattern key of "*".
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PatternMatchingClassifier<T> implements Classifier<String, T> {
|
||||
|
||||
private PatternMatcher<T> values;
|
||||
|
||||
/**
|
||||
* Default constructor. Use the setter or the other constructor to create a
|
||||
* sensible classifier, otherwise all inputs will cause an exception.
|
||||
*/
|
||||
public PatternMatchingClassifier() {
|
||||
this(new HashMap<String, T>());
|
||||
}
|
||||
|
||||
/**
|
||||
* Create a classifier from the provided map. The keys are patterns, using
|
||||
* '?' as a single character and '*' as multi-character wildcard.
|
||||
*
|
||||
* @param values the values to use in the {@link PatternMatcher}
|
||||
*/
|
||||
public PatternMatchingClassifier(Map<String, T> values) {
|
||||
super();
|
||||
this.values = new PatternMatcher<T>(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* A map from pattern to value
|
||||
* @param values the pattern map to set
|
||||
*/
|
||||
public void setPatternMap(Map<String, T> values) {
|
||||
this.values = new PatternMatcher<T>(values);
|
||||
}
|
||||
|
||||
/**
|
||||
* Classify the input by matching it against the patterns provided in
|
||||
* {@link #setPatternMap(Map)}. The most specific pattern that matches will
|
||||
* be used to locate a value.
|
||||
*
|
||||
* @return the value matching the most specific pattern possible
|
||||
*
|
||||
* @throws IllegalStateException if no matching value is found.
|
||||
*/
|
||||
public T classify(String classifiable) {
|
||||
T value = values.match(classifiable);
|
||||
return value;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,38 +1,38 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Mark a method as capable of classifying its input to an instance of its
|
||||
* output. Should only be used on non-void methods with one parameter.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Classifier {
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify.annotation;
|
||||
|
||||
import java.lang.annotation.Documented;
|
||||
import java.lang.annotation.ElementType;
|
||||
import java.lang.annotation.Inherited;
|
||||
import java.lang.annotation.Retention;
|
||||
import java.lang.annotation.RetentionPolicy;
|
||||
import java.lang.annotation.Target;
|
||||
|
||||
/**
|
||||
* Mark a method as capable of classifying its input to an instance of its
|
||||
* output. Should only be used on non-void methods with one parameter.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
@Target(ElementType.METHOD)
|
||||
@Retention(RetentionPolicy.RUNTIME)
|
||||
@Inherited
|
||||
@Documented
|
||||
public @interface Classifier {
|
||||
|
||||
}
|
||||
|
||||
@@ -1,234 +1,234 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.retry.interceptor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.classify.Classifier;
|
||||
import org.springframework.retry.RecoveryCallback;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryOperations;
|
||||
import org.springframework.retry.RetryState;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.support.DefaultRetryState;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A {@link MethodInterceptor} that can be used to automatically retry calls to a method
|
||||
* on a service if it fails. The argument to the service method is treated as an item to
|
||||
* be remembered in case the call fails. So the retry operation is stateful, and the item
|
||||
* that failed is tracked by its unique key (via {@link MethodArgumentsKeyGenerator})
|
||||
* until the retry is exhausted, at which point the {@link MethodInvocationRecoverer} is
|
||||
* called.
|
||||
*
|
||||
* The main use case for this is where the service is transactional, via a transaction
|
||||
* interceptor on the interceptor chain. In this case the retry (and recovery on
|
||||
* exhausted) always happens in a new transaction.
|
||||
*
|
||||
* The injected {@link RetryOperations} is used to control the number of retries. By
|
||||
* default it will retry a fixed number of times, according to the defaults in
|
||||
* {@link RetryTemplate}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class StatefulRetryOperationsInterceptor implements MethodInterceptor {
|
||||
|
||||
private transient final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private MethodArgumentsKeyGenerator keyGenerator;
|
||||
|
||||
private MethodInvocationRecoverer<?> recoverer;
|
||||
|
||||
private NewMethodArgumentsIdentifier newMethodArgumentsIdentifier;
|
||||
|
||||
private RetryOperations retryOperations;
|
||||
|
||||
private String label;
|
||||
|
||||
private Classifier<? super Throwable, Boolean> rollbackClassifier;
|
||||
|
||||
public StatefulRetryOperationsInterceptor() {
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
this.retryOperations = retryTemplate;
|
||||
}
|
||||
|
||||
public void setRetryOperations(RetryOperations retryTemplate) {
|
||||
Assert.notNull(retryTemplate, "'retryOperations' cannot be null.");
|
||||
this.retryOperations = retryTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link MethodInvocationRecoverer} to use if the retry is
|
||||
* exhausted. The recoverer should be able to return an object of the same type as the
|
||||
* target object because its return value will be used to return to the caller in the
|
||||
* case of a recovery.
|
||||
* @param recoverer the {@link MethodInvocationRecoverer} to set
|
||||
*/
|
||||
public void setRecoverer(MethodInvocationRecoverer<?> recoverer) {
|
||||
this.recoverer = recoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback classifier for the retry state. Default to null (meaning rollback
|
||||
* for all).
|
||||
*
|
||||
* @param rollbackClassifier the rollbackClassifier to set
|
||||
*/
|
||||
public void setRollbackClassifier(
|
||||
Classifier<? super Throwable, Boolean> rollbackClassifier) {
|
||||
this.rollbackClassifier = rollbackClassifier;
|
||||
}
|
||||
|
||||
public void setKeyGenerator(MethodArgumentsKeyGenerator keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link NewMethodArgumentsIdentifier}. Only set this if the
|
||||
* arguments to the intercepted method can be inspected to find out if they have never
|
||||
* been processed before.
|
||||
* @param newMethodArgumentsIdentifier the {@link NewMethodArgumentsIdentifier} to set
|
||||
*/
|
||||
public void setNewItemIdentifier(
|
||||
NewMethodArgumentsIdentifier newMethodArgumentsIdentifier) {
|
||||
this.newMethodArgumentsIdentifier = newMethodArgumentsIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the method invocation in a stateful retry with the policy and other helpers
|
||||
* provided. If there is a failure the exception will generally be re-thrown. The only
|
||||
* time it is not re-thrown is when retry is exhausted and the recovery path is taken
|
||||
* (though the {@link MethodInvocationRecoverer} provided if there is one). In that
|
||||
* case the value returned from the method invocation will be the value returned by
|
||||
* the recoverer (so the return type for that should be the same as the intercepted
|
||||
* method).
|
||||
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
|
||||
* @see MethodInvocationRecoverer#recover(Object[], Throwable)
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Executing proxied method in stateful retry: "
|
||||
+ invocation.getStaticPart() + "("
|
||||
+ ObjectUtils.getIdentityHexString(invocation) + ")");
|
||||
}
|
||||
|
||||
Object[] args = invocation.getArguments();
|
||||
Object arg = args;
|
||||
if (args.length == 1) {
|
||||
arg = args[0];
|
||||
}
|
||||
final Object item = arg;
|
||||
|
||||
RetryState retryState = new DefaultRetryState(
|
||||
this.keyGenerator != null ? this.keyGenerator.getKey(args) : item,
|
||||
this.newMethodArgumentsIdentifier != null
|
||||
&& this.newMethodArgumentsIdentifier.isNew(args),
|
||||
this.rollbackClassifier);
|
||||
|
||||
Object result = this.retryOperations.execute(
|
||||
new MethodInvocationRetryCallback(invocation, label), this.recoverer != null
|
||||
? new ItemRecovererCallback(args, this.recoverer) : null,
|
||||
retryState);
|
||||
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Exiting proxied method in stateful retry with result: ("
|
||||
+ result + ")");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static final class MethodInvocationRetryCallback
|
||||
implements RetryCallback<Object, Throwable> {
|
||||
|
||||
private final MethodInvocation invocation;
|
||||
private String label;
|
||||
|
||||
private MethodInvocationRetryCallback(MethodInvocation invocation, String label) {
|
||||
this.invocation = invocation;
|
||||
if (label!=null) {
|
||||
this.label = label;
|
||||
} else {
|
||||
this.label = invocation.getMethod().toGenericString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object doWithRetry(RetryContext context) throws Exception {
|
||||
context.setAttribute(RetryContext.NAME, label);
|
||||
try {
|
||||
return this.invocation.proceed();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Error e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static final class ItemRecovererCallback implements RecoveryCallback<Object> {
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
private final MethodInvocationRecoverer<?> recoverer;
|
||||
|
||||
/**
|
||||
* @param args the item that failed.
|
||||
*/
|
||||
private ItemRecovererCallback(Object[] args,
|
||||
MethodInvocationRecoverer<?> recoverer) {
|
||||
this.args = Arrays.asList(args).toArray();
|
||||
this.recoverer = recoverer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object recover(RetryContext context) {
|
||||
return this.recoverer.recover(this.args, context.getLastThrowable());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.retry.interceptor;
|
||||
|
||||
import java.util.Arrays;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.apache.commons.logging.Log;
|
||||
import org.apache.commons.logging.LogFactory;
|
||||
import org.springframework.classify.Classifier;
|
||||
import org.springframework.retry.RecoveryCallback;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.RetryOperations;
|
||||
import org.springframework.retry.RetryState;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.support.DefaultRetryState;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
import org.springframework.util.Assert;
|
||||
import org.springframework.util.ObjectUtils;
|
||||
|
||||
/**
|
||||
* A {@link MethodInterceptor} that can be used to automatically retry calls to a method
|
||||
* on a service if it fails. The argument to the service method is treated as an item to
|
||||
* be remembered in case the call fails. So the retry operation is stateful, and the item
|
||||
* that failed is tracked by its unique key (via {@link MethodArgumentsKeyGenerator})
|
||||
* until the retry is exhausted, at which point the {@link MethodInvocationRecoverer} is
|
||||
* called.
|
||||
*
|
||||
* The main use case for this is where the service is transactional, via a transaction
|
||||
* interceptor on the interceptor chain. In this case the retry (and recovery on
|
||||
* exhausted) always happens in a new transaction.
|
||||
*
|
||||
* The injected {@link RetryOperations} is used to control the number of retries. By
|
||||
* default it will retry a fixed number of times, according to the defaults in
|
||||
* {@link RetryTemplate}.
|
||||
*
|
||||
* @author Dave Syer
|
||||
*/
|
||||
public class StatefulRetryOperationsInterceptor implements MethodInterceptor {
|
||||
|
||||
private transient final Log logger = LogFactory.getLog(getClass());
|
||||
|
||||
private MethodArgumentsKeyGenerator keyGenerator;
|
||||
|
||||
private MethodInvocationRecoverer<?> recoverer;
|
||||
|
||||
private NewMethodArgumentsIdentifier newMethodArgumentsIdentifier;
|
||||
|
||||
private RetryOperations retryOperations;
|
||||
|
||||
private String label;
|
||||
|
||||
private Classifier<? super Throwable, Boolean> rollbackClassifier;
|
||||
|
||||
public StatefulRetryOperationsInterceptor() {
|
||||
RetryTemplate retryTemplate = new RetryTemplate();
|
||||
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
this.retryOperations = retryTemplate;
|
||||
}
|
||||
|
||||
public void setRetryOperations(RetryOperations retryTemplate) {
|
||||
Assert.notNull(retryTemplate, "'retryOperations' cannot be null.");
|
||||
this.retryOperations = retryTemplate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link MethodInvocationRecoverer} to use if the retry is
|
||||
* exhausted. The recoverer should be able to return an object of the same type as the
|
||||
* target object because its return value will be used to return to the caller in the
|
||||
* case of a recovery.
|
||||
* @param recoverer the {@link MethodInvocationRecoverer} to set
|
||||
*/
|
||||
public void setRecoverer(MethodInvocationRecoverer<?> recoverer) {
|
||||
this.recoverer = recoverer;
|
||||
}
|
||||
|
||||
/**
|
||||
* Rollback classifier for the retry state. Default to null (meaning rollback
|
||||
* for all).
|
||||
*
|
||||
* @param rollbackClassifier the rollbackClassifier to set
|
||||
*/
|
||||
public void setRollbackClassifier(
|
||||
Classifier<? super Throwable, Boolean> rollbackClassifier) {
|
||||
this.rollbackClassifier = rollbackClassifier;
|
||||
}
|
||||
|
||||
public void setKeyGenerator(MethodArgumentsKeyGenerator keyGenerator) {
|
||||
this.keyGenerator = keyGenerator;
|
||||
}
|
||||
|
||||
public void setLabel(String label) {
|
||||
this.label = label;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public setter for the {@link NewMethodArgumentsIdentifier}. Only set this if the
|
||||
* arguments to the intercepted method can be inspected to find out if they have never
|
||||
* been processed before.
|
||||
* @param newMethodArgumentsIdentifier the {@link NewMethodArgumentsIdentifier} to set
|
||||
*/
|
||||
public void setNewItemIdentifier(
|
||||
NewMethodArgumentsIdentifier newMethodArgumentsIdentifier) {
|
||||
this.newMethodArgumentsIdentifier = newMethodArgumentsIdentifier;
|
||||
}
|
||||
|
||||
/**
|
||||
* Wrap the method invocation in a stateful retry with the policy and other helpers
|
||||
* provided. If there is a failure the exception will generally be re-thrown. The only
|
||||
* time it is not re-thrown is when retry is exhausted and the recovery path is taken
|
||||
* (though the {@link MethodInvocationRecoverer} provided if there is one). In that
|
||||
* case the value returned from the method invocation will be the value returned by
|
||||
* the recoverer (so the return type for that should be the same as the intercepted
|
||||
* method).
|
||||
* @see org.aopalliance.intercept.MethodInterceptor#invoke(org.aopalliance.intercept.MethodInvocation)
|
||||
* @see MethodInvocationRecoverer#recover(Object[], Throwable)
|
||||
*
|
||||
*/
|
||||
@Override
|
||||
public Object invoke(final MethodInvocation invocation) throws Throwable {
|
||||
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Executing proxied method in stateful retry: "
|
||||
+ invocation.getStaticPart() + "("
|
||||
+ ObjectUtils.getIdentityHexString(invocation) + ")");
|
||||
}
|
||||
|
||||
Object[] args = invocation.getArguments();
|
||||
Object arg = args;
|
||||
if (args.length == 1) {
|
||||
arg = args[0];
|
||||
}
|
||||
final Object item = arg;
|
||||
|
||||
RetryState retryState = new DefaultRetryState(
|
||||
this.keyGenerator != null ? this.keyGenerator.getKey(args) : item,
|
||||
this.newMethodArgumentsIdentifier != null
|
||||
&& this.newMethodArgumentsIdentifier.isNew(args),
|
||||
this.rollbackClassifier);
|
||||
|
||||
Object result = this.retryOperations.execute(
|
||||
new MethodInvocationRetryCallback(invocation, label), this.recoverer != null
|
||||
? new ItemRecovererCallback(args, this.recoverer) : null,
|
||||
retryState);
|
||||
|
||||
if (this.logger.isDebugEnabled()) {
|
||||
this.logger.debug("Exiting proxied method in stateful retry with result: ("
|
||||
+ result + ")");
|
||||
}
|
||||
|
||||
return result;
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static final class MethodInvocationRetryCallback
|
||||
implements RetryCallback<Object, Throwable> {
|
||||
|
||||
private final MethodInvocation invocation;
|
||||
private String label;
|
||||
|
||||
private MethodInvocationRetryCallback(MethodInvocation invocation, String label) {
|
||||
this.invocation = invocation;
|
||||
if (label!=null) {
|
||||
this.label = label;
|
||||
} else {
|
||||
this.label = invocation.getMethod().toGenericString();
|
||||
}
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object doWithRetry(RetryContext context) throws Exception {
|
||||
context.setAttribute(RetryContext.NAME, label);
|
||||
try {
|
||||
return this.invocation.proceed();
|
||||
}
|
||||
catch (Exception e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Error e) {
|
||||
throw e;
|
||||
}
|
||||
catch (Throwable e) {
|
||||
throw new IllegalStateException(e);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
private static final class ItemRecovererCallback implements RecoveryCallback<Object> {
|
||||
|
||||
private final Object[] args;
|
||||
|
||||
private final MethodInvocationRecoverer<?> recoverer;
|
||||
|
||||
/**
|
||||
* @param args the item that failed.
|
||||
*/
|
||||
private ItemRecovererCallback(Object[] args,
|
||||
MethodInvocationRecoverer<?> recoverer) {
|
||||
this.args = Arrays.asList(args).toArray();
|
||||
this.recoverer = recoverer;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object recover(RetryContext context) {
|
||||
return this.recoverer.recover(this.args, context.getLastThrowable());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,71 +1,71 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.classify.annotation.Classifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BackToBackPatternClassifierTests {
|
||||
|
||||
private BackToBackPatternClassifier<String, String> classifier = new BackToBackPatternClassifier<String, String>();
|
||||
|
||||
private Map<String, String> map;
|
||||
|
||||
@Before
|
||||
public void createMap() {
|
||||
map = new HashMap<String, String>();
|
||||
map.put("foo", "bar");
|
||||
map.put("*", "spam");
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testNoClassifiers() {
|
||||
classifier.classify("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromConstructor() {
|
||||
classifier = new BackToBackPatternClassifier<String, String>(
|
||||
new PatternMatchingClassifier<String>(Collections.singletonMap(
|
||||
"oof", "bucket")),
|
||||
new PatternMatchingClassifier<String>(map));
|
||||
assertEquals("spam", classifier.classify("oof"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetRouterDelegate() {
|
||||
classifier.setRouterDelegate(new Object() {
|
||||
@Classifier
|
||||
public String convert(String value) {
|
||||
return "bucket";
|
||||
}
|
||||
});
|
||||
classifier.setMatcherMap(map);
|
||||
assertEquals("spam", classifier.classify("oof"));
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.classify.annotation.Classifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class BackToBackPatternClassifierTests {
|
||||
|
||||
private BackToBackPatternClassifier<String, String> classifier = new BackToBackPatternClassifier<String, String>();
|
||||
|
||||
private Map<String, String> map;
|
||||
|
||||
@Before
|
||||
public void createMap() {
|
||||
map = new HashMap<String, String>();
|
||||
map.put("foo", "bar");
|
||||
map.put("*", "spam");
|
||||
}
|
||||
|
||||
@Test(expected = NullPointerException.class)
|
||||
public void testNoClassifiers() {
|
||||
classifier.classify("foo");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromConstructor() {
|
||||
classifier = new BackToBackPatternClassifier<String, String>(
|
||||
new PatternMatchingClassifier<String>(Collections.singletonMap(
|
||||
"oof", "bucket")),
|
||||
new PatternMatchingClassifier<String>(map));
|
||||
assertEquals("spam", classifier.classify("oof"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetRouterDelegate() {
|
||||
classifier.setRouterDelegate(new Object() {
|
||||
@Classifier
|
||||
public String convert(String value) {
|
||||
return "bucket";
|
||||
}
|
||||
});
|
||||
classifier.setMatcherMap(map);
|
||||
assertEquals("spam", classifier.classify("oof"));
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,124 +1,124 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.classify.annotation.Classifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ClassifierAdapterTests {
|
||||
|
||||
private ClassifierAdapter<String, Integer> adapter = new ClassifierAdapter<String, Integer>();
|
||||
|
||||
@Test
|
||||
public void testClassifierAdapterObject() {
|
||||
adapter = new ClassifierAdapter<String, Integer>(new Object() {
|
||||
@Classifier
|
||||
public Integer getValue(String key) {
|
||||
return Integer.parseInt(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Integer getAnother(String key) {
|
||||
throw new UnsupportedOperationException("Not allowed");
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testClassifierAdapterObjectWithNoAnnotation() {
|
||||
adapter = new ClassifierAdapter<String, Integer>(new Object() {
|
||||
@SuppressWarnings("unused")
|
||||
public Integer getValue(String key) {
|
||||
return Integer.parseInt(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Integer getAnother(String key) {
|
||||
throw new UnsupportedOperationException("Not allowed");
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifierAdapterObjectSingleMethodWithNoAnnotation() {
|
||||
adapter = new ClassifierAdapter<String, Integer>(new Object() {
|
||||
@SuppressWarnings("unused")
|
||||
public Integer getValue(String key) {
|
||||
return Integer.parseInt(key);
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
public void doNothing(String key) {
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
public String doNothing(String key, int value) {
|
||||
return "foo";
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifierAdapterClassifier() {
|
||||
adapter = new ClassifierAdapter<String, Integer>(
|
||||
new org.springframework.classify.Classifier<String, Integer>() {
|
||||
public Integer classify(String classifiable) {
|
||||
return Integer.valueOf(classifiable);
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifyWithSetter() {
|
||||
adapter.setDelegate(new Object() {
|
||||
@Classifier
|
||||
public Integer getValue(String key) {
|
||||
return Integer.parseInt(key);
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void testClassifyWithWrongType() {
|
||||
adapter.setDelegate(new Object() {
|
||||
@Classifier
|
||||
public String getValue(Integer key) {
|
||||
return key.toString();
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifyWithClassifier() {
|
||||
adapter.setDelegate(new org.springframework.classify.Classifier<String, Integer>() {
|
||||
public Integer classify(String classifiable) {
|
||||
return Integer.valueOf(classifiable);
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
}
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
import org.junit.Test;
|
||||
import org.springframework.classify.annotation.Classifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class ClassifierAdapterTests {
|
||||
|
||||
private ClassifierAdapter<String, Integer> adapter = new ClassifierAdapter<String, Integer>();
|
||||
|
||||
@Test
|
||||
public void testClassifierAdapterObject() {
|
||||
adapter = new ClassifierAdapter<String, Integer>(new Object() {
|
||||
@Classifier
|
||||
public Integer getValue(String key) {
|
||||
return Integer.parseInt(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Integer getAnother(String key) {
|
||||
throw new UnsupportedOperationException("Not allowed");
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test(expected = IllegalStateException.class)
|
||||
public void testClassifierAdapterObjectWithNoAnnotation() {
|
||||
adapter = new ClassifierAdapter<String, Integer>(new Object() {
|
||||
@SuppressWarnings("unused")
|
||||
public Integer getValue(String key) {
|
||||
return Integer.parseInt(key);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unused")
|
||||
public Integer getAnother(String key) {
|
||||
throw new UnsupportedOperationException("Not allowed");
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifierAdapterObjectSingleMethodWithNoAnnotation() {
|
||||
adapter = new ClassifierAdapter<String, Integer>(new Object() {
|
||||
@SuppressWarnings("unused")
|
||||
public Integer getValue(String key) {
|
||||
return Integer.parseInt(key);
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
public void doNothing(String key) {
|
||||
}
|
||||
@SuppressWarnings("unused")
|
||||
public String doNothing(String key, int value) {
|
||||
return "foo";
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifierAdapterClassifier() {
|
||||
adapter = new ClassifierAdapter<String, Integer>(
|
||||
new org.springframework.classify.Classifier<String, Integer>() {
|
||||
public Integer classify(String classifiable) {
|
||||
return Integer.valueOf(classifiable);
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifyWithSetter() {
|
||||
adapter.setDelegate(new Object() {
|
||||
@Classifier
|
||||
public Integer getValue(String key) {
|
||||
return Integer.parseInt(key);
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test(expected=IllegalArgumentException.class)
|
||||
public void testClassifyWithWrongType() {
|
||||
adapter.setDelegate(new Object() {
|
||||
@Classifier
|
||||
public String getValue(Integer key) {
|
||||
return key.toString();
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testClassifyWithClassifier() {
|
||||
adapter.setDelegate(new org.springframework.classify.Classifier<String, Integer>() {
|
||||
public Integer classify(String classifiable) {
|
||||
return Integer.valueOf(classifiable);
|
||||
}
|
||||
});
|
||||
assertEquals(23, adapter.classify("23").intValue());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -1,58 +1,58 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.classify.PatternMatchingClassifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PatternMatchingClassifierTests {
|
||||
|
||||
private PatternMatchingClassifier<String> classifier = new PatternMatchingClassifier<String>();
|
||||
|
||||
private Map<String, String> map;
|
||||
|
||||
@Before
|
||||
public void createMap() {
|
||||
map = new HashMap<String, String>();
|
||||
map.put("foo", "bar");
|
||||
map.put("*", "spam");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPatternMap() {
|
||||
classifier.setPatternMap(map);
|
||||
assertEquals("bar", classifier.classify("foo"));
|
||||
assertEquals("spam", classifier.classify("bucket"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromMap() {
|
||||
classifier = new PatternMatchingClassifier<String>(map);
|
||||
assertEquals("bar", classifier.classify("foo"));
|
||||
assertEquals("spam", classifier.classify("bucket"));
|
||||
}
|
||||
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.classify;
|
||||
|
||||
import static org.junit.Assert.*;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.classify.PatternMatchingClassifier;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class PatternMatchingClassifierTests {
|
||||
|
||||
private PatternMatchingClassifier<String> classifier = new PatternMatchingClassifier<String>();
|
||||
|
||||
private Map<String, String> map;
|
||||
|
||||
@Before
|
||||
public void createMap() {
|
||||
map = new HashMap<String, String>();
|
||||
map.put("foo", "bar");
|
||||
map.put("*", "spam");
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testSetPatternMap() {
|
||||
classifier.setPatternMap(map);
|
||||
assertEquals("bar", classifier.classify("foo"));
|
||||
assertEquals("spam", classifier.classify("bucket"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testCreateFromMap() {
|
||||
classifier = new PatternMatchingClassifier<String>(map);
|
||||
assertEquals("bar", classifier.classify("foo"));
|
||||
assertEquals("spam", classifier.classify("bucket"));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,303 +1,303 @@
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.retry.interceptor;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.SingletonTargetSource;
|
||||
import org.springframework.retry.ExhaustedRetryException;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.listener.RetryListenerSupport;
|
||||
import org.springframework.retry.policy.AlwaysRetryPolicy;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StatefulRetryOperationsInterceptorTests {
|
||||
|
||||
private StatefulRetryOperationsInterceptor interceptor;
|
||||
|
||||
private RetryTemplate retryTemplate = new RetryTemplate();
|
||||
|
||||
private Service service;
|
||||
|
||||
private Transformer transformer;
|
||||
|
||||
private RetryContext context;
|
||||
|
||||
private static int count;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
interceptor = new StatefulRetryOperationsInterceptor();
|
||||
retryTemplate.registerListener(new RetryListenerSupport() {
|
||||
@Override
|
||||
public <T, E extends Throwable> void close(RetryContext context,
|
||||
RetryCallback<T, E> callback, Throwable throwable) {
|
||||
StatefulRetryOperationsInterceptorTests.this.context = context;
|
||||
}
|
||||
});
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
service = (Service) ProxyFactory.getProxy(Service.class,
|
||||
new SingletonTargetSource(new ServiceImpl()));
|
||||
transformer = (Transformer) ProxyFactory.getProxy(Transformer.class,
|
||||
new SingletonTargetSource(new TransformerImpl()));
|
||||
count = 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultInterceptorSunnyDay() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultInterceptorWithLabel() throws Exception {
|
||||
interceptor.setLabel("FOO");
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
assertEquals("FOO", context.getAttribute(RetryContext.NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultTransformerInterceptorSunnyDay() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultInterceptorAlwaysRetry() throws Exception {
|
||||
retryTemplate.setRetryPolicy(new AlwaysRetryPolicy());
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterceptorChainWithRetry() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
final List<String> list = new ArrayList<String>();
|
||||
((Advised) service).addAdvice(new MethodInterceptor() {
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
list.add("chain");
|
||||
return invocation.proceed();
|
||||
}
|
||||
});
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2,
|
||||
Collections.<Class<? extends Throwable>, Boolean>singletonMap(
|
||||
Exception.class, true)));
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
service.service("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformerWithSuccessfulRetry() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2,
|
||||
Collections.<Class<? extends Throwable>, Boolean>singletonMap(
|
||||
Exception.class, true)));
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
Collection<String> result = transformer.transform("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryExceptionAfterTooManyAttemptsWithNoRecovery() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected ExhaustedRetryException");
|
||||
}
|
||||
catch (ExhaustedRetryException e) {
|
||||
// expected
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Retry exhausted"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecoveryAfterTooManyAttempts() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
interceptor.setRecoverer(new MethodInvocationRecoverer<Object>() {
|
||||
public Object recover(Object[] data, Throwable cause) {
|
||||
count++;
|
||||
return null;
|
||||
}
|
||||
});
|
||||
service.service("foo");
|
||||
assertEquals(2, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformerRecoveryAfterTooManyAttempts() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
interceptor.setRecoverer(new MethodInvocationRecoverer<Collection<String>>() {
|
||||
public Collection<String> recover(Object[] data, Throwable cause) {
|
||||
count++;
|
||||
return Collections.singleton((String) data[0]);
|
||||
}
|
||||
});
|
||||
Collection<String> result = transformer.transform("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
public static interface Service {
|
||||
void service(String in) throws Exception;
|
||||
}
|
||||
|
||||
public static class ServiceImpl implements Service {
|
||||
|
||||
public void service(String in) throws Exception {
|
||||
count++;
|
||||
if (count < 2) {
|
||||
throw new Exception("Not enough calls: " + count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static interface Transformer {
|
||||
Collection<String> transform(String in) throws Exception;
|
||||
}
|
||||
|
||||
public static class TransformerImpl implements Transformer {
|
||||
|
||||
public Collection<String> transform(String in) throws Exception {
|
||||
count++;
|
||||
if (count < 2) {
|
||||
throw new Exception("Not enough calls: " + count);
|
||||
}
|
||||
return Collections.singleton(in + ":" + count);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
/*
|
||||
* Copyright 2006-2007 the original author or authors.
|
||||
*
|
||||
* 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 org.springframework.retry.interceptor;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
import static org.junit.Assert.assertTrue;
|
||||
import static org.junit.Assert.fail;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
import org.aopalliance.intercept.MethodInterceptor;
|
||||
import org.aopalliance.intercept.MethodInvocation;
|
||||
import org.junit.Before;
|
||||
import org.junit.Test;
|
||||
import org.springframework.aop.framework.Advised;
|
||||
import org.springframework.aop.framework.ProxyFactory;
|
||||
import org.springframework.aop.target.SingletonTargetSource;
|
||||
import org.springframework.retry.ExhaustedRetryException;
|
||||
import org.springframework.retry.RetryCallback;
|
||||
import org.springframework.retry.RetryContext;
|
||||
import org.springframework.retry.listener.RetryListenerSupport;
|
||||
import org.springframework.retry.policy.AlwaysRetryPolicy;
|
||||
import org.springframework.retry.policy.NeverRetryPolicy;
|
||||
import org.springframework.retry.policy.SimpleRetryPolicy;
|
||||
import org.springframework.retry.support.RetryTemplate;
|
||||
|
||||
/**
|
||||
* @author Dave Syer
|
||||
*
|
||||
*/
|
||||
public class StatefulRetryOperationsInterceptorTests {
|
||||
|
||||
private StatefulRetryOperationsInterceptor interceptor;
|
||||
|
||||
private RetryTemplate retryTemplate = new RetryTemplate();
|
||||
|
||||
private Service service;
|
||||
|
||||
private Transformer transformer;
|
||||
|
||||
private RetryContext context;
|
||||
|
||||
private static int count;
|
||||
|
||||
@Before
|
||||
public void setUp() throws Exception {
|
||||
interceptor = new StatefulRetryOperationsInterceptor();
|
||||
retryTemplate.registerListener(new RetryListenerSupport() {
|
||||
@Override
|
||||
public <T, E extends Throwable> void close(RetryContext context,
|
||||
RetryCallback<T, E> callback, Throwable throwable) {
|
||||
StatefulRetryOperationsInterceptorTests.this.context = context;
|
||||
}
|
||||
});
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
service = (Service) ProxyFactory.getProxy(Service.class,
|
||||
new SingletonTargetSource(new ServiceImpl()));
|
||||
transformer = (Transformer) ProxyFactory.getProxy(Transformer.class,
|
||||
new SingletonTargetSource(new TransformerImpl()));
|
||||
count = 0;
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultInterceptorSunnyDay() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultInterceptorWithLabel() throws Exception {
|
||||
interceptor.setLabel("FOO");
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
assertEquals("FOO", context.getAttribute(RetryContext.NAME));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultTransformerInterceptorSunnyDay() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testDefaultInterceptorAlwaysRetry() throws Exception {
|
||||
retryTemplate.setRetryPolicy(new AlwaysRetryPolicy());
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testInterceptorChainWithRetry() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
final List<String> list = new ArrayList<String>();
|
||||
((Advised) service).addAdvice(new MethodInterceptor() {
|
||||
public Object invoke(MethodInvocation invocation) throws Throwable {
|
||||
list.add("chain");
|
||||
return invocation.proceed();
|
||||
}
|
||||
});
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2,
|
||||
Collections.<Class<? extends Throwable>, Boolean>singletonMap(
|
||||
Exception.class, true)));
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
service.service("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(2, list.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformerWithSuccessfulRetry() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new SimpleRetryPolicy(2,
|
||||
Collections.<Class<? extends Throwable>, Boolean>singletonMap(
|
||||
Exception.class, true)));
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
Collection<String> result = transformer.transform("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRetryExceptionAfterTooManyAttemptsWithNoRecovery() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected ExhaustedRetryException");
|
||||
}
|
||||
catch (ExhaustedRetryException e) {
|
||||
// expected
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Retry exhausted"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testRecoveryAfterTooManyAttempts() throws Exception {
|
||||
((Advised) service).addAdvice(interceptor);
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
try {
|
||||
service.service("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
interceptor.setRecoverer(new MethodInvocationRecoverer<Object>() {
|
||||
public Object recover(Object[] data, Throwable cause) {
|
||||
count++;
|
||||
return null;
|
||||
}
|
||||
});
|
||||
service.service("foo");
|
||||
assertEquals(2, count);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testTransformerRecoveryAfterTooManyAttempts() throws Exception {
|
||||
((Advised) transformer).addAdvice(interceptor);
|
||||
interceptor.setRetryOperations(retryTemplate);
|
||||
retryTemplate.setRetryPolicy(new NeverRetryPolicy());
|
||||
try {
|
||||
transformer.transform("foo");
|
||||
fail("Expected Exception.");
|
||||
}
|
||||
catch (Exception e) {
|
||||
String message = e.getMessage();
|
||||
assertTrue("Wrong message: " + message,
|
||||
message.startsWith("Not enough calls"));
|
||||
}
|
||||
assertEquals(1, count);
|
||||
interceptor.setRecoverer(new MethodInvocationRecoverer<Collection<String>>() {
|
||||
public Collection<String> recover(Object[] data, Throwable cause) {
|
||||
count++;
|
||||
return Collections.singleton((String) data[0]);
|
||||
}
|
||||
});
|
||||
Collection<String> result = transformer.transform("foo");
|
||||
assertEquals(2, count);
|
||||
assertEquals(1, result.size());
|
||||
}
|
||||
|
||||
public static interface Service {
|
||||
void service(String in) throws Exception;
|
||||
}
|
||||
|
||||
public static class ServiceImpl implements Service {
|
||||
|
||||
public void service(String in) throws Exception {
|
||||
count++;
|
||||
if (count < 2) {
|
||||
throw new Exception("Not enough calls: " + count);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public static interface Transformer {
|
||||
Collection<String> transform(String in) throws Exception;
|
||||
}
|
||||
|
||||
public static class TransformerImpl implements Transformer {
|
||||
|
||||
public Collection<String> transform(String in) throws Exception {
|
||||
count++;
|
||||
if (count < 2) {
|
||||
throw new Exception("Not enough calls: " + count);
|
||||
}
|
||||
return Collections.singleton(in + ":" + count);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user