How to pass arguments to a function written inside an MVEL expression? - mvel

I have a JAVA class that has two methods. The first one is the main method and the second one is method1().
Let's say the following is the class:
public class SomeClass() {
public static void main(String[] args) {
SomeClass myObj = new SomeClass();
Map<String,Object> map = new HashMap<String,Object>();
map.put("obj", myObj);
MVEL.eval("System.out.println(\"I am inside main method\");obj.method1();",map);
}
public static void method1(List<String> listOfStrings){
System.out.println("I am inside method 1");
}
}
Now as you can see in the expression, to call method1, I need to pass a list as arguments. How to do that? What changes are required in the expression? What if I want to pass dynamic arguments in my program?

You can create a List or have it coming from some other source as an argument.
Only thing you need to take care is to put inside the map object,
which used by MVEL for evaluation.
Need to pass list as mentioned -> obj.method1(myList);
Working Code Below
public class SomeClass {
public static void main(String[] args) {
SomeClass myObj = new SomeClass();
Map<String, Object> map = new HashMap<String, Object>();
map.put("obj", myObj);
List<String> listOfStrings = new ArrayList<String>();
listOfStrings.add("my ");
listOfStrings.add("List ");
listOfStrings.add("is printing");
map.put("obj", myObj);
map.put("myList", listOfStrings);
MVEL.eval("System.out.println(\"I am inside main method\");obj.method1(myList);",map);
}
public static void method1(List<String> listOfStrings) {
System.out.println("I am inside method 1");
for (String s : listOfStrings) {
System.out.print(s);
}
}
}
output
I am inside main method
I am inside method 1
my List is printing

Related

how to intercept a method with specific parameters with bytebuddy

I want to intercept method named methodA with one arg which's type is String as blow, what should i do. How to use hasParameters() api?
public class Demo {
public static void main(String[] args) {
new ByteBuddy()
.subclass(A.class)
.method(named("methodA").and(hasParameters(?)))
}
static class A {
public void methodA() {
System.out.println("methodA() invoked.");
}
public void methodA(String arg) {
System.out.println("methodA(" + arg + ") invoked.");
}
}
}
For this you want the ElementMatchers.takesArguments(String.class) matcher.
So something like that:
Class<? extends A> loaded = new ByteBuddy().subclass(A.class)
.method(ElementMatchers.named("methodA").and(ElementMatchers.takesArguments(String.class)))
.intercept(MethodDelegation.to(Demo.class))
.make().load(Demo.class.getClassLoader(), ClassLoadingStrategy.Default.INJECTION).getLoaded();
A instance = loaded.getConstructor().newInstance();
instance.methodA("abc");
instance.methodA();
public class Demo {
static void intercept(String arg){
System.out.println("intercepted");
}
}
To clarify, you need to define a matcher (similar to a filter) to apply to methods. Create some constraint in the matcher so it will only match to some parameter structure you specify:
ElementMatcher<Iterable<? extends ParameterDescription>> matcher = parameterDescriptions -> {
for (ParameterDescription p : parameterDescriptions) {
if (p.getType().equals(TypeDescription.STRING.asGenericType()) && p.getIndex() == 0) return true;
}
return false;
};
ByteBuddyAgent.install();
new ByteBuddy()
.redefine(SomeType.class)
.method(ElementMatchers.hasParameters(matcher))
.intercept(FixedValue.value("Hello World"))
.make()
.load(SomeType.class.getClassLoader(),
ClassReloadingStrategy.fromInstalledAgent());

How to use #DataProvider present in different class

How to use #DataProvider that is present in a different class?
I have created a different package and I have defined data providers next to each test cases. Please share how I may to use that in a different class.
You can use the dataProviderClass attribute of #Test:
public class StaticProvider {
#DataProvider(name = "create")
public static Object[][] createData() {
return new Object[][] {
new Object[] { new Integer(42) }
};
}
}
public class MyTest {
#Test(dataProvider = "create", dataProviderClass = StaticProvider.class)
public void test(Integer n) {
// ...
}
}
Check the documentation for more details.
If you have unique dataProvider method name (createData), and if you choose not to give name after DataProvider annotation as below,
#DataProvider
public Object[][] createData(){
}
Then you can use the method name as below,
#Test(dataProvider = "createData", dataProviderClass = StaticProvider.class)

How to access a variable declared inside a MVEL expression?

Suppose I write a code like this:
public class SomeClass() {
public static void main(String[] args) {
MVEL.eval("boolean boolVar = 2<3;");
}
}
Now is it possible to access this boolVar variable in the Java code anywhere.
Example: Can I print the value of boolVar using
System.out.print(boolVar);
in the main method just below the MVEL line.
Remember doing as above, boolean boolVar becomes local variable, and MVEL cannot compile it also.
1.) Need to pass class object.
2.) Create boolean property in class, and assign to it.
Expression to be evaluated : MVEL.eval("obj.output = 2<3;", map);
Please try below code :-
import java.util.HashMap;
import java.util.Map;
import org.mvel2.MVEL;
public class SomeClass {
private boolean output;
public boolean isOutput() {
return output;
}
public void setOutput(boolean output) {
this.output = output;
}
public static void main(String[] args) {
SomeClass myObj = new SomeClass();
Map<String, Object> map = new HashMap<String, Object>();
map.put("obj", myObj);
MVEL.eval("obj.output = 2<3;", map);
System.out.println(myObj.isOutput());
MVEL.eval("obj.output = 2>3;", map);
System.out.println(myObj.isOutput());
}
}
output
true
false

