mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2025-02-05 23:21:22 +01:00
Use a dynamically generated class instead of a reflection Proxy in NonClosableConnection
This commit is contained in:
parent
2028d65579
commit
9f0fe9e0cb
@ -25,15 +25,45 @@
|
||||
|
||||
package me.lucko.luckperms.common.storage.implementation.sql.connection.file;
|
||||
|
||||
import java.lang.reflect.InvocationHandler;
|
||||
import java.lang.reflect.Method;
|
||||
import java.lang.reflect.Proxy;
|
||||
import net.bytebuddy.ByteBuddy;
|
||||
import net.bytebuddy.dynamic.scaffold.subclass.ConstructorStrategy;
|
||||
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.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}.
|
||||
@ -42,40 +72,42 @@ public interface NonClosableConnection extends Connection {
|
||||
* @return a non closable connection
|
||||
*/
|
||||
static NonClosableConnection wrap(Connection connection) {
|
||||
return (NonClosableConnection) Proxy.newProxyInstance(
|
||||
NonClosableConnection.class.getClassLoader(),
|
||||
new Class[]{NonClosableConnection.class},
|
||||
new Handler(connection)
|
||||
);
|
||||
try {
|
||||
return (NonClosableConnection) CONSTRUCTOR.invokeExact(connection);
|
||||
} catch (Throwable e) {
|
||||
throw new RuntimeException(e);
|
||||
}
|
||||
}
|
||||
|
||||
protected final Connection delegate;
|
||||
|
||||
protected NonClosableConnection(Connection delegate) {
|
||||
this.delegate = delegate;
|
||||
}
|
||||
|
||||
/**
|
||||
* Actually {@link #close() closes} the underlying connection.
|
||||
*/
|
||||
void shutdown();
|
||||
public final void shutdown() throws SQLException {
|
||||
this.delegate.close();
|
||||
}
|
||||
|
||||
final class Handler implements InvocationHandler {
|
||||
private final Connection connection;
|
||||
@Override
|
||||
public final void close() throws SQLException {
|
||||
// do nothing
|
||||
}
|
||||
|
||||
Handler(Connection connection) {
|
||||
this.connection = connection;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
||||
// block calls directly to #close
|
||||
if (method.getName().equals("close")) {
|
||||
return null;
|
||||
}
|
||||
|
||||
// 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);
|
||||
@Override
|
||||
public final boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||
return iface.isInstance(this.delegate) || this.delegate.isWrapperFor(iface);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
@Override
|
||||
public final <T> T unwrap(Class<T> iface) throws SQLException {
|
||||
if (iface.isInstance(this.delegate)) {
|
||||
return (T) this.delegate;
|
||||
}
|
||||
return this.delegate.unwrap(iface);
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user