Use a dynamically generated class instead of a reflection Proxy in NonClosableConnection

This commit is contained in:
Luck 2020-03-31 14:29:09 +01:00
parent 2028d65579
commit 9f0fe9e0cb
No known key found for this signature in database
GPG Key ID: EFA9B3EC5FD90F8B

View File

@ -25,15 +25,45 @@
package me.lucko.luckperms.common.storage.implementation.sql.connection.file; package me.lucko.luckperms.common.storage.implementation.sql.connection.file;
import java.lang.reflect.InvocationHandler; import net.bytebuddy.ByteBuddy;
import java.lang.reflect.Method; import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
import java.lang.reflect.Proxy; import net.bytebuddy.implementation.MethodDelegation;
import java.lang.invoke.MethodHandle;
import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.sql.Connection; import java.sql.Connection;
import java.sql.SQLException;
import static net.bytebuddy.matcher.ElementMatchers.isDeclaredBy;
import static net.bytebuddy.matcher.ElementMatchers.isFinal;
import static net.bytebuddy.matcher.ElementMatchers.not;
/** /**
* Represents a connection which cannot be closed using the standard {@link #close()} method. * A wrapper around a {@link Connection} which blocks usage of the default {@link #close()} method.
*/ */
public interface NonClosableConnection extends Connection { public abstract class NonClosableConnection implements Connection {
private static final MethodHandle CONSTRUCTOR;
static {
// construct an implementation of NonClosableConnection
Class<? extends NonClosableConnection> implClass = new ByteBuddy()
.subclass(NonClosableConnection.class, ConstructorStrategy.Default.IMITATE_SUPER_CLASS_OPENING)
.name(NonClosableConnection.class.getName() + "Impl")
.method(not(isFinal()).and(not(isDeclaredBy(Object.class))))
.intercept(MethodDelegation.toField("delegate"))
.make()
.load(NonClosableConnection.class.getClassLoader())
.getLoaded();
try {
CONSTRUCTOR = MethodHandles.publicLookup().in(implClass)
.findConstructor(implClass, MethodType.methodType(void.class, Connection.class))
.asType(MethodType.methodType(NonClosableConnection.class, Connection.class));
} catch (ReflectiveOperationException e) {
throw new ExceptionInInitializerError(e);
}
}
/** /**
* Creates a {@link NonClosableConnection} that delegates calls to the given {@link Connection}. * Creates a {@link NonClosableConnection} that delegates calls to the given {@link Connection}.
@ -42,40 +72,42 @@ public interface NonClosableConnection extends Connection {
* @return a non closable connection * @return a non closable connection
*/ */
static NonClosableConnection wrap(Connection connection) { static NonClosableConnection wrap(Connection connection) {
return (NonClosableConnection) Proxy.newProxyInstance( try {
NonClosableConnection.class.getClassLoader(), return (NonClosableConnection) CONSTRUCTOR.invokeExact(connection);
new Class[]{NonClosableConnection.class}, } catch (Throwable e) {
new Handler(connection) throw new RuntimeException(e);
); }
}
protected final Connection delegate;
protected NonClosableConnection(Connection delegate) {
this.delegate = delegate;
} }
/** /**
* Actually {@link #close() closes} the underlying connection. * Actually {@link #close() closes} the underlying connection.
*/ */
void shutdown(); public final void shutdown() throws SQLException {
this.delegate.close();
}
final class Handler implements InvocationHandler { @Override
private final Connection connection; public final void close() throws SQLException {
// do nothing
}
Handler(Connection connection) { @Override
this.connection = connection; public final boolean isWrapperFor(Class<?> iface) throws SQLException {
} return iface.isInstance(this.delegate) || this.delegate.isWrapperFor(iface);
}
@Override
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable { @SuppressWarnings("unchecked")
// block calls directly to #close @Override
if (method.getName().equals("close")) { public final <T> T unwrap(Class<T> iface) throws SQLException {
return null; if (iface.isInstance(this.delegate)) {
} return (T) this.delegate;
// proxy calls to #shutdown to the real #close method
if (method.getName().equals("shutdown")) {
this.connection.close();
return null;
}
// delegate all other calls
return method.invoke(this.connection, args);
} }
return this.delegate.unwrap(iface);
} }
} }