# Mac-spezifische Fehlermeldung auf nicht OS-X-Systemen



## Daniel_L (2. Jan 2009)

Hallo,

für mein Java-Programm habe ich für Mac OS X spezielle Methoden, um das Apple-Menü nutzen zu können. Allerdings kommt offensichtlich auf nicht-OS-X-Systemen eine Fehlermeldung:




> ~$ Zettelkasten
> Exception in thread "AWT-EventQueue-0" java.lang.NoClassDefFoundError:
> com/apple/eawt/ApplicationListener
> at zettelkasten.ZettelkastenApp.startup(ZettelkastenApp.java:39)
> ...



Im Konstruktor meiner Main-Klasse frage ich die OS-Version folgendermaßen ab und initialisiere bei Bedarf einen Application-Listener für Mac OS X:

```
IS_MAC = System.getProperty("os.name").toLowerCase().startsWith("mac os");
        IS_MAC_AQUA = IS_MAC & settings.getLookAndFeel().contains("Aqua");
...
        // when we have a mac, we need an extra quit-hanlder...
        if (IS_MAC) setupMacOSXApplicationListener();
```

Den Source für die Methode setupMacOSX... habe ich in einem Blog-Eintrag gefunden und nach eigenen Bedürfnissen angepasst:


```
/**
     * This is an application listener that is initialised when running the program
     * on mac os x. by using this appListener, we can use the typical apple-menu bar
     * which provides own about, preferences and quit-menu-items.
     */
    private void setupMacOSXApplicationListener() {
        try {
            final com.apple.eawt.Application application = com.apple.eawt.Application.getApplication();
            // damit ein about-Menü erscheint
            application.setEnabledAboutMenu(true);
            // preference pane
            application.addPreferencesMenuItem();
            application.setEnabledPreferencesMenu(true);

            application.addApplicationListener(new com.apple.eawt.ApplicationListener() {
                // about dialog
                @Override
                public void handleAbout(com.apple.eawt.ApplicationEvent ae) {
                    showAboutBox();
                    ae.setHandled(true);
                }

                @Override
                public void handleOpenApplication(com.apple.eawt.ApplicationEvent ae) {
                }

                @Override
                public void handlePreferences(com.apple.eawt.ApplicationEvent ae) {
                    settingsWindow();
                }

                @Override
                public void handlePrintFile(com.apple.eawt.ApplicationEvent ae) {
                }

                @Override
                public void handleQuit(com.apple.eawt.ApplicationEvent ae) {
                    ZettelkastenApp.getApplication().exit();
                }

                @Override
                public void handleReOpenApplication(com.apple.eawt.ApplicationEvent ae) {
                }

                @Override
                public void handleOpenFile(com.apple.eawt.ApplicationEvent ae) {
                }
            });
        } catch (Throwable e) {}
    }
```


Wie kann ich diese Fehlermeldung "umgehen" bzw. an welcher Stelle mit try/catch abfangen?


----------



## Tobias (2. Jan 2009)

Du mußt die Bibliothek, in der com.apple... drinsteckt, im Classpath haben - auch auf Nicht-Macs.


----------



## Daniel_L (2. Jan 2009)

Hm... wie füge ich die Bibliothek denn zum Classpath hinzu, wenn ich mit NetBeans 6.1 arbeite? Weißt du das zufällig?


----------



## Tobias (2. Jan 2009)

Such mal nach Classpath, das sollte dutzende relevante Treffer bringen. Wie das in Netbeans geht, weiß ich nicht, nutze Eclipse.


----------



## Daniel_L (2. Jan 2009)

Ich suche gerade nach Hinweisen, hab aber noch keine Lösung gefunden. Aber ich suche weiter. Vielen Dank auf jeden Fall für deinen Hinweis!


----------



## Spacerat (2. Jan 2009)

Hinweise... Hmmm? Das das Dateisystem beim Mac wieder ein anderes ist als bei Windows oder Linux ist hoffentlich beim laden der Classfiles beachtet worden. Immerhin 'ne Fehlerquelle, wenn man eigene Classloader verwendet.

mfg Spacerat


----------



## Daniel_L (2. Jan 2009)

Ich verwende zumindest bewusst keine eigenen Classloader. Ich habe mit NetBeans eine DesktopApp (JSR 296) erstellt und programmiere ein bisschen vor mich in. ;-)

Die notwendigen Jar-Dateien, bspw. für JDOM, habe ich in den Projekteigenschaften hinzugefügt.

Und beim Kompilieren und Starten meines Programms gibt es auch keine Fehlermeldungen, die ClassNotFoundException kommt nur auf nicht-Mac-Systemen. Mich wundert nur, dass darauf nicht von NetBeans hingewiesen wurde? Vielleicht liegt das auch an der Mac-eigenen Java-Version...

Ich weiß jetzt zwar, wie ich bei NetBeans neue Libraries anlege und den Classpath zu diesen Bibliotheken festlege, aber ich weiß nicht, welche Jar-Dateien ich nun für diese Apple-Bibliothek auswählen soll geschweige denn, wo ich diese auf meiner Festplatte finde?


----------



## Daniel_L (3. Jan 2009)

