JOOQ execution listener does not catch exception - spring

I'm trying to implement a generic solution for optimized locking. What I want to achieve is to have a specific piece of code run when record's version changes. I have it implemented as an ExecuteListener instance that looks for DataChangedException. It's registered as a Spring bean.
class LockingListener : DefaultExecuteListener() {
override fun exception(ctx: ExecuteContext) {
val exception = ctx.exception()
if (exception is DataChangedException) {
ctx.exception(IllegalStateException("Accessed data has been altered mid-operation."))
}
}
}
#Configuration
class JooqConfig {
#Bean
fun lockingListenerProvider() = DefaultExecuteListenerProvider(LockingListener())
}
I had a breakpoint set in org.jooq.impl.ExecuteListeners#get and it does look like it gets picked up alongside LoggerListener and JooqExceptionTranslator.
When I try to run a test case though, DataChangedException does not get picked up on UpdateableRecord#update and I get the following stacktrace instead, no IllegalStateException in sight.
org.jooq.exception.DataChangedException: Database record has been changed or doesn't exist any longer
at org.jooq.impl.UpdatableRecordImpl.checkIfChanged(UpdatableRecordImpl.java:540)
at org.jooq.impl.UpdatableRecordImpl.storeMergeOrUpdate0(UpdatableRecordImpl.java:349)
at org.jooq.impl.UpdatableRecordImpl.storeUpdate0(UpdatableRecordImpl.java:241)
at org.jooq.impl.UpdatableRecordImpl.access$100(UpdatableRecordImpl.java:89)
at org.jooq.impl.UpdatableRecordImpl$2.operate(UpdatableRecordImpl.java:232)
at org.jooq.impl.RecordDelegate.operate(RecordDelegate.java:149)
at org.jooq.impl.UpdatableRecordImpl.storeUpdate(UpdatableRecordImpl.java:228)
at org.jooq.impl.UpdatableRecordImpl.update(UpdatableRecordImpl.java:165)
Debugging shows that LockingListener#exception does not even get entered into.

That exception is not part of the ExecuteListener lifecycle, i.e. the lifecycle that deals with interactions with the JDBC API. In other words, it's not a SQLException, it happens higher up the stack. Use the RecordListener.exception() callback, instead.

Related

Jooq configuration per request

I'm struggling to find a way to define some settings in DSLContext per request.
What I want to achieve is the following:
I've got a springboot API and a database with multiple schemas that share the same structure.
Depending on some parameters of each request I want to connect to one specific schema, if no parameters is set I want to connect to no schema and fail.
To not connect to any schema I wrote the following:
#Autowired
public DefaultConfiguration defaultConfiguration;
#PostConstruct
public void init() {
Settings currentSettings = defaultConfiguration.settings();
Settings newSettings = currentSettings.withRenderSchema(false);
defaultConfiguration.setSettings(newSettings);
}
Which I think works fine.
Now I need a way to set schema in DSLContext per request, so everytime I use DSLContext during a request I get automatically a connection to that schema, without affecting other requests.
My idea is to intercept the request, get the parameters and do something like "DSLContext.setSchema()" but in a way that applies to all usage of DSLContext during the current request.
I tried to define a request scopeBean of a custom ConnectionProvider as follows:
#Component
#RequestScope
public class ScopeConnectionProvider implements ConnectionProvider {
#Override
public Connection acquire() throws DataAccessException {
try {
Connection connection = dataSource.getConnection();
String schemaName = getSchemaFromRequestContext();
connection.setSchema(schemaName);
return connection;
} catch (SQLException e) {
throw new DataAccessException("Error getting connection from data source " + dataSource, e);
}
}
#Override
public void release(Connection connection) throws DataAccessException {
try {
connection.setSchema(null);
connection.close();
} catch (SQLException e) {
throw new DataAccessException("Error closing connection " + connection, e);
}
}
}
But this code only executes on the first request. Following requests don't execute this code and hence it uses the schema of the first request.
Any tips on how can this be done?
Thank you
Seems like your request-scope bean is getting injected into a singleton.
You're already using #RequestScope which is good, but you could forget to add #EnableAspectJAutoProxy on your Spring configuration class.
#Configuration
#EnableAspectJAutoProxy
class Config {
}
This will make your bean run within a proxy inside of the singleton and therefore change per request.
Nevermind, It seems that the problem I was having was caused by an unexpected behaviour of some cacheable function I defined. The function is returning a value from the cache although the input is different, that's why no new connection is acquired. I still need to figure out what causes this unexpected behaviour thought.
For now, I'll stick with this approach since it seems fine at a conceptual level, although I expect there is a better way to do this.
*** UPDATE ***
I found out that this was the problem I had with the cache Does java spring caching break reflection?
*** UPDATE 2 ***
Seems that setting schema in the underlying datasource is ignored. I'm currently trying this other approach I just found (https://github.com/LinkedList/spring-jooq-multitenancy)

What is the most likely cause of exceptions mysteriously escaping a try-catch block in this case?

