mirror of
https://github.com/LuckPerms/LuckPerms.git
synced 2024-09-09 12:38:01 +02: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;
|
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 {
|
|
||||||
private final Connection connection;
|
|
||||||
|
|
||||||
Handler(Connection connection) {
|
|
||||||
this.connection = connection;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Object invoke(Object proxy, Method method, Object[] args) throws Throwable {
|
public final void close() throws SQLException {
|
||||||
// block calls directly to #close
|
// do nothing
|
||||||
if (method.getName().equals("close")) {
|
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// proxy calls to #shutdown to the real #close method
|
@Override
|
||||||
if (method.getName().equals("shutdown")) {
|
public final boolean isWrapperFor(Class<?> iface) throws SQLException {
|
||||||
this.connection.close();
|
return iface.isInstance(this.delegate) || this.delegate.isWrapperFor(iface);
|
||||||
return null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// delegate all other calls
|
@SuppressWarnings("unchecked")
|
||||||
return method.invoke(this.connection, args);
|
@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