Remove optional dependency on javassist

Motivation:

We shipped a javassist based implementation for typematching and logged a confusing debug message about missing javassist. We never were able to prove it really gives any perf improvements so we should just remove it.

Modifications:

- Remove javassist dependency and impl
- Fix possible classloader deadlock as reported by intellij

Result:

Less code to maintain and less confusing log message.
This commit is contained in:
Norman Maurer 2017-02-21 20:33:11 +01:00
parent 77e65fe6bb
commit 7d08b4fc35
4 changed files with 8 additions and 164 deletions

View File

@ -37,13 +37,6 @@
</properties> </properties>
<dependencies> <dependencies>
<!-- Byte code generator - completely optional -->
<dependency>
<groupId>org.javassist</groupId>
<artifactId>javassist</artifactId>
<scope>compile</scope> <!-- override the 'test' scope defined at parent pom.xml -->
<optional>true</optional>
</dependency>
<dependency> <dependency>
<groupId>org.jctools</groupId> <groupId>org.jctools</groupId>
<artifactId>jctools-core</artifactId> <artifactId>jctools-core</artifactId>

View File

@ -1,103 +0,0 @@
/*
* Copyright 2013 The Netty Project
*
* The Netty Project 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 io.netty.util.internal;
import io.netty.util.internal.logging.InternalLogger;
import io.netty.util.internal.logging.InternalLoggerFactory;
import javassist.ClassClassPath;
import javassist.ClassPath;
import javassist.ClassPool;
import javassist.CtClass;
import javassist.Modifier;
import javassist.NotFoundException;
import java.lang.reflect.Method;
public final class JavassistTypeParameterMatcherGenerator {
private static final InternalLogger logger =
InternalLoggerFactory.getInstance(JavassistTypeParameterMatcherGenerator.class);
private static final ClassPool classPool = new ClassPool(true);
static {
classPool.appendClassPath(new ClassClassPath(NoOpTypeParameterMatcher.class));
}
public static void appendClassPath(ClassPath classpath) {
classPool.appendClassPath(classpath);
}
public static void appendClassPath(String pathname) throws NotFoundException {
classPool.appendClassPath(pathname);
}
public static ClassPool classPool() {
return classPool;
}
public static TypeParameterMatcher generate(Class<?> type) {
ClassLoader classLoader = PlatformDependent.getContextClassLoader();
if (classLoader == null) {
classLoader = PlatformDependent.getSystemClassLoader();
}
return generate(type, classLoader);
}
public static TypeParameterMatcher generate(Class<?> type, ClassLoader classLoader) {
final String typeName = typeName(type);
final String className = "io.netty.util.internal.__matchers__." + typeName + "Matcher";
try {
try {
return (TypeParameterMatcher) Class.forName(className, true, classLoader).newInstance();
} catch (Exception e) {
// Not defined in the specified class loader.
}
CtClass c = classPool.getAndRename(NoOpTypeParameterMatcher.class.getName(), className);
c.setModifiers(c.getModifiers() | Modifier.FINAL);
c.getDeclaredMethod("match").setBody("{ return $1 instanceof " + typeName + "; }");
byte[] byteCode = c.toBytecode();
c.detach();
Method method = ClassLoader.class.getDeclaredMethod(
"defineClass", String.class, byte[].class, int.class, int.class);
method.setAccessible(true);
Class<?> generated = (Class<?>) method.invoke(classLoader, className, byteCode, 0, byteCode.length);
if (type != Object.class) {
logger.debug("Generated: {}", generated.getName());
} else {
// Object.class is only used when checking if Javassist is available.
}
return (TypeParameterMatcher) generated.newInstance();
} catch (RuntimeException e) {
throw e;
} catch (Exception e) {
throw new RuntimeException(e);
}
}
private static String typeName(Class<?> type) {
if (type.isArray()) {
return typeName(type.getComponentType()) + "[]";
}
return type.getName();
}
private JavassistTypeParameterMatcherGenerator() { }
}

View File

@ -98,8 +98,6 @@ public final class PlatformDependent {
private static final long BYTE_ARRAY_BASE_OFFSET = byteArrayBaseOffset0(); private static final long BYTE_ARRAY_BASE_OFFSET = byteArrayBaseOffset0();
private static final boolean HAS_JAVASSIST = hasJavassist0();
private static final File TMPDIR = tmpdir0(); private static final File TMPDIR = tmpdir0();
private static final int BIT_MODE = bitMode0(); private static final int BIT_MODE = bitMode0();
@ -243,13 +241,6 @@ public final class PlatformDependent {
return MAX_DIRECT_MEMORY; return MAX_DIRECT_MEMORY;
} }
/**
* Returns {@code true} if and only if Javassist is available.
*/
public static boolean hasJavassist() {
return HAS_JAVASSIST;
}
/** /**
* Returns the temporary directory. * Returns the temporary directory.
*/ */
@ -1174,33 +1165,6 @@ public final class PlatformDependent {
return maxDirectMemory; return maxDirectMemory;
} }
private static boolean hasJavassist0() {
if (isAndroid()) {
return false;
}
boolean noJavassist = SystemPropertyUtil.getBoolean("io.netty.noJavassist", false);
logger.debug("-Dio.netty.noJavassist: {}", noJavassist);
if (noJavassist) {
logger.debug("Javassist: unavailable (io.netty.noJavassist)");
return false;
}
try {
JavassistTypeParameterMatcherGenerator.generate(Object.class, getClassLoader(PlatformDependent.class));
logger.debug("Javassist: available");
return true;
} catch (Throwable t) {
// Failed to generate a Javassist-based matcher.
logger.debug("Javassist: unavailable");
logger.debug(
"You don't have Javassist in your class path or you don't have enough permission " +
"to load dynamically generated classes. Please check the configuration for better performance.");
return false;
}
}
private static File tmpdir0() { private static File tmpdir0() {
File f; File f;
try { try {

View File

@ -26,8 +26,12 @@ import java.util.Map;
public abstract class TypeParameterMatcher { public abstract class TypeParameterMatcher {
private static final TypeParameterMatcher NOOP = new NoOpTypeParameterMatcher(); private static final TypeParameterMatcher NOOP = new TypeParameterMatcher() {
private static final Object TEST_OBJECT = new Object(); @Override
public boolean match(Object msg) {
return true;
}
};
public static TypeParameterMatcher get(final Class<?> parameterType) { public static TypeParameterMatcher get(final Class<?> parameterType) {
final Map<Class<?>, TypeParameterMatcher> getCache = final Map<Class<?>, TypeParameterMatcher> getCache =
@ -37,23 +41,9 @@ public abstract class TypeParameterMatcher {
if (matcher == null) { if (matcher == null) {
if (parameterType == Object.class) { if (parameterType == Object.class) {
matcher = NOOP; matcher = NOOP;
} else if (PlatformDependent.hasJavassist()) { } else {
try {
matcher = JavassistTypeParameterMatcherGenerator.generate(parameterType);
matcher.match(TEST_OBJECT);
} catch (IllegalAccessError e) {
// Happens if parameterType is not public.
matcher = null;
} catch (Exception e) {
// Will not usually happen, but just in case.
matcher = null;
}
}
if (matcher == null) {
matcher = new ReflectiveMatcher(parameterType); matcher = new ReflectiveMatcher(parameterType);
} }
getCache.put(parameterType, matcher); getCache.put(parameterType, matcher);
} }
@ -172,5 +162,5 @@ public abstract class TypeParameterMatcher {
} }
} }
protected TypeParameterMatcher() { } TypeParameterMatcher() { }
} }