I was really pulling my hair out over some code that did programmatic transaction handling using Spring's PlatformTransactionManager on top of Hibernate. It is fairly complicated, with up to 4 different transactions running concurrently. At one point it needs to process 3 different result sets, like this:
TransactionStatus status1 = transactionManager.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW));
PreparedStatement statement1 = sessionFactory.getCurrentSession().connection().prepareStatement(...);
TransactionStatus status2 = transactionManager.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW));
PreparedStatement statement2 = sessionFactory.getCurrentSession().connection().prepareStatement(...);
TransactionStatus status3 = transactionManager.getTransaction(new DefaultTransactionDefinition(TransactionDefinition.PROPAGATION_REQUIRES_NEW));
PreparedStatement statement3 = sessionFactory.getCurrentSession().connection().prepareStatement(...);
... // set parameters on the statements
ResultSet rs1 = statement1.executeQuery();
ResultSet rs2 = statement1.executeQuery();
ResultSet rs3 = statement1.executeQuery();
... // process the result sets
... // close the result sets and statements
transactionManager.commit(status1);
transactionManager.commit(status2);
transactionManager.commit(status3);
This part of the code ran fine the first time, but the next time, it would throw an exception saying "Session is closed", when trying to get the first transaction. Do you see the problem? I didn't see it for way too long. I spent time removing sections of code, staring at logs, and pulling lots of hair out, before I finally noticed that in the spring logging, after finishing that section of code, it was falling back to trying to use the wrong session.
Finally it dawned on me that Spring must be using a stack to keep track of the current session. When a transaction is started with PROPAGATION_REQUIRES_NEW, the current session is pushed on the stack, and a new session is created and becomes the current session. When that transaction finishes, the current session is closed, and the previous session is popped off of the stack to resume as the now current session.
Looking at the documentation about transaction propagation, it does talk about "inner" and "outer" transactions, but I don't think it's quite explicit enough at explaining the nesting relationship. And my problem is that I wasn't really considering them to be nested, thinking of them more as simply independent transactions (in my defense, the docs do say that they are "completely independent" transactions). That is why it took me so long to realize my mistake.
My problem was that I was creating transaction 1, then transaction 2, then 3. But I was trying to commit transaction 1 first, then 2, then 3. Doing this messed up the stack, and in the end Spring was left with a session that had been closed as the current session. So the next attempt to use the session would cause an exception to be thrown telling me that the "Session is closed".
Rearranging the code to commit transaction 3 first, then 2, then 1, fixed the problem. I now have a better sense of how Spring works, working code, and less hair. I thought I'd write this up in case it helps someone else avoid this simple mistake.
mainly thoughts on: Database development, Web development, Anything else I'm working on
Showing posts with label spring. Show all posts
Showing posts with label spring. Show all posts
Wednesday, March 9, 2011
Wednesday, October 15, 2008
Spring AOP and @annotation pointcuts
I'm working on a Spring-AOP and annotation-based solution for caching web service requests. Here's the overview:
I have created an Annotation named "Cacheable", that you use like this:
@Cacheable(seconds=60)
public Data getData(int id)
{
...
}
Then using Spring's AOP functionality, I want to wrap every method that is annotated as @Cacheable in around advice that uses memcache to return cached results.
The problem I ran into was getting access to the seconds attribute of the annotation in the advice (for setting the cache timeout).
What I didn't understand, and wasn't clear to me from the Spring docs, was how to pass the annotation to the advice.
(Note: I'm using schema-based aop configuration)
Normally, if you are just trying to match methods that are annotated in a pointcut expression, you would make a pointcut definition with "@annotation(com.xyz.AnnotationName)". But what if you want to have access to the annotation in the advice? Your advice looks like this:
public Object aroundCacheable(ProceedingJoinPoint pjp, Cacheable cachable) throws Throwable
{
int timeout = cacheable.seconds();
...
}
Your pointcut expression has to specify what to pass as the cacheable parameter in the advice. So you have to modify the pointcut definition and add arg-names like this:
<aop:around pointcut="@annotation(cacheable)" method="aroundCacheable" arg-names="cacheable"/>
My understanding is that this tell Spring to look for an argument named cacheable in the advice method, and figure out the type from that. This seems a little strange because the pointcut definition is dependent on the advice. It seems like a pointcut should be self-contained, and not depend on how it is used. But maybe I'm missing something. I'll have to look into it more later, but for now, I'm just glad I got it working.
I have created an Annotation named "Cacheable", that you use like this:
@Cacheable(seconds=60)
public Data getData(int id)
{
...
}
Then using Spring's AOP functionality, I want to wrap every method that is annotated as @Cacheable in around advice that uses memcache to return cached results.
The problem I ran into was getting access to the seconds attribute of the annotation in the advice (for setting the cache timeout).
What I didn't understand, and wasn't clear to me from the Spring docs, was how to pass the annotation to the advice.
(Note: I'm using schema-based aop configuration)
Normally, if you are just trying to match methods that are annotated in a pointcut expression, you would make a pointcut definition with "@annotation(com.xyz.AnnotationName)". But what if you want to have access to the annotation in the advice? Your advice looks like this:
public Object aroundCacheable(ProceedingJoinPoint pjp, Cacheable cachable) throws Throwable
{
int timeout = cacheable.seconds();
...
}
Your pointcut expression has to specify what to pass as the cacheable parameter in the advice. So you have to modify the pointcut definition and add arg-names like this:
<aop:around pointcut="@annotation(cacheable)" method="aroundCacheable" arg-names="cacheable"/>
My understanding is that this tell Spring to look for an argument named cacheable in the advice method, and figure out the type from that. This seems a little strange because the pointcut definition is dependent on the advice. It seems like a pointcut should be self-contained, and not depend on how it is used. But maybe I'm missing something. I'll have to look into it more later, but for now, I'm just glad I got it working.
Wednesday, April 2, 2008
stubbing out java.util.Random with EasyMock and Spring
Assuming you are familiar with Spring and EasyMock, here is a little tutorial on how to stub out randomness for your JUnit tests.
First I probably need to give a little context on how I have my test framework set up with Spring. I have two spring config files, one of them is just for testing and injects stubs/mock objects where appropriate. I have a base test class that extends AbstractDependencyInjectionSpringContextTests. It has members (and setters) for all of my classes that I want to test, and all of the stubs that get injected.
I make all of my classes to test scope="prototype" and my stubs are all singletons, so that I can easy get access to the same stub that will be injected into my class under test. Before running a test, I set up the stubs appropriately, and get them ready to replay. After the test, I call EasyMock.reset() to reset them for the next test (since they are singleton).
Ok, on to the example. Let's say you have a method that uses java.util.Random:
public class MyRandomClass
{
private void Random rand;
public int doSomethingRandom()
{
return rand.nextInt();
}
public void setRand(Random rand)
{
this.rand = rand;
}
}
In your real spring config file you would have something like this:
<bean id="myRandomClass" class="MyRandomClass">
<property name="rand" ref="rand"/>
</bean>
<bean id="rand" scope="prototype" class="java.util.Random"></bean>
And in your test spring config you would set up your 'rand' bean like this:
<bean id="rand" class="org.easymock.classextension.EasyMock" factory-method="createMock">
<constructor-arg value="java.util.Random"/>
</bean>
Notice that I am using the EasyMock Class Extension because Random does not have an interface (Sun should add one, in my opinion). The regular EasyMock library can only mock interfaces, and the class extension adds the ability to mock classes themselves.
Now when setting up your test case, you can specify what 'random' values you want, like this:
EasyMock.expect(rand.nextInt()).andReturn(0);
EasyMock.expect(rand.nextInt()).andReturn(1);
...
And then you know what to expect:
assertEquals("returned wrong result", 0, myRandomClass.doSomethingRandom());
assertEquals("returned wrong result", 1, myRandomClass.doSomethingRandom());
That's it! (at least for this simple toy example)
One thing I noticed is that when using the EasyMock Class Extension, you have to remember to use the org.easymock.classextension.EasyMock createMock(), reset(), replay(), verify() etc. methods on any actual classes that you stub/mock. But I was able to use org.easymock.EasyMock.expect(), etc. methods when setting up the stubs.
First I probably need to give a little context on how I have my test framework set up with Spring. I have two spring config files, one of them is just for testing and injects stubs/mock objects where appropriate. I have a base test class that extends AbstractDependencyInjectionSpringContextTests. It has members (and setters) for all of my classes that I want to test, and all of the stubs that get injected.
I make all of my classes to test scope="prototype" and my stubs are all singletons, so that I can easy get access to the same stub that will be injected into my class under test. Before running a test, I set up the stubs appropriately, and get them ready to replay. After the test, I call EasyMock.reset() to reset them for the next test (since they are singleton).
Ok, on to the example. Let's say you have a method that uses java.util.Random:
public class MyRandomClass
{
private void Random rand;
public int doSomethingRandom()
{
return rand.nextInt();
}
public void setRand(Random rand)
{
this.rand = rand;
}
}
In your real spring config file you would have something like this:
<bean id="myRandomClass" class="MyRandomClass">
<property name="rand" ref="rand"/>
</bean>
<bean id="rand" scope="prototype" class="java.util.Random"></bean>
And in your test spring config you would set up your 'rand' bean like this:
<bean id="rand" class="org.easymock.classextension.EasyMock" factory-method="createMock">
<constructor-arg value="java.util.Random"/>
</bean>
Notice that I am using the EasyMock Class Extension because Random does not have an interface (Sun should add one, in my opinion). The regular EasyMock library can only mock interfaces, and the class extension adds the ability to mock classes themselves.
Now when setting up your test case, you can specify what 'random' values you want, like this:
EasyMock.expect(rand.nextInt()).andReturn(0);
EasyMock.expect(rand.nextInt()).andReturn(1);
...
And then you know what to expect:
assertEquals("returned wrong result", 0, myRandomClass.doSomethingRandom());
assertEquals("returned wrong result", 1, myRandomClass.doSomethingRandom());
That's it! (at least for this simple toy example)
One thing I noticed is that when using the EasyMock Class Extension, you have to remember to use the org.easymock.classextension.EasyMock createMock(), reset(), replay(), verify() etc. methods on any actual classes that you stub/mock. But I was able to use org.easymock.EasyMock.expect(), etc. methods when setting up the stubs.
Tuesday, April 1, 2008
using Spring in an Axis Web Service Impl
This is a re-post from another blog of mine:
I have an existing code base using Axis for web services, and am working on integrating Spring (2.0) into the system, for transaction management.
This is not straightforward, because Spring likes to be the one that creates your objects, so that it can inject dependencies. But with web services, Axis creates the service implementation class, not the Spring container. A 'hack' is necessary as an alternative to the standard Spring injection. And while it is not straightforward, it's not difficult, either. It just seemed that way to me because all the examples I found on the internet were confusing.
Here's the short version of how to get things working:
Now your impl class can use Spring for dependency injection just like any other class.
Here's the long version:
30,000 feet
What we want is to have a service interface (MyService), a service impl (MyServiceImpl) with the actual code, and be able to inject dependencies into MyServiceImpl (dao, etc.).
public interface MyService
{
public int doSomething();
}
public class MyServiceImpl implements MyService
{
private HelperBean myHelperObj;
public void setMyHelperObj(HelperBean myHelperObj)
{
this.myHelperObj = myHelperObj;
}
public int doSomething()
{
return myHelperObj.doTheSomething();
}
}
We want to be able to set this up in Spring:
<bean id="helperObj" class="HelperBean"></bean>
<bean id="myService" class="MyServiceImpl">
<property name="myHelperObj" ref="helperObj"/>
</bean>
And then set that service class up as a web service. This means that your wsdd would have something like this:
<service name="MyService" provider="java:RPC"></service>
...
<parameter name="className" value="MyServiceImpl"/>
...
</service>
The problem is that since MyServiceImpl is your web service class, Axis is in charge of it, not Spring. Since Spring does not create the class, it can't inject the HelperObj!
So let's see what the workaround is. First, I need to explain how I have things set up.
Axis/service class setup
I generate the wsdl from the service interface using the ant java2wsdl task, and then generate the stub, locator, etc. classes from the wsdl using the ant wsdl2java task. Because of this, my service impl class is actually named MyServiceSoapBindingImpl, just because that's what the ant task generates. The classes generated are:
To get around Spring not being able to inject dependencies into your service impl, it's necessary to make your service impl class (the one that your deploy.wsdd points to) a wrapper around the real impl class that you want to inject dependencies into. The wrapper class extends Spring's ServletEndpointSupport class, which provides an onInit method that you can override to get access to the spring application context:
public class MyServiceImpl implements MyService
{
private HelperBean myHelperObj;
public void setMyHelperObj(HelperBean myHelperObj)
{
this.myHelperObj = myHelperObj;
}
public int doSomething()
{
return myHelperObj.doTheSomething();
}
}
public class MyServiceSoapBindingImpl extends ServletEndpointSupport implements MyService
{
private MyService impl;
protected void onInit() throws ServiceException
{
impl = (MyService) getWebApplicationContext().getBean("myService");
}
public int doSomething()
{
return impl.doSomething();
}
}
So the 'hack' is that you have to use ServletEndpointSupport and access the Spring context directly, but from that point on, your real impl class can behave like any other Spring bean.
The part that messed me up was the Skeleton class. Since the wsdd pointed to the Skeleton class instead of the Impl, Spring for some reason didn't call my onInit() callback method. Hopefully this helps someone else having the same problem.
web.xml
Your web.xml doesn't need to have anything special. Just the normal configation. I got confused because all the examples I found on the web made it seem that you had to register a DispatcherServlet, which is not necessary just for what we are trying to do here.
I have an existing code base using Axis for web services, and am working on integrating Spring (2.0) into the system, for transaction management.
This is not straightforward, because Spring likes to be the one that creates your objects, so that it can inject dependencies. But with web services, Axis creates the service implementation class, not the Spring container. A 'hack' is necessary as an alternative to the standard Spring injection. And while it is not straightforward, it's not difficult, either. It just seemed that way to me because all the examples I found on the internet were confusing.
Here's the short version of how to get things working:
- If you are generating a Skeleton class with wsdl2java (-skeletonDeploy), stop doing it.
- Create a wrapper class for your service impl. For me this meant renaming my MyServiceSoapBindingImpl class to MyServiceImpl, and then regenerating MyServiceSoapBindingImpl. MyServiceSoapBindingImpl is now the wrapper class, and you have it contain a MyServiceImpl object and delegate all calls to that object.
- Change the wrapper class to extend ServletEndpointSupport, and make sure that your wsdd points to that wrapper class.
- Override ServletEndpointSupport.onInit(), and get the real impl (MyServiceImpl) as a bean using getWebApplicationContext().getBean("myServiceImplBean");
Now your impl class can use Spring for dependency injection just like any other class.
Here's the long version:
30,000 feet
What we want is to have a service interface (MyService), a service impl (MyServiceImpl) with the actual code, and be able to inject dependencies into MyServiceImpl (dao, etc.).
public interface MyService
{
public int doSomething();
}
public class MyServiceImpl implements MyService
{
private HelperBean myHelperObj;
public void setMyHelperObj(HelperBean myHelperObj)
{
this.myHelperObj = myHelperObj;
}
public int doSomething()
{
return myHelperObj.doTheSomething();
}
}
We want to be able to set this up in Spring:
<bean id="helperObj" class="HelperBean"></bean>
<bean id="myService" class="MyServiceImpl">
<property name="myHelperObj" ref="helperObj"/>
</bean>
And then set that service class up as a web service. This means that your wsdd would have something like this:
<service name="MyService" provider="java:RPC"></service>
...
<parameter name="className" value="MyServiceImpl"/>
...
</service>
The problem is that since MyServiceImpl is your web service class, Axis is in charge of it, not Spring. Since Spring does not create the class, it can't inject the HelperObj!
So let's see what the workaround is. First, I need to explain how I have things set up.
Axis/service class setup
I generate the wsdl from the service interface using the ant java2wsdl task, and then generate the stub, locator, etc. classes from the wsdl using the ant wsdl2java task. Because of this, my service impl class is actually named MyServiceSoapBindingImpl, just because that's what the ant task generates. The classes generated are:
- MyServiceSoapBindingStub
- MyServiceService
- MyServiceServiceLocator
- MyServiceSoapBindingImpl
To get around Spring not being able to inject dependencies into your service impl, it's necessary to make your service impl class (the one that your deploy.wsdd points to) a wrapper around the real impl class that you want to inject dependencies into. The wrapper class extends Spring's ServletEndpointSupport class, which provides an onInit method that you can override to get access to the spring application context:
public class MyServiceImpl implements MyService
{
private HelperBean myHelperObj;
public void setMyHelperObj(HelperBean myHelperObj)
{
this.myHelperObj = myHelperObj;
}
public int doSomething()
{
return myHelperObj.doTheSomething();
}
}
public class MyServiceSoapBindingImpl extends ServletEndpointSupport implements MyService
{
private MyService impl;
protected void onInit() throws ServiceException
{
impl = (MyService) getWebApplicationContext().getBean("myService");
}
public int doSomething()
{
return impl.doSomething();
}
}
So the 'hack' is that you have to use ServletEndpointSupport and access the Spring context directly, but from that point on, your real impl class can behave like any other Spring bean.
The part that messed me up was the Skeleton class. Since the wsdd pointed to the Skeleton class instead of the Impl, Spring for some reason didn't call my onInit() callback method. Hopefully this helps someone else having the same problem.
web.xml
Your web.xml doesn't need to have anything special. Just the normal configation. I got confused because all the examples I found on the web made it seem that you had to register a DispatcherServlet, which is not necessary just for what we are trying to do here.
Subscribe to:
Posts (Atom)