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 2021/07/29 13:30:26 UTC
[isis] branch master updated: ISIS-2829: provide a monkey-patch for
net.sf.cglib.core.ReflectUtils
This is an automated email from the ASF dual-hosted git repository.
ahuber pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/isis.git
The following commit(s) were added to refs/heads/master by this push:
new 4adadee ISIS-2829: provide a monkey-patch for net.sf.cglib.core.ReflectUtils
4adadee is described below
commit 4adadee39a11ffebb4420307c9679789cd46e636
Author: Andi Huber <ah...@apache.org>
AuthorDate: Thu Jul 29 15:30:12 2021 +0200
ISIS-2829: provide a monkey-patch for net.sf.cglib.core.ReflectUtils
---
viewers/wicket/viewer/pom.xml | 37 +-
.../main/java/net/sf/cglib/core/ReflectUtils.java | 592 +++++++++++++++++++++
.../wicket/viewer/integration/CglibTest.java | 35 ++
3 files changed, 660 insertions(+), 4 deletions(-)
diff --git a/viewers/wicket/viewer/pom.xml b/viewers/wicket/viewer/pom.xml
index 4468ece..0d4dc95 100644
--- a/viewers/wicket/viewer/pom.xml
+++ b/viewers/wicket/viewer/pom.xml
@@ -69,19 +69,44 @@
<groupId>org.ow2.asm</groupId>
<artifactId>asm</artifactId>
</exclusion>
- <!-- TODO[2112] exclusions might no longer be relevant -->
<exclusion>
<groupId>org.slf4j</groupId>
<artifactId>slf4j-api</artifactId>
</exclusion>
<exclusion>
- <!-- for Java7 compatibility (we don't use the proxying capability
- of this component) -->
<groupId>cglib</groupId>
<artifactId>cglib</artifactId>
</exclusion>
+ <exclusion>
+ <groupId>cglib</groupId>
+ <artifactId>cglib-nodep</artifactId>
+ </exclusion>
</exclusions>
</dependency>
+
+ <!-- Cglib patch START
+ as of 2021 Cglib seems inactive - while yet Wicket 8. and 9. rely on it
+ we provide a monkey-patch for net.sf.cglib.core.ReflectUtils so we can run on JVM 16+
+ no guarantees, that this works for all class-loaders and scenarios
+ provided as is - not worth any more effort - should be rather fixed by the Apache Wicket project
+ -->
+ <dependency>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ <version>3.3.0</version>
+ <exclusions>
+ <exclusion>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ </exclusion>
+ </exclusions>
+ </dependency>
+ <dependency>
+ <groupId>org.ow2.asm</groupId>
+ <artifactId>asm</artifactId>
+ <version>9.2</version>
+ </dependency>
+ <!-- Cglib patch END -->
<dependency>
<groupId>org.apache.wicket</groupId>
@@ -184,7 +209,11 @@
<groupId>junit</groupId>
<artifactId>junit-dep</artifactId>
</exclusion>
- </exclusions>
+ <exclusion>
+ <groupId>cglib</groupId>
+ <artifactId>cglib</artifactId>
+ </exclusion>
+ </exclusions>
</dependency>
</dependencies>
diff --git a/viewers/wicket/viewer/src/main/java/net/sf/cglib/core/ReflectUtils.java b/viewers/wicket/viewer/src/main/java/net/sf/cglib/core/ReflectUtils.java
new file mode 100644
index 0000000..a8e9da3
--- /dev/null
+++ b/viewers/wicket/viewer/src/main/java/net/sf/cglib/core/ReflectUtils.java
@@ -0,0 +1,592 @@
+/*
+ * 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 net.sf.cglib.core;
+
+import java.beans.*;
+import java.lang.reflect.*;
+import java.security.AccessController;
+import java.security.PrivilegedAction;
+import java.security.PrivilegedExceptionAction;
+import java.security.ProtectionDomain;
+import java.util.*;
+import org.objectweb.asm.Attribute;
+import org.objectweb.asm.Type;
+
+import lombok.extern.log4j.Log4j2;
+
+/**
+ * forked from https://github.com/cglib/cglib
+ * provided as monkey-patch
+ * <p>
+ * credits to https://github.com/rototor/cglib/commit/cc8632ed0930879b929b0d07e4ede5ac15103e9f
+ * @deprecated as of 2021 Cglib seems inactive - while yet Wicket 8. and 9. rely on it
+ */
+@Deprecated
+@Log4j2
+public class ReflectUtils {
+ private ReflectUtils() { }
+
+ private static final Map primitives = new HashMap(8);
+ private static final Map transforms = new HashMap(8);
+ private static final ClassLoader defaultLoader = ReflectUtils.class.getClassLoader();
+ private static Method DEFINE_CLASS, DEFINE_CLASS_UNSAFE, DEFINE_CLASS_LOOKUP;
+ private static Class mh;
+ private static Object lookupObject;
+ private static final ProtectionDomain PROTECTION_DOMAIN;
+ private static final Object UNSAFE;
+ private static final Throwable THROWABLE;
+
+ private static final List<Method> OBJECT_METHODS = new ArrayList<Method>();
+
+ static {
+
+ log.warn("initializing patched, yet unrealeased Cglib");
+
+ ProtectionDomain protectionDomain;
+ Method defineClass, defineClassUnsafe;
+ Object unsafe;
+ Throwable throwable = null;
+ try {
+ protectionDomain = getProtectionDomain(ReflectUtils.class);
+ try {
+ defineClass = (Method) AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ @Override
+ public Object run() throws Exception {
+ Class loader = Class.forName("java.lang.ClassLoader"); // JVM crash w/o this
+ Method defineClass = loader.getDeclaredMethod("defineClass",
+ new Class[]{ String.class,
+ byte[].class,
+ Integer.TYPE,
+ Integer.TYPE,
+ ProtectionDomain.class });
+ defineClass.setAccessible(true);
+ return defineClass;
+ }
+ });
+ defineClassUnsafe = null;
+ unsafe = null;
+ } catch (Throwable t) {
+ // Fallback on Jigsaw where this method is not available.
+ throwable = t;
+ defineClass = null;
+ try {
+ unsafe = AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ @Override
+ public Object run() throws Exception {
+ Class u = Class.forName("sun.misc.Unsafe");
+ Field theUnsafe = u.getDeclaredField("theUnsafe");
+ theUnsafe.setAccessible(true);
+ return theUnsafe.get(null);
+ }
+ });
+ Class u = Class.forName("sun.misc.Unsafe");
+ defineClassUnsafe = u.getMethod("defineClass",
+ new Class[]{ String.class,
+ byte[].class,
+ Integer.TYPE,
+ Integer.TYPE,
+ ClassLoader.class,
+ ProtectionDomain.class });
+ } catch(Throwable e) {
+ defineClassUnsafe = null;
+ unsafe = null;
+ mh = Class.forName("java.lang.invoke.MethodHandles");
+ lookupObject = mh.getMethod("lookup").invoke(null);
+ DEFINE_CLASS_LOOKUP = lookupObject.getClass().getDeclaredMethod("defineClass", byte[].class);
+ }
+ }
+ AccessController.doPrivileged(new PrivilegedExceptionAction() {
+ @Override
+ public Object run() throws Exception {
+ Method[] methods = Object.class.getDeclaredMethods();
+ for (Method method : methods) {
+ if ("finalize".equals(method.getName())
+ || (method.getModifiers() & (Modifier.FINAL | Modifier.STATIC)) > 0) {
+ continue;
+ }
+ OBJECT_METHODS.add(method);
+ }
+ return null;
+ }
+ });
+ } catch (Throwable t) {
+ if (throwable == null) {
+ throwable = t;
+ }
+ protectionDomain = null;
+ defineClass = null;
+ defineClassUnsafe = null;
+ unsafe = null;
+ }
+ PROTECTION_DOMAIN = protectionDomain;
+ DEFINE_CLASS = defineClass;
+ DEFINE_CLASS_UNSAFE = defineClassUnsafe;
+ UNSAFE = unsafe;
+ THROWABLE = throwable;
+ }
+
+ private static final String[] CGLIB_PACKAGES = {
+ "java.lang",
+ };
+
+ static {
+ primitives.put("byte", Byte.TYPE);
+ primitives.put("char", Character.TYPE);
+ primitives.put("double", Double.TYPE);
+ primitives.put("float", Float.TYPE);
+ primitives.put("int", Integer.TYPE);
+ primitives.put("long", Long.TYPE);
+ primitives.put("short", Short.TYPE);
+ primitives.put("boolean", Boolean.TYPE);
+
+ transforms.put("byte", "B");
+ transforms.put("char", "C");
+ transforms.put("double", "D");
+ transforms.put("float", "F");
+ transforms.put("int", "I");
+ transforms.put("long", "J");
+ transforms.put("short", "S");
+ transforms.put("boolean", "Z");
+ }
+
+ public static ProtectionDomain getProtectionDomain(final Class source) {
+ if(source == null) {
+ return null;
+ }
+ return (ProtectionDomain)AccessController.doPrivileged(new PrivilegedAction() {
+ @Override
+ public Object run() {
+ return source.getProtectionDomain();
+ }
+ });
+ }
+
+ public static Type[] getExceptionTypes(final Member member) {
+ if (member instanceof Method) {
+ return TypeUtils.getTypes(((Method)member).getExceptionTypes());
+ } else if (member instanceof Constructor) {
+ return TypeUtils.getTypes(((Constructor)member).getExceptionTypes());
+ } else {
+ throw new IllegalArgumentException("Cannot get exception types of a field");
+ }
+ }
+
+ public static Signature getSignature(final Member member) {
+ if (member instanceof Method) {
+ return new Signature(member.getName(), Type.getMethodDescriptor((Method)member));
+ } else if (member instanceof Constructor) {
+ Type[] types = TypeUtils.getTypes(((Constructor)member).getParameterTypes());
+ return new Signature(Constants.CONSTRUCTOR_NAME,
+ Type.getMethodDescriptor(Type.VOID_TYPE, types));
+
+ } else {
+ throw new IllegalArgumentException("Cannot get signature of a field");
+ }
+ }
+
+ public static Constructor findConstructor(final String desc) {
+ return findConstructor(desc, defaultLoader);
+ }
+
+ public static Constructor findConstructor(final String desc, final ClassLoader loader) {
+ try {
+ int lparen = desc.indexOf('(');
+ String className = desc.substring(0, lparen).trim();
+ return getClass(className, loader).getConstructor(parseTypes(desc, loader));
+ } catch (ClassNotFoundException e) {
+ throw new CodeGenerationException(e);
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ public static Method findMethod(final String desc) {
+ return findMethod(desc, defaultLoader);
+ }
+
+ public static Method findMethod(final String desc, final ClassLoader loader) {
+ try {
+ int lparen = desc.indexOf('(');
+ int dot = desc.lastIndexOf('.', lparen);
+ String className = desc.substring(0, dot).trim();
+ String methodName = desc.substring(dot + 1, lparen).trim();
+ return getClass(className, loader).getDeclaredMethod(methodName, parseTypes(desc, loader));
+ } catch (ClassNotFoundException e) {
+ throw new CodeGenerationException(e);
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ private static Class[] parseTypes(final String desc, final ClassLoader loader) throws ClassNotFoundException {
+ int lparen = desc.indexOf('(');
+ int rparen = desc.indexOf(')', lparen);
+ List params = new ArrayList();
+ int start = lparen + 1;
+ for (;;) {
+ int comma = desc.indexOf(',', start);
+ if (comma < 0) {
+ break;
+ }
+ params.add(desc.substring(start, comma).trim());
+ start = comma + 1;
+ }
+ if (start < rparen) {
+ params.add(desc.substring(start, rparen).trim());
+ }
+ Class[] types = new Class[params.size()];
+ for (int i = 0; i < types.length; i++) {
+ types[i] = getClass((String)params.get(i), loader);
+ }
+ return types;
+ }
+
+ private static Class getClass(final String className, final ClassLoader loader) throws ClassNotFoundException {
+ return getClass(className, loader, CGLIB_PACKAGES);
+ }
+
+ private static Class getClass(String className, final ClassLoader loader, final String[] packages) throws ClassNotFoundException {
+ String save = className;
+ int dimensions = 0;
+ int index = 0;
+ while ((index = className.indexOf("[]", index) + 1) > 0) {
+ dimensions++;
+ }
+ StringBuffer brackets = new StringBuffer(className.length() - dimensions);
+ for (int i = 0; i < dimensions; i++) {
+ brackets.append('[');
+ }
+ className = className.substring(0, className.length() - 2 * dimensions);
+
+ String prefix = (dimensions > 0) ? brackets + "L" : "";
+ String suffix = (dimensions > 0) ? ";" : "";
+ try {
+ return Class.forName(prefix + className + suffix, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ for (int i = 0; i < packages.length; i++) {
+ try {
+ return Class.forName(prefix + packages[i] + '.' + className + suffix, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ }
+ if (dimensions == 0) {
+ Class c = (Class)primitives.get(className);
+ if (c != null) {
+ return c;
+ }
+ } else {
+ String transform = (String)transforms.get(className);
+ if (transform != null) {
+ try {
+ return Class.forName(brackets + transform, false, loader);
+ } catch (ClassNotFoundException ignore) { }
+ }
+ }
+ throw new ClassNotFoundException(save);
+ }
+
+
+ public static Object newInstance(final Class type) {
+ return newInstance(type, Constants.EMPTY_CLASS_ARRAY, null);
+ }
+
+ public static Object newInstance(final Class type, final Class[] parameterTypes, final Object[] args) {
+ return newInstance(getConstructor(type, parameterTypes), args);
+ }
+
+ public static Object newInstance(final Constructor cstruct, final Object[] args) {
+
+ boolean flag = cstruct.isAccessible();
+ try {
+ if (!flag) {
+ cstruct.setAccessible(true);
+ }
+ Object result = cstruct.newInstance(args);
+ return result;
+ } catch (InstantiationException e) {
+ throw new CodeGenerationException(e);
+ } catch (IllegalAccessException e) {
+ throw new CodeGenerationException(e);
+ } catch (InvocationTargetException e) {
+ throw new CodeGenerationException(e.getTargetException());
+ } finally {
+ if (!flag) {
+ cstruct.setAccessible(flag);
+ }
+ }
+
+ }
+
+ public static Constructor getConstructor(final Class type, final Class[] parameterTypes) {
+ try {
+ Constructor constructor = type.getDeclaredConstructor(parameterTypes);
+ constructor.setAccessible(true);
+ return constructor;
+ } catch (NoSuchMethodException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+ public static String[] getNames(final Class[] classes)
+ {
+ if (classes == null)
+ return null;
+ String[] names = new String[classes.length];
+ for (int i = 0; i < names.length; i++) {
+ names[i] = classes[i].getName();
+ }
+ return names;
+ }
+
+ public static Class[] getClasses(final Object[] objects) {
+ Class[] classes = new Class[objects.length];
+ for (int i = 0; i < objects.length; i++) {
+ classes[i] = objects[i].getClass();
+ }
+ return classes;
+ }
+
+ public static Method findNewInstance(final Class iface) {
+ Method m = findInterfaceMethod(iface);
+ if (!m.getName().equals("newInstance")) {
+ throw new IllegalArgumentException(iface + " missing newInstance method");
+ }
+ return m;
+ }
+
+ public static Method[] getPropertyMethods(final PropertyDescriptor[] properties, final boolean read, final boolean write) {
+ Set methods = new HashSet();
+ for (int i = 0; i < properties.length; i++) {
+ PropertyDescriptor pd = properties[i];
+ if (read) {
+ methods.add(pd.getReadMethod());
+ }
+ if (write) {
+ methods.add(pd.getWriteMethod());
+ }
+ }
+ methods.remove(null);
+ return (Method[])methods.toArray(new Method[methods.size()]);
+ }
+
+ public static PropertyDescriptor[] getBeanProperties(final Class type) {
+ return getPropertiesHelper(type, true, true);
+ }
+
+ public static PropertyDescriptor[] getBeanGetters(final Class type) {
+ return getPropertiesHelper(type, true, false);
+ }
+
+ public static PropertyDescriptor[] getBeanSetters(final Class type) {
+ return getPropertiesHelper(type, false, true);
+ }
+
+ private static PropertyDescriptor[] getPropertiesHelper(final Class type, final boolean read, final boolean write) {
+ try {
+ BeanInfo info = Introspector.getBeanInfo(type, Object.class);
+ PropertyDescriptor[] all = info.getPropertyDescriptors();
+ if (read && write) {
+ return all;
+ }
+ List properties = new ArrayList(all.length);
+ for (int i = 0; i < all.length; i++) {
+ PropertyDescriptor pd = all[i];
+ if ((read && pd.getReadMethod() != null) ||
+ (write && pd.getWriteMethod() != null)) {
+ properties.add(pd);
+ }
+ }
+ return (PropertyDescriptor[])properties.toArray(new PropertyDescriptor[properties.size()]);
+ } catch (IntrospectionException e) {
+ throw new CodeGenerationException(e);
+ }
+ }
+
+
+
+ public static Method findDeclaredMethod(final Class type,
+ final String methodName, final Class[] parameterTypes)
+ throws NoSuchMethodException {
+
+ Class cl = type;
+ while (cl != null) {
+ try {
+ return cl.getDeclaredMethod(methodName, parameterTypes);
+ } catch (NoSuchMethodException e) {
+ cl = cl.getSuperclass();
+ }
+ }
+ throw new NoSuchMethodException(methodName);
+
+ }
+
+ public static List addAllMethods(final Class type, final List list) {
+
+
+ if (type == Object.class) {
+ list.addAll(OBJECT_METHODS);
+ } else
+ list.addAll(java.util.Arrays.asList(type.getDeclaredMethods()));
+
+ Class superclass = type.getSuperclass();
+ if (superclass != null) {
+ addAllMethods(superclass, list);
+ }
+ Class[] interfaces = type.getInterfaces();
+ for (int i = 0; i < interfaces.length; i++) {
+ addAllMethods(interfaces[i], list);
+ }
+
+ return list;
+ }
+
+ public static List addAllInterfaces(final Class type, final List list) {
+ Class superclass = type.getSuperclass();
+ if (superclass != null) {
+ list.addAll(Arrays.asList(type.getInterfaces()));
+ addAllInterfaces(superclass, list);
+ }
+ return list;
+ }
+
+
+ public static Method findInterfaceMethod(final Class iface) {
+ if (!iface.isInterface()) {
+ throw new IllegalArgumentException(iface + " is not an interface");
+ }
+ Method[] methods = iface.getDeclaredMethods();
+ if (methods.length != 1) {
+ throw new IllegalArgumentException("expecting exactly 1 method in " + iface);
+ }
+ return methods[0];
+ }
+
+ public static Class defineClass(final String className, final byte[] b, final ClassLoader loader) throws Exception {
+ return defineClass(className, b, loader, PROTECTION_DOMAIN);
+ }
+
+ public static Class defineClass(final String className, final byte[] b, final ClassLoader loader, final ProtectionDomain protectionDomain) throws Exception {
+ Class c;
+ if (DEFINE_CLASS != null) {
+ Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), protectionDomain };
+ c = (Class)DEFINE_CLASS.invoke(loader, args);
+ } else if (DEFINE_CLASS_UNSAFE != null) {
+ Object[] args = new Object[]{className, b, new Integer(0), new Integer(b.length), loader, protectionDomain };
+ c = (Class)DEFINE_CLASS_UNSAFE.invoke(UNSAFE, args);
+ } else if (DEFINE_CLASS_LOOKUP != null) {
+ Object[] args = new Object[]{b};
+ try {
+ org.objectweb.asm.ClassReader reader = new org.objectweb.asm.ClassReader(b);
+ String clsName = reader.getClassName().replace("/",".");
+ Class packageClass = loader.loadClass(clsName.substring(0,clsName.indexOf("$$")).replace("Wicket_Proxy_",""));
+ Object privateLookup = mh.getMethod("privateLookupIn", Class.class, lookupObject.getClass()).invoke(null, new Object[]{packageClass,lookupObject});
+ c = (Class)DEFINE_CLASS_LOOKUP.invoke(privateLookup, args);
+ }catch(Throwable t) {
+ t.printStackTrace();
+ throw new CodeGenerationException(t);
+ }
+ } else {
+ throw new CodeGenerationException(THROWABLE);
+ }
+ // Force static initializers to run.
+ Class.forName(className, true, loader);
+ return c;
+ }
+
+ public static int findPackageProtected(final Class[] classes) {
+ for (int i = 0; i < classes.length; i++) {
+ if (!Modifier.isPublic(classes[i].getModifiers())) {
+ return i;
+ }
+ }
+ return 0;
+ }
+
+ public static MethodInfo getMethodInfo(final Member member, final int modifiers) {
+ final Signature sig = getSignature(member);
+ return new MethodInfo() {
+ private ClassInfo ci;
+ @Override
+ public ClassInfo getClassInfo() {
+ if (ci == null)
+ ci = ReflectUtils.getClassInfo(member.getDeclaringClass());
+ return ci;
+ }
+ @Override
+ public int getModifiers() {
+ return modifiers;
+ }
+ @Override
+ public Signature getSignature() {
+ return sig;
+ }
+ @Override
+ public Type[] getExceptionTypes() {
+ return ReflectUtils.getExceptionTypes(member);
+ }
+ public Attribute getAttribute() {
+ return null;
+ }
+ };
+ }
+
+ public static MethodInfo getMethodInfo(final Member member) {
+ return getMethodInfo(member, member.getModifiers());
+ }
+
+ public static ClassInfo getClassInfo(final Class clazz) {
+ final Type type = Type.getType(clazz);
+ final Type sc = (clazz.getSuperclass() == null) ? null : Type.getType(clazz.getSuperclass());
+ return new ClassInfo() {
+ @Override
+ public Type getType() {
+ return type;
+ }
+ @Override
+ public Type getSuperType() {
+ return sc;
+ }
+ @Override
+ public Type[] getInterfaces() {
+ return TypeUtils.getTypes(clazz.getInterfaces());
+ }
+ @Override
+ public int getModifiers() {
+ return clazz.getModifiers();
+ }
+ };
+ }
+
+ // used by MethodInterceptorGenerated generated code
+ public static Method[] findMethods(final String[] namesAndDescriptors, final Method[] methods)
+ {
+ Map map = new HashMap();
+ for (int i = 0; i < methods.length; i++) {
+ Method method = methods[i];
+ map.put(method.getName() + Type.getMethodDescriptor(method), method);
+ }
+ Method[] result = new Method[namesAndDescriptors.length / 2];
+ for (int i = 0; i < result.length; i++) {
+ result[i] = (Method)map.get(namesAndDescriptors[i * 2] + namesAndDescriptors[i * 2 + 1]);
+ if (result[i] == null) {
+ // TODO: error?
+ }
+ }
+ return result;
+ }
+}
diff --git a/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/CglibTest.java b/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/CglibTest.java
new file mode 100644
index 0000000..88e6b0e
--- /dev/null
+++ b/viewers/wicket/viewer/src/test/java/org/apache/isis/viewer/wicket/viewer/integration/CglibTest.java
@@ -0,0 +1,35 @@
+/*
+ * 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.viewer.wicket.viewer.integration;
+
+import org.junit.jupiter.api.Test;
+
+class CglibTest {
+
+ /**
+ * particularly useful if run on the JVM of interest
+ */
+ @Test
+ void canInitCglib() {
+ // expected not to throw
+ new net.sf.cglib.proxy.Enhancer();
+ net.sf.cglib.core.ReflectUtils.getClassInfo(this.getClass());
+ }
+
+}