You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@isis.apache.org by ah...@apache.org on 2018/10/28 15:44:05 UTC

[isis] branch v2 updated: ISIS-2026: Implementing a byte-buddy class-loading-strategy that

This is an automated email from the ASF dual-hosted git repository.

ahuber pushed a commit to branch v2
in repository https://gitbox.apache.org/repos/asf/isis.git


The following commit(s) were added to refs/heads/v2 by this push:
     new ac7da8e  ISIS-2026: Implementing a byte-buddy class-loading-strategy that
ac7da8e is described below

commit ac7da8e0e1d356c2772854efe0186d50ba27b31b
Author: Andi Huber <ah...@apache.org>
AuthorDate: Sun Oct 28 16:43:58 2018 +0100

    ISIS-2026: Implementing a byte-buddy class-loading-strategy that
    
    allows to run byte-buddy 1.9.2 on both JDK 8 and JDK 9+ runtimes
    
    Task-Url: https://issues.apache.org/jira/browse/ISIS-2026
---
 .../ImposteriserTestUsingCodegenPlugin.java        | 24 +++---
 .../codegen/ClassLoadingStrategyAdvisor.java       | 90 ++++++++++++++++++++++
 .../codegen/ProxyFactoryPluginUsingByteBuddy.java  | 26 ++++---
 .../services/i18n/po/TranslationServicePo.java     |  4 +-
 .../runtime/services/i18n/po/PoReaderTest.java     |  5 +-
 5 files changed, 122 insertions(+), 27 deletions(-)

diff --git a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/ImposteriserTestUsingCodegenPlugin.java b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/ImposteriserTestUsingCodegenPlugin.java
index 7c99042..2e5dadd 100644
--- a/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/ImposteriserTestUsingCodegenPlugin.java
+++ b/core/detached-tests/src/test/java/org/apache/isis/core/unittestsupport/jmocking/ImposteriserTestUsingCodegenPlugin.java
@@ -37,10 +37,8 @@ import org.junit.Assert;
 import org.junit.Before;
 import org.junit.Test;
 
-import org.apache.isis.applib.services.i18n.TranslationsResolver;
 import org.apache.isis.commons.internal.context._Context;
 import org.apache.isis.core.plugins.codegen.ProxyFactoryPlugin;
-import org.apache.isis.core.runtime.services.i18n.po.TranslationServicePo;
 
 import static org.junit.Assert.assertEquals;
 import static org.junit.Assert.assertFalse;
@@ -99,6 +97,13 @@ public class ImposteriserTestUsingCodegenPlugin {
         imposter.toString();
     }
     
+    // class we want to mock, while making sure, that we have access to non public fields 
+    static class NonPublicMethodStub {
+        Integer getInteger() {
+            return 1;
+        }
+    }
+    
     @Test
     public void imposteriserShouldBeUsableForMockery() {
         
@@ -108,17 +113,16 @@ public class ImposteriserTestUsingCodegenPlugin {
             }
         };
         
-        // using TranslationService here is just arbitrary, can be replaced any time ...
-        final TranslationServicePo mockTranslationServicePo = context.mock(TranslationServicePo.class);
-        final TranslationsResolver mockTranslationsResolver = context.mock(TranslationsResolver.class);
-        
+        final NonPublicMethodStub mocked = context.mock(NonPublicMethodStub.class);
+                
         context.checking(new Expectations() {{
-            allowing(mockTranslationServicePo).getTranslationsResolver();
-            will(returnValue(mockTranslationsResolver));
+            allowing(mocked).getInteger();
+            will(returnValue(Integer.valueOf(2)));
         }});
         
-        Assert.assertNotNull(mockTranslationServicePo);
-        Assert.assertNotNull(mockTranslationServicePo.getTranslationsResolver());
+        Assert.assertNotNull(mocked);
+        Assert.assertNotNull(mocked.getInteger());
+        Assert.assertEquals(2, mocked.getInteger().intValue());
     }
 
     // //////////////////////////////////////
