I have a controller class, it basically holds an event list.
ArrayList <Event> eventList = new ArrayList<>();
The controller has an addEvent(Event e) method.
I've extended the controller class to be a specific kind of controller, and extended event to provide specific kinds of events for my new controller as inner classes.
public class NewController extends Controller{
//.. controller code/methods
class SpecificEvent extends Event{
//..
}
}
I've hard coded my controller to work as I intended, but I wanted to be able to make a configuration file that would populate my event list.
I made a .txt file with a list of events:
Event=SpecificEvent, Arg1=<Integer>val, .. ArgN=<?>val, Event=SpecificEventN, ArgN=<?>val
I filtered out the event class name and arguments into a list:
fileContents.stream()
.forEach(s -> {
Scanner sc = new Scanner(s)
.useDelimiter("=|,");
while (sc.hasNext()){
Scanner sc2 = new Scanner(sc.next()).useDelimiter("[//w]");
args.add(sc.next());
}
});
My problem is that events have different constructor argument types and lengths; I don't know how to build them from my file. I'm new to this kind of work and I figure this is run of the mill implementation.
Do I use the Reflect package? Please help. I was thinking off an Event Factory?
Thanks for the community help. This factory will do the job when provided a string array argument, {String classname, args.... }
/**
* ClassFactory method, build an event.
*
* #param arguments Required arguments to build an Event.
* #return Built event.
* #throws java.lang.ClassNotFoundException
*/
public Event buildEvent(String [] arguments) throws ClassNotFoundException {
Class<?>[] argumentTypes = {};
Object[] parameters = {};
try {
//define the class type from the first argument
Class<?> eventClass
= Class.forName(packageName + arguments[0]);
if (arguments.length() > 1) {
argumentTypes = new Class<?>[]{Long.TYPE};
parameters = new Object[]{Long.parseLong(arguments[1])};
Constructor<?> constructor
= eventClass.getDeclaredConstructor(argumentTypes);
Object instance = constructor.newInstance(parameters);
return ((Event) instance);
//default
} else {
Constructor<?> constructor
= eventClass.getDeclaredConstructor();
Object instance = constructor.newInstance();
return ((Event) instance);
}
} catch (ClassNotFoundException cnfe) {
System.out.println("Class not available in this package.");
} catch (NoSuchMethodException |
SecurityException |
InstantiationException |
IllegalAccessException |
IllegalArgumentException |
InvocationTargetException e) {
log.log(Level.SEVERE, "Class Builder: {0}", e.getMessage());
}
return null;
}
Related
Goal: create a util class that will contain some reflection code, in particular code that creates a new class instance. That code is not very simple, so I want to keep it in a util function so that I can reuse it multiple times.
Approach/Idea: create ClassUtil that will have a function that returns a lambda that creates a new class instance. I will execute that lambda in MyClassFactory, a class that I know can create a new instance of MyClassOne because it will be in the same package and the default constructor is package-private access modifier, so ClassUtil cannot make an instance of that class, but since I am executing the lambda in a class that can all should be good.
Error:
java.lang.IllegalAccessException: class com.util.ClassUtil cannot access a member of class com.somepackage.MyClassOne with modifiers ""
Question: How to make Java runtime think that it is MyClassFactory the one that is trying the instantiation?
UML:
Code:
package com.somepackage
import com.util.ClassUtil;
public class MyClassFactory {
public MyClass createMyClass() {
String implClassFQN = <get impl class FQN from somewhere>;
return (MyClass ) ClassUtil.createClassInstance().apply(implClassFQN);
}
}
package com.util;
import java.lang.reflect.InvocationTargetException;
import java.util.function.Function;
public class ClassUtil {
/**
* #return a new instance of the class given the class fully qualified name
*/
public static Function<String, Object> createClassInstance() {
return (classFQN) -> {
try {
return Class.forName(classFQN).getDeclaredConstructor().newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("Impl class specified in properties file not found", e);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Default constructor not found", e);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
}
Workaround/New Approach: would anyone know how I could have taken a different approach to achieve the same goal?
Note: If I put the code from ClassUtil into MyClassFactory it works fine, but I want it reusable.
To answer your question literally, there is a class capable of encapsulating an access context that can be passed to another method to perform actions with it without the need for access override, assuming that the caller has enough trust in the invoked method.
Unlike access override, this will work even in restricted environments, e.g. with an installed security manager or when the access would cross module boundaries. Also, the class lookup is performed as-if happening in the caller’s code which can be relevant when the two packages belong to different class loaders.
package com.somepackage;
import com.util.ClassUtil;
import java.lang.invoke.MethodHandles;
public class MyClassFactory {
public static void main(String[] args) {
MyClass obj = new MyClassFactory().createMyClass();
System.out.println("created "+obj);
}
public MyClass createMyClass() {
String implClassFQN = MyClassFactory.class.getName()+"$MyClass";
return (MyClass)ClassUtil.createClassInstance(MethodHandles.lookup())
.apply(implClassFQN);
}
private static class MyClass { // normally inaccessible by com.util.ClassUtil
}
}
package com.util;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.util.function.Function;
public class ClassUtil {
/**
* #return a new instance of the class given the class fully qualified name
*/
public static Function<String, Object>
createClassInstance(MethodHandles.Lookup context) {
return name -> {
try {
return context.findConstructor(context.findClass(name),
MethodType.methodType(void.class)).invoke();
} catch(ClassNotFoundException e) {
throw new RuntimeException(
"Impl class specified in properties file not found", e);
} catch(NoSuchMethodException e) {
throw new RuntimeException("Default constructor not found", e);
} catch(RuntimeException | Error e) {
throw e;
} catch(Throwable e) {
throw new RuntimeException(e);
}
};
}
}
created com.somepackage.MyClassFactory$MyClass#4783da3f
Try setting the constructor accessible via:
AccessibleObject#setAccessible(true)
public static Function<String, Object> createClassInstance() {
return (classFQN) -> {
try {
Constructor<?> constructor = Class.forName(classFQN).getDeclaredConstructor();
constructor.setAccessible(true);
return constructor.newInstance();
} catch (ClassNotFoundException e) {
throw new RuntimeException("Impl class specified in properties file not found", e);
} catch (NoSuchMethodException e) {
throw new RuntimeException("Default constructor not found", e);
} catch (IllegalAccessException | InstantiationException | InvocationTargetException e) {
throw new RuntimeException(e);
}
};
}
I have several classes in the same package in Java. I want to instantiate objects of these classes from an array that has the class names as strings.
Here is an example of a class I would like to use, they all have the same structure.
class Class1 {
public String[] firstMethod(){
String[] data = {"NEW_ITEM"};
return data;
}
}
Here is the class I am attemtempting to instantiate them from.
class Main {
static {
String[] classes = {"Class1","Class2"};
for (String cls : classes) {
try {
Object o = Class.forName(cls).newInstance();
o.firstMethod();
} catch(ClassNotFoundException | IllegalAccessException | InstantiationException ex) {
System.out.println(ex.toString());
}
}
My problem is that when I try to call firstMethod() using the object o, I am getting this error.
exit status 1
Main.java:19: error: cannot find symbol
o.firstMethod();
^
symbol: method firstMethod()
location: variable o of type Object
1 error
I suspect that it is because it is of type Object and not type Class1. I have seen solutions where you typecast the object to the object of the class that you need. However when you typcast, you need to use the name of the class, which is exactly what I am trying to avoid. I need to use the class name as a string.
Does anyone know of a solution where I can call methods with the objects that are created?
You can't call your method the way in your code because you have an object which does not know the type Class1. You need to cast it explicitly like
((Class1)o).firstMethod()
which I don't think this is what you want.
Or, you can iterate through object methods and invoke it dynamically like below:
String[] classes = {"com.yourpackage.Class1", "com.yourpackage.Class2"};
for (String cls : classes) {
try {
Object o = Class.forName(cls).newInstance();
for(Method m : o.getClass().getMethods()) {
System.out.println(m.getName());
if ("firstMethod".equals(m.getName())) {
String[] data = (String[])m.invoke(o, null); // here are the parameters
for(String d : data){
System.out.println(d);
}
}
}
} catch (ClassNotFoundException | IllegalAccessException | InstantiationException ex) {
System.out.println(ex.toString());
} catch (InvocationTargetException e) {
e.printStackTrace();
}
}
The output is :
NEW_ITEM
So my application uses a number of TableViews within different FXMLViewControllers to present a number of different JPA Entities. The example below is for JobSupplierParts.
/**
* renderDoubleColumn takes a TableColumn setting its value and type before setting up edit event handling.
* #param column the tableColumn to be set up.
* #param field the name of the field to be mapped to.
* #param methodName the set method name of the field.
*/
protected void renderDoubleColumn(TableColumn<JobSupplierPart, Double> column, String field, String methodName) {
String className = "BiasDB.JobSupplierPart";
column.setCellValueFactory(new PropertyValueFactory<>(field));
column.setCellFactory(TextFieldTableCell.<JobSupplierPart, Double>forTableColumn(new DoubleStringConverter()));
column.setOnEditCommit(
new EventHandler<TableColumn.CellEditEvent<JobSupplierPart, Double>>() {
#Override
public void handle(TableColumn.CellEditEvent<JobSupplierPart, Double> t) {
JobSupplierPart supplierPart = t.getTableView().getItems().get(t.getTablePosition().getRow());
try {
Class<?> c = Class.forName(className);
Method method = c.getDeclaredMethod(methodName, Double.class);
method.invoke(supplierPart, t.getNewValue());
supplierPart.setTotal(updateItem(supplierPart));
} catch (ClassNotFoundException|NoSuchMethodException|IllegalAccessException|InvocationTargetException ex) {
logger.error("renderDoubleColumn",ex);
} //End try to get method from String.
try {
jobSupplierPartController.edit(supplierPart);
} catch (Exception ex) {
logger.error("renderDoubleColumn",ex);
}
t.getTableView().refresh();
}
} //END Event Handler
); //END SetOnEditCommit.
}
//END renderDoubleColumn
I can call this with:
renderDoubleColumn(discountColumn, "discount", "setDiscount");
BUT - I have to create new methods for each JPA Entity. Is it possible to replace the references to JobSupplierPart such that it becomes a generic method much like I have achieved with the methods? I have tried and alternatives such as T or K but they all returned errrors. The controller can just be passed as a parameter. Or is this a really bad practice/poor performance thing to do?
So I don't know if the Java aficionados will agree with this solution but in response to an answer posted and then deleted shortly after I was able to make the code cleaner. I also moved the set/edit section into a method so now I have:
/**
* renderBigDecimalColumn takes a TableColumn setting its value and type before setting up edit event handling.
* #param column the tableColumn to be set up.
* #param field the name of the field to be mapped to.
*/
private void renderBigDecimalColumn(TableColumn<AccountAsset, BigDecimal> column, String field) {
//Set an observable value for the column
column.setCellValueFactory(new PropertyValueFactory<>(field));
//Set how we want the cell to be rendered
// This line varies for the different cell types e.g. Strings, Bools etc.
column.setCellFactory(TextFieldTableCell.<AccountAsset, BigDecimal>forTableColumn(new BigDecimalStringConverter()));
//Set how we want the cell to be edited including the row update.
column.setOnEditCommit(t -> {
handleEditCommit(t, field);
}); //END SetOnEditCommit.
} //END renderBigDecimalColumn
And my handleEditCommit method looks like:
/** handleEditCommit deals with updating and saving the new data from the table view.
*
* #param t
* #param field
*/
private void handleEditCommit(javafx.scene.control.TableColumn.CellEditEvent<AccountAsset,?> t, String field) {
AccountAsset rowData = t.getTableView().getItems().get(t.getTablePosition().getRow());
//Set the new value.
try {
BeanUtils.setProperty(rowData, field, t.getNewValue());
} catch (IllegalAccessException | InvocationTargetException ex) {
logger.error("handleEditCommit / Setter", ex);
}
//Save the new rowData back to the database.
try {
tableDataController.edit(rowData);
} catch (Exception ex) {
logger.error("handleEditCommit / Edit", ex);
}
}
I've tried using a variable to invoke a java method, using method.invoke(), as suggested in this example. But it seems there should be an object or something as a parameter in method.invoke(). I've tried using null, but the method didn't get invoked. My code is as follows:
String ACTION = "cart";
Method method = SolverService.class.getDeclaredMethod("Method" + ACTION);
method.invoke(null);
I've got a method as:
public void Methodcart(){
Toast.makeText(this,"Method called",Toast.LENGTH_LONG).show();
}
PS: I HAVE TO make this method.invoke() work. Otherwise, I need to write a very long list of switch-case statements.I've gone through the documentation but couldn't understand much about the object instance i might need to use here as I'm new to android app developing.
You can try something similar to the code shown below (Java Reflection) -
Suppose I have a class ClassWithMethods.java with the methods I want to invoke in some other class as shown below -
public class ClassWithMethods {
private int counter;
public void printIt(){
System.out.println("printIt() no param");
}
public void printItString(String temp){
System.out.println("printIt() with param String : " + temp);
}
}
Now I also have another class TestApp.java which will invoke methods of the ClassWithMethods class at runtime using Java Reflection -
public class TestApp {
public static void main(String[] args) {
//no paramater
Class noparams[] = {};
//String parameter
Class[] paramString = new Class[1];
paramString[0] = String.class;
//int parameter
Class[] paramInt = new Class[1];
paramInt[0] = Integer.TYPE;
try{
//load the ClassWithMethods at runtime
Class cls = Class.forName("com.myapps.ClassWithMethods");
Object obj = cls.newInstance();
//call the printIt method
Method method = cls.getDeclaredMethod("printIt", noparams);
method.invoke(obj, null);
//call the printItString method, pass a String param
method = cls.getDeclaredMethod("printItString", paramString);
method.invoke(obj, new String("someString"));
}catch(Exception ex){
ex.printStackTrace();
}
}
I am using Java Reflection in my current project (since you mentioned you are using Android Studio) to get Battery Capacity of device from PowerProfile class which is internal to the Android OS.
public double getBatteryCapacity() {
Object mPowerProfile = null;
try {
mPowerProfile = Class.forName("com.android.internal.os.PowerProfile")
.getConstructor(Context.class)
.newInstance(getContext());
} catch (Exception e) {
e.printStackTrace();
}
try {
// get access to method named "getAveragePower()" in the class "PowerProfile"
Method getAveragePower = Class.forName("com.android.internal.os.PowerProfile").getMethod("getAveragePower", String.class);
//Get total battery capacity in mAh.
double batteryCapacity = (Double) getAveragePower.invoke(mPowerProfile, "battery.capacity");
return batteryCapacity;
} catch (Exception e) {
e.printStackTrace();
}
return 0.0;
}
Here is a screenshot of how the actual method structure looks like in the PowerProfile class -
In CDI 1.2 there is a way to check if a class instance is proxified? I need this because I need to get the name of original class, not the proxy name.
#Inject Bean bean;
public void sysout() {
// will print something like com.Bean$$Weld9239823
System.out.println(bean.getClass());
// I don't know how to check if the bean instance if a proxy or real class instance
}
Using Weld classes I can do this job:
public void sysout() {
// will print true because this is a proxy
System.out.println(ProxyObject.class.isAssignableFrom(bean));
// will print com.Bean
System.out.println(((TargetInstanceProxy) bean).getTargetInstance());
}
In CDI 1.1 there is no method to do this. I search inside CDI 1.2 docs if a method was added about this, but I don't found anything.
So... I miss something and CDI 1.2 there is a method to get original class name and instance? Or if not, there is a plain to add this feature in near feature?
For Weld on WildFly do this:
public boolean isProxy(Object obj) {
try{
return Class.forName("org.jboss.weld.bean.proxy.ProxyObject").isInstance(obj);
} catch (Exception e) {
log.error("Unable to check if object is proxy", e);
}
return false;
}
To retrive actual object instead of proxy (I need to serialize it) I do this:
public Object getObject(Object obj) {
Field f = null;
boolean isAccessible = false;
try {
for(Field fi : Class.forName(handler).getDeclaredFields()) {
if(fi.getName().equals(field)) {
f = fi;
isAccessible = f.isAccessible();
f.setAccessible(true);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
}
if(f == null) {
throw new RuntimeException(new NoSuchFieldException(String.format(
"The required field '%s' not found in '%s'. " +
"May be the code is obsolete for running on this application server.",
field, method)));
} else {
try{
obj = f.get(getHandler(obj));
for(Method m : Class.forName(instance).getMethods()) {
if(m.getName().equals(value)) {
return m.invoke(obj);
}
}
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
f.setAccessible(isAccessible);
}
throw new NoSuchMethodError(String.format(
"The required method '%s' not found in '%s'. " +
"May be the code is obsolete for running on this application server.",
value, instance));
}
}
Be aware, that it is the darkest magic as possible, have very poor performance and can break at any WildFly update, if they change classes, methods for fields in it.
This is a terrible hack, but for Weld (and possibly other implementations) you can check if the class name contains "Proxy": possibleProxy.getClass().getSimpleName().contains("Proxy"). I use it only for logging purposes to get a cleaned up version of the wrapped class name:
/**
* Get the actual simple name of the objects class that might be wrapped by
* a proxy. A "simple" class name is not fully qualified (no package name).
*
* #param possibleProxy an object that might be a proxy to the actual
* object.
* #return the simple name of the actual object's class
*/
public static String getActualSimpleClassName(final Object possibleProxy) {
final String outerClassName = possibleProxy.getClass().getSimpleName();
final String innerClassName;
if (outerClassName.contains("Proxy")) {
innerClassName = outerClassName.substring(0, outerClassName.indexOf('$'));
} else {
innerClassName = outerClassName;
}
return innerClassName;
}
you can make a method inside your proxied cdi bean like
public String getClassName() {
return this.getClass().getName();
}
this is not the best solution, but a simple pragmatic way to get the class name through the proxy... the downside of this is that the method must be on every implementation...