Deserialize JSON array into Map using Jackson in Java

I'm using fasterXML's Jackson (v2.3.3) library to deserialize and serialize a custom class. The class is defined as following:
public class Person {
private String name;
private Map<String, Person> children;
// lots of other fields of different types with no issues
}
the keys of map children are the name fields. I receive data in JSON with each person object structured as following (I have omitted the other fields):
{"name":"Bob", "children":[{"name":"Jimmmy"},{"name":"Judy"}]}
(Many Fields such as children are optional and aren't serialized when null)
I have been storing children in a List<Person> so far with no issues, but many new use cases need to have access to the set of names or to a specific Person using his name as key. This is why I have decided to store them using a Map.
After some research, I think the best way is to use Annotations #JsonDeserialize and #JsonSerialize with a JsonDeserializer and JsonSerializer as parameter respectively for the field children:
public class Person {
private String id;
#JsonSerialize(using=MySerializer.class)
#JsonDeserialize(using=MyDeserializer.class)
private Map<String, Person> friends;
// lots of other fields
}
My question is: Does such a JsonSerializer/JsonDeserializer exist and if not, how do I define one?
edit: I have started implementing a custom Deserializer, but I get this exception:
com.fasterxml.jackson.databind.JsonMappingException: Class has no default (no arg) constructor
which is weird because I have defined a default constructor. Here is my custom Deserializer:
public class MyDeserializer extends JsonDeserializer<Map<String, Person>> {
public MyDeserializer() {
}
#Override
public Map<String, Person> deserialize(JsonParser jp, DeserializationContext ctxt)
throws IOException, JsonProcessingException {
JsonNode personsNodeArray = jp.getCodec().readTree(jp);
Map<String, Person> newChildren = null;
if (personsNodeArray.isArray() && personsNodeArray.size() > 0) {
newChildren = new HashMap<String, Person>();
for (JsonNode personNode : personsNodeArray) {
String id = personNode.get("name").asText();
// jsonMapper is a default ObjectMapper
newChildren.put(id, jsonMapper.readValue(personNode.toString(), Person.class));
}
}
return newChildren;
}
}
You can also consider reading children information as a collection of persons with subsequent conversion into a map. You can define a setter method (or a constructor parameter) to accept a List<Person> and then put each element into the Map<String, Person> children field. That would avoid unnecessary complexity of custom serialisation.
Here is an example:
public class JacksonChildren {
public static final String JSON = "{\"name\":\"Bob\", \"children\":[{\"name\":\"Jimmmy\"}," +
"{\"name\":\"Judy\"}]}";
public static class Person {
public String name;
private Map<String, Person> children = new HashMap<>();
public void setChildren(final List<Person> children) {
for (Person p : children) {
this.children.put(p.name, p);
}
}
#Override
public String toString() {
return "Person{" +
"name='" + name + '\'' +
", children=" + children +
'}';
}
}
public static void main(String[] args) throws IOException {
ObjectMapper mapper = new ObjectMapper();
System.out.println(mapper.readValue(JSON, Person.class));
}
}
Output:
Person{name='Bob', children={Judy=Person{name='Judy', children={}}, Jimmmy=Person{name='Jimmmy', children={}}}}

Static Method Call

I have one class which has one static method as shown below.
class A
{
A()
{
Initialize();
}
static void fm()
{
;
}
void Initialize()
{
;
}
}
Now in the program if i call A.fm(), Will it call the Initialize method or not?
Assuming that this is in a language like C++, Java, or C#:
It will not. Constructors only get called when new is used or when a variable of that type (A in this case) is declared as a local variable.
You should be looking for a static constructor, if so and if youre using c# you might wanna run this code. Static constructors grants that you run initializing code before running any other code within the class.
public class A
{
public static void Method()
{
Console.WriteLine("METHOD!!!");
}
public void Method2()
{
Console.WriteLine("INSTANCE METHOD!");
}
static A()
{
Console.WriteLine("STATIC CTOR");
}
}
class Program
{
static void Main(string[] args)
{
A.Method();
new A().Method2();
A.Method();
A.Method();
A.Method();
A.Method();
A.Method();
A.Method();
}
}
Its then the output!
STATIC CTOR
METHOD!!!
INSTANCE METHOD!
METHOD!!!
METHOD!!!
METHOD!!!
METHOD!!!
METHOD!!!
METHOD!!!
In your case, the Initialize will not be called as it is inside a default constructor. If you make your default constructor also static, then the Initialize method will be called first in sequence and after that the fm() method will be called..