I am using a Spring WebClient in a Kotlin project like this:
data class DTO(val name: String)
#Component
class Runner: ApplicationRunner
{
override fun run(args: ApplicationArguments?)
{
try
{
val dto = get<DTO>()
}
catch (e: Exception)
{
println("ERROR, all exceptions should have been caught in 'get' ")
}
}
}
inline private fun<reified TResult: Any> get(): TResult?
{
var result: TResult? = null
try
{
result = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting")
.get()
.retrieve()
.bodyToMono<TResult>()
.block()
}
catch (e: Exception)
{
println("WORKS AS EXPECTED!!")
}
return result
}
The client will throw an exception, because the API will return a 404. However the exception is not caught where it should be, namely in the body of the get function, but it is propagated to the outer exception handler.
It is interesting to note that this happens only if the exception is thrown by the WebClient. If I replace the code in the try clause with a simple throw Exception("error"), the exception is caught where it should be.
Similarly, when I change the signature of get to a non-generic inline private fun get(): DTO? the problem also goes away.
For an exception to escape the try-catch block seems like a fundamental bug in the Kotlin tools. On the other hand, the fact that this happens only with the WebClient class indicates that this is a Spring problem. Or, it may be just me, using the tools in a wrong way.
I am really baffled here and have no idea how to proceed. Any ideas on why this might be happening are most welcome. Just for completeness, this is what it looks like in the debugger:
EDIT
The issue goes away after upgrading Spring Boot to 2.0.0.M6, it is still present in M5.
So it seems that this was a Spring issue and not a Kotlin issue. On the other hand it would be still nice to understand how a library that you include can seemingly cause the program to violate the laws of the programming language it is written in.
I tried the code with Spring Boot version 2.0.0.M5 and 2.0.0.M6, and it seems the behavior of the following block is different between those 2 versions:
result = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting")
.get()
.retrieve()
.bodyToMono<TResult>()
.block()
somewhere along the chain, on Spring Boot 2.0.0.M5, the WebClientResponseException is returned, on Spring Boot 2.0.0.M6 it is thrown.
If you add a e.printStackTrace() to your outer catch, you will notice that the stack trace is:
java.lang.ClassCastException:
org.springframework.web.reactive.function.client.WebClientResponseException
cannot be cast to com.example.demo.DTO at
com.example.demo.Runner.run(Test.kt:18) at
org.springframework.boot.SpringApplication.callRunner(SpringApplication.java:780)
at
org.springframework.boot.SpringApplication.callRunners(SpringApplication.java:770)
at
org.springframework.boot.SpringApplication.afterRefresh(SpringApplication.java:760)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:328)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:1245)
at
org.springframework.boot.SpringApplication.run(SpringApplication.java:1233)
at com.example.demo.DemoApplicationKt.main(DemoApplication.kt:10)
So, actually, problem is, the returned WebClientResponseException is tried to be cast to DTO class on the moment of return of the call val dto = get<DTO>(). This means that, when you assign result = ..., there is no type checking done yet. So, if you change your code to, for example, call get<Object>() instead of get<DTO>(), it won't hit any catch blocks.
If you convert it to bytecode in IntelliJ Idea, and then decompile it to Java, you can see this block:
public class Runner implements ApplicationRunner {
public void run(#Nullable ApplicationArguments args) {
try {
Object result$iv = null;
try {
ResponseSpec $receiver$iv$iv = WebClient.create("https://maps.googleapis.com/maps/api/nonexisting").get().retrieve();
Mono var10000 = $receiver$iv$iv.bodyToMono((ParameterizedTypeReference)(new Runner$run$$inlined$get$1()));
Intrinsics.checkExpressionValueIsNotNull(var10000, "bodyToMono(object : Para…zedTypeReference<T>() {})");
result$iv = var10000.block();
} catch (Exception var7) {
String var5 = "WORKS AS EXPECTED!!";
System.out.println(var5);
}
DTO var2 = (DTO)result$iv;
} catch (Exception var8) {
String var3 = "ERROR, all exceptions should have been caught in 'get' ";
System.out.println(var3);
}
}
}
Here you can notice that casting to DTO is done on the point of method return (which is not a return anymore because it is inlined), after the inner catch block: DTO var2 = (DTO)result$iv;. It seems like that's the behavior for the inlined methods with reified type parameters.
This is due to SPR-16025 (see related commit) since the Kotlin extension is using internally the ParameterizedTypeReference variant, which has been fixed in Spring Framework 5.0.1, and transitively in Spring Boot 2.0.0.M6.
Note than if you use bodyToMono(TResult::class.java) with Spring Boot 2.0.0.M5, it will works as expected.

Trouble in Using Quartz with spring webapp - Not able to see errors on cosole

