org.jdbi.v3.sqlobject.internal.SqlObjectInitData Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jdbi3-sqlobject Show documentation
Show all versions of jdbi3-sqlobject Show documentation
SqlObject is a declarative, annotation-driven API
for database access. It complements the core API.
Jdbi 3 is designed to provide convenient tabular data access in
Java(tm) and other JVM based languages.
It uses the Java collections framework for query results,
provides a convenient means of externalizing SQL statements, and
named parameter support for any database that supports JDBC.
/*
* Licensed 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.jdbi.v3.sqlobject.internal;
import java.lang.reflect.Method;
import java.util.Arrays;
import java.util.Map;
import java.util.concurrent.Callable;
import java.util.function.BiConsumer;
import java.util.function.Supplier;
import java.util.function.UnaryOperator;
import org.jdbi.v3.core.config.ConfigRegistry;
import org.jdbi.v3.core.extension.ExtensionMethod;
import org.jdbi.v3.core.extension.HandleSupplier;
import org.jdbi.v3.core.internal.Invocations;
import org.jdbi.v3.core.internal.MemoizingSupplier;
import org.jdbi.v3.core.internal.exceptions.Sneaky;
import org.jdbi.v3.sqlobject.GenerateSqlObject;
import org.jdbi.v3.sqlobject.Handler;
import org.jdbi.v3.sqlobject.SqlObject;
import org.jdbi.v3.sqlobject.UnableToCreateSqlObjectException;
public final class SqlObjectInitData {
private static final Object[] NO_ARGS = new Object[0];
public static final ThreadLocal INIT_DATA = new ThreadLocal<>();
private final boolean concrete;
private final Class> extensionType;
private final UnaryOperator instanceConfigurer;
private final Map> methodConfigurers;
private final Map methodHandlers;
public SqlObjectInitData(
Class> extensionType,
UnaryOperator instanceConfigurer,
Map> methodConfigurers,
Map methodHandlers) {
concrete = isConcrete(extensionType);
this.extensionType = extensionType;
this.instanceConfigurer = instanceConfigurer;
this.methodConfigurers = methodConfigurers;
this.methodHandlers = methodHandlers;
}
public static boolean isConcrete(Class> extensionType) {
return extensionType.getAnnotation(GenerateSqlObject.class) != null;
}
public static SqlObjectInitData initData() {
final SqlObjectInitData result = INIT_DATA.get();
if (result == null) {
throw new IllegalStateException("Implemented SqlObject types must be initialized by SqlObjectFactory");
}
return result;
}
public static Method lookupMethod(String methodName, Class>... parameterTypes) {
return lookupMethod(initData().extensionType, methodName, parameterTypes);
}
private static Method lookupMethod(Class> klass, String methodName, Class>... parameterTypes) {
try {
return klass.getMethod(methodName, parameterTypes);
} catch (NoSuchMethodException | SecurityException e) {
try {
return klass.getDeclaredMethod(methodName, parameterTypes);
} catch (Exception x) {
e.addSuppressed(x);
try {
return SqlObject.class.getMethod(methodName, parameterTypes);
} catch (Exception x2) {
e.addSuppressed(x2);
}
}
throw new IllegalStateException(
String.format("can't find %s#%s%s", klass.getName(), methodName, Arrays.asList(parameterTypes)), e);
}
}
public boolean isConcrete() {
return concrete;
}
public Class> extensionType() {
return extensionType;
}
public E instantiate(Class passExtensionType, HandleSupplier handle, ConfigRegistry instanceConfig) {
if (!extensionType.equals(passExtensionType)) {
throw new IllegalArgumentException("mismatch extension type");
}
try {
return Invocations.invokeWith(SqlObjectInitData.INIT_DATA, this, () ->
passExtensionType.cast(
Class.forName(extensionType.getPackage().getName() + "." + extensionType.getSimpleName() + "Impl")
.getConstructor(HandleSupplier.class, ConfigRegistry.class)
.newInstance(handle, instanceConfig)));
} catch (Exception | ExceptionInInitializerError e) {
throw new UnableToCreateSqlObjectException(e);
}
}
public ConfigRegistry configureInstance(ConfigRegistry config) {
return instanceConfigurer.apply(config);
}
public void forEachMethodHandler(BiConsumer action) {
methodHandlers.forEach(action);
}
public Supplier lazyInvoker(Object target, Method method, HandleSupplier handle, ConfigRegistry instanceConfig) {
return MemoizingSupplier.of(() -> {
ExtensionMethod extensionMethod = new ExtensionMethod(extensionType, method);
ConfigRegistry methodConfig = methodConfigurers.get(method).apply(instanceConfig.createCopy());
Handler methodHandler = methodHandlers.get(method);
methodHandler.warm(methodConfig);
return new InContextInvoker() {
@Override
public Object invoke(Object[] args) {
return call(() -> methodHandler.invoke(target, args == null ? NO_ARGS : args, handle));
}
@Override
public Object call(Callable> task) {
try {
return handle.invokeInContext(
extensionMethod,
methodConfig,
task);
} catch (Exception x) {
throw Sneaky.throwAnyway(x);
}
}
};
});
}
public interface InContextInvoker {
Object invoke(Object[] args);
Object call(Callable> task);
default Object call(Runnable task) { // NOPMD
return call(() -> {
task.run();
return null;
});
}
}
}