I am getting Fortify issue Dynamic Code Evaluation: Unsafe Deserialization on the lines below:
#RequestMapping(value="/v2/doc", method=RequestMethod.POST)
public JsonDocVerifyResponse verify(#RequestBody JsonDocVerifyRequestV3 request)
JsonDocVerifyRequestV3 temp = (JsonDocVerifyRequestV3)SerializationUtils.clone(request);
The solution for unsafe deserialization is this https://www.ibm.com/developerworks/library/se-lookahead/
But as you can see in my codes I am not using ByteArrayOutputStream to deserialize the object.
Is this a false positive by Fortify? If not, how can I use
org.apache.commons.io.serialization.ValidatingObjectInputStream
to validate the class? Any code sample will be of great help!
These are the snippets:
#RequestMapping(value="/v2/doc", method=RequestMethod.POST)
public JsonDocVerifyResponse verify(#RequestBody JsonDocVerifyRequestV3 request) {
debugJsonRequest(request, DOC_TYPE.khIdBack);
JsonDocVerifyResponse response = new JsonDocVerifyResponse();
return response;
}
public void debugJsonRequest(JsonDocVerifyRequestV3 request, DOC_TYPE docType) {
try {
JsonDocVerifyRequestV3 temp(JsonDocVerifyRequestV3) SerializationUtils.clone(request);
LOGGER.debug("{}|{}", docType, CommonUtil.debugJsonObject(temp));
} catch(Exception e) {
LOGGER.error("Error in debug json object", e);
}
}
You can use accept and reject methods to safer deserialization operation.
Example:
import com.sun.xml.internal.ws.util.ByteArrayBuffer;
import org.apache.commons.io.serialization.ValidatingObjectInputStream;
import java.io.*;
public class Main {
public static void main(String[] args) throws IOException, ClassNotFoundException {
Bar bar = new Bar("bar-test");
Foo foo = new Foo("test-foo", bar);
// write into an array buffer
ByteArrayBuffer buffer = new ByteArrayBuffer();
try (ObjectOutputStream serializeStream = new ObjectOutputStream(buffer)) {
serializeStream.writeObject(foo);
}
try (ValidatingObjectInputStream stream = new ValidatingObjectInputStream(buffer.newInputStream())) {
// add validated classes
stream.accept(Foo.class);
stream.accept(Bar.class);
Foo foo2 = (Foo) stream.readObject();
System.out.println(foo2);
}
}
public static class Foo implements Serializable {
private String name;
private Bar bar;
public Foo(String name, Bar bar) {
this.name = name;
this.bar = bar;
}
#Override
public String toString() {
return "Foo{" +
"name='" + name + '\'' +
", bar=" + bar +
'}';
}
}
public static class Bar implements Serializable {
private String name;
public Bar(String name) {
this.name = name;
}
#Override
public String toString() {
return "Bar{" +
"name='" + name + '\'' +
'}';
}
}
}
Related
I have the following Test class:
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.core.JsonGenerationException;
import com.fasterxml.jackson.databind.DeserializationFeature;
import com.fasterxml.jackson.databind.JsonMappingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ibm.cio.cloud.cost.model.ElasticResponse;
import com.jayway.jsonpath.JsonPath;
#JsonIgnoreProperties(ignoreUnknown = true)
public class TestJSONPaths {
private static final String json = "{\"hits\":{\"total\":1,\"hits\":[{\"_id\":\"oEE4j2QBXCNPxFWHqq3i\",\"_score\":1.0,\"_source\":{\"status\":\"SUCCESSFUL\",\"reason\":\"OK, Single ACTIVE status can process\"}}]}}";
public static void main(String[] args) {
List<String> strippedJSON = JsonPath.read(json, "$.hits.hits._source");
ElasticResponse response = null;
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
try {
System.out.println("From this json string:" + strippedJSON + "\n");
response = mapper.readValue(strippedJSON.toString(), ElasticResponse.class);
System.out.println("ElasticDocument=" + mapper.writerWithDefaultPrettyPrinter().writeValueAsString(response.getDocuments()));
} catch (JsonGenerationException e) {
e.printStackTrace();
} catch (JsonMappingException e) {
e.printStackTrace();
} catch (IOException e) {
e.printStackTrace();
}
}
}
Here is the ElasticResponse class def:
public class ElasticResponse {
private List<ElasticDocument> documents;
public List<ElasticDocument> getDocuments() {
return documents;
}
public void setDocuments(List<ElasticDocument> documents) {
this.documents = documents;
}
}
public class ElasticDocument {
private String _id;
private String status;
private String reason;
... getters/setters
}
I'm trying to get a ElasticDocument object mapped from the JSON given but it's throwing the following errors below. I'm attempting to filtered out the JSON to simply be: [{_source document values }]. This error occurs on the very first line in the Main class. How can I map this Json?
[DEBUG] Evaluating path: $['hits']['hits']['_source']
Exception in thread "main" com.jayway.jsonpath.PathNotFoundException: Expected to find an object with property ['_source'] in path $['hits']['hits'] but found 'net.minidev.json.JSONArray'. This is not a json object according to the JsonProvider: 'com.jayway.jsonpath.spi.json.JsonSmartJsonProvider'.
at com.jayway.jsonpath.internal.path.PropertyPathToken.evaluate(PropertyPathToken.java:71)
at com.jayway.jsonpath.internal.path.PathToken.handleObjectProperty(PathToken.java:81)
at com.jayway.jsonpath.internal.path.PropertyPathToken.evaluate(PropertyPathToken.java:79)
at com.jayway.jsonpath.internal.path.PathToken.handleObjectProperty(PathToken.java:81)
at com.jayway.jsonpath.internal.path.PropertyPathToken.evaluate(PropertyPathToken.java:79)
at com.jayway.jsonpath.internal.path.RootPathToken.evaluate(RootPathToken.java:62)
at com.jayway.jsonpath.internal.path.CompiledPath.evaluate(CompiledPath.java:53)
at com.jayway.jsonpath.internal.path.CompiledPath.evaluate(CompiledPath.java:61)
at com.jayway.jsonpath.JsonPath.read(JsonPath.java:187)
at com.jayway.jsonpath.internal.JsonContext.read(JsonContext.java:102)
at com.jayway.jsonpath.internal.JsonContext.read(JsonContext.java:89)
at com.jayway.jsonpath.JsonPath.read(JsonPath.java:502)
at com.ibm.cio.cloud.cost.TestJSONPaths.main(TestJSONPaths.java:18)
The exception is due to the jsonpath returning an array instead of an object, so if you fix the jsonpath to look like this:
$.hits.hits[*]._source
Then it will evaluate properly. However, this probably still doesn't do what you want it to do.. The JsonPath.read() will deserialise the JSON for you. But you have to watch out with this:
public class Test {
private static final String json = "{\"hits\":{\"total\":1,\"hits\":[{\"_id\":\"oEE4j2QBXCNPxFWHqq3i\",\"_score\":1.0,\"_source\":{\"status\":\"SUCCESSFUL\",\"reason\":\"OK, Single ACTIVE status can process\"}}]}}";
public static void main(String[] args) {
List<ElasticDocument> docs = JsonPath.read(json, "$.hits.hits[*]._source");
System.out.println("elasticDoc: " + docs.get(0));
}
public static class ElasticDocument {
public String _id;
public String status;
public String reason;
#Override
public String toString() {
return "ElasticDocument{" +
"_id='" + _id + '\'' +
", status='" + status + '\'' +
", reason='" + reason + '\'' +
'}';
}
}
}
Looks like it works, however the docs List is now actually a List of Maps. Apparently It's possible to register JsonPath with Jackson but I can't make it work
Alternatively you can use Jackson to deserialise the JSON, then you should create an object model that matches the json structure and then you can use the ObjectMapper to do the deserialisation
public class Test {
private static final String json = "{\"hits\":{\"total\":1,\"hits\":[{\"_id\":\"oEE4j2QBXCNPxFWHqq3i\",\"_score\":1.0,\"_source\":{\"status\":\"SUCCESSFUL\",\"reason\":\"OK, Single ACTIVE status can process\"}}]}}";
public static void main(String[] args) {
System.out.println("From this json string:" + json + "\n");
ObjectMapper mapper = new ObjectMapper();
mapper.configure(DeserializationFeature.FAIL_ON_UNKNOWN_PROPERTIES, false);
mapper.configure(DeserializationFeature.USE_JAVA_ARRAY_FOR_JSON_ARRAY, true);
try {
HitsResource hitsResource = mapper.readValue(json, HitsResource.class);
System.out.println("jackson elasticDoc: " + hitsResource.hitsParent.hits.get(0).source);
} catch (Exception e) {
e.printStackTrace();
}
}
public static class HitsResource {
#JsonProperty("hits")
public HitsParent hitsParent;
#Override
public String toString() {
return "HitsResource{" +
"hitsParent=" + hitsParent +
'}';
}
}
public static class HitsParent {
#JsonProperty("total")
public Long total;
#JsonProperty("hits")
public List<Hits> hits;
#Override
public String toString() {
return "HitsParent{" +
"total=" + total +
", hits=" + hits +
'}';
}
}
public static class Hits {
#JsonProperty("_id")
public String id;
#JsonProperty("_score")
public BigDecimal score;
#JsonProperty("_source")
public ElasticDocument source;
#Override
public String toString() {
return "Hits{" +
"id='" + id + '\'' +
", score=" + score +
", source=" + source +
'}';
}
}
public static class ElasticDocument {
#JsonProperty("_id")
public String _id;
#JsonProperty("status")
public String status;
#JsonProperty("reason")
public String reason;
#Override
public String toString() {
return "ElasticDocument{" +
"_id='" + _id + '\'' +
", status='" + status + '\'' +
", reason='" + reason + '\'' +
'}';
}
}
}
lets asume i have a Interface like that:
public interface User extends Element {
String getName();
String getPassword();
}
and a implementing class like that:
public class BaseUser implements User {
#Override
public String getId() {
return id;
}
#Override
public String getName() {
return name;
}
#Override
public String getPassword() {
return password;
}
public void setId(String id) {
this.id = id;
}
public void setName(String name) {
this.name = name;
System.out.println("Set name to " + name);
}
public void setPassword(String password) {
this.password = password;
}
private String id;
private String name;
private String password;
}
Now i want to use bytebuddy to create a interceptor/proxy which catches the call onto the setter, store the changed value and call the real method also.
At the end i want to "ask" the interceptor/proxy for the called setter and the changed values.
I tried a lot considering also the tutorials but up to now i found no working solution. Maybe someone could help me pls.
And here is the Interceptor:
public class GenericInterceptor implements InvocationHandler {
#Override
#RuntimeType
public Object invoke(#This Object proxy, #Origin Method method, #AllArguments Object[] args) throws Throwable {
if (isSetter(method, args)) {
intercept(proxy, method, args);
}
return method.invoke(proxy, args);
}
}
Here is my current 'test' code:
public static void main(String[] args) {
final ByteBuddy bb = new ByteBuddy();
final GenericInterceptor interceptor = new GenericInterceptor();
bb.subclass(BaseUser.class)
.method(isDeclaredBy(BaseUser.class).and(isSetter()))
.intercept(MethodDelegation.to(interceptor))
.make()
.load(BaseUser.class.getClassLoader(), ClassLoadingStrategy.Default.WRAPPER);
final BaseUser user = new BaseUser();
user.setName("my name");
}
EDIT:
public interface Element {
String getId();
}
public class GenericInterceptor<T extends Element> {
public GenericInterceptor(Class<T> type) {
this.type = type;
}
public Map<String, Object> getChanges(T obj) {
final String id = obj.getId();
return changes.get(id);
}
#RuntimeType
public void invoke(#This T proxy, #Origin Method method, #AllArguments Object[] args) throws Throwable {
System.out.println("invoke " + method.getName() + " " + Arrays.toString(args));
intercept(proxy, method, args);
}
private Object getCurrentValue(T proxy, final Field field) {
try {
return field.get(proxy);
} catch (IllegalArgumentException | IllegalAccessException e) {
return null;
}
}
private Field getSetterField(Method setter) {
final String setterName = setter.getName();
Field f = assignedFields.get(setterName);
if (f != null) return f;
final String fieldName = Character.toLowerCase(setterName.charAt(3)) + setterName.substring(4);
try {
f = type.getDeclaredField(fieldName);
if (f == null) return null;
f.setAccessible(true);
assignedFields.put(setterName, f);
return f;
} catch (NoSuchFieldException | SecurityException e) {
return null;
}
}
private void intercept(T proxy, Method setter, Object[] args) {
final Field field = getSetterField(setter);
if (field == null)
return;
final Object currentValue = getCurrentValue(proxy, field);
final Object newValue = args[0];
System.out.println("Set from " + currentValue + " to " + newValue);
final String id = proxy.getId();
Map<String, Object> changeMap = changes.get(id);
if (changeMap == null) {
changeMap = new HashMap<>();
}
changeMap.put(field.getName(), currentValue);
changes.put(id, changeMap);
}
private final Map<String, Field> assignedFields = new HashMap<>();
private final Map<String, Map<String, Object>> changes = new LinkedHashMap<>();
private final Class<T> type;
}
You can call orignal method using MethodDelegation.to(...).andThen(SuperMethodCall.INSTANCE).
public class ByteBuddyTest {
public static void main(String[] args) throws IllegalAccessException, InvocationTargetException, InstantiationException {
GenericInterceptor interceptor = new GenericInterceptor ();
Class<?> clazz = new ByteBuddy()
.subclass(BaseUser.class)
.method(ElementMatchers.isDeclaredBy(BaseUser.class).and(ElementMatchers.isSetter()))
.intercept(SuperMethodCall.INSTANCE.andThen(MethodDelegation.to(interceptor))))
.make()
.load(ByteBuddyTest.class.getClassLoader())
.getLoaded();
BaseUser user1 = (BaseUser) clazz.getConstructors()[0].newInstance();
BaseUser user2 = (BaseUser) clazz.getConstructors()[0].newInstance();
user1.setName("user1");
user1.setPassword("password1");
user2.setName("user2");
user2.setPassword("password2");
System.out.println(interceptor.getInterceptedValue("user1", "name"));
System.out.println(interceptor.getInterceptedValue("user1", "password"));
System.out.println(interceptor.getInterceptedValue("user2", "name"));
System.out.println(interceptor.getInterceptedValue("user2", "password"));
user1.setPassword("password2");
user1.setPassword("password3");
}
public static class GenericInterceptor {
private Map<String, Object> interceptedValuesMap = new HashMap();
public void set(String obj, #This User user, #Origin Method setter) {
// assume that user name is unique so we can use it as a key in values map.
// or define equals/hashcode in GenericUser object and use it as a key directly
String setterName = setter.getName();
String propertyName = setterName.substring(3, setterName.length()).toLowerCase();
String key = user.getName() + "_" + propertyName;
System.out.println("Setting " + propertyName + " to " + obj);
System.out.println("Previous value " + interceptedValuesMap.get(key));
interceptedValuesMap.put(key, obj);
}
public Object getInterceptedValue(String userName, String fieldName) {
return interceptedValuesMap.get(userName + "_" + fieldName);
}
}
public static interface User {
String getName();
String getPassword();
}
public static class BaseUser implements User {
#Override
public String getName() {
return name;
}
#Override
public String getPassword() {
return password;
}
public void setName(String name) {
this.name = name;
}
public void setPassword(String password) {
this.password = password;
}
private String name;
private String password;
}
}
Two classes have similar fields, but they don't have superclass. In my code : First and Second classes. I need to write method convertToAnother, what will be return object of class resultClassObject with values of fields from object one.
Both classes have Json annotation. That annotation have vaule of property equals name of class in lowercase (in my code class First have className = "first".
import com.fasterxml.jackson.annotation.JsonSubTypes;
import com.fasterxml.jackson.annotation.JsonTypeInfo;
import com.fasterxml.jackson.databind.ObjectMapper;
import java.io.IOException;
import java.io.StringReader
public class Solution {
public static void main(String[] args) throws IOException {
Second s = (Second) convertOneToAnother(new First(), Second.class);
First f = (First) convertOneToAnother(new Second(), First.class);
}
public static Object convertOneToAnother(Object one, Class resultClassObject) throws IOException {
try {
ObjectMapper mapper = new ObjectMapper();
String obj = mapper.writeValueAsString(one);
obj = obj.replace("\"className\":\"" + one.getClass().getSimpleName().toLowerCase() + "\"", "\"className\":\"" + resultClassObject.getSimpleName().toLowerCase() + "\"");
return new ObjectMapper().readValue(new StringReader(obj), resultClassObject);
}
catch (Exception e) {
e.printStackTrace();
}
return null;
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property="className")
#JsonSubTypes(#JsonSubTypes.Type(value=First.class, name="first"))
public static class First {
public int i;
public String name;
}
#JsonTypeInfo(use = JsonTypeInfo.Id.NAME, property="className")
#JsonSubTypes(#JsonSubTypes.Type(value=Second.class, name="second"))
public static class Second {
public int i;
public String name;
}
}
Maybe another decision exist?
The only correct way to do that using jackson is to marshall instance to json and then unmarshall. I would recommend to use convertion on the level of java objects - using Dozer: http://dozer.sourceforge.net
I want to offer such a solution, example for class First:
First first = new First();
first.i = 1;
first.name = "first";
Second s = (Second) convertOneToAnother(first, Second.class);
System.out.println(s.name); // first
ObjectMapper mapper = new ObjectMapper();
// here we disable uses annototions, since by the condition of the
// problem, we have two classes have similar fields
mapper.disable(MapperFeature.USE_ANNOTATIONS);
StringWriter writer = new StringWriter();
mapper.writeValue(writer, one);
//writer.toString() == {"i":1,"name":"first"}
mapper.readValue(writer.toString, resultClassObject);
if we don't use method mapper.disable(), we'll have for writer, such string {"className":"first","i":1,"name":"first"}
You can write code like below for model mapping:
public class ModelConverter {
public static void main(String[] args) throws IOException {
Test1 t1 = new Test1();
ObjectMapper mapper1 = new ObjectMapper();
String jsonString = mapper1.writeValueAsString(t1);
System.out.println(jsonString);
Test2 t2 = mapper1.readValue(jsonString, Test2.class);
System.out.println(t2);
}
}
public class Test1 implements Serializable {
private int i = 10;
private String name = "demo1";
private Test3 test3 = new Test3();
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Test3 getTest3() {
return test3;
}
public void setTest3(Test3 test3) {
this.test3 = test3;
}
#Override
public String toString() {
return "test1 [i=" + i + ", name=" + name + ", Test3=" + test3 + "]";
}
}
public class Test2 implements Serializable {
private int i = 11;
private String name = "demo2";
private Test3 test3;
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public Test3 getTest3() {
return test3;
}
public void setTest3(Test3 test3) {
this.test3 = test3;
}
#Override
public String toString() {
return "test2 [i=" + i + ", name=" + name + ", Test3=" + test3 + "]";
}
}
public class Test3 implements Serializable {
private int i = 12;
private String name = "demo3";
public int getI() {
return i;
}
public void setI(int i) {
this.i = i;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
#Override
public String toString() {
return "test3 [i=" + i + ", name=" + name + "]";
}
}
For this kind of task would be right to use object mappers like Dozer (http://dozer.sourceforge.net/)
You can replace value of className without String manipulations
ObjectReader reader = mapper.reader();
JsonNode node = reader.readTree(writer.toString());
((ObjectNode)node).put("className",resultClassObject.getSimpleName().toLowerCase());
Also if you don't know the name of the field where name of the class is stored, but you know it is in the annotations, you can try and get the first field from JsonNode (in your code you assume that the field name is "className", but what if it is not).
ObjectMapper mapper1 = new ObjectMapper();
Map<String, Object> result = mapper1.convertValue(node, Map.class);
String key1 = result.keySet().toArray()[0].toString();
And now you can replace the value for the key1 field, which should be the field where class name is stored.
public class ViewBooking extends javax.swing.JFrame {
/**
* Creates new form ViewBooking
*/
public ViewBooking() {
initComponents();
}
public void dispBookingInfo(){
Reservation[] reservations = new Reservation[1];
try {
String searchCust = SearchName.getText();
FileInputStream inStream = new FileInputStream(searchCust +
"booking.dat");
ObjectInputStream objectInputFile = new ObjectInputStream(inStream);
reservations[0] = (Reservation) objectInputFile.readObject();
objectInputFile.close();
} catch (Exception e) {
}
JOptionPane.showMessageDialog(null, reservations[0].getDetails());
}
This is my Reservation class
public class Reservation implements Serializable {
private String name;
private String sDate;
private String eDate;
private String noOfDays;
private String roomNo;
private String totalAmt;
Reservation(String name, String sDate, String eDate, String noOfDays,
String totalAmt, String roomNo) {
this.name = name;
this.totalAmt = totalAmt;
this.roomNo = roomNo;
//throw new UnsupportedOperationException("Not supported yet.");
//To change body of generated methods, choose Tools | Templates.
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public String getsDate() {
return sDate;
}
public void setsDate(String sDate) {
this.sDate = sDate;
}
public String geteDate() {
return eDate;
}
public void seteDate(String eDate) {
this.eDate = eDate;
}
public String getNoOfDays() {
return noOfDays;
}
public void setNoOfDays(String noOfDays) {
this.noOfDays = noOfDays;
}
public String getRoomNo() {
return roomNo;
}
public void setRoomNo(String roomNo) {
this.roomNo = roomNo;
}
public String getTotalAmt() {
return totalAmt;
}
public void setTotalAmt(String totalAmt) {
this.totalAmt = totalAmt;
}
public String getDetails(){
return "Name: " + name + "\n" + "From: " + sDate + " to " + eDate
+ "\n" + "Duration: " + noOfDays + "Room No: " + roomNo
+ "Total amount: RM" + totalAmt;
}
}
I am able to serialize the Reservation object but when i try to deserialize it and read the data, i get a NullPointerException error at this line:
JOptionPane.showMessageDialog(null, reservations[0].getDetails());
What is the problem here?
I have changed my code into the following:
public void dispBookingInfo() throws Exception{
String searchCust = SearchName.getText();
FileInputStream inStream = new FileInputStream(searchCust + " booking.dat");
ObjectInputStream objectInputFile = new ObjectInputStream(inStream);
Reservation[] reservations = new Reservation[1];
try {
if (reservations[0] != null) {
reservations[0] = (Reservation) objectInputFile.readObject();
}
objectInputFile.close();
} catch (Exception e) {
System.out.println("error!");
}
JOptionPane.showMessageDialog(null, reservations[0].getDetails());
}
The NullPointerException error is gone but I still cant retrieve any data. Why is my reservation[0] null?
In your
try {
...
} catch () {
...
}
statement you ignore any exception thrown. Hence it is possible that
reservations[0] = (Reservation) objectInputFile.readObject();
does not initialize reservation[0] at all, which would cause an NullPointerException when accessing:
reservation[0].getDetails();
Here is your problem:
if (reservations[0] != null) {
reservations[0] = (Reservation) objectInputFile.readObject();
}
reservations[0] will always be null at this point because you've only just initialised the array. This stops the call that would populate the data into here, so when you try and access it later on with reservations[0].getDetails() that element is inevitably still null. The null check is completely unneeded, so remove it.
You may also wish to consider defining a serialVersionUID for your class.
To do so, add this as a class variable:
private static final long serialVersionUID = <some_long_number>;
Replace <some_long_number> with any long that you like. Once done, you'll have to re-create your file with a 'new' version of your class, otherwise the version numbers won't match.
If you don't do this, the JVM automatically generates a serialVersionUID for you based upon the class itself, so if you've changed certain things about the class, you may suddenly find that you have problems deserialising older versions of the class.
I have a question regarding reflection in Java.
Following problem:
Depending on a configuration I want to call a method via reflection, but not only of a class CLASS_A, but also from a class CLASS_B that is referenced by CLASS_A.
But I want to use always only class CLASS_A to access the attribute.
Here an example what I mean:
public class Foo
{
private String _name;
private Bar _bar;
public Foo(String name, Bar bar)
{
_name = name;
_bar = bar;
}
public String getName()
{
return _name;
}
public Bar getBar()
{
return _name;
}
}
public class Bar
{
private String _name;
public Bar(String name)
{
_name = name;
}
public String getName()
{
return _name;
}
}
I want to use always an instance of class Foo to invoke the method that is returned by getMethod ... no matter whether the method of Foo should be called or the method of Bar.
public class Executor
{
public static void main(String[] args)
{
Foo foo = new Foo("fooName", new Bar("barName"));
String attribute = "barName";
Method method = getMethod(Foo.class, attribute);
try
{
System.out.println(String.valueOf(method.invoke(foo, new Object[]{})));
}
catch(Exception ex)
{
ex.printStackTrace();
}
}
private static Method getMethod(Class< ? > clazz, String attribute)
{
try
{
if (attribute.equals("fooName"))
{
return clazz.getDeclaredMethod("getName", new Class[] {});
}
else if (attribute.equals("barName"))
{
//Is that somehow possible?
Method method = clazz.getDeclaredMethod("getBar.getName", new Class[] {});
return method;
}
}
catch (Exception ex)
{
ex.printStackTrace();
}
return null;
}
}
Is something like that possible?
Thanks!
You can use Apache BeanUtils library.
Here is a good example of how you can use it.