I am using quartz within a Spring MVC application.
I have a task class, all functionality and logic is written there. I have a separate quartz configuration file. I am just hitting a URL and in controller function initializing the quartz conf file. The job is running fine. The issue I am facing is:
In my task class, some code is running and from a point of time. I am not getting it to run and I am not able to see any error or exception. Here is the code for my task class. I am able to run the getValues() function on a timely basis with quartz. The problem is it's printing the value hi and nothing else. It's not going in if nor else and neither is showing any error or exception.
public class TeamUpdateImpl implements TeamUpdate {
// #Autowired
ReadXmlDao readXmlDao;
public void setReadXmlDao(ReadXmlDao readXmlDao) {
this.readXmlDao = readXmlDao;
}
public void getValues() {
System.out.print("Hi");
if (readXmlDao.getName().equals("Hema")) {
System.out.print("if cond");
} else {
System.out.print("else cond");
}
}
}
Please suggest a solution, some logging thing or something so that I could get at least errors on my console to fix them.
Thanks.
I guess readXmlDao or readXmlDao.getName() is null.
Try to print it.
System.out.print("readXmlDao = "+readXmlDao);
System.out.print("readXmlDao.getName() = "+readXmlDao.getName());
You will get npe on printing readXmlDao.getName() if readXmlDao is null.
Also
Try to set #Autowired on setReadXmlDao method.
#Autowired
public void setReadXmlDao(ReadXmlDao readXmlDao) {
this.readXmlDao = readXmlDao;
}

Server-side schema validation with JAX-WS

I have JAX-WS container-less service (published via Endpoint.publish() right from main() method). I want my service to validate input messages. I have tried following annotation: #SchemaValidation(handler=MyErrorHandler.class) and implemented an appropriate class. When I start the service, I get the following:
Exception in thread "main" javax.xml.ws.WebServiceException:
Annotation #com.sun.xml.internal.ws.developer.SchemaValidation(outbound=true,
inbound=true, handler=class mypackage.MyErrorHandler) is not recognizable,
atleast one constructor of class
com.sun.xml.internal.ws.developer.SchemaValidationFeature
should be marked with #FeatureConstructor
I have found few solutions on the internet, all of them imply the use of WebLogic container. I can't use container in my case, I need embedded service. Can I still use schema validation?
The #SchemaValidation annotation is not defined in the JAX-WS spec, but validation is left open. This means you need something more than only the classes in the jdk.
As long as you are able to add some jars to your classpath, you can set this up pretty easily using metro (which is also included in WebLogic. This is why you find solutions that use WebLogic as container.). To be more precise, you need to add two jars to your classpath. I'd suggest to
download the most recent metro release.
Unzip it somewhere.
Add the jaxb-api.jar and jaxws-api.jar to your classpath. You can do this for example by putting them into the JAVA_HOME/lib/endorsed or by manually adding them to your project. This largely depends on the IDE or whatever you are using.
Once you have done this, your MyErrorHandler should work even if it is deployed via Endpoint.publish(). At least I have this setup locally and it compiles and works.
If you are not able to modify your classpath and need validation, you will have to validate the request manually using JAXB.
Old question, but I solved the problem using the correct package and minimal configuration, as well using only provided services from WebLogic. I was hitting the same problem as you.
Just make sure you use correct java type as I described here.
As I am planning to expand to a tracking mechanism I also implemented the custom error handler.
Web Service with custom validation handler
import com.sun.xml.ws.developer.SchemaValidation;
#Stateless
#WebService(portName="ValidatedService")
#SchemaValidation(handler=MyValidator.class)
public class ValidatedService {
public ValidatedResponse operation(#WebParam(name = "ValidatedRequest") ValidatedRequest request) {
/* do business logic */
return response;
}
}
Custom Handler to log and store error in database
public class MyValidator extends ValidationErrorHandler{
private static java.util.logging.Logger log = LoggingHelper.getServerLogger();
#Override
public void warning(SAXParseException exception) throws SAXException {
handleException(exception);
}
#Override
public void error(SAXParseException exception) throws SAXException {
handleException(exception);
}
#Override
public void fatalError(SAXParseException exception) throws SAXException {
handleException(exception);
}
private void handleException(SAXParseException e) throws SAXException {
log.log(Level.SEVERE, "Validation error", e);
// Record in database for tracking etc
throw e;
}
}

Controlling inner transaction settings from outer transaction with Spring 2.5

I'm using Spring 2.5 transaction management and I have the following set-up:
Bean1
#Transactional(noRollbackFor = { Exception.class })
public void execute() {
try {
bean2.execute();
} catch (Exception e) {
// persist failure in database (so the transaction shouldn't fail)
// the exception is not re-thrown
}
}
Bean2
#Transactional
public void execute() {
// do something which throws a RuntimeException
}
The failure is never persisted into DB from Bean1 because the whole transaction is rolled back.
I don't want to add noRollbackFor in Bean2 because it's used in a lot of places which don't have logic to handle runtime exceptions properly.
Is there a way to avoid my transaction to be rolled back only when Bean2.execute() is called from Bean1?
Otherwise, I guess my best option is to persist my failure within a new transaction? Anything else clean I can do?
This is one of the caveats of annotations... your class is not reusable!
If you'd configure your transactions in the XML, if would have been possible.
Assuming you use XML configuration: if it's not consuming expensive resources, you can create another instance of bean2 for the use of the code you specified. That is, you can configure one been as you specified above, and one with no roll back for exception.

Resources