I have a compiled executable JAR file that fails on Windows platforms.
The reason for this is because I want to properly integrate certain OS X-specific properties — such as the About window.
Even though I specifically cordoned off the code using a conditional, the JAR is still crashing with a NoClassDefFoundError
on the very first line of execution.
if (isOSX()) {
com.apple.eawt.Application application = com.apple.eawt.Application.getApplication();
application.setAboutHandler(new com.apple.eawt.AboutHandler() {
@Override
public void handleAbout(com.apple.eawt.AppEvent.AboutEvent ae) {
new AboutWindow();
}
});
application.setDefaultMenuBar(MenuSystem.createMenu());
}
Is it possible to include this code in my JAR file so I can have one consistent codebase?
JAR (Java Archive) is a cross-platform file format based on the ZIP format, which can be used in Windows, Mac OSX, Linux, Android OS and more.
Java is cross platform because a program's source code is compiled into an intermediate "bytecode" language. The bytecode is then executed by a Java Virtual Machine (Java interpreter) that was written for that particular hardware platform.
Using Brian's answer as a base, I was able to use the following code to set the Mac dock icon image using OS X specific methods. Unlike the other answer, this answer includes a complete example and does not have the problem of trying to cast the object to a OS X specific class object.
What the code does:
Class.forName(String)
to get the Class
object associated with the OS X specific class. In this example, the com.apple.eawt.Application
library is used to set the dock icon image.getClass()
is used to get the Application
object, and getMethod().invoke()
is used to call the getApplication()
and setDockIconImage()
methods.I've included comments showing the code that would be normally used in a OS X exclusive program, to make it clear what the reflection code is replacing. (If you are creating a program that only needs to run on Mac, see How do you change the Dock Icon of a Java program? for the code needed to set the dock icon image.)
// Retrieve the Image object from the locally stored image file
// "frame" is the name of my JFrame variable, and "filename" is the name of the image file
Image image = Toolkit.getDefaultToolkit().getImage(frame.getClass().getResource(fileName));
try {
// Replace: import com.apple.eawt.Application
String className = "com.apple.eawt.Application";
Class<?> cls = Class.forName(className);
// Replace: Application application = Application.getApplication();
Object application = cls.newInstance().getClass().getMethod("getApplication")
.invoke(null);
// Replace: application.setDockIconImage(image);
application.getClass().getMethod("setDockIconImage", java.awt.Image.class)
.invoke(application, image);
}
catch (ClassNotFoundException | IllegalAccessException | IllegalArgumentException
| InvocationTargetException | NoSuchMethodException | SecurityException
| InstantiationException e) {
e.printStackTrace();
}
Although this code works, this method still seems to be a messy workaround, and it would be great if anyone has any other suggestions on how to include OS X specific code in a program used on Mac and Windows.
Have you tried loading the surrounding class dynamically using Class.forName ?
Class.forName("com.myproject.ClassContainingApple");
This way you can refer to all your Apple-specific classes inside one class, and dynamically load it in your isOSX()
branch. You have to do it this way since you're not able to load a class that refers to other unavailable classes - you'll have to determine if you're in OSX, and only then load up anything referring to OSX-only classes.
An extensible way to do this if you have more OS-specific requirements is to name your class after the OS and then load a classname based upon the detected OS. e.g. call your classes WindowsExtensions
, OSXExtensions
, LinuxExtensions
etc. (you will have to look up the appropriate names - I'm just providing examples)
e.g. here's an example of usage:
String className = ""java.util.ArrayList";
Class cls = Class.forName(className);
List list = (List)cls.newInstance();
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With