I have a legacy project with Spring version 3.0 (I can't use Retriable annotation from spring package).
I want to implement Retryable annotation to annotate my methods which execution should be retry on fail.
This is my class:
#Component
public final class RetryBpp implements BeanPostProcessor {
private final ConcurrentMap<String, ClassDefinition> map = new ConcurrentHashMap<>();
#Override
public Object postProcessBeforeInitialization(final Object bean, final String beanName) throws BeansException {
final Class<?> asClass = bean.getClass();
final Method[] methods = asClass.getMethods();
final List<Method> collect = Stream.of(methods)
.filter(method -> method.isAnnotationPresent(Repitable.class))
.collect(Collectors.toList());
if(!collect.isEmpty()){
this.map.put(beanName,new ClassDefinition(collect,asClass));
}
return bean;
}
#Override
public Object postProcessAfterInitialization(final Object bean, final String beanName) throws BeansException {
final ClassDefinition definition = this.map.get(beanName);
if(definition != null){
final Class beanClass = definition.asClass;
return Proxy.newProxyInstance(beanClass.getClassLoader(), beanClass.getInterfaces(), (proxy, method, args) -> {
if(definition.isMethodPresent(method)){
System.out.println("Present");
return this.retry(definition.originalMethod(method),bean,args);
} else{
return method.invoke(bean,args);
}
});
} else{
return bean;
}
}
private Object retry(final Method method,final Object originalBean,Object[] argc){
final Repitable repitable = method.getAnnotation(Repitable.class);
int attempts = repitable.attempts();
Throwable exc = null;
while(attempts!=0){
try{
return method.invoke(originalBean,argc);
}catch (final Throwable throwable){
exc = throwable;
attempts--;
this.sleep(repitable.delay(),repitable.timeUnit());
}
}
throw new RuntimeException(exc);
}
#SneakyThrows(InterruptedException.class)
private void sleep(final int time, final TimeUnit timeUnit){
timeUnit.sleep(time);
}
#AllArgsConstructor
private static final class ClassDefinition{
private final List<Method> methods;
private final Class asClass;
boolean isMethodPresent(final Method method){
return this.methods.stream().anyMatch(mthd->mthd.getName().equals(method.getName()));
}
Method originalMethod(final Method method){
return this.methods.stream().filter(mthd->mthd.getName().equals(method.getName())).findFirst().orElseThrow(NullPointerException::new);
}
}
}
And it work but I want to change two things
1)In retry method I want to keep last exception and throw when repeateCount = 0 for this I need to declare null ptr to exc, but I want all my fields to be final. Is it any possible way to rewrite my code?
2) In ClassDefinition I compare Method by name because original equals method of Method class compare by class, I can't do it because original class replaced by proxy, Is it possible to compare two Method's in different way?
For the first part: one option is to use a List of the Throwables. And then throw them something like:
final RuntimeException theError = new RuntimeException(list.get(list.size() - 1));
for (int i = 0; i < list.size() - 1; i++) {
theError.addSuppressed(list.get(i));
}
throw theError;
This also gives the benefit of providing all the failures.
For the latter part, this could be complicated and expensive depending on whether there's gonna be generics, var-args, etc. on some of the methods.
Since you're already using spring, you could try a combination of: AopUtils.getTargetClass and AopUtils.getMostSpecificMethod to get what you want (would need to refactor what you pass to some of the test methods).
Something simple to provide some more (note, not infallible) tests in the vein of what you're already running with though:
return methods.stream().anyMatch(mthd -> {
if (!method.getName().equals(mthd.getName())) {
return false;
}
if (!method.getReturnType().isAssignableFrom(mthd.getReturnType())) {
return false;
}
final Class<?>[] parameterTypes = method.getParameterTypes();
if (mthd.getParameterTypes().length != parameterTypes.length) {
return false;
}
for (int i = 0; i < parameterTypes.length; i++) {
if (!parameterTypes[i].equals(mthd.getParameterTypes()[i])) {
return false;
}
}
return true;
});
I'm sure there are some more checks you can do (Method.getExceptionTypes() to confirm they could override; Method.getDeclaringClass() and work up the tree from there; Method.getModifiers() to determine if it could be overridden, etc.), but that depends on how safe you need to be.
Related
I want to create a Mock Library class that implements InvocationHandler interface from Java Reflection.
This is the template I have created:
import java.lang.reflect.*;
import java.util.*;
class MyMock implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// todo
}
public MyMock when(String method, Object[] args) {
// todo
}
public void thenReturn(Object val) {
// todo
}
}
The when and thenReturn methods are chained methods.
Then when method registers the given mock parameters.
thenReturn method registers the expected return values for the given mock parameters.
Also, I want to throw java.lang.IllegalArgumentException if the proxied interface calls methods or uses parameters that are not registered.
This is a sample interface:
interface CalcInterface {
int add(int a, int b);
String add(String a, String b);
String getValue();
}
Here we have two overloaded add methods.
This is a program to test the mock class I wanted to implement.
class TestApplication {
public static void main(String[] args) {
MyMock m = new MyMock();
CalcInterface ref = (CalcInterface) Proxy.newProxyInstance(MyMock.class.getClassLoader(), new Class[]{CalcInterface.class}, m);
m.when("add", new Object[]{1,2}).thenReturn(3);
m.when("add", new Object[]{"x","y"}).thenReturn("xy");
System.out.println(ref.add(1,2)); // prints 3
System.out.println(ref.add("x","y")); // prints "xy"
}
}
This is the code which I have implemented so far to check the methods in CalcInterface:
class MyMock implements InvocationHandler {
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
int n = args.length;
if(n == 2 && method.getName().equals("add")) {
Object o1 = args[0], o2 = args[1];
if((o1 instanceof String) && (o2 instanceof String)) {
String s1 = (String) o1, s2 = (String) o2;
return s1+ s2;
} else if((o1 instanceof Integer) && (o2 instanceof Integer)) {
int s1 = (Integer) o1, s2 = (Integer) o2;
return s1+ s2;
}
}
throw new IllegalArgumentException();
}
public MyMock when(String method, Object[] args) {
return this;
}
public void thenReturn(Object val) {
}
}
Here I am checking only for methods with the name add and having 2 arguments, with their type as String or Integer.
But I wanted to create this MyMock class in a general fashion, supporting different interfaces not just CalcInterface, and also supporting different methods not just the add method I implemented here.
You have to separate the builder logic from the object to build. The method when has to return something which remembers the arguments, so that the invocation of thenReturn still knows the context.
For example
public class MyMock implements InvocationHandler {
record Key(String name, List<?> arguments) {
Key { // stream().toList() creates an immutable list allowing null
arguments = arguments.stream().toList();
}
Key(String name, Object... arg) {
this(name, arg == null? List.of(): Arrays.stream(arg).toList());
}
}
final Map<Key, Function<Object[], Object>> rules = new HashMap<>();
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
var rule = rules.get(new Key(method.getName(), args));
if(rule == null) throw new IllegalStateException("No matching rule");
return rule.apply(args);
}
public record Rule(MyMock mock, Key key) {
public void thenReturn(Object val) {
var existing = mock.rules.putIfAbsent(key, arg -> val);
if(existing != null) throw new IllegalStateException("Rule already exist");
}
public void then(Function<Object[], Object> f) {
var existing = mock.rules.putIfAbsent(key, Objects.requireNonNull(f));
if(existing != null) throw new IllegalStateException("Rule already exist");
}
}
public Rule when(String method, Object... args) {
Key key = new Key(method, args);
if(rules.containsKey(key)) throw new IllegalStateException("Rule already exist");
return new Rule(this, key);
}
}
This is already capable of executing your example literally, but also supports something like
MyMock m = new MyMock();
CalcInterface ref = (CalcInterface) Proxy.newProxyInstance(
CalcInterface.class.getClassLoader(), new Class[]{CalcInterface.class}, m);
m.when("add", 1,2).thenReturn(3);
m.when("add", "x","y").thenReturn("xy");
AtomicInteger count = new AtomicInteger();
m.when("getValue").then(arg -> "getValue invoked " + count.incrementAndGet() + " times");
System.out.println(ref.add(1,2)); // prints 3
System.out.println(ref.add("x","y")); // prints "xy"
System.out.println(ref.getValue()); // prints getValue invoked 1 times
System.out.println(ref.getValue()); // prints getValue invoked 2 times
Note that when you want to add support for rules beyond simple value matching, a hash lookup will not work anymore. In that case you have to resort to a data structure you have to search linearly for a match.
The example above uses newer Java features like record classes but it shouldn’t be too hard to rewrite it for previous Java versions if required.
It’s also possible to redesign this code to use the real builder pattern, i.e. to use a builder to describe the configuration prior to creating the actual handler/mock instance. This allows the handler/mock to use an immutable state:
public class MyMock2 {
public static Builder builder() {
return new Builder();
}
public interface Rule {
Builder thenReturn(Object val);
Builder then(Function<Object[], Object> f);
}
public static class Builder {
final Map<Key, Function<Object[], Object>> rules = new HashMap<>();
public Rule when(String method, Object... args) {
Key key = new Key(method, args);
if(rules.containsKey(key))
throw new IllegalStateException("Rule already exist");
return new RuleImpl(this, key);
}
public <T> T build(Class<T> type) {
Map<Key, Function<Object[], Object>> rules = Map.copyOf(this.rules);
return type.cast(Proxy.newProxyInstance(type.getClassLoader(),
new Class[]{ type }, (proxy, method, args) -> {
var rule = rules.get(new Key(method.getName(), args));
if(rule == null) throw new IllegalStateException("No matching rule");
return rule.apply(args);
}));
}
}
record RuleImpl(MyMock2.Builder builder, Key key) implements Rule {
public Builder thenReturn(Object val) {
var existing = builder.rules.putIfAbsent(key, arg -> val);
if(existing != null) throw new IllegalStateException("Rule already exist");
return builder;
}
public Builder then(Function<Object[], Object> f) {
var existing = builder.rules.putIfAbsent(key, Objects.requireNonNull(f));
if(existing != null) throw new IllegalStateException("Rule already exist");
return builder;
}
}
record Key(String name, List<?> arguments) {
Key { // stream().toList() createns an immutable list allowing null
arguments = arguments.stream().toList();
}
Key(String name, Object... arg) {
this(name, arg == null? List.of(): Arrays.stream(arg).toList());
}
}
}
which can be used like
AtomicInteger count = new AtomicInteger();
CalcInterface ref = MyMock2.builder()
.when("add", 1,2).thenReturn(3)
.when("add", "x","y").thenReturn("xy")
.when("getValue")
.then(arg -> "getValue invoked " + count.incrementAndGet() + " times")
.build(CalcInterface.class);
System.out.println(ref.add(1,2)); // prints 3
System.out.println(ref.add("x","y")); // prints "xy"
System.out.println(ref.getValue()); // prints getValue invoked 1 times
System.out.println(ref.getValue()); // prints getValue invoked 2 times
InvocationHandler is used to invoke a method signature against an object instance (proxy) that's instantiated in memory (or a class, if you're tapping a static method), not to support methods that aren't in the supplied proxy's class at all, which is how you've implemented it here. That said, you can probably achieve what you're trying to do by holding the method signature you're trying to mock (as well as the value you want to return when the args match) in private variables.
This may work, with the proviso that I haven't done Java in a couple of years, so I may be a bit rusty:
class MyMock implements InvocationHandler {
private String methodName = null;
private Object[] supportedArgs = null;
private Object returnValue = null;
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
// If we don't know when this mock is supposed to be used, it's useless
assert this.methodName != null: "when(method, args) hasn't been called against the mock yet!";
// Note that both args and supportedArgs will be null if the method signature has no params
if (method.getName().equals(this.methodName) && this.supportedArgs == args) {
return this.returnValue;
}
try {
return method.invoke(proxy, args);
}
catch (IllegalAccessException | InvocationTargetException innerException){
// The proxy didn't support the method either, so let's throw an IllegalArgumentException
throw new IllegalArgumentException("The supplied method signature isn't implemented in the proxy.");
}
}
public MyMock when(String method, Object[] args) {
this.methodName = method;
this.supportedArgs = args;
return this;
}
public void thenReturn(Object val) {
this.returnValue = val;
}
}
I have two unrelated java classes (only *.class, no *.java) like this:
public class Trick {
public String getName() { return "Jack"; }
public String trick() { ... }
}
and
public class Treat {
public String getName() { return "John"; }
public String treat() { ... }
}
and I would like to generate a sort of Proxy class at runtime that represents the union of both classes and forwards them to the respective instance, and maybe throw if that's not possible. I assume that can be done with cglib but I don't know where to start.
This is what I would like to do (pseudocode):
// prepare: generate a common interface
TrickOrTreat trickOrTreat = magic.createUnionInterface(Trick.class, Treat.class);
// use with concrete interface A:
Trick trick = new Trick();
TrickOrTreat proxyA = magic.createProxy(trickOrTreat.class, trick);
System.out.println("trick name: " + proxyA.getName());
// use with concrete interface B:
Treat treat = new Treat();
TrickOrTreat proxyB = magic.createProxy(trickOrTreat.class, treat);
System.out.println("treat name: " + proxyB.getName());
Or something to that effect. I would like to do it completely dynamically, probably cglib-based? If thats not possible I would do it with a code generation step in between?
If you are willing to trade in cglib, you can do this with Byte Buddy. I typically refuse to call it magic but here you go:
class Magic {
Class<?> createUnionInterface(Class<?> a, Class<?> b) {
DynamicType.Builder<?> builder = new ByteBuddy().makeInterface();
Set<MethodDescription.SignatureToken> tokens = new HashSet<>();
for (MethodDescription m : new TypeDescription.ForLoadedType(a)
.getDeclaredMethods()
.filter(ElementMatchers.isVirtual())) {
tokens.add(m.asSignatureToken());
builder = builder.defineMethod(m.getName(),
m.getReturnType(),
m.getModifiers()).withoutCode();
}
for (MethodDescription m : new TypeDescription.ForLoadedType(b)
.getDeclaredMethods()
.filter(ElementMatchers.isVirtual())) {
if (!tokens.contains(m.asSignatureToken())) {
builder = builder.defineMethod(m.getName(),
m.getReturnType(),
m.getModifiers()).withoutCode();
}
}
return builder.make()
.load(Magic.class.getClassLoader())
.getLoaded();
}
Object createProxy(Class<?> m, final Object delegate) throws Exception {
return new ByteBuddy()
.subclass(m)
.method(new ElementMatcher<MethodDescription>() {
#Override
public boolean matches(MethodDescription target) {
for (Method method : delegate.getClass()
.getDeclaredMethods()) {
if (new MethodDescription.ForLoadedMethod(method)
.asSignatureToken()
.equals(target.asSignatureToken())) {
return true;
}
}
return false;
}
}).intercept(MethodDelegation.to(delegate))
.make()
.load(Magic.class.getClassLoader())
.getLoaded()
.newInstance();
}
}
Note that you cannot reference a runtime-generated type at compile-time. This is however a given constraint with runtime code generation.
Magic magic = new Magic();
Class<?> trickOrTreat = magic.createUnionInterface(Trick.class, Treat.class);
Trick trick = new Trick();
Object proxyA = magic.createProxy(trickOrTreat, trick);
System.out.println("trick name: " + trickOrTreat.getDeclaredMethod("getName").invoke(proxyA));
Treat treat = new Treat();
Object proxyB = magic.createProxy(trickOrTreat, treat);
System.out.println("trick name: " + trickOrTreat.getDeclaredMethod("getName").invoke(proxyB));
You can overcome this by generating your TrickOrTreat class prior to runtime such that you can reference the type at runtime.
As for the suggested union-type approach, this would require you to have at least one class to be an interface type as Java does not support multiple inheritance.
If you need functionality of both classes/interfaces you can use
public <TT extends Trick & Treat> void process(TT thing){
//...
}
edit:
Implement new Interface MyProxyHandler
public interface MyProxyHandler {}
Extend it with interfaces of classes say TreatInterface and TrickInterface
Create class ProxyManager that implements java.lang.reflect.InvocationHandler
public abstract class ProxyManager<T extends MyProxyHandler> implements InvocationHandler {
protected static String LOCK_OBJECT = new String("LOCK");
protected T proxyHandler;
protected List<T> handlers = new ArrayList<>();
#SuppressWarnings("unchecked")
public ProxyManager(Class<T> _clazz) {
proxyHandler = (T) Proxy.newProxyInstance(_clazz.getClassLoader(), new Class[]{_clazz}, this);
}
public T getProxy() {
return proxyHandler;
}
public List<T> getHandlers() {
return handlers;
}
public void setHandlers(List<T> handlers) {
this.handlers = handlers;
}
public boolean registerHandler(T handler) {
synchronized (LOCK_OBJECT) {
boolean add = true;
for (T item : this.handlers) {
if (item.getClass().equals(handler.getClass())) {
add = false;
}
}
if (add)
this.handlers.add(handler);
return add;
}
}
#Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
String result = "";
for (MyProxyHandler handler : getHandlers()) {
try {
//I recommend that methods returns some enum like HANDLED/NOTHANDLED
result = (String) method.invoke(handler, args);
if (result.equals("Some flag"))
break;
} catch (InvocationTargetException e) {
throw e.getCause();
}
}
return result;
}
}
Extend that class with your concrete class
public class TreatTrickProxyManager<T extends TreatInterface & TreatInterface> extends ProxyManager<T> {
public TreatTrickProxyManager(Class<T> _clazz) {
super(_clazz);
}
}
In your bussines logic class get an instance of TreatTrickProxyManager
In your method
public void retrieveSomeData(){
((TreatTrickProxyManager)getTreatTrickProxyManager().getProxy()).someMethodInvocation()
}
Is it possible to allow multiple #QueryParam keys for a single object/variable in Jersey?
Actual:
#POST
public Something getThings(#QueryParam("customer-number") Integer n) {
...
}
so, if I add ?customer-number=3 after the URL it works.
Expected:
I want to get the behavior above if I add any of the following values:
?customer-number=3
?customerNumber=3
?customerNo=3
Obs:
The QueryParam annotation looks like:
...
public #interface QueryParam {
String value();
}
so, it cannot accept multiple String values (like #Produces).
The approach below allows the user to use multiple keys having the same meaning at the same time (and I want to have an "OR" condition between them):
#POST
public Something getThings(#QueryParam("customer-number") Integer n1,
#QueryParam("customerNumber") Integer n2,
#QueryParam("customerNo") Integer n3) {
...
}
Something like this doesn't work:
#POST
public Something getThings(#QueryParam("customer-number|customerNumber|customerNo") Integer n) {
...
}
How can I do this?
Details:
Jersey 2.22.1
Java 8
To be honest: this is not how webservices are supposed to be designed. You lay down a strict contract that both client and server follow; you define one parameter and that's it.
But of course it would be a perfect world where you have the freedom to dictate what is going to happen. So if you must allow three parameters in, then you'll have to make that the contract. This is one way following approach #2 which I have to provide without being able to test it for goofs:
public Something getThings(#QueryParam("customer-number") Integer n1,
#QueryParam("customerNumber") Integer n2,
#QueryParam("customerNo") Integer n3) throws YourFailureException {
Integer customerNumber = getNonNullValue("Customer number", n1, n2, n3);
// things with stuff
}
private static Integer getNonNullValue(String label, Integer... params) throws YourFailureException {
Integer value = null;
for(Integer choice : params){
if(choice != null){
if(value != null){
// this means there are at least two query parameters passed with a value
throw new YourFailureException("Ambiguous " + label + " parameters");
}
value = choice;
}
}
if(value == null){
throw new YourFailureException("Missing " + label + " parameter");
}
return value;
}
So basically reject any call that does not pass specifically one of the parameters, and let an exception mapper translate the exception you throw into a HTTP response code in the 4xx range of course.
(I made the getNonNullValue() method static is it strikes me as a reusable utility function).
Maybe the simplest and easiest way would be to use a custom #BeanParam:
First define the custom bean merging all the query parameters as:
class MergedIntegerValue {
private final Integer value;
public MergedIntegerValue(
#QueryParam("n1") Integer n1,
#QueryParam("n2") Integer n2,
#QueryParam("n3") Integer n3) {
this.value = n1 != null ? n1
: n2 != null ? n2
: n3 != null ? n3
: null;
// Throw an exception if value == null ?
}
public Integer getValue() {
return value;
}
}
and then use it with #BeanParam in your resource method:
public Something getThings(
#BeanParam MergedIntegerValue n) {
// Use n.getValue() ...
}
Reference: https://jersey.java.net/documentation/latest/user-guide.html#d0e2403
You can create a custom annotation. I won't go in too much about how to do it, you can see this post, or this post. Basically it relies on a different infrastructure than the usual dependency injection with Jersey. You can see this package from the Jersey project. This is where all the injection providers live that handle the #XxxParam injections. If you examine the source code, you will see the the implementations are fairly the same. The two links I provided above follow the same pattern, as well as the code below.
What I did was created a custom annotation
#Target({ElementType.FIELD, ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface VaryingParam {
String value();
#SuppressWarnings("AnnotationAsSuperInterface")
public static class Factory
extends AnnotationLiteral<VaryingParam> implements VaryingParam {
private final String value;
public static VaryingParam create(final String newValue) {
return new Factory(newValue);
}
public Factory(String newValue) {
this.value = newValue;
}
#Override
public String value() {
return this.value;
}
}
}
It may seem odd that I have a factory to create it, but this was required for the implementation of the below code, where I split the value of the String, and end up creating a new annotation instance for each split value.
Here is the ValueFactoryProvider (which, if you've read either of the above articles, you will see that is required for custom method parameter injection). It a large class, only because I put all the required classes into a single class, following the pattern you see in the Jersey project.
public class VaryingParamValueFactoryProvider extends AbstractValueFactoryProvider {
#Inject
public VaryingParamValueFactoryProvider(
final MultivaluedParameterExtractorProvider mpep,
final ServiceLocator locator) {
super(mpep, locator, Parameter.Source.UNKNOWN);
}
#Override
protected Factory<?> createValueFactory(final Parameter parameter) {
VaryingParam annotation = parameter.getAnnotation(VaryingParam.class);
if (annotation == null) {
return null;
}
String value = annotation.value();
if (value == null || value.length() == 0) {
return null;
}
String[] variations = value.split("\\s*\\|\\s*");
return new VaryingParamFactory(variations, parameter);
}
private static Parameter cloneParameter(final Parameter original, final String value) {
Annotation[] annotations = changeVaryingParam(original.getAnnotations(), value);
Parameter clone = Parameter.create(
original.getRawType(),
original.getRawType(),
true,
original.getRawType(),
original.getRawType(),
annotations);
return clone;
}
private static Annotation[] changeVaryingParam(final Annotation[] annos, final String value) {
for (int i = 0; i < annos.length; i++) {
if (annos[i] instanceof VaryingParam) {
annos[i] = VaryingParam.Factory.create(value);
break;
}
}
return annos;
}
private class VaryingParamFactory extends AbstractContainerRequestValueFactory<Object> {
private final String[] variations;
private final Parameter parameter;
private final boolean decode;
private final Class<?> paramType;
private final boolean isList;
private final boolean isSet;
VaryingParamFactory(final String[] variations, final Parameter parameter) {
this.variations = variations;
this.parameter = parameter;
this.decode = !parameter.isEncoded();
this.paramType = parameter.getRawType();
this.isList = paramType == List.class;
this.isSet = paramType == Set.class;
}
#Override
public Object provide() {
MultivaluedParameterExtractor<?> e = null;
try {
Object value = null;
MultivaluedMap<String, String> params
= getContainerRequest().getUriInfo().getQueryParameters(decode);
for (String variant : variations) {
e = get(cloneParameter(parameter, variant));
if (e == null) {
return null;
}
if (isList) {
List list = (List<?>) e.extract(params);
if (value == null) {
value = new ArrayList();
}
((List<?>) value).addAll(list);
} else if (isSet) {
Set set = (Set<?>) e.extract(params);
if (value == null) {
value = new HashSet();
}
((Set<?>) value).addAll(set);
} else {
value = e.extract(params);
if (value != null) {
return value;
}
}
}
return value;
} catch (ExtractorException ex) {
if (e == null) {
throw new ParamException.QueryParamException(ex.getCause(),
parameter.getSourceName(), parameter.getDefaultValue());
} else {
throw new ParamException.QueryParamException(ex.getCause(),
e.getName(), e.getDefaultValueString());
}
}
}
}
private static class Resolver extends ParamInjectionResolver<VaryingParam> {
public Resolver() {
super(VaryingParamValueFactoryProvider.class);
}
}
public static class Binder extends AbstractBinder {
#Override
protected void configure() {
bind(VaryingParamValueFactoryProvider.class)
.to(ValueFactoryProvider.class)
.in(Singleton.class);
bind(VaryingParamValueFactoryProvider.Resolver.class)
.to(new TypeLiteral<InjectionResolver<VaryingParam>>() {
})
.in(Singleton.class);
}
}
}
You will need to register this class' Binder (bottom of class) with Jersey to use it.
What differentiates this class from Jersey QueryParamValueFactoryProvider is that instead of just processing a single String value of the annotation, it splits the value, and tries to extract the values from the query param map. The first value found will be returned. If the parameter is a List or Set, it just continues to keep looking up all the options, and adding them to the list.
For the most part this keeps all the functionality you would expect from an #XxxParam annotation. The only thing that was difficult to implement (so I left out supporting this use case), is multiple parameters, e.g.
#GET
#Path("multiple")
public String getMultipleVariants(#VaryingParam("param-1|param-2|param-3") String value1,
#VaryingParam("param-1|param-2|param-3") String value2) {
return value1 + ":" + value2;
}
I actually don't think it should be that hard to implement, if you really need it, it's just a matter of creating a new MultivaluedMap, removing a value if it is found. This would be implemented in the provide() method of the VaryingParamFactory above. If you need this use case, you could just use a List or Set instead.
See this GitHub Gist (it's rather long) for a complete test case, using Jersey Test Framework. You can see all the use cases I tested in the QueryTestResource, and where I register the Binder with the ResourceConfig in the test configure() method.
I am trying to get method regardless of what parameters that method takes (as of now there is no method overloading and there wouldn't be in future). The only possible solution that i could come up with was
private Method getMethod(Class<?> clas, String methodName) {
try {
Method[] methods = clas.getMethods();
for (Method method : methods) {
if (method.getName().equalsIgnoreCase(methodName)) {
return method;
}
}
} catch (SecurityException e) {
e.printStackTrace();
}
return null;
}
What i want to ask that is there a way to fetch a method regardless of its parameters ? I was looking at clas.getMethod ("methodName", parameters) and if i provide null in there it will try to fetch a method which has no parameters. Which wouldn't be no case.
Any ideas ?
EDIT
Thanks guys for input. In my case, i know that there would be only one method regardless of its case. The reason i am using ignoreCase is because the input will be coming from a developer (in other team) and he will be providing the name as a hard-coded string. So to keep things from spilling out of our hands, I am using a safe approach.
No. The way you've done it is the way to go. A method is identified by its signature and the signature includes the name and the parameter types.
Here is a solution that retrieves all methods with the specified class and method name regardless of the method's parameters:
public class Test
{
private class Foo
{
public void bar()
{
}
public void bar(String s)
{
}
public void goo()
{
}
}
private static Method[] getMethods(Class<?> clazz, String methodName)
{
List<Method> methods = new ArrayList<Method>();
Method[] declaredMethods = clazz.getDeclaredMethods();
for (Method declaredMethod: declaredMethods)
{
if (declaredMethod.getName().equals(methodName))
{
methods.add(declaredMethod);
}
}
return methods.toArray(new Method[methods.size()]);
}
public static void main(String[] args)
{
Method[] methods = getMethods(Foo.class, "bar");
System.out.println(Arrays.toString(methods));
}
}
This generates the following output:
[public void com.example.Test$Foo.bar(java.lang.String), public void com.example.Test$Foo.bar()]
You've done just fine. This is basically the same as the solution to a similar problem I dealt with four years ago, creating a means to create callback methods in Java. The constructors for my Callback class were:
public Callback(Class<?> clazz, String methodName, Object parentObj) {
// Find a method with the matching name
Method[] allMethods;
try { allMethods = clazz.getMethods(); }
catch(SecurityException se) { allMethods = new Method[0]; }
int count = 0;
Method single = null;
for(Method m : allMethods) {
if(m.getName().equals(methodName)) {
single = m;
count++;
}
// Can't have more than one instance
if(count > 1)
throw new IllegalArgumentException(clazz.getName()
+ " has more than one method named " + methodName);
}
if(count == 0) // No instances found
throw new IllegalArgumentException(clazz.getName()
+ " has no method named " + methodName);
this.parentObj = parentObj;
this.method = single;
this.parameters = single.getParameterTypes();
}
public Callback(
Class<?> clazz,
String methodName,
Object parentObj,
Class<?>...parameters)
{
try { this.method = clazz.getMethod(methodName, parameters); }
catch(NoSuchMethodException nsme) { nsme.printStackTrace(); }
catch(SecurityException se) { se.printStackTrace(); }
this.parentObj = parentObj;
this.parameters = parameters;
}
My Callback class isn't really useful any more in the era of Java 8, but at the time the only real means for a "callback" in java was anonymous interface implementations, which wasn't sufficient for my use-case.
As you can see in the first constructor, it throws an exception if it finds multiple methods with the same name.
Using java streams there is a really short method of finding a method, the first match, by its name only:
Stream.of(type.getMethods())
.filter((m) -> m.getName().equals(searchedName))
.findFirst()
.get();
I think this is a short and readable possibility in this case.
If I mock a method to return a new instance of some object, how can I capture the returned instance?
E.g.:
when(mock.someMethod(anyString())).thenAnswer(new Answer() {
Object answer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return new Foo(args[0])
}
});
Obviously, I can have a field of type Foo and inside answer set it to the new instance, but is there a nicer way? Something like ArgumentCaptor?
I wanted to do something similar, but with a spied object rather than a mock. Specifically, given a spied object, I want to capture the return value. Based on Andreas_D's answer, here's what I came up with.
public class ResultCaptor<T> implements Answer {
private T result = null;
public T getResult() {
return result;
}
#Override
public T answer(InvocationOnMock invocationOnMock) throws Throwable {
result = (T) invocationOnMock.callRealMethod();
return result;
}
}
Intended usage:
// spy our dao
final Dao spiedDao = spy(dao);
// instantiate a service that does some stuff, including a database find
final Service service = new Service(spiedDao);
// let's capture the return values from spiedDao.find()
final ResultCaptor<QueryResult> resultCaptor = new ResultCaptor<>();
doAnswer(resultCaptor).when(spiedDao).find(any(User.class), any(Query.class));
// execute once
service.run();
assertThat(resultCaptor.getResult()).isEqualTo(/* something */);
/// change conditions ///
// execute again
service.run();
assertThat(resultCaptor.getResult()).isEqualTo(/* something different */);
Looks like you want to observe and then Answer instances, and receive notifications each time the answer method is called (which triggers the creation of a new Foo). So why not invent an ObservableAnswer class:
public abstract class ObservableAnswer implements Answer {
private Listener[] listeners; // to keep it very simple...
public ObservableAnswer(Listener...listeners) {
this.listeners = listeners;
}
#Override
public Object answer(InvocationOnMock invocation) {
Object answer = observedAnswer(invocation);
for (Listener listener:listeners) {
listener.send(answer);
}
return answer;
}
// we'll have to implement this method now
public abstract Object observedAnswer(InvocationOnMock invocation);
}
Intended use:
Listener[] myListeners = getListeners(); // some magic (as usual)
when(mock.someMethod(anyString())).thenAnswer(new ObservableAnswer(myListeners) {
Object observedAnswer(InvocationOnMock invocation) {
Object[] args = invocation.getArguments();
Object mock = invocation.getMock();
return new Foo(args[0])
}
});
As an alternative to #JeffFairley's answer, you can leverage AtomicReference<T>. It will act as a Holder<T>, but I prefer this over real holders because it's defined in Java's base framework.
// spy our dao
final Dao spiedDao = spy(dao);
// instantiate a service that does some stuff, including a database find
final Service service = new Service(spiedDao);
// let's capture the return values from spiedDao.find()
AtomicReference<QueryResult> reference = new AtomicReference<>();
doAnswer(invocation -> {
QueryResult result = (QueryResult)invocation.callRealMethod();
reference.set(result);
return result;
}).when(spiedDao).find(any(User.class), any(Query.class));
// execute once
service.run();
assertThat(reference.get()).isEqualTo(/* something */);
/// change conditions ///
// execute again
service.run();
assertThat(result.get()).isEqualTo(/* something different */);
In my opinion: ResultCaptor is cool stuff that may be integrated in Mockito in the future, is widely reusable and short in syntax. But if you need that sporadically, then few lines of a lambda can be more concise
Call doAnswer, then call the real method and add the returning value to a list, as the following:
final var capturedValues = new ArrayList<Integer>();
final var myObjectList = spy(new MyObject());
doAnswer(invocation -> {
final var r = invocation.callRealMethod();
capturedValues.add((Integer) r);
return r;
})
.when(myObjectList)
.mySuperMethod;
A full example:
#Test
public void test() {
// arrange
final var capturedValues = new ArrayList<Integer>();
final var myObjectList = spy(new ArrayList<>());
doAnswer(invocation -> {
final var r = invocation.callRealMethod();
capturedValues.add((Integer) r);
return r;
})
.when(myObjectList)
.size();
// act
myObjectList.size();
myObjectList.add("one");
myObjectList.size();
myObjectList.add("two");
myObjectList.size();
// assert
assertEquals(3, capturedValues.size());
assertEquals("[0, 1, 2]", capturedValues.toString());
}