Hier habe ich was gefunden, was ich aber nicht ganz verstehe, und hier ist ein Foren-Beitrag, der mir etwas einfacher umzusetzen erscheint.

Ich werde mal schauen, ob ich das hinkriege.


----------



## Daniel_L (3. Jan 2009)

So, ich denke ich bin einen Schritt weiter. Ich habe jetzt Folgendes:


```
try {
            Class appc = Class.forName("com.apple.eawt.Application");
            Object app = appc.newInstance();

            Class lc = Class.forName("com.apple.eawt.ApplicationListener");
            Object listener = Proxy.newProxyInstance(lc.getClassLoader(), new Class[] { lc }, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy,Method method,Object[] args) {
                    if (method.getName().equals("handleQuit")) {
                        ZettelkastenApp.getApplication().exit();
                    }
                    if (method.getName().equals("handlePreferences")) {
                        settingsWindow();
                    }
                    if (method.getName().equals("handleAbout")) {
                        showAboutBox();
                    }
                    return null;
                }
            });

            try {
                Method m = appc.getMethod("addApplicationListener", lc);
                m.invoke(app, listener);
            } catch (NoSuchMethodException ex) {
                ex.printStackTrace();
            } catch (SecurityException ex) {
                ex.printStackTrace();
            } catch (InvocationTargetException ex) {
                ex.printStackTrace();
            }
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        catch (InstantiationException e) {
            e.printStackTrace();
        }
```

Das scheint auch soweit zu funktionieren. Zwei Fragen habe ich aber noch:

1.) Bei _ublic Object invoke_ wird ein Object als Rückgabewert verlangt. Da ich nicht wusste, was ich zurückgeben sollte, habe ich einfach Null zurückgegeben. Meine Methoden im Listener werden ja trotzdem ausgeführt. Was wäre hier ein "korrekter" Rückgabe-Wert?

2.) In meiner bisherigen Variante hatte ich folgenden Code, der dazu führte, dass auch ein Einstellungs-Menü gezeigt wurde, was jetzt nicht mehr der Fall ist:

```
final com.apple.eawt.Application application = com.apple.eawt.Application.getApplication();
            // damit ein about-Menü erscheint
            application.setEnabledAboutMenu(true);
            // preference pane
            application.addPreferencesMenuItem();
            application.setEnabledPreferencesMenu(true);

            application.addApplicationListener(new com.apple.eawt.ApplicationListener() {
            ...
```

Kann mir jemand verraten, wie ich das PreferencesMenu wieder sichtbar kriege, welchen Code ich also im ersten Code-Beispiel einfügen muss (bei dem Classloader)?

Vielen Dank und Gruß
Daniel


----------



## Daniel_L (10. Jan 2009)

Hab's herausgefunden. Dies ist jetzt die Lösung, um auf Mac das Apple-Menü zu nutzen, sodass aber das Java-Programm auch auf anderen Plattformen startet:


```
/**
     * This is an application listener that is initialised when running the program
     * on mac os x. by using this appListener, we can use the typical apple-menu bar
     * which provides own about, preferences and quit-menu-items.
     */
    private void setupMacOSXApplicationListener() {
        try {
            // get mac os-x application class
            Class appc = Class.forName("com.apple.eawt.Application");
            // create a new instance for it.
            Object app = appc.newInstance();
            
            // get the application-listener class. here we can set our action to the apple menu
            Class lc = Class.forName("com.apple.eawt.ApplicationListener");
            Object listener = Proxy.newProxyInstance(lc.getClassLoader(), new Class[] { lc }, new InvocationHandler() {
                @Override
                public Object invoke(Object proxy,Method method,Object[] args) {
                    if (method.getName().equals("handleQuit")) {
                        // call the general exit-handler from the desktop-application-api
                        // here we do all the stuff we need when exiting the application
                        ZettelkastenApp.getApplication().exit();
                    }
                    if (method.getName().equals("handlePreferences")) {
                        // show settings window
                        settingsWindow();
                    }
                    if (method.getName().equals("handleAbout")) {
                        // show own aboutbox
                        showAboutBox();
                        // set handled to true, so other actions won't take place any more.
                        // if we leace this out, a second, system-own aboutbox would be displayed
                        com.apple.eawt.ApplicationEvent ae = (ApplicationEvent) args[0];
                        ae.setHandled(true);
                    }
                    return null;
                }
            });

            try {
                // add application listener that listens to actions on the apple menu items
                Method m = appc.getMethod("addApplicationListener", lc);
                m.invoke(app, listener);
                // register that we want that Preferences menu. by default, only the about box is shown
                // but no pref-menu-item
                Method enablePreferenceMethod = appc.getMethod("setEnabledPreferencesMenu", new Class[] {boolean.class});
                enablePreferenceMethod.invoke(app, new Object[] {Boolean.TRUE});
            } catch (NoSuchMethodException ex) {
                ex.printStackTrace();
            } catch (SecurityException ex) {
                ex.printStackTrace();
            } catch (InvocationTargetException ex) {
                ex.printStackTrace();
            }
        }
        catch (ClassNotFoundException e) {
            e.printStackTrace();
        }
        catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        catch (InstantiationException e) {
            e.printStackTrace();
        }
    }
```

Siehe dazu auch diese Seite

Gruß
Daniel


----------