diff --git a/core/plugins/codegen-bytebuddy/src/main/java/org/apache/isis/core/plugins/codegen/ClassLoadingStrategyAdvisor.java b/core/plugins/codegen-bytebuddy/src/main/java/org/apache/isis/core/plugins/codegen/ClassLoadingStrategyAdvisor.java
new file mode 100644
index 0000000..f9e2e61
--- /dev/null
+++ b/core/plugins/codegen-bytebuddy/src/main/java/org/apache/isis/core/plugins/codegen/ClassLoadingStrategyAdvisor.java
@@ -0,0 +1,90 @@
+/*
+ *  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.isis.core.plugins.codegen;
+
+import java.lang.invoke.MethodHandle;
+import java.lang.invoke.MethodHandles;
+import java.lang.reflect.Method;
+
+import net.bytebuddy.dynamic.loading.ClassInjector;
+import net.bytebuddy.dynamic.loading.ClassLoadingStrategy;
+
+/**
+ * package private utility to advise on the ClassLoadingStrategy, dependent on the JVM version we are running on
+ * 
+ * see <a href="https://mydailyjava.blogspot.com/2018/04/jdk-11-and-proxies-in-world-past.html">byte-buddy blog</a>
+ */
+class ClassLoadingStrategyAdvisor {
+    
+    private final MethodHandle privateLookupMethodHandle;
+
+    ClassLoadingStrategyAdvisor() {
+        this.privateLookupMethodHandle = createPrivateLookupMethodHandle();
+    }
+
+    public ClassLoadingStrategy<ClassLoader> getSuitableStrategy(final Class<?> targetClass) {
+
+        // JDK 9+
+        if (privateLookupMethodHandle!=null) {
+            
+            try {
+                Object privateLookup = privateLookupMethodHandle.invoke(targetClass, MethodHandles.lookup());
+                return ClassLoadingStrategy.UsingLookup.of(privateLookup);
+            } catch (Throwable e) {
+                throw new IllegalStateException(
+                        String.format("Failed to utilize code generation strategy on class '%s'", 
+                                targetClass.getName())
+                        , e);
+            }
+        }
+        
+        // JDK 8
+        return ClassLoadingStrategy.Default.INJECTION;
+
+    }
+    
+    // -- HELPER
+    
+    private MethodHandle createPrivateLookupMethodHandle() {
+        
+        // JDK 9+
+        if (ClassInjector.UsingLookup.isAvailable()) {
+            
+            try {
+                Class<?> methodHandles = java.lang.invoke.MethodHandles.class;
+                Method privateLookupIn = methodHandles.getMethod("privateLookupIn", 
+                        Class.class, 
+                        java.lang.invoke.MethodHandles.Lookup.class);
+                
+                MethodHandle mh = MethodHandles.publicLookup().unreflect(privateLookupIn);
+                return mh;
+            } catch (Exception e) {
+                throw new IllegalStateException("No code generation strategy available", e);
+            }
+        }
+        
+        // JDK 8
+        if (ClassInjector.UsingReflection.isAvailable()) {
+            return null;
+        }
+
+        throw new IllegalStateException("No code generation strategy available");
+    }
+
+
+
+}
diff --git a/core/plugins/codegen-bytebuddy/src/main/java/org/apache/isis/core/plugins/codegen/ProxyFactoryPluginUsingByteBuddy.java b/core/plugins/codegen-bytebuddy/src/main/java/org/apache/isis/core/plugins/codegen/ProxyFactoryPluginUsingByteBuddy.java
index a0b088c..21db918 100644
--- a/core/plugins/codegen-bytebuddy/src/main/java/org/apache/isis/core/plugins/codegen/ProxyFactoryPluginUsingByteBuddy.java
+++ b/core/plugins/codegen-bytebuddy/src/main/java/org/apache/isis/core/plugins/codegen/ProxyFactoryPluginUsingByteBuddy.java
@@ -37,17 +37,9 @@ import net.bytebuddy.implementation.InvocationHandlerAdapter;
 import net.bytebuddy.matcher.ElementMatchers;
 
 public class ProxyFactoryPluginUsingByteBuddy implements ProxyFactoryPlugin {
-
-    private static <T> ImplementationDefinition<T> nextProxyDef(
-            Class<T> base,
-            Class<?>[] interfaces) {
-        return new ByteBuddy()
-                .with(new NamingStrategy.SuffixingRandom("bb"))
-                .subclass(base)
-                .implement(interfaces)
-                .method(ElementMatchers.any());
-    }
-
+    
+    private final ClassLoadingStrategyAdvisor strategyAdvisor = new ClassLoadingStrategyAdvisor(); 
+    
     @Override
     public <T> ProxyFactory<T> factory(
             Class<T> base,
@@ -60,7 +52,7 @@ public class ProxyFactoryPluginUsingByteBuddy implements ProxyFactoryPlugin {
         nextProxyDef(base, interfaces)
         .intercept(InvocationHandlerAdapter.of(handler))
         .make()
-        .load(_Context.getDefaultClassLoader())
+        .load(_Context.getDefaultClassLoader(), strategyAdvisor.getSuitableStrategy(base))
         .getLoaded();
 
         return new ProxyFactory<T>() {
@@ -121,6 +113,16 @@ public class ProxyFactoryPluginUsingByteBuddy implements ProxyFactoryPlugin {
     }
 
     // -- HELPER
+    
+    private static <T> ImplementationDefinition<T> nextProxyDef(
+            Class<T> base,
+            Class<?>[] interfaces) {
+        return new ByteBuddy()
+                .with(new NamingStrategy.SuffixingRandom("bb"))
+                .subclass(base)
+                .implement(interfaces)
+                .method(ElementMatchers.any());
+    }
 
     private static void ensureSameSize(Class<?>[] a, Object[] b) {
         if(_NullSafe.size(a) != _NullSafe.size(b)) {
diff --git a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePo.java b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePo.java
index 48b8db0..e4480f9 100644
--- a/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePo.java
+++ b/core/runtime/src/main/java/org/apache/isis/core/runtime/services/i18n/po/TranslationServicePo.java
@@ -184,7 +184,7 @@ public class TranslationServicePo implements TranslationService {
     private TranslationsResolver translationsResolver;
 
     @Programmatic
-    public TranslationsResolver getTranslationsResolver() {
+    TranslationsResolver getTranslationsResolver() {
         return translationsResolver;
     }
 
@@ -192,7 +192,7 @@ public class TranslationServicePo implements TranslationService {
     private LocaleProvider localeProvider;
 
     @Programmatic
-    public LocaleProvider getLocaleProvider() {
+    LocaleProvider getLocaleProvider() {
         return localeProvider;
     }
 
diff --git a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/i18n/po/PoReaderTest.java b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/i18n/po/PoReaderTest.java
index 093bde5..b696742 100644
--- a/core/runtime/src/test/java/org/apache/isis/core/runtime/services/i18n/po/PoReaderTest.java
+++ b/core/runtime/src/test/java/org/apache/isis/core/runtime/services/i18n/po/PoReaderTest.java
@@ -62,9 +62,8 @@ public class PoReaderTest {
             will(returnValue(Locale.UK));
         }});
         
-        //[ahuber] with update of byte-buddy 1.8.0 -> 1.9.2, Apache Isis runs on JDK 11+, but
-        // it seems to no longer support mockery of non public methods, so we 
-        // explicitly test proper mockery here ...  
+        //[ahuber] with update of byte-buddy 1.8.0 -> 1.9.2, Apache Isis runs on JDK 11+, 
+        // we explicitly test proper mockery of non-public methods here ...  
         Assert.assertNotNull(mockTranslationServicePo.getLocaleProvider());
         Assert.assertNotNull(mockTranslationServicePo.getTranslationsResolver());
         Assert.assertNotNull(mockLocaleProvider.getLocale());