You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cayenne.apache.org by nt...@apache.org on 2017/10/02 13:25:16 UTC

[2/3] cayenne git commit: Java 9 support (compile and runtime) - OSX Wrapper to dynamically use proper app handlers - remove usage of JDK internals

Java 9 support (compile and runtime)
 - OSX Wrapper to dynamically use proper app handlers
 - remove usage of JDK internals


Project: http://git-wip-us.apache.org/repos/asf/cayenne/repo
Commit: http://git-wip-us.apache.org/repos/asf/cayenne/commit/253887b2
Tree: http://git-wip-us.apache.org/repos/asf/cayenne/tree/253887b2
Diff: http://git-wip-us.apache.org/repos/asf/cayenne/diff/253887b2

Branch: refs/heads/master
Commit: 253887b2daa414fd2e89add8813de7cd2863ffff
Parents: 7ba05f3
Author: Nikita Timofeev <st...@gmail.com>
Authored: Mon Oct 2 16:21:31 2017 +0300
Committer: Nikita Timofeev <st...@gmail.com>
Committed: Mon Oct 2 16:21:31 2017 +0300

----------------------------------------------------------------------
 .../modeler/osx/OSXApplicationWrapper.java      | 149 +++++++++++++++++++
 .../modeler/osx/OSXPlatformInitializer.java     |  38 ++---
 .../modeler/osx/OSXQuitResponseWrapper.java     |  73 +++++++++
 .../components/image/FilteredIconFactory.java   |   5 +-
 4 files changed, 236 insertions(+), 29 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cayenne/blob/253887b2/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXApplicationWrapper.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXApplicationWrapper.java b/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXApplicationWrapper.java
new file mode 100644
index 0000000..f02bff5
--- /dev/null
+++ b/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXApplicationWrapper.java
@@ -0,0 +1,149 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.modeler.osx;
+
+import java.lang.reflect.InvocationHandler;
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+import java.lang.reflect.Proxy;
+import java.util.function.Consumer;
+
+import com.apple.eawt.Application;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * This class wraps apple {@link com.apple.eawt.Application} class and dynamically
+ * proxying it's lifecycle handlers setup.
+ * <p>
+ * This code exists to support both Java 8 and 9, as handler interfaces where incompatibly moved
+ * to other package between these versions.
+ * <p>
+ * See <a href="https://bugs.openjdk.java.net/browse/JDK-8160437">JDK-8160437 issue</a> for details.
+ *
+ * @see #setAboutHandler(Runnable) run action on "About App" menu item select
+ * @see #setPreferencesHandler(Runnable) run action on "Preferences..." menu item select
+ * @see #setQuitHandler(Consumer) run action on "Quit App" menu item select
+ *
+ * @see OSXQuitResponseWrapper
+ *
+ * @since 4.1
+ */
+public class OSXApplicationWrapper {
+
+    private static final Logger logger = LoggerFactory.getLogger(OSXApplicationWrapper.class);
+
+    // package for handler classes for Java 8 and older
+    private static final String JAVA8_PACKAGE = "com.apple.eawt.";
+
+    // package for handler classes for Java 9 and newer
+    private static final String JAVA9_PACKAGE = "java.awt.desktop.";
+
+    private final Application application;
+
+    private Class<?> aboutHandlerClass;
+    private Method setAboutHandler;
+
+    private Class<?> preferencesHandlerClass;
+    private Method setPreferencesHandler;
+
+    private Class<?> quitHandlerClass;
+    private Method setQuitHandler;
+
+    public OSXApplicationWrapper(Application application) {
+        this.application = application;
+        initMethods();
+    }
+
+    public void setPreferencesHandler(Runnable action) {
+        setHandler(setPreferencesHandler, preferencesHandlerClass, action);
+    }
+
+    public void setAboutHandler(Runnable action) {
+        setHandler(setAboutHandler, aboutHandlerClass, action);
+    }
+
+    public void setQuitHandler(Consumer<OSXQuitResponseWrapper> action) {
+        InvocationHandler handler = (proxy, method, args) -> {
+            // args: 0 - event, 1 - quitResponse
+            action.accept(new OSXQuitResponseWrapper(args[1]));
+            return null;
+        };
+        Object proxy = createProxy(quitHandlerClass, handler);
+        try {
+            setQuitHandler.invoke(application, proxy);
+        } catch (IllegalAccessException | InvocationTargetException ex) {
+            logger.warn("Unable to call " + setQuitHandler.getName(), ex);
+        }
+    }
+
+    /**
+     * Find required handlers' methods and classes
+     */
+    private void initMethods() {
+        aboutHandlerClass = getHandlerClass("AboutHandler");
+        setAboutHandler = getMethod("setAboutHandler", aboutHandlerClass);
+
+        preferencesHandlerClass = getHandlerClass("PreferencesHandler");
+        setPreferencesHandler = getMethod("setPreferencesHandler", preferencesHandlerClass);
+
+        quitHandlerClass = getHandlerClass("QuitHandler");
+        setQuitHandler = getMethod("setQuitHandler", quitHandlerClass);
+    }
+
+    private void setHandler(Method setMethod, Class<?> handlerClass, Runnable action) {
+        InvocationHandler handler = (proxy, method, args) -> {
+            action.run();
+            return null;
+        };
+        Object proxy = createProxy(handlerClass, handler);
+        try {
+            setMethod.invoke(application, proxy);
+        } catch (IllegalAccessException | InvocationTargetException ex) {
+            logger.warn("Unable to call " + setMethod.getName(), ex);
+        }
+    }
+
+    private Object createProxy(Class<?> handlerClass, InvocationHandler handler) {
+        return Proxy.newProxyInstance(OSXApplicationWrapper.class.getClassLoader(), new Class<?>[]{handlerClass}, handler);
+    }
+
+    private Method getMethod(String name, Class<?> ... parameters) {
+        try {
+            return application.getClass().getMethod(name, parameters);
+        } catch (NoSuchMethodException ex) {
+            logger.warn("Unable to find method " + name, ex);
+            return null;
+        }
+    }
+
+    private Class<?> getHandlerClass(String className) {
+        try {
+            return Class.forName(JAVA8_PACKAGE + className);
+        } catch (ClassNotFoundException ex) {
+            try {
+                return Class.forName(JAVA9_PACKAGE + className);
+            } catch (ClassNotFoundException ex2) {
+                return null;
+            }
+        }
+    }
+
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/253887b2/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXPlatformInitializer.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXPlatformInitializer.java b/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXPlatformInitializer.java
index fa47935..21c8805 100644
--- a/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXPlatformInitializer.java
+++ b/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXPlatformInitializer.java
@@ -40,12 +40,7 @@ import org.apache.cayenne.modeler.action.ConfigurePreferencesAction;
 import org.apache.cayenne.modeler.action.ExitAction;
 import org.apache.cayenne.modeler.init.platform.PlatformInitializer;
 
-import com.apple.eawt.AboutHandler;
-import com.apple.eawt.AppEvent;
 import com.apple.eawt.Application;
-import com.apple.eawt.PreferencesHandler;
-import com.apple.eawt.QuitHandler;
-import com.apple.eawt.QuitResponse;
 
 public class OSXPlatformInitializer implements PlatformInitializer {
 
@@ -58,27 +53,18 @@ public class OSXPlatformInitializer implements PlatformInitializer {
         overrideUIDefaults();
 
         // configure special Mac menu handlers
-        Application app = Application.getApplication();
-        app.setAboutHandler(new AboutHandler() {
-            @Override
-            public void handleAbout(AppEvent.AboutEvent aboutEvent) {
-                actionManager.getAction(AboutAction.class).showAboutDialog();
-            }
-        });
-
-        app.setPreferencesHandler(new PreferencesHandler() {
-            @Override
-            public void handlePreferences(AppEvent.PreferencesEvent preferencesEvent) {
-                actionManager.getAction(ConfigurePreferencesAction.class).showPreferencesDialog();
-            }
-        });
-
-        app.setQuitHandler(new QuitHandler() {
-            @Override
-            public void handleQuitRequestWith(AppEvent.QuitEvent quitEvent, QuitResponse quitResponse) {
-                if(!actionManager.getAction(ExitAction.class).exit()) {
-                    quitResponse.cancelQuit();
-                }
+        OSXApplicationWrapper wrapper = new OSXApplicationWrapper(Application.getApplication());
+        wrapper.setAboutHandler(()
+                -> actionManager.getAction(AboutAction.class).showAboutDialog());
+
+        wrapper.setPreferencesHandler(()
+                -> actionManager.getAction(ConfigurePreferencesAction.class).showPreferencesDialog());
+
+        wrapper.setQuitHandler(r -> {
+            if(!actionManager.getAction(ExitAction.class).exit()) {
+                r.cancelQuit();
+            } else {
+                r.performQuit();
             }
         });
     }

http://git-wip-us.apache.org/repos/asf/cayenne/blob/253887b2/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXQuitResponseWrapper.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXQuitResponseWrapper.java b/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXQuitResponseWrapper.java
new file mode 100644
index 0000000..f7e049b
--- /dev/null
+++ b/modeler/cayenne-modeler-mac-ext/src/main/java/org/apache/cayenne/modeler/osx/OSXQuitResponseWrapper.java
@@ -0,0 +1,73 @@
+/*****************************************************************
+ *   Licensed to the Apache Software Foundation (ASF) under one
+ *  or more contributor license agreements.  See the NOTICE file
+ *  distributed with this work for additional information
+ *  regarding copyright ownership.  The ASF licenses this file
+ *  to you under the Apache License, Version 2.0 (the
+ *  "License"); you may not use this file except in compliance
+ *  with the License.  You may obtain a copy of the License at
+ *
+ *    http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *  Unless required by applicable law or agreed to in writing,
+ *  software distributed under the License is distributed on an
+ *  "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ *  KIND, either express or implied.  See the License for the
+ *  specific language governing permissions and limitations
+ *  under the License.
+ ****************************************************************/
+
+package org.apache.cayenne.modeler.osx;
+
+import java.lang.reflect.InvocationTargetException;
+import java.lang.reflect.Method;
+
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Small wrapper around QuitResponse class that can reside in different packages:
+ * com.apple.eawt.QuitResponse in JDK 8 and java.awt.desktop.QuitResponse in JDK 9.
+ * Luckily it has same signature so we can dynamically resolve it's methods.
+ *
+ * @since 4.1
+ */
+public class OSXQuitResponseWrapper {
+
+    private static final Logger logger = LoggerFactory.getLogger(OSXQuitResponseWrapper.class);
+
+    private Method performQuit;
+
+    private Method cancelQuit;
+
+    private final Object quitResponse;
+
+    public OSXQuitResponseWrapper(Object quitResponse) {
+        this.quitResponse = quitResponse;
+        try {
+            performQuit = quitResponse.getClass().getMethod("performQuit");
+            cancelQuit = quitResponse.getClass().getMethod("cancelQuit");
+        } catch (NoSuchMethodException ex) {
+            logger.warn("Unable to find methods for quit response", ex);
+        }
+    }
+
+    public void performQuit() {
+        safePerform(performQuit);
+    }
+
+    public void cancelQuit() {
+        safePerform(cancelQuit);
+    }
+
+    private void safePerform(Method method) {
+        if(method == null) {
+            return;
+        }
+        try {
+            method.invoke(quitResponse);
+        } catch (IllegalAccessException | InvocationTargetException ex) {
+            logger.warn("Unable to call " + method.getName(), ex);
+        }
+    }
+}

http://git-wip-us.apache.org/repos/asf/cayenne/blob/253887b2/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/swing/components/image/FilteredIconFactory.java
----------------------------------------------------------------------
diff --git a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/swing/components/image/FilteredIconFactory.java b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/swing/components/image/FilteredIconFactory.java
index 4e92238..cb92b1c 100644
--- a/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/swing/components/image/FilteredIconFactory.java
+++ b/modeler/cayenne-modeler/src/main/java/org/apache/cayenne/swing/components/image/FilteredIconFactory.java
@@ -25,12 +25,11 @@ import java.awt.image.FilteredImageSource;
 import java.awt.image.ImageProducer;
 import java.awt.image.RGBImageFilter;
 import javax.swing.Icon;
+import javax.swing.ImageIcon;
 import javax.swing.JComponent;
 import javax.swing.JPanel;
 import javax.swing.UIManager;
 
-import sun.swing.ImageIconUIResource;
-
 /**
  * @since 4.0
  */
@@ -64,7 +63,7 @@ public class FilteredIconFactory {
             icon.paintIcon(DUMMY, img.getGraphics(), 0, 0);
             ImageProducer producer = new FilteredImageSource(img.getSource(), filterType.filter);
             Image resultImage = DUMMY.createImage(producer);
-            return new ImageIconUIResource(resultImage);
+            return new ImageIcon(resultImage);
         }
         return null;
     }