I would like to know if there exists any kind of library or workaround to use my runtime compiled classes ( I dynamically generate my classes, writing .java, then compiling them in .class all of this happens at runtime) without using a reflection library?
Loading using reflection I always have to work like this:
//path where my newly generated classes are
File file = new File("e:\\tmp");
// Convert File to a URL
URL url = file.toURI().toURL();
URL[] urls = new URL[]{url};
ClassLoader cl = new URLClassLoader(urls);
Class cls = cl.loadClass("eu.dataaccess.footballpool.TGoal");
// Create an instance of the class just loaded
Object o = cls.newInstance();
Method method2 = cls.getDeclaredMethod("getIMinute");
Object result2 = method2.invoke(o);
My question is if it's somehow possible to use at runtime
TGoal x = new TGoal();
and
x.getIMinute();
EDIT: It seems it is impossible to act the way I hoped to. I modify my question so: is it possible to load a whole package at runtime, in a way that the classes of the loaded package are able to communicate each other without the method.invoke, meanwhile the main activation of the package is done with the method invoke() ?
I tried without any luck, i have some "error: cannot find symbol", it seems the runtime-loaded classes can't communicate each other
If you're not using the reflection api you have to instantiate classes via the new operator. This requires having the class file at compile time. So it is not possible. If your class TGoal is implementing an Interface IGoal, then you can cast your Object o to IGoal. This enables you to work with your Object like with any other object without using the reflection api.
You can do
x.getIMinute();
by using interfaces, but AFAIK you cannot create new class instances without reflection.
Instead, to call methods of your dynamically created objects, you
can declare an interface:
public interface MinuteGetter{
public int getIMinute();
}
Your dynamic objects have to implement his, than you can do:
Class cls = cl.loadClass("eu.dataaccess.footballpool.TGoal");
// Create an instance of the class just loaded
MinuteGetter o = (MinuteGetter) cls.newInstance();
o.getIMinute();
If applying an interface to your code is not an option for you, you can try to give a try to AnonymousClassLoader. See this detailed post about it.
It work only on Java 7, and please note that I never used it, so how don't know if it offer better performance than reflection.
Are you sure you have to create the Code at runtime? The easiest way is to avoid this.
If the TGoal x = new TGoal() doesn't have to happen in this program, then create a main function in the generated code and start a second java application.
Related
Trying to use https://github.com/japlscript/obstmusic to talk to Apple Music app on macOS with Java, I used to write native AppleScript and then java applescript library but that was removed from Java.
In this method it looks for a existing folder playlist called songkong, it it finds it then returns it. If none exists then it creates such a folder and then returns it.
private FolderPlaylist getPlayListFolder()
{
Application app = Application.getInstance();
com.tagtraum.macos.music.Playlist[] songKongPlaylists = app.getPlaylists();
for(com.tagtraum.macos.music.Playlist next:songKongPlaylists)
{
if(next.getName().equals("songkong"))
{
return (com.tagtraum.macos.music.FolderPlaylist)next;
}
}
Object songkongPlaylist = app.make(FolderPlaylist.class);
if(songkongPlaylist instanceof FolderPlaylist)
{
((FolderPlaylist)songkongPlaylist).setName("songkong");
return ((FolderPlaylist)songkongPlaylist);
}
return null;
}
First time I run it when I have to create a folder playlist, because non exists it works, but if I run again so it finds an existing folder playlist it then fails complaining as follows
4/04/2022 14.53.25:BST:OSXUpdateItunesWithChanges:updateItunes:SEVERE:
*** Unable to run itunes update:class jdk.proxy2.$Proxy62 cannot be cast to class com.tagtraum.macos.music.FolderPlaylist
(jdk.proxy2.$Proxy62 is in module jdk.proxy2 of loader 'app';
com.tagtraum.macos.music.FolderPlaylist is in unnamed module of loader
'app') java.lang.ClassCastException: class jdk.proxy2.$Proxy62 cannot
be cast to class com.tagtraum.macos.music.FolderPlaylist
(jdk.proxy2.$Proxy62 is in module jdk.proxy2 of loader 'app';
com.tagtraum.macos.music.FolderPlaylist is in unnamed module of loader
'app') at
com.jthink.songkong.ituneshelper.OSXUpdateMusicWithChanges.getPlayListFolder(OSXUpdateMusicWithChanges.java:41)
at
com.jthink.songkong.ituneshelper.OSXUpdateMusicWithChanges.createPlaylist(OSXUpdateMusicWithChanges.java:56)
at
com.jthink.songkong.ituneshelper.OSXUpdateItunesWithChanges.analyseFiles(OSXUpdateItunesWithChanges.java:246)
at
com.jthink.songkong.ituneshelper.OSXUpdateItunesWithChanges.updateItunes(OSXUpdateItunesWithChanges.java:126)
at
com.jthink.songkong.ituneshelper.UpdateItunesWithChanges.call(UpdateItunesWithChanges.java:184)
at
com.jthink.songkong.ituneshelper.UpdateItunesWithChanges.call(UpdateItunesWithChanges.java:33)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at
java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at
java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
Im not using modules so I think references to modules is probably misleading. More likely the issue I have to do something more than just cast from Playlist to FolderPlaylist but I cannot find an alternative.
ObstMusic uses JaplScript to talk to Apple's Music app via AppleScript (in an imperfect way). It does this by creating dynamic proxies for Java interfaces that have been generated for Music's AppleScript classes.
Now, what happens in your code?
com.tagtraum.macos.music.Playlist[] songKongPlaylists = app.getPlaylists();
Here, in essence, ObstMusic generates an AppleScript snippet that asks Music for all playlists. The signature of the getPlaylist() method is as follows:
Playlist[] getPlaylists​();
Now, when JaplScript generates dynamic proxies for the returned AppleScript references, it has to figure out what types it has to use. Ideally, it would look at the AppleScript references (and it could) to figure out what type to use. But it would imply another AppleScript roundtrip (or not... see update below). So for large collections this could take a while. For performance reasons, JaplScript simply uses the type declared in the method you have called. In this case Playlist, which is a superclass of FolderPlaylist. But since the FolderPlaylist is not specified during dynamic proxy generation, you cannot simply cast to it. That's why you see the ClassCastException.
The described behavior is obviously not the most convenient, since it does not adhere to the usual Java behavior (or that of many other OO languages for that matter).
If you want to work around this and are willing to take the performance hit, you can ask a JaplScript instance for its real AppleScript runtime type by calling TypeClass typeClass = someJaplScriptProxy.getTypeClass(). You can also get the TypeClass of each Music app interface by calling e.g. TypeClass tc = Playlist.CLASS (note the casing). Finally, you can get all Music app interfaces by calling Set<java.lang.Class<?>> classes = Application.APPLICATION_CLASSES, which returns a set of all Java interfaces declared for Music app.
Putting this all together, you can create a map from real TypeClass to most specific Java interface and use this in your cast() call, roughly like this:
Set<java.lang.Class<?>> classes = Application.APPLICATION_CLASSES;
Map<TypeClass, java.lang.Class<?>> typeClassToJava = new HashMap<>();
for (final Class<?> c : classes) {
typeClassToJava.put(TypeClass.fromClass(c), c);
}
Using this map you can iterate over the returned playlist array and cast all playlist objects to their actual (most specific) types and work around the issue you experienced.
Update 4/21/2022:
Starting with version 3.4.11 (Obstmusic 0.9.6), JaplScript is much better at creating dynamic proxies with the most specific Java interface that is suitable for the AppleScript object specifier. This means, you may not have to manually cast at all anymore.
Okay I solved this problem you have to use Reference cast() method rather than doing a usual Java cast
e.g
for(com.tagtraum.macos.music.Playlist next:songKongPlaylists)
{
if(next.getName().equals("songkong"))
{
return next.cast(FolderPlaylist.class);
}
}
but I don't understand why it works that way, I suppose its just the imperfect mapping between the java way of doing things and the Apple way of doing things.
Normally you create an protobuf class instance like this:
Bar.Builder bld = Bar.newBuilder();
bld.setXYZ(...
I have a usecase that uses the Java reflection to instantiate a protobuf class:
Class clsBar = Class.forName("com.xyz.Foo$Bar");
Object instance = clsBar.newInstance(); // error here!
Method mth = clsBar.getMethod(...);
The above code works fine with normal Java classes. But for a generated protobuf class "com.xyz.Foo$Bar", It gives me an NoSuchMethodException, as there isn't a default public constructor there.
Any suggestions on how to use Java refection to create protobuf instances? The question is for someone who's really good at protobuf internals. Thank!
I think you should go the full way: through the Builder class:
//get Bar class
Class barClass = Class.forName("com.xyz.Foo$Bar");
//instantiate Builder through newBuilder method
Method newBuilderMethod = barClass.getMethod("newBuilder");
Bar.Builder builder = (Bar.Builder) newBuilderMethod.invoke(null);
// ... set properties -- can be through reflection if necessary
//build:
Bar bar = builder.build();
Though I don't exactly see how reflection is of any use in this case, that would probably require a deeper understanding of the exact problem you are trying to solve.
We are building a product that needs to run on production environments. We need to modify some of the functionality of a existing library. The existing library has class's and methods, we need to override 1 or more methods so that the caller uses our overriden methods instead of the original library.
OriginalLibrary
package com.original.library ;
public class OriginalLibrary {
public int getValue() {
return 1 ;
}
public int getAnotherValue() {
return 1 ;
}
}
Original Client
public class MyClient {
private OriginalLibraryClass originalLibraryObject ;
public MyClient () {
originalLibraryObject = new OriginalLibraryClass() ;
System.out.println(originalLibraryObject.getValue()) ;
System.out.println(originalLibraryObject.getAnotherValue()) ;
}
}
Output
1
2
Now, I need to change getValue() to return 3, instead of 1
Needed Output
3
2
package com.original.library.improved ;
public class OriginalLibrary extends com.original.library.OriginalLibrary {
public int getValue() {
return 3 ;
}
public int getAnotherValue() {
return super.getAnotherValue() ;
}
}
If I do the above, I need to tell my Original Client to reorder and use my new com.original.library.improved jar file before com.original.library.
I am almost convinced that this is the most non intrusive way to launch my improved services over and above the OriginalLibrary. I would have preferred a solution where I need to tell the customer to just add my jar file, no need to recompile, relink your client code.
Similar (not same) questions on a google search
here
here
java assist is excellent library for bytecode manipulation. I have modified code below as per your sample code given, You have to explore javaassist more for your actual requirenment
CtClass etype = ClassPool.getDefault().get("com.original.library.OriginalLibrary");
// get method from class
CtMethod cm = etype.getDeclaredMethod("getValue");
// change the method bosy
cm.setBody("return 3;");
etype.rebuildClassFile();
// give the path where classes is placed, In my eclipse it is bin
etype.writeFile("bin");
OriginalLibrary originalLibraryObject;
originalLibraryObject = new OriginalLibrary();
System.out.println(originalLibraryObject.getValue());
System.out.println(originalLibraryObject.getAnotherValue());
Now output of getValue is 3 because I changed body of that method.
A couple of questions -
How is the client getting an instance of your library's class?
If they are using new OriginalLibrary(), then you're pretty much stuck with creating a new subclass of OriginalLibrary and then asking your client to use your new OriginalLibraryImproved class. This is a common problem encountered in projects and is one reason why a library should not allow its clients to instantiate its classes directly using the new operator.
If instead, your client is instantiating OriginalLibrary using a factory method provided by the library (say, OriginalLibrary.getInstance()), you may want to check if there are any hooks into the factory that allow you to change the object being returned.
Do you have full control of the source code of the original library?
If yes, then you definitely should (and I cannot emphasize this strongly enough) provide factory methods for any class in the library that is instantiable. Doing this allows you to change the actual object being returned without modifying the client (as long as the returned object's class is a subclass of the return value from the factory method).
If not, then I suggest you do the following.
Create a subclass of OriginalLibrary (say, OriginalLibraryImproved).
Create a Factory class named OriginalLibraryFactory that has a static method named getInstance(). Write code to return an instance of OriginalLibraryImproved from this method.
Ask your client to replace all occurrences of new OriginalLibrary() with OriginalLibraryFactory.getInstance(). Note that this approach will only involve adding an extra import for the factory class. The client will still refer to the returned instance using the same OriginalLibrary reference as before.
The advantage of this approach is that it gives you complete flexibility to change the implementation details of OriginalLibraryImproved without affecting the client in anyway. You could also swap OriginalLibararyImproved with a newer version like OriginalLibraryImprovedVer2 and the client will be oblivious to the fact that it is using a new class. You'll just have to make sure that OriginalLibraryImprovedVer2 subclasses OriginalLibrary.
An even more flexible approach is to use the Wrapper or Decorator pattern to avoid the pitfalls of inheritance. You can understand more about the Decorator pattern here.
In a nutshell, try to avoid forcing your clients to use new and try to avoid inheritance unless you have very compelling reasons.
I have a web-app which I want to work roughly as follows:
Someone uploads a jar (say myjar.jar) along with the name of class to analyze (say test.Test). The exact purpose of analysis is irrelevant so assume that the web-app does some static analysis on it.
I am able to upload the jar and load the class in Java using the hacks described here and here (MultiClassLoader).
If I know the methods inside the class, I can invoke them using reflection. Following assumes a method with signature foo(String)
Class c = jarLoader.loadClass("test.Test", true);
Object instance = c.newInstance();
Method foo = instance.getClass().getDeclaredMethod("foo", new Class[]{String.class});
// foo takes one para String
foo.setAccessible(true);
foo.invoke(instance, (Object) "hello");
Now, suppose, I need to find out what methods are there inside test.Test. To do this, I use the ASM framework, which needs the class as a stream. This is where I am running into a problem. I am unable to do getResourceAsStream or its equivalents. This is what I tried:
Type t = org.objectweb.asm.Type.getType(c); // uses asm library
String url = t.getInternalName() + ".class";
c.getClassLoader().getResourceAsStream(url); // gives null
Same problem if I use:
c.getClassLoader().getResourceAsStream(c.getSimpleName() + ".class");
etc.
What is the right way to get resource as stream, where the resource is a class file loaded from a jar NOT in the class path. Note that the entire thing is done via a web-app.
EDIT: Getting methods is just an example. What I really want is to get the class as a stream. I need to use ASM because it gives me other information (such as parameter names), which I cannot get via getDeclaredMethods or getMethods.
Re: "Now I need to find out what methods are there inside test.Test"
You get the class via Class c = jarLoader.loadClass("test.Test", true);. Class also has getDeclaredMethods() and getMethods().
EDIT: #Jus12, from your second link Java Tip 70: Create objects from jar files!:
protected byte[] loadClassBytes (String className)
ASM has a ClassReader(byte[] b) constructor.
You can use a URLClassLoader, which takes a List of URLs in its constructor. You can then load the class by invoking the loadClass method on the URLClassLoader instance to get the Class<?> object for the class that you are after. Once you have that, you can then use reflection to create instances, and/or get general information about the class.
I have not tried, but the URLClassLoader does also have a getResourceAsStream method.
I currently use Com4j to talk to iTunes from my Java app, unfortunately it does not work with 64bit Java and looks like it never will, so Im trying to use an alternative called Jacob instead.
Both libraries provide a tool to generate Java classes from a DLL, and the resultant classes are very similar and its been straightforward to change most of the code but Im failing on how to find subtypes
IITPlaylist object = itunes.createFolder(TextLabel.SAVE_ITUNES_PLAYLIST_FOLDER.getMsg());
IITUserPlaylist playlistFolder = object.queryInterface(IITUserPlaylist.class);
Both libraries have created IITPlaylist and IITUSerPlaylist classes but only com4j provides the queryInterface class, and no IITUserPlaylist is not actually a subclass of IITPlaylist.
Also com4j provides an is method, but jacob does not
if (next.is(IITFileOrCDTrack.class))
Anyone know how to resolve these issues ?
EDIT:
Made some progress but still not got it working, there is a QueryInterface method that takes the guid of the class (include the curly brackets) , I found the guid by looking at the jacobgenlog.txt file which is created when you run jacobgen on the iTunes executable
This then returns another Dispatch object that is meant to relate to the subclass, however the simple cast Ive done is invalid, whats the mising step ?
private static final String USER_PLAYLIST_GUID = "{0A504DED-A0B5-465A-8A94-50E20D7DF692}";
IITPlaylist object = itunes.createFolder(TextLabel.SAVE_ITUNES_PLAYLIST_FOLDER.getMsg());
IITUserPlaylist playlistFolder = (IITUserPlaylist)object.QueryInterface(USER_PLAYLIST_GUID);
The is() functionality is replaced by checking the kind
IITTrack next = tracks.getItem(i);
if(next.getKind().equals(ITTrackKind.ITTrackKindFile))
A spanner in the works is that jacobgen getKind() methods are invalid Java because they try to return a new interface, and of course you cannot instantiate an interface, so I had to modify them as follows
ITPlayListKind goes from
public interface ITPlaylistKind extends __MIDL___MIDL_itf_iTunesCOMInterface_0001_0081_0001 {
}
to
public enum ITPlaylistKind {
ITPlaylistKindUnknown,
ITPlaylistKindLibrary,
ITPlaylistKindUser,
ITPlaylistKindCD,
ITPlaylistKindDevice,
ITPlaylistKindRadioTuner;
}
Within IITUserPlaylist
public ITPlaylistKind getKind() {
return new ITPlaylistKind(Dispatch.get(this, "Kind").toDispatch());
}
to
public ITPlaylistKind getKind() {
return ITPlaylistKind.values()[Dispatch.get(this, "Kind").getInt()];
}
this wasnt an original idea by me, I got the idea from http://dot-totally.co.uk/software/itunescon/ which appears to be a modified version of the iTunes classes created by jacobgen, I didnt find it added that much and decided to stick with the jacobgen generated classes.
As soon as I set a bounty I work out the answer for myself.
Simply just use the constructor
IITPlaylist object = itunes.createFolder
(TextLabel.SAVE_ITUNES_PLAYLIST_FOLDER.getMsg());
IITUserPlaylist playlistFolder = new IITUserPlayList(object);
The QueryInterface and GUID sctrings re not required.
I was also having a problem working out how to add a track to a playlist, but you just need to
construct a Variant from the track ( I dont have to do this anywhere else)
IITTrack next = itunes.getLibraryPlaylist().getTracks()
.getItemByPersistentID(persistentId.getHighBit(),
persistentId.getLowBit());
playlist.addTrack(new Variant(nextTrack));