This is the code I'm working on which instantiates and creates object at run time with provided arguments for the constructor.
private static void createInstancesFromSpecfication() {
String[] specifierFileNames = resourceMap.get("instances");
if (null == specifierFileNames)
return;
List<SpecifierObjects> instances = new ArrayList<SpecifierObjects>();
for (int i = 0; i < specifierFileNames.length; i++) {
try {
instances.addAll(XMLFileUtility.readXML(specifierFileNames[i],
new ClassInstantiationHandler()));
} catch (ResourceNotAvailableException | DocumentException
| ResourceDataFailureException e) {
e.printStackTrace();
}
}
for (Iterator<SpecifierObjects> iterator = instances.iterator(); iterator
.hasNext();) {
InstanceSpecifierObject instanceSpecifierObject = (InstanceSpecifierObject) iterator
.next();
try {
Class<?> cast = Class.forName(instanceSpecifierObject
.getInterfaceName());
Class<?> clazz = Class.forName(instanceSpecifierObject
.getClassName());
Constructor<?> constructor = clazz
.getConstructor(String[].class);
Object obj = constructor.newInstance(instanceSpecifierObject
.getFilesName());
objects.put(instanceSpecifierObject.getObjectName(),
cast.cast(obj));
} catch (ClassNotFoundException | InstantiationException
| IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException
| SecurityException e) {
e.printStackTrace();
}
}
}
The exception log is :
instances=/home/user/workspace/CoreLibs/src/instanceSpecification.xml
/home/user/workspace/CoreLibs/src/instanceSpecification.xml
java.lang.IllegalArgumentException: wrong number of arguments
at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
at com.framew.confinit.ApplicationResource.createInstancesFromSpecfication(ApplicationResource.java:87)
at com.framew.confinit.ApplicationResource.importResource(ApplicationResource.java:58)
at com.framew.confinit.ApplicationResource.main(ApplicationResource.java:40)
I'm getting the exception in this statement:
Object obj = constructor.newInstance(instanceSpecifierObject.getFilesName());
instanceSpecifierObject.getFilesNames() retruns an array of filenames as String objects (like String[]). I also tried passing them as Object[], but got the same exception.
Any help is appreciated. Thanks in Advance.
Related
I have a XmlParserException while trying to request a list of object in MinIO, I don't understand why, i've done many research but i wasn't able to find an answer, here is what I've done:
public void minioListener() {
AvroParserService avroParserService = new AvroParserService();
try {
List<String> downloadedObjects = new ArrayList<String>();
while(true) {
Iterable<Result<Item>> objects = getListObjects();
for (Result<Item> result : objects) {
Item object;
object = result.get();
if(!object.isDir() && object != null) {
String objectName = object.objectName();
if (!downloadedObjects.contains(objectName)) {
// Get object
InputStream objectStream = getMinioClient().getObject(GetObjectArgs.builder()
.bucket(minioConfig.getBucket())
.object(objectName)
.build()
);
avroParserService.deserialize(objectStream);
downloadedObjects.add(objectName);
// Traiter l'objet ici
}
}
}
}
}
catch (InvalidKeyException | ErrorResponseException | IllegalArgumentException
| InsufficientDataException | InternalException | InvalidResponseException
| NoSuchAlgorithmException | ServerException | XmlParserException | IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
And Here is the output:
io.minio.errors.XmlParserException: com.ctc.wstx.exc.WstxUnexpectedCharException: Unexpected character 'd' (code 100) after '<!' (malformed comment?)
at [row,col {unknown-source}]: [1,3]
at io.minio.Xml.unmarshal(Xml.java:55)
at io.minio.S3Base.lambda$getRegionAsync$2(S3Base.java:847)
at java.base/java.util.concurrent.CompletableFuture$UniApply.tryFire(CompletableFuture.java:646)
at java.base/java.util.concurrent.CompletableFuture.postComplete(CompletableFuture.java:510)
at java.base/java.util.concurrent.CompletableFuture.complete(CompletableFuture.java:2147)
at io.minio.S3Base$1.onResponse(S3Base.java:559)
at okhttp3.internal.connection.RealCall$AsyncCall.run(RealCall.kt:519)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:833)
It seems that the problem occur because I don't provide details for region, but in the example show on the MinIO Api documentation, they don't necessary provide region, (I can't provide region because it's not defined)
I study reflection and try to change field's value in Record.
public record Account(Integer id, String login, Boolean blocked) {}
public class Main {
public static void main(String[] args) {
Account account = new Account(null, null, null);
setFieldValue(account, "id", 1);
setFieldValue(account, "login", "admin");
setFieldValue(account, "blocked", false);
System.out.println(account);
}
public static void setFieldValue(Object instance,
String fieldName,
Object value) {
try {
Field field = instance.getClass().getDeclaredField(fieldName);
field.setAccessible(true);
field.set(instance, value);
} catch (NoSuchFieldException | IllegalAccessException e) {
e.printStackTrace();
}
}
}
If I convert Record to Class everything works, but with Record I get Exception
java.lang.IllegalAccessException: Can not set final java.lang.Integer field Account.id to java.lang.Integer
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:76)
at java.base/jdk.internal.reflect.UnsafeFieldAccessorImpl.throwFinalFieldIllegalAccessException(UnsafeFieldAccessorImpl.java:80)
at java.base/jdk.internal.reflect.UnsafeQualifiedObjectFieldAccessorImpl.set(UnsafeQualifiedObjectFieldAccessorImpl.java:79)
at java.base/java.lang.reflect.Field.set(Field.java:799)
What do I have to do to make the code work with records?
In general the commenters saying that this "can't be done" or is "impossible" aren't wrong... unless you are willing to slightly bend the rules of the JVM :) For example, by using unsafe reflection to change the relevant value directly at the memory location in the record like this:
public static void setFieldValue(Object instance, String fieldName, Object value) {
try {
Field f = instance.getClass().getDeclaredField(fieldName);
Field unsafeField = Unsafe.class.getDeclaredField("theUnsafe");
unsafeField.setAccessible(true);
Unsafe unsafe = (Unsafe) unsafeField.get(null);
Field theInternalUnsafeField = Unsafe.class.getDeclaredField("theInternalUnsafe");
theInternalUnsafeField.setAccessible(true);
Object theInternalUnsafe = theInternalUnsafeField.get(null);
Method offset = Class.forName("jdk.internal.misc.Unsafe").getMethod("objectFieldOffset", Field.class);
unsafe.putBoolean(offset, 12, true);
unsafe.putObject(instance, (long) offset.invoke(theInternalUnsafe, f), value);
} catch (IllegalAccessException | NoSuchFieldException | ClassNotFoundException | NoSuchMethodException | InvocationTargetException e) {
e.printStackTrace();
}
}
I have the next Java code:
public static <T> T buildSAMLObject(final Class<T> clazz) {
T object = null;
try {
XMLObjectBuilderFactory builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();
QName defaultElementName = (QName)clazz.getDeclaredField("DEFAULT_ELEMENT_NAME").get(null);
object = (T)builderFactory.getBuilder(defaultElementName).buildObject(defaultElementName);
} catch (IllegalAccessException e) {
throw new IllegalArgumentException("Could not create SAML object");
} catch (NoSuchFieldException e) {
throw new IllegalArgumentException("Could not create SAML object");
}
return object;
}
I am trying to convert it to Scala and so far I got:
def buildSAMLObject(clazz: asInstanceOf[T]): [T] T {
var obj = builderFactory.getBuilder(defaultElementName).buildObject(defaultElementName).asInstanceOf[T];
try {
val builderFactory = XMLObjectProviderRegistrySupport.getBuilderFactory();
val defaultElementName = clazz.getDeclaredField("DEFAULT_ELEMENT_NAME").get(null).asInstanceOf[QName];
obj = builderFactory.getBuilder(defaultElementName).buildObject(defaultElementName).asInstanceOf[T];
} catch
{
case e: IllegalAccessException => throw new IllegalArgumentException("Could not create SAML object")
case e: NoSuchFieldException => throw new IllegalArgumentException("Could not create SAML object")
}
obj
}
The latter is not compiling and I believe this is because I do not know how to represent [T] T. Any suggestions, please?
Generics in method signature causes the error, it should go like this:
def buildSAMLObject[T](clazz: Class[T]): T = {
P.S.: In scala newline sumbol is an equivalent of semicolon, you don't need to write them.
I am successfully loading a class from an external MyClass.class file at runtime, and am able to call it's methods assuming I know the name of them, which I do.
The problem I'm having is I cannot figure out how to pass a parameter to the constructor of the class I am loading.
How can I modify this to pass a parameter to MyClass's constructor? Also, how can I access MyClass's public variable: infoToAccess?
Here are the files I'm working with. (Keep in mind, ClassLoaderExample.pde was written for Processing, but other than sketchPath("") and the lack of a main function, it is identical.
ClassLoaderExample.pde: The main file that loads the class:
import java.io.File;
import java.net.URL;
import java.net.URLClassLoader;
import java.net.MalformedURLException;
import java.lang.ClassLoader;
import java.lang.reflect.Method;
import java.lang.reflect.InvocationTargetException;
/////// FILE STRUCTURE /////
// ClassLoaderExample //
// --this file //
// --tmp //
// ----MyClass.class //
////////////////////////////
// TODO
// Send parameter to MyClass constructor (by reference)
// Get public int number from MyClass
void setup()
{
String className = "MyClass";
Object instance;
Method updateMethod;
// sketchPath("") returns the directory this file is in
File file = new File(sketchPath(""));
try
{
URL url = file.toURL();
URL[] urls = new URL[]{url};
ClassLoader classLoader = new URLClassLoader(urls);
Class<?> loadedClass = classLoader.loadClass("tmp."+className);
try
{
instance = loadedClass.newInstance();
updateMethod = loadedClass.getDeclaredMethod("update");
// Calls MyClass's update() method
try
{
updateMethod.invoke(instance);
}
catch (InvocationTargetException e) {System.out.println(e);}
catch (IllegalAccessException e) {System.out.println(e);}
}
catch (InstantiationException e) {System.out.println(e);}
catch (IllegalAccessException e) {System.out.println(e);}
catch (NoSuchMethodException e) {System.out.println(e);}
}
catch (MalformedURLException e) {System.out.println(e);}
catch (ClassNotFoundException e) {System.out.println(e);}
}
MyClass.java: The class I am loading:
package tmp;
public class MyClass
{
public int infoToAccess = 1337;
public MyClass(int i)
{
System.out.println("MyClass constructor was called with numebr: " + i);
}
public void update()
{
System.out.println("Update was called.");
}
}
Thanks for any help with this!
you can use the class you loaded to get hold of the constructor
you need an use that to create a new instance.
import java.lang.reflect.Constructor;
...
Class<?> loadedClass = classLoader.loadClass("tmp."+className);
try {
Constructor<?> ctor=loadedClass.getConstructor(int.class);
ctor.newInstance(42);
...
}
...
You need to use reflection for both of the things you asked about:
Class<?> loadedClass = classLoader.loadClass("tmp." + className);
Object oInstance = null;
try {
Constructor<?> c = loadedClass.getConstructor(Integer.class);
oInstance = c.newInstance(10);
} catch (InstantiationException | IllegalAccessException | InvocationTargetException | NoSuchMethodException e) {
//Handle the possibility that you cannot access this constructor
}
To access the public field of your object:
try {
Field field = loadedClass.getField("infoToAccess");
int fieldValue = field.getInt(oInstance);
} catch (NoSuchFieldException | IllegalAccessException e) {
//Handle the possibility that you cannot access this field
}
As mentioned in the link I wrote in the comments, you can pass parameters in this call
updateMethod = loadedClass.getDeclaredMethod("update", parameters go here);
then you should be able to invoke the method with a parameter(s)
If you want to pass arguments to the constructor you cannot use getInstance(), you have to get the constructor object and pass the parameters there.
Example:
Constructor constructor = aClass.getConstructor(new Class[]{String.class});
MyObject myObject = (MyObject) constructor.newInstance("constructor-arg1");
See tutorial here: Java Reflection - Constructors
So I'm working with JSON in Java and JSON can have a base of either an Array or an Object. In my Config class, I take a class as an argument so I can create the file accordingly if it doesn't exist. I also store the class as a private field so I know in future.
However, when I get to reading the file, I'd prefer to have multiple return types though the same method name. If I return Object, I then have to cast the returned value which I want to avoid.
Current code:
public class Config {
private File dir = null;
private File file = null;
private Class clazz = null;
public Config(String program, String fileName, Class root) throws IOException {
this.dir = new File(System.getProperty("user.home") + File.separator + program);
if (!this.dir.exists()) {
this.dir.mkdir();
}
this.file = new File(this.dir + File.separator + fileName);
if (!this.file.exists()) {
this.file.createNewFile();
if (root.getName().equals(JSONArray.class.getName())) {
Files.write(this.file.toPath(), "[]".getBytes());
} else if (root.getName().equals(JSONObject.class.getName())) {
Files.write(this.file.toPath(), "{}".getBytes());
}
}
this.clazz = root;
}
public JSONArray readConfig() {
return null;
}
public JSONObject readConfig() {
return null;
}
}
Is there anyway I can do what I want without having to return Object?
multiple return types though the same method name
well, it is possible to use generic function to achieve that. For example,
public static void main(String[] args) {
try {
String t = getObject(String.class);
Integer d = getObject(Integer.class);
} catch (Exception e) {
e.printStackTrace();
}
}
public static <T> T getObject(Class<T> returnType) throws Exception {
if(returnType == String.class) {
return (T) "test";
} else if(returnType == Integer.class) {
return (T) new Integer(0);
} else {
return (T) returnType.newInstance();
}
}
Will the following code even compile?
I'm afraid no. There are few compilation errors such as
public Object readConfig() {
try {
// Assume jsonString exists
return (this.clazz.getDeclaredConstructor(String.class).newInstance(jsonString)); <--- clazz should be getClass()
} catch (InstantiationException | IllegalAccessException
| IllegalArgumentException | InvocationTargetException
| NoSuchMethodException | SecurityException e) {
e.printStackTrace();
<---- missing return statement
}
}