How to reuse logic for class instantiation and get around IllegalAccessException? - java

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);
}
};
}

Related

Unable to use #spring annotations when class object is new

Actually i am having a spring main class as follows.
ClassLoader loader = null;
try {
loader = URLClassLoader.newInstance(new URL[]{new
File(plugins + "/" + pluginName + "/" + pluginName +
".jar").toURI().toURL()}, getClass().getClassLoader());
} catch (MalformedURLException e) {
e.printStackTrace();
}
Class<?> clazz = null;
try {
clazz = Class.forName("com.sample.Specific", true, loader);
} catch (ClassNotFoundException e) {
e.printStackTrace();
}
Method method = null;
try {
method = clazz.getMethod("run",new Class[]{});
} catch (NoSuchMethodException e) {
e.printStackTrace();
}
try {
method.invoke(clazz.newinstance,new Object[]{});
} catch (IllegalAccessException e) {
e.printStackTrace();
} catch (InvocationTargetException e) {
e.printStackTrace();
}
Specific Class is follow :
package com.sample
#Service
public class Specific {
#Autowired
private FD fd;
public void run(){
fd.init();
}
}
#Autowired FD comes to be null. Can anyone give me some solution as i also know new operator will not work for #autowired. As i am loading class with new instance then only it becomes null. Can anyone guide me in this thing
Spring has its own way to provide you new objects. As long as you're consistent using #Autowired and #Component/#Service/#Repository/#Controller there should be no problem
And since all "business" object instantiation is handled by Spring you should never use new. If you have no other way of getting an instance (something I realy doubt about it) you can use ApplicationContext.getBean() but as I said, in most cases this is not required (and this is also a bad practice)
If you need several instances of a class instead of injecting them (by using #Autowired) you can inject a Provider<T>
UPDATE
Since the class is known at runtime you need to inject an ApplicationContext and use it to get the bean:
public class TheClassWhereYouAreCreatingTheObject {
#Autowired
private ApplicationContext context; // You definitely need this
public void theMethodWhereYouAreCreatingTheObject() {
Class<?> clazz = ... // getting the object class
Object instance = context.getBean(clazz); // getting and instance trough Spring
// If you know that kind of object you will get cast it at call its methods
((Specific) instance).run();
// If you know anything about the class you will have to use reflection
Method method = clazz.getMethod("run", new Class[]{});
method.invoke(instance, new Object[]{});
}
}
Add Specific Service bean inside your main class. As long as the service is inside one your component scan packages then you shall be fine. Do not use new operator.
#Autowired
private Specific specific;
If you want to take advantage of autowiring then I think we have to think from spring terms.
you can use Beanutils to create a new instance and play with reflections supporting spring features.
Please go through below methods:
https://docs.spring.io/spring/docs/current/javadoc-api/org/springframework/beans/BeanUtils.html

Using reflection to customize private field of superclass

I am running into a problem trying to modify the behavior of my superclass' private fields from the sub-class (The superclass wasn't designed to be extended and I can't change that).
Basically, what I have is :
public class OrthoCanvas {
private OrthoView xy, xz, zy;
public class OrthoView { ... }
}
And I want to do something like that :
public class CustomOrthoCanvas extends OrthoCanvas {
public CustomOrthoCanvas {
// Sets superclass xy, xz, zy to instances of CustomOrthoView
// This seem to work fine (I'm using reflection to change the fields)
}
public class CustomOrthoView extends OrthoView { ... }
}
As I said, the reflection seems to work (I'm building CustomOrthoView). But, for the moment, I didn't override any method, and my constructor is just super(whatever), and a Sysout to check what I'm doing. Yet, the original behavior of OrthoView just disappeared, and nothing is working.
Did I make a mistake in my code or is this something more related to my specific case ?
Thanks a lot
Edit : I've just thought that it would be easier if I showed you how I used reflection, so there it is :
Field fieldXY = null;
Field fieldXZ = null;
Field fieldZY = null;
try {
System.out.println(this.getClass().getSuperclass().getName());
fieldXY = Class.forName(this.getClass().getSuperclass().getName()).getDeclaredField("xy");
fieldXZ = Class.forName(this.getClass().getSuperclass().getName()).getDeclaredField("xz");
fieldZY = Class.forName(this.getClass().getSuperclass().getName()).getDeclaredField("zy");
} catch (NoSuchFieldException e) {
System.out.println("-- No such field --");
System.out.println(e.getMessage());
} catch (SecurityException e) {
System.out.println("-- Security failure --");
System.out.println(e.getMessage());
} catch (ClassNotFoundException e) {
System.out.println("-- Class not found --");
System.out.println(e.getMessage());
}
fieldXY.setAccessible(true);
fieldXZ.setAccessible(true);
fieldZY.setAccessible(true);
try {
fieldXY.set(this, new CustomOrthoView(this, DimensionId.Z));
fieldXZ.set(this, new CustomOrthoView(this, DimensionId.Y));
fieldZY.set(this, new CustomOrthoView(this, DimensionId.X));
} catch (IllegalArgumentException e) {
System.out.println("-- Illegal argument --");
System.out.println(e.getMessage());
} catch (IllegalAccessException e) {
System.out.println("-- Illegal access --");
System.out.println(e.getMessage());
}
Edit2 : This is a simplified behavior of the superclass :
public class Orthoviewer {
// This class creates a canvas to display an image
public class OrthoCanvas {
// This class represents how an image is displayed
// It implements listeners (to navigate through the image for
// example), and ways to refresh the image
public class OrthoView extends JPanel {
// This class displays one part of the image (one plane)
// To represent a 3D point by the intersection of the three corresponding planes
// It has an attribute which indicates its dimension
// (X is for ZY plane, Y for XZ plane etc)
// It overrides the method paintComponent to draw itself
public class ImageCache implements Runnable {
// This handles the image to display on the corresponding plane
}
}
}
}

How to instantiate an object using it's constructor outside a method java

I'd like to instantiate an object with it's constructor outside a method. Example:
public class Toplevel {
Configuration config = new Configuration("testconfig.properties");
public void method1() {
config.getValue();
...etc
}
}
If I do this right now...I get this error..
Default constructor cannot handle exception type IOException thrown by implicit super constructor. Must define an explicit constructor
I'd like to do something like this so I could call config anywhere in my class, right now I keep having to instantiate the Configuration objects...there's gotta be a way to do this...any help would be much appreciated, thanks in advance.
EDIT:
Configuration class:
public class Configuration {
private String mainSchemaFile;
public Configuration() {
}
public Configuration( String configPath ) throws IOException {
Properties prop = new Properties();
prop.load( new FileInputStream( configPath ));
this.mainSchemaFile= prop.getProperty("MAINSCHEMA_FILE");
}
Your Configuration constructor is declared to throw an IOException. Any code that instantiates a Configuration using this constructor must catch it. If you use a variable initializer, then you can't catch it, because you can't supply a catch block; there is no block you can put here, only an expression. There is no method to declare a throws clause on either.
Your alternatives:
Instantiate the Configuration in a Toplevel constructor. You can catch the exception in the constructor body, or you can declare that constructor that it throws the exception.
public class Toplevel {
Configuration config;
public Toplevel() {
try {
config = new Configuration("testconfig.properties");
} catch (IOException e) { // handle here }
}
// ...
Instantiate the Configuration in an instance initializer in the TopLevel class, where you can catch the exception and handle it.
public class Toplevel {
Configuration config;
{
try {
config = new Configuration("testconfig.properties");
} catch (IOException e) { // handle here }
}
// ...
Catch and handle the exception in the Configuration constructor, so calling code doesn't have to catch the exception. This isn't preferred, because you may have an invalid Configuration object instantiated. Calling code would still need to determine if it's valid.
public class Configuration {
// Your instance variables
private boolean isValid;
public Configuration( String configPath ) {
try {
// Your code that might throw an IOE
isValid = true;
} catch (IOException e) {
isValid = false;
}
}
When you create a new Toplevel object then you have not declared a specific constructor for it and the attribute of Toplevel is instantiated as your code describes it with Configuration config = new Configuration("testconfig.properties");
So you do not handle the IoException of the Configuration constructor!
A better way would be to declare a specific constructor of Toplevel like this:
public Toplevel(){
try{
this.config = new Configuration("testconfig.properties");
} catch (IOException e) {
// handle Exception
}
}

JAVA creating an event list from a text file

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;
}

Launch main method from object with type "Class<?>"

I have a object like this
Class<?> myClass = getMyClass();
// don't ask about getMyClass() method, it's not important,
just be sure that this method returns a class.
Secondly, I'm sure that "myClass" contains "main(String args[])" method.
How can I launch this main method. I guess I should use java.lang.reflect, but I don't know how.
All I want, it's do something like this.
String params[] = {"arg1", "arg2"};
cl.main(params);
From the Java tutorial on the reflection API:
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.Arrays;
public class InvokeMain {
public static void main(String... args) {
try {
Class<?> c = Class.forName(args[0]);
Class[] argTypes = new Class[] { String[].class };
Method main = c.getDeclaredMethod("main", argTypes);
String[] mainArgs = Arrays.copyOfRange(args, 1, args.length);
System.out.format("invoking %s.main()%n", c.getName());
main.invoke(null, (Object)mainArgs);
// production code should handle these exceptions more gracefully
} catch (ClassNotFoundException x) {
x.printStackTrace();
} catch (NoSuchMethodException x) {
x.printStackTrace();
} catch (IllegalAccessException x) {
x.printStackTrace();
} catch (InvocationTargetException x) {
x.printStackTrace();
}
}
}
This should do the trick
(I assume that main is 1) public (tanks to that getMethod will work) and 2) static, that is why I pass null as first parameter in invoke)
myClass.getMethod("main", String[].class).invoke(null, (Object) params);
Yes, you can use reflection.
myClass.getDeclaredMethod("main", String[].class).invoke(null, (Object)args);
Obtain a reference to the main method (myClass.getDeclaredMethod) and then call the method (Method.invoke). The details should be obvious if you take a look at the javadocs of the methods.

Categories

Resources