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));
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.
This morning I fell into a particular case that never happened to me before. I'm developing a Minecraft plugin using the minecraft server API which is usually called NMS with reference to the name of its packages (eg net.minecraft.server.v1_13_R1 for version 1.13).
The main problem with the use of the minecraft server API is that it is difficult to write a cross version code: indeed the name of the packages changes with each new version.
When the plugin only supports two versions it is usually easier to use the interfaces to write two different codes depending on the version. But when you have to support a dozen different versions (and this is my case), it's a bad idea (the plugin would be much too heavy, it would have to import every jar in the IDE, and I would have to redo the code with each new version).
In these cases I usually use reflection but I do not think it's possible here:
packet = packetConstructor.newInstance(
new MinecraftKey("q", "q") {
#Override
public String toString() {
return "FML|HS";
}
},
packetDataSerializerConstructor.newInstance(Unpooled.wrappedBuffer(data)));
As you probably guessed MinecraftKey is a class from NMS and I was told to use Java Dynamic Proxy API. I have never used it and would like to know if you would know a place that would explain to me how to do it simply? If you know of another better method that interests me too!
When I think about it, I think that this is really a lot of trouble for a tiny piece of code x)
EDIT :
My plugin uses the PacketPlayOutCustomPayload (aka plugin messages) to communicate with the mods of the players. It allows me to send a message (a byte []) on a particular channel (a String). But with the 1.13 this String has been replaced by a MinecraftKey (a wrapper for the String that replaces some characters and requires the use of a ":"). This poses a problem when players connect to 1.12 on my 1.13 server so I do not have a choice: I have to override the MinecraftKey object in this case.
I don’t really think using proxy classes is good solution here, it will only make it harder to debug, but if you need something like that you should use library like ByteBuddy: (as java can’t generate proxy for a class, only interfaces are allowed)
import net.bytebuddy.ByteBuddy;
import net.bytebuddy.implementation.FixedValue;
import static net.bytebuddy.matcher.ElementMatchers.*;
public class Main {
public static void main(String[] args) throws Exception {
SomeKey someKey = new SomeKey("my", "key");
System.out.println(someKey); // :<
// this class should be cached/saved somewhere, do not create new one each time.
Class<? extends SomeKey> loaded = new ByteBuddy()
.subclass(SomeKey.class)
.method(named("toString").and(returns(String.class).and(takesArguments(0))))
.intercept(FixedValue.value("something"))
.make()
.load(Main.class.getClassLoader()).getLoaded();
someKey = loaded.getConstructor(String.class, String.class).newInstance("what", "ever");
System.out.println(someKey); // YeY
}
}
class SomeKey {
final String group;
final String name;
public SomeKey(String group, String name) {
this.group = group;
this.name = name;
}
public String getGroup() { return this.group; }
public String getName() { return this.name; }
#Override public String toString() {
return group+":"+name;
}
}
But I would just create separate modules in my project, one that does only work with real bukkit API and contains many interfaces to represent NMS types in some normalised and readable way.
And separate modules for each version, then you will not have much code to duplicate, as most of it will be abstracted and handled by that “core/base” module.
Then you can build it as one single fat jar or separate .jar per version.
Other solution might be using some template engines and preprocessors to generate java sources on build time, see how fastutil is doing this:
https://github.com/vigna/fastutil
And yet another solution for simple classes and parts of code would be to use build-in javascript or external script language like groovy to also create this pattern-line but in runtime. But I would use this only for simplest stuff.
Also for just using methods you can just use normal reflections.
You can also always inject into netty and instead of using default packet serializer just write own bytes, then you don't need that key at all.
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.
Looking through the Play documentation for Java I noticed the following block of code:
public static Result show(Long id) {
Client client = Client.findById(id);
return ok(views.html.Client.show(client));
}
Source: http://www.playframework.com/documentation/2.1.0/JavaRouting
I am having some trouble understanding the second line, my understanding of Java Object creation is a typical constructor looks like the following:
Person john = new Person();
What is the second line doing? Creating a Object called client from Class called Client, also what is Client? It doesn't appear to be a part of the Play Framework, certainly I cannot find anything in JavaDocs.
Thanks
Edit:
I found this to be a good point of reference for the answer (http://docs.oracle.com/javase/tutorial/java/javaOO/classvars.html)
Also I think the class Client comes from the following documentation (http://www.playframework.com/documentation/1.1.1/controllers) with Client being just a example model class, the new documentation probably needs updating to clear up this confusion.
Pretty clearly, the class Client has a static function of findById, which takes a Long and returns a Client. Static functions are functions that are defined without any access to object properties, and therefore can be accessed through the class name, rather than having to be accessed through an object. Most likely, the class has a static property containing a collection of all clients in the system by index, and findById grabs an existing Client from that list.
I really have no idea where the class Client is defined, however. I've also made a quick look around for it, and couldn't find it in the obvious places.
There must be a static method called show(Client) on the views.html.Client class that returns some object. That object is passed into an ok(whatever) method, and that ok method returns a Result object.
You're missing some basic knowledge/experience. The sample you gave has nothing to do with routes and in this snippet only first line is important, second is just some hypothetical usage. De facto it could be just...
public static Result show(Long id) {
return ok("You want to display details of client with ID: " + id);
}
Although #BenBarden explained what is that mean correctly , this static method isn't declared anywhere, it's (again) hyphotetical usage of some ORM. For an example the real usage with Ebean's model will be:
Client = Client.find.byId(id);
Of course you can also declare own method in your Client model and name it the same as in the sample, however it will be just only wrapper:
public static Finder<Long, Client> find
= new Finder<>(Long.class, Client.class);
public Client findById(Long id) {
return find.byId(id);
}
Conclusions
You need to examine some samples available with your Play sources to get familiar with some basic syntax, fortunately you'll find it easy.
DO NOT MIX documentation from Play 1.x with Play 2.x they are not compatible!
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.