Having
public Object callRemote (String sRemoteInterface, String sMethod,
Object... arguments);
I want to do something like:
method.invoke(
instance,
arguments...,
sessionId
);
So it works for methods with a variable number of arguments.
How can I do it?
[EDIT] Here is the actual code I am using right now. It works for 1 or 2 arguments, but it needs to be made generic for N arguments:
public Object callRemote (String sRemoteInterface, String sMethod,
Object... arguments) throws Exception {
Object instance;
Method method;
if (sRemoteInterface==null) {
throw new Exception("Must specify a remote interface to locate.");
}
instance = this.locator.getRemoteReference(sRemoteInterface, this.sessionId);
if (arguments.length == 2) {
method = instance.getClass().getMethod(
sMethod,
arguments[0].getClass(),
arguments[1].getClass(),
int.class
);
return method.invoke(
instance,
arguments[0],
arguments[1],
sessionId
);
} else {
method = instance.getClass().getMethod(
sMethod,
arguments[0].getClass(),
int.class
);
return method.invoke(
instance,
arguments[0],
sessionId
);
}
}
You could try it with following snippet. The idea is that you pass the varargs through (getMethod also has varargs). I omitted the code for instance creation.
int sessionId;
Object callRemote(Object instance, String sMethod, Object... arguments) throws Exception {
Class<?>[] argumentTypes = createArgumentTypes(arguments);
Method method = instance.getClass().getMethod(sMethod, argumentTypes );
Object[] argumentsWithSession = createArguments(arguments);
return method.invoke(instance, argumentsWithSession);
}
Object[] createArguments(Object[] arguments) {
Object[] args = new Object[arguments.length + 1];
System.arraycopy(arguments, 0, args, 0, arguments.length);
args[arguments.length] = sessionId;
return args;
}
Class<?>[] createArgumentTypes(Object[] arguments) {
Class[] types = new Class[arguments.length + 1];
for (int i = 0; i < arguments.length; i++) {
types[i] = arguments[i].getClass();
}
types[arguments.length] = int.class;
return types;
}
Related
I am try to use reflect to create some instance through the json configuration:
config.json:
{
"clazz":com.kk.Manager",
"args":{
"path":"/home/kk/a.txt",
"scale":1
}
}
Implementation:
packge com.kk;
public class Manager{
private File file;
private int scale;
public Manager(String path,int scale){
this.file = new File(path)
this.scale=scale;
//other initialization work omitted
}
}
private static Object buildObject(String clazz, JSONObject args) {
Object instance;
try {
Class clas = Class.forName(clazz);
if (args == null) {
instance = clas.newInstance();
} else {
Class[] pamas = new Class[args.length()];
Object[] vals = new Object[args.length()];
Iterator<String> parameterIt = args.keys();
int i = 0;
while (parameterIt.hasNext()) {
String fd = parameterIt.next();
Object val = args.get(fd);
pamas[i] = val.getClass();
vals[i] = val;
i++;
}
instance = clas.getConstructor(pamas).newInstance(vals);
}
return instance;
} catch (Exception e) {
return null;
}
return null;
}
After I parse the json config file, I will call it like this:
String clazz = parent.getString("clazz");
JSONObject args = parent.getJSONObject("args")
buildObject(clazz,args);
While the code throw error:
java.lang.NoSuchMethodException: com.kk.Manager.<init>(java.lang.Integer, java.lang.String)
It seem that the constructor of Manager is called with parameters in wrong order.
After google, it seems that the order of keys in a JSONObject is undefined.
So I wonder if I can get the constructor by the parameter name, something like:
Class cls = Class.forName(clazz);
cls.getConstructor("path","scale");
Or if not, how can I fix this without add the setter method?
Hi i am using reflections to achieve something.
I have been given class name, method name of that class and parameter values that needs to be passed to that method in a file(Take any file. Not a constraint).
I have to call that method with the parameters. This methods do not return anything.
There is a huge list of methods in this classes and parameter list of each varies.
E.g: method1(String, String, int, boolean)
method1(String, int, boolean) and likewise i have different permutations and combinations.
So how can i achieve this.
I have tried hard coding things with different switch clauses but it is a real overhead and risky thing to maintain.
Can we dynamically do this thing, like on the fly read the method name and its parameter from the file and call it.
Any small code snippet will be helpful.
TIA.
Hi all i have found the solution to the above question. below is the sample code snippet.
package reflections;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
public class ReflectionTest {
public void method1(String str, int number) {
System.out.println(str + number);
}
public void method1(String str) {
System.out.println(str);
}
public void method1() {
System.out.println("helloworld");
}
public static void main(String[] args) throws ClassNotFoundException,
InstantiationException, IllegalAccessException,
NoSuchMethodException, SecurityException, IllegalArgumentException,
InvocationTargetException {
// Step 1) Make an object array and store the parameters that you wish
// to pass it.
Object[] obj = {};// for method1()
// Object[] obj={"hello"}; for method1(String str)
// Object[] obj={"hello",1}; for method1(String str,int number)
// Step 2) Create a class array which will hold the signature of the
// method being called.
Class<?> params[] = new Class[obj.length];
for (int i = 0; i < obj.length; i++) {
if (obj[i] instanceof Integer) {
params[i] = Integer.TYPE;
} else if (obj[i] instanceof String) {
params[i] = String.class;
}
// you can do additional checks for other data types if you want.
}
String methoName = "method1"; // methodname to be invoked
String className = "reflections.ReflectionTest";// Class name
Class<?> cls = Class.forName(className);
Object _instance = cls.newInstance();
Method myMethod = cls.getDeclaredMethod(methoName, params);
myMethod.invoke(_instance, obj);
}
}
I hope this will help others too.
public class ReflectionSample
{
private Object mString = null;
private int mValue;
public ReflectionSample()
{
}
public ReflectionSample(int oValue)
{
mValue = oValue;
}
public ReflectionSample(String oString)
{
mString = oString;
}
public ReflectionSample(String oString, int oValue)
{
setValues(oString, oValue);
}
public void setValues(String oString, int oValue)
{
mString = oString;
mValue = oValue;
}
public String toString()
{
return ""+mString+":"+mValue;
}
public void run()
{
String oInput = "Teststring";
Class<?> cls;
String clsname = "main.ReflectionSample";
Object rs = null; // ReflectionSample
Object rsc = null;
System.out.println(this.getClass().getName());
try
{
System.out.println(clsname);
cls = Class.forName(clsname);
if(cls == null)
{
System.err.println(clsname + " doesn't exist");
return;
}
// Look for a constructor which has a single string
Constructor<?> ct = null;
Class<?>[] param_types = new Class<?>[1];
Object[] arguments = new Object[1];
param_types[0] = String.class;
// get the string constructor
ct = cls.getConstructor(param_types);
// We only have one object
arguments = new Object[1];
arguments[0] = oInput;
// Instantiate the object with passed in argument.
rs = ct.newInstance(arguments);
System.out.println("String constructor sample: "+rs);
// Instantiate with default constructor
param_types = new Class<?>[0];
arguments = new Object[0];
ct = cls.getConstructor(param_types);
rs = ct.newInstance(arguments);
rsc = rs; // Keep it for later, to lazy to call it again
System.out.println("Default constructor sample: "+rs);
// Instantiate with string and int constructor
param_types = new Class<?>[2];
arguments = new Object[2];
// Must be in the same order as the params I think
param_types[0] = String.class;
param_types[1] = Integer.TYPE; // <-- Its a primitive so use TYPE not Class
arguments[0] = oInput;
arguments[1] = new Integer(1);
ct = cls.getConstructor(param_types);
rs = ct.newInstance(arguments);
System.out.println("String plus int constructor sample: "+rs);
// call the setValues method
param_types[0] = String.class;
param_types[1] = Integer.TYPE; // <-- Its a primitive so use TYPE not Class
arguments[0] = oInput;
arguments[1] = 1;
System.out.println("setValues invocation before: "+rsc);
Method m = cls.getMethod("setValues", param_types);
m.invoke(rsc, arguments);
System.out.println("setValues invocation after: "+rsc);
// An alternative method to pass the parameters
m = cls.getMethod("setValues", String.class, Integer.TYPE);
m.invoke(rsc, oInput+"x", 2);
System.out.println("setValues invocation after: "+rsc);
}
catch(Throwable e)
{
System.err.println(e.getLocalizedMessage());
}
}
}
Output:
main.ReflectionSample
main.ReflectionSample
String constructor sample: Teststring:0
Default constructor sample: null:0
String plus int constructor sample: Teststring:1
setValues invocation before: null:0
setValues invocation after: Teststring:1
Hope this helps.
I don't know if this is a newer feature in Java, but I have seen that you can use invoke now with parameters as well, instead of using an array, which might make your code better to read (This is the alternative way). If you need a variable number of arguments and you don't know beforehand how many there will be, allocating the array is defeinitly working and should also be backwardcompatible.
A simple solution would be to create a Class with the Arguments required to be passed:
public class ObjectArguments {
private PrintWriter out;
private String productId;
private int action;
public ObjectArguments(PrintWriter out, String productId, int action) {
this.out = out;
this.productId = productId;
this.action = action;
}
public PrintWriter getOut() {
return out;
}
public String getProductId() {
return productId;
}
public int getAction() {
return action;
}
}
Assuming that you want to invoke a class Foo with a method named bar.
Then it would be done like this.
PrintWriter out = null;
String productId = null;
int action = 0;
Class[] paramArguments = new Class[1];
paramArguments[0] = ObjectArguments.class;
ObjectArguments newObj = new ObjectArguments(out,productId,action);
Class cls = Class.forName("Foo");
Object obj = cls.newInstance();
Method method = cls.getDeclaredMethod("bar", paramArguments);
method.invoke(obj, newObj);
For two int parameters the example is as below, similarly other datatype parameters can also be called
Method method=new Test1().getClass().getMethod(x, new Class[] {int.class,int.class});
We can call a method that needs 3 arguments int,int,string as below :
Method method=new Test1().getClass().getMethod(x, new Class[] {int.class,int.class, String.class});
I am using Spring AOP and have below aspect:
#Aspect
public class LoggingAspect {
#Before("execution(* com.mkyong.customer.bo.CustomerBo.addCustomer(..))")
public void logBefore(JoinPoint joinPoint) {
System.out.println("logBefore() is running!");
System.out.println("hijacked : " + joinPoint.getSignature().getName());
System.out.println("******");
}
}
Above aspect intercepts addCustomer method execution. addCustomer method takes string as an input.
But I need to log input passed to addCustomer method inside logBefore method.
Is it possible to do so ?
You have a few options:
First, you can use the JoinPoint#getArgs() method which returns an Object[] containing all the arguments of the advised method. You might have to do some casting depending on what you want to do with them.
Second, you can use the args pointcut expression like so:
// use '..' in the args expression if you have zero or more parameters at that point
#Before("execution(* com.mkyong.customer.bo.CustomerBo.addCustomer(..)) && args(yourString,..)")
then your method can instead be defined as
public void logBefore(JoinPoint joinPoint, String yourString)
Yes, the value of any argument can be found using getArgs
#Before("execution(* com.mkyong.customer.bo.CustomerBo.addCustomer(..))")
public void logBefore(JoinPoint joinPoint) {
Object[] signatureArgs = thisJoinPoint.getArgs();
for (Object signatureArg: signatureArgs) {
System.out.println("Arg: " + signatureArg);
...
}
}
If you have to log all args or your method have one argument, you can simply use getArgs like described in previous answers.
If you have to log a specific arg, you can annoted it and then recover its value like this :
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.PARAMETER)
public #interface Data {
String methodName() default "";
}
#Aspect
public class YourAspect {
#Around("...")
public Object around(ProceedingJoinPoint point) throws Throwable {
Method method = MethodSignature.class.cast(point.getSignature()).getMethod();
Object[] args = point.getArgs();
StringBuilder data = new StringBuilder();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
for (int argIndex = 0; argIndex < args.length; argIndex++) {
for (Annotation paramAnnotation : parameterAnnotations[argIndex]) {
if (!(paramAnnotation instanceof Data)) {
continue;
}
Data dataAnnotation = (Data) paramAnnotation;
if (dataAnnotation.methodName().length() > 0) {
Object obj = args[argIndex];
Method dataMethod = obj.getClass().getMethod(dataAnnotation.methodName());
data.append(dataMethod.invoke(obj));
continue;
}
data.append(args[argIndex]);
}
}
}
}
Examples of use :
public void doSomething(String someValue, #Data String someData, String otherValue) {
// Apsect will log value of someData param
}
public void doSomething(String someValue, #Data(methodName = "id") SomeObject someData, String otherValue) {
// Apsect will log returned value of someData.id() method
}
There is also another way if you define one pointcut for many advices it can be helpful:
#Pointcut("execution(#com.stackoverflow.MyAnnotation * *(..))")
protected void myPointcut() {
}
#AfterThrowing(pointcut = "myPointcut() && args(someId,..)", throwing = "e")
public void afterThrowingException(JoinPoint joinPoint, Exception e, Integer someId) {
System.out.println(someId.toString());
}
#AfterReturning(pointcut = "myPointcut() && args(someId,..)")
public void afterSuccessfulReturn(JoinPoint joinPoint, Integer someId) {
System.out.println(someId.toString());
}
Your can use either of the following methods.
#Before("execution(* ong.customer.bo.CustomerBo.addCustomer(String))")
public void logBefore1(JoinPoint joinPoint) {
System.out.println(joinPoint.getArgs()[0]);
}
or
#Before("execution(* ong.customer.bo.CustomerBo.addCustomer(String)), && args(inputString)")
public void logBefore2(JoinPoint joinPoint, String inputString) {
System.out.println(inputString);
}
joinpoint.getArgs() returns object array. Since, input is single string, only one object is returned.
In the second approach, the name should be same in expression and input parameter in the advice method i.e. args(inputString) and public void logBefore2(JoinPoint joinPoint, String inputString)
Here, addCustomer(String) indicates the method with one String input parameter.
you can get method parameter and its value and if annotated with a annotation with following code:
Map<String, Object> annotatedParameterValue = getAnnotatedParameterValue(MethodSignature.class.cast(jp.getSignature()).getMethod(), jp.getArgs());
....
private Map<String, Object> getAnnotatedParameterValue(Method method, Object[] args) {
Map<String, Object> annotatedParameters = new HashMap<>();
Annotation[][] parameterAnnotations = method.getParameterAnnotations();
Parameter[] parameters = method.getParameters();
int i = 0;
for (Annotation[] annotations : parameterAnnotations) {
Object arg = args[i];
String name = parameters[i++].getDeclaringExecutable().getName();
for (Annotation annotation : annotations) {
if (annotation instanceof AuditExpose) {
annotatedParameters.put(name, arg);
}
}
}
return annotatedParameters;
}
If it's a single String argument, do:
joinPoint.getArgs()[0];
if your using #Aspect an option is add this method inside your Aspect and send the JoinPoint and the name of parameter you need.
private Object getParameter(ProceedingJoinPoint joinPoint, String parameterName) {
Object valueParameter = null;
if (Objects.nonNull(joinPoint) && joinPoint.getSignature() instanceof MethodSignature
&& Objects.nonNull(parameterName) ) {
MethodSignature method = (MethodSignature)joinPoint.getSignature();
String[] parameters = method.getParameterNames();
for (int t = 0; t< parameters.length; t++) {
if( Objects.nonNull(parameters[t]) && parameters[t].equals(parameterName)) {
Object[] obj = joinPoint.getArgs();
valueParameter = obj[t];
}
}
}
return valueParameter;
}
and the call example:
Object parameterObject = getParameter(joinPoint, "nameClient");
if ( Objects.nonNull(parameterObject) ) {
String parametro = String.valueOf(parameterObject);
}
Only need know the type of object for convert
TLDR: I'd like to know how to extend fit.TypeAdaptor so that I can invoke a method that expects parameters as default implementation of TypeAdaptor invokes the binded (bound ?) method by reflection and assumes it's a no-param method...
Longer version -
I'm using fit to build a test harness for my system (a service that returns a sorted list of custom objects). In order to verify the system, I thought I'd use fit.RowFixture to assert attributes of the list items.
Since RowFixture expects the data to be either a public attribute or a public method, I thought of using a wrapper over my custom object (say InstanceWrapper) - I also tried to implement the suggestion given in this previous thread about formatting data in RowFixture.
The trouble is that my custom object has around 41 attributes and I'd like to provide testers with the option of choosing which attributes they want to verify in this RowFixture. Plus, unless I dynamically add fields/methods to my InstanceWrapper class, how will RowFixture invoke either of my getters since both expect the attribute name to be passed as a param (code copied below) ?
I extended RowFixture to bind on my method but I'm not sure how to extend TypeAdaptor so that it invokes with the attr name..
Any suggestions ?
public class InstanceWrapper {
private Instance instance;
private Map<String, Object> attrs;
public int index;
public InstanceWrapper() {
super();
}
public InstanceWrapper(Instance instance) {
this.instance = instance;
init(); // initialise map
}
private void init() {
attrs = new HashMap<String, Object>();
String attrName;
for (AttrDef attrDef : instance.getModelDef().getAttrDefs()) {
attrName = attrDef.getAttrName();
attrs.put(attrName, instance.getChildScalar(attrName));
}
}
public String getAttribute(String attr) {
return attrs.get(attr).toString();
}
public String description(String attribute) {
return instance.getChildScalar(attribute).toString();
}
}
public class MyDisplayRules extends fit.RowFixture {
#Override
public Object[] query() {
List<Instance> list = PHEFixture.hierarchyList;
return convertInstances(list);
}
private Object[] convertInstances(List<Instance> instances) {
Object[] objects = new Object[instances.size()];
InstanceWrapper wrapper;
int index = 0;
for (Instance instance : instances) {
wrapper = new InstanceWrapper(instance);
wrapper.index = index;
objects[index++] = wrapper;
}
return objects;
}
#Override
public Class getTargetClass() {
return InstanceWrapper.class;
}
#Override
public Object parse(String s, Class type) throws Exception {
return super.parse(s, type);
}
#Override
protected void bind(Parse heads) {
columnBindings = new TypeAdapter[heads.size()];
for (int i = 0; heads != null; i++, heads = heads.more) {
String name = heads.text();
String suffix = "()";
try {
if (name.equals("")) {
columnBindings[i] = null;
} else if (name.endsWith(suffix)) {
columnBindings[i] = bindMethod("description", name.substring(0, name.length()
- suffix.length()));
} else {
columnBindings[i] = bindField(name);
}
} catch (Exception e) {
exception(heads, e);
}
}
}
protected TypeAdapter bindMethod(String name, String attribute) throws Exception {
Class partypes[] = new Class[1];
partypes[0] = String.class;
return PHETypeAdaptor.on(this, getTargetClass().getMethod("getAttribute", partypes), attribute);
}
}
For what it's worth, here's how I eventually worked around the problem:
I created a custom TypeAdapter (extending TypeAdapter) with the additional public attribute (String) attrName. Also:
#Override
public Object invoke() throws IllegalAccessException, InvocationTargetException {
if ("getAttribute".equals(method.getName())) {
Object params[] = { attrName };
return method.invoke(target, params);
} else {
return super.invoke();
}
}
Then I extended fit.RowFixture and made the following overrides:
public getTargetClass() - to return my class reference
protected TypeAdapter bindField(String name) throws Exception - this is a protected method in ColumnFixture which I modified so that it would use my class's getter method:
#Override
protected TypeAdapter bindField(String name) throws Exception {
String fieldName = camel(name);
// for all attributes, use method getAttribute(String)
Class methodParams[] = new Class[1];
methodParams[0] = String.class;
TypeAdapter a = TypeAdapter.on(this, getTargetClass().getMethod("getAttribute", methodParams));
PHETypeAdapter pheAdapter = new PHETypeAdapter(fieldName);
pheAdapter.target = a.target;
pheAdapter.fixture = a.fixture;
pheAdapter.field = a.field;
pheAdapter.method = a.method;
pheAdapter.type = a.type;
return pheAdapter;
}
I know this is not a neat solution, but it was the best I could come up with. Maybe I'll get some better solutions here :-)
I have a class that uses XML and reflection to return Objects to another class.
Normally these objects are sub fields of an external object, but occasionally it's something I want to generate on the fly. I've tried something like this but to no avail. I believe that's because Java won't allow you to access private methods for reflection.
Element node = outerNode.item(0);
String methodName = node.getAttribute("method");
String objectName = node.getAttribute("object");
if ("SomeObject".equals(objectName))
object = someObject;
else
object = this;
method = object.getClass().getMethod(methodName, (Class[]) null);
If the method provided is private, it fails with a NoSuchMethodException. I could solve it by making the method public, or making another class to derive it from.
Long story short, I was just wondering if there was a way to access a private method via reflection.
You can invoke private method with reflection. Modifying the last bit of the posted code:
Method method = object.getClass().getDeclaredMethod(methodName);
method.setAccessible(true);
Object r = method.invoke(object);
There are a couple of caveats. First, getDeclaredMethod will only find method declared in the current Class, not inherited from supertypes. So, traverse up the concrete class hierarchy if necessary. Second, a SecurityManager can prevent use of the setAccessible method. So, it may need to run as a PrivilegedAction (using AccessController or Subject).
Use getDeclaredMethod() to get a private Method object and then use method.setAccessible() to allow to actually call it.
If the method accepts non-primitive data type then the following method can be used to invoke a private method of any class:
public static Object genericInvokeMethod(Object obj, String methodName,
Object... params) {
int paramCount = params.length;
Method method;
Object requiredObj = null;
Class<?>[] classArray = new Class<?>[paramCount];
for (int i = 0; i < paramCount; i++) {
classArray[i] = params[i].getClass();
}
try {
method = obj.getClass().getDeclaredMethod(methodName, classArray);
method.setAccessible(true);
requiredObj = method.invoke(obj, params);
} catch (NoSuchMethodException e) {
e.printStackTrace();
} catch (IllegalArgumentException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
return requiredObj;
}
The Parameter accepted are obj, methodName and the parameters. For example
public class Test {
private String concatString(String a, String b) {
return (a+b);
}
}
Method concatString can be invoked as
Test t = new Test();
String str = (String) genericInvokeMethod(t, "concatString", "Hello", "Mr.x");
you can do this using ReflectionTestUtils of Spring (org.springframework.test.util.ReflectionTestUtils)
ReflectionTestUtils.invokeMethod(instantiatedObject,"methodName",argument);
Example : if you have a class with a private method square(int x)
Calculator calculator = new Calculator();
ReflectionTestUtils.invokeMethod(calculator,"square",10);
Let me provide complete code for execution protected methods via reflection. It supports any types of params including generics, autoboxed params and null values
#SuppressWarnings("unchecked")
public static <T> T executeSuperMethod(Object instance, String methodName, Object... params) throws Exception {
return executeMethod(instance.getClass().getSuperclass(), instance, methodName, params);
}
public static <T> T executeMethod(Object instance, String methodName, Object... params) throws Exception {
return executeMethod(instance.getClass(), instance, methodName, params);
}
#SuppressWarnings("unchecked")
public static <T> T executeMethod(Class clazz, Object instance, String methodName, Object... params) throws Exception {
Method[] allMethods = clazz.getDeclaredMethods();
if (allMethods != null && allMethods.length > 0) {
Class[] paramClasses = Arrays.stream(params).map(p -> p != null ? p.getClass() : null).toArray(Class[]::new);
for (Method method : allMethods) {
String currentMethodName = method.getName();
if (!currentMethodName.equals(methodName)) {
continue;
}
Type[] pTypes = method.getParameterTypes();
if (pTypes.length == paramClasses.length) {
boolean goodMethod = true;
int i = 0;
for (Type pType : pTypes) {
if (!ClassUtils.isAssignable(paramClasses[i++], (Class<?>) pType)) {
goodMethod = false;
break;
}
}
if (goodMethod) {
method.setAccessible(true);
return (T) method.invoke(instance, params);
}
}
}
throw new MethodNotFoundException("There are no methods found with name " + methodName + " and params " +
Arrays.toString(paramClasses));
}
throw new MethodNotFoundException("There are no methods found with name " + methodName);
}
Method uses apache ClassUtils for checking compatibility of autoboxed params
One more variant is using very powerfull JOOR library https://github.com/jOOQ/jOOR
MyObject myObject = new MyObject()
on(myObject).get("privateField");
It allows to modify any fields like final static constants and call yne protected methods without specifying concrete class in the inheritance hierarhy
<!-- https://mvnrepository.com/artifact/org.jooq/joor-java-8 -->
<dependency>
<groupId>org.jooq</groupId>
<artifactId>joor-java-8</artifactId>
<version>0.9.7</version>
</dependency>