Atomically maintaining service layer transactions and database logging with Spring framework - java

I have a web application implemented using Spring and Hibernate. A typical controller method in the application looks like the following:
#RequestMapping(method = RequestMethod.POST)
public #ResponseBody
Foo saveFoo(#RequestBody Foo foo, HttpServletRequest request) throws Exception {
// authorize
User user = getAuthorizationService().authorizeUserFromRequest(request);
// service call
return fooService.saveFoo(foo);
}
And a typical service class looks like the following:
#Service
#Transactional
public class FooService implements IFooService {
#Autowired
private IFooDao fooDao;
#Override
public Foo saveFoo(Foo foo) {
// ...
}
}
Now, I want to create a Log object and insert it to database every time a Foo object is saved. These are my requirements:
The Log object should contain userId from the authorised User object.
The Log object should contain some properties from the HttpServletRequest object.
The save operation and log creation operation should be atomic. I.e. if a foo object is saved in the object we should have a corresponding log in the database indicating the user and other properties of the operation.
Since transaction management is handled in the service layer, creating the log and saving it in the controller violates the atomicity requirement.
I could pass the Log object to the FooService but that seems to be violation of separation of concerns principle since logging is a cross cutting concern.
I could move the transactional annotation to the controller which is not suggested in many of the places I have read.
I have also read about accomplishing the job using spring AOP and interceptors about which I have very little experience. But they were using information already present in the service class and I could not figure out how to pass the information from HttpServletRequest or authorised User to that interceptors.
I appreciate any direction or sample code to fulfill the requirements in this scenario.

There are multiple steps which are to be implemented to solve your problem:
Passing Log object non-obtrusively to service classes.
Create AOP based interceptors to start inserting Log instances to DB.
Maintaining the order to AOP interceptors (Transaction interceptor and Log interceptor) such that transaction interceptor is invoked first. This will ensure that user insert and log insert happens in a single transaction.
1. Passing Log object
You can use ThreadLocal to set the Log instance.
public class LogThreadLocal{
private static ThreadLocal<Log> t = new ThreadLocal();
public static void set(Log log){}
public static Log get(){}
public static void clear(){}
}
Controller:saveFoo(){
try{
Log l = //create log from user and http request.
LogThreadLocal.set(l);
fooService.saveFoo(foo);
} finally {
LogThreadLocal.clear();
}
}
2. Log Interceptor
See how spring AOP works (http://docs.spring.io/spring/docs/current/spring-framework-reference/html/aop-api.html)
a) Create an annotation (acts as pointcut), #Log for method level. This annotation will be put on the service methods for which logging is to be done.
#Log
public Foo saveFoo(Foo foo) {}
b) Create an implementation, LogInteceptor (acts as the advice) of org.aopalliance.intercept.MethodInterceptor.
public class LogInterceptor implements MethodInterceptor, Ordered{
#Transactional
public final Object invoke(MethodInvocation invocation) throws Throwable {
Object r = invocation.proceed();
Log l = LogThreadLocal.get();
logService.save(l);
return r;
}
}
c) Wire the pointcut & advisor.
<bean id="logAdvice" class="com.LogInterceptor" />
<bean id="logAnnotation" class="org.springframework.aop.support.annotation.AnnotationMatchingPointcut">
<constructor-arg type="java.lang.Class" value="" />
<constructor-arg type="java.lang.Class" value="com.Log" />
</bean>
<bean id="logAdvisor" class="org.springframework.aop.support.DefaultPointcutAdvisor">
<property name="advice" ref="logAdvice" />
<property name="pointcut" ref="logAnnotation" />
</bean>
3. Ordering of interceptors (transaction and log)
Make sure you implement org.springframework.core.Ordered interface to LogInterceptor and return Integer.MAX_VALUE from getOrder() method. In your spring configuration, make sure your transaction interceptor has lower order value.
So, first your transaction interceptor is called and creates a transaction. Then, your LogInterceptor is called. This interceptor first proceed the invocation (saving foo) and then save log (extracting from thread local).

One more example based Spring AOP but using java configuration, I hate XMLs :) Basically the idea is almost the same as mohit has but without ThreadLocals, Interceptor Orders and XML configs:)
So you will need :
#Loggable annotation to mark methods as the once which create the logs.
TransactionTemplate which we will use to programmatically control the transactions.
Simple Aspect which will put every thing in its place.
So at first lets create the annotation
#Retention(RetentionPolicy.RUNTIME)
#Target(ElementType.METHOD)
public #interface Loggable {}
If you are missing the TransactionTemplate configuration or EnableAspectJAutoProxy just add following to your Java Config.
#EnableAspectJAutoProxy
#Configuration
public class ApplicationContext {
.....
#Bean
TransactionTemplate transactionTemplate(PlatformTransactionManager transactionManager){
TransactionTemplate template = new TransactionTemplate();
template.setTransactionManager(transactionManager);
return template;
}
}
And next we will need an Aspect which will do all the magic :)
#Component
#Aspect
public class LogAspect {
#Autowired
private HttpServletRequest request;
#Autowired
private TransactionTemplate template;
#Autowired
private LogService logService;
#Around("execution(* *(..)) && #annotation(loggable)")
public void logIt(ProceedingJoinPoint pjp, Loggable loggable) {
template.execute(s->{
try{
Foo foo = (Foo) pjp.proceed();
Log log = new Log();
log.setFoo(foo);
// check may be this is a internal call, not from web
if(request != null){
log.setSomeRequestData(request.getAttribute("name"));
}
logService.saveLog(log);
} catch (Throwable ex) {
// lets rollback everything
throw new RuntimeException();
}
return null;
});
}
}
And finally in your FooService
#Loggable
public Foo saveFoo(Foo foo) {}
Your controller remains the same.

If you use LocalSessionFactoryBean or it's subclass (for instance AnnotationSessionFactoryBean) with inside your Spring context, then the best option would be using entityInterceptor property. You have to pass instance of orh.hibernate.Interceptor interface. For instance:
// java file
public class LogInterceptor extends ScopedBeanInterceptor {
// you may use your authorization service to retrieve current user
#Autowired
private AutorizationService authorizationService
// or get the user from request
#Autowired
private HttpServletRequest request;
#Override
public boolean onSave(final Object entity, final Serializable id, final Object[] state, final String[] propertyNames, final Type[] types) {
// get data from request
// your save logic here
return true;
}
}
// in spring context
<bean id="sessionFactory"
class="org.springframework.orm.hibernate3.annotation.AnnotationSessionFactoryBean" destroy-method="destroy">
<property name="dataSource" ref="dataSource"/>
<property name="hibernateProperties">
....
</property>
....
<property name="entityInterceptor" ref="logInterceptor"/>
</bean>
Add the following to your web.xml (or add listener in java code, depending on what you use).
<listener>
<listener-class>
org.springframework.web.context.request.RequestContextListener
</listener-class>
</listener>
Add request scope bean, so it'll be request aware.
<bean id="logInterceptor" class="LogInterceptor" scope="request">
<aop:scoped-proxy proxy-target-class="false" />
</bean>
You can separate log data fetch from interceptor, so there will be a different request scoped component, or also you can use filters to store data in ThreadLocal.

Related

Why is caching not working on wrapped reactive Methods [duplicate]

Spring cache is not working when calling cached method from another method of the same bean.
Here is an example to explain my problem in clear way.
Configuration:
<cache:annotation-driven cache-manager="myCacheManager" />
<bean id="myCacheManager" class="org.springframework.cache.ehcache.EhCacheCacheManager">
<property name="cacheManager" ref="myCache" />
</bean>
<!-- Ehcache library setup -->
<bean id="myCache"
class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" p:shared="true">
<property name="configLocation" value="classpath:ehcache.xml"></property>
</bean>
<cache name="employeeData" maxElementsInMemory="100"/>
Cached service :
#Named("aService")
public class AService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
Result :
aService.getEmployeeData(someDate);
output: Cache is not being used
aService.getEmployeeData(someDate);
output:
aService.getEmployeeEnrichedData(someDate);
output: Cache is not being used
The getEmployeeData method call uses cache employeeData in the second call as expected. But when the getEmployeeData method is called within the AService class (in getEmployeeEnrichedData), Cache is not being used.
Is this how spring cache works or am i missing something ?
I believe this is how it works. From what I remember reading, there is a proxy class generated that intercepts all requests and responds with the cached value, but 'internal' calls within the same class will not get the cached value.
From https://code.google.com/p/ehcache-spring-annotations/wiki/UsingCacheable
Only external method calls coming in through the proxy are
intercepted. This means that self-invocation, in effect, a method
within the target object calling another method of the target object,
will not lead to an actual cache interception at runtime even if the
invoked method is marked with #Cacheable.
Since Spring 4.3 the problem could be solved using self-autowiring over #Resource annotation:
#Component
#CacheConfig(cacheNames = "SphereClientFactoryCache")
public class CacheableSphereClientFactoryImpl implements SphereClientFactory {
/**
* 1. Self-autowired reference to proxified bean of this class.
*/
#Resource
private SphereClientFactory self;
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull TenantConfig tenantConfig) {
// 2. call cached method using self-bean
return self.createSphereClient(tenantConfig.getSphereClientConfig());
}
#Override
#Cacheable(sync = true)
public SphereClient createSphereClient(#Nonnull SphereClientConfig clientConfig) {
return CtpClientConfigurationUtils.createSphereClient(clientConfig);
}
}
The example below is what I use to hit the proxy from within the same bean, it is similar to #mario-eis' solution, but I find it a bit more readable (maybe it's not:-). Anyway, I like to keep the #Cacheable annotations at the service level:
#Service
#Transactional(readOnly=true)
public class SettingServiceImpl implements SettingService {
#Inject
private SettingRepository settingRepository;
#Inject
private ApplicationContext applicationContext;
#Override
#Cacheable("settingsCache")
public String findValue(String name) {
Setting setting = settingRepository.findOne(name);
if(setting == null){
return null;
}
return setting.getValue();
}
#Override
public Boolean findBoolean(String name) {
String value = getSpringProxy().findValue(name);
if (value == null) {
return null;
}
return Boolean.valueOf(value);
}
/**
* Use proxy to hit cache
*/
private SettingService getSpringProxy() {
return applicationContext.getBean(SettingService.class);
}
...
See also Starting new transaction in Spring bean
Here is what I do for small projects with only marginal usage of method calls within the same class. In-code documentation is strongly advidsed, as it may look strage to colleagues. But its easy to test, simple, quick to achieve and spares me the full blown AspectJ instrumentation. However, for more heavy usage I'd advice the AspectJ solution.
#Service
#Scope(proxyMode = ScopedProxyMode.TARGET_CLASS)
class AService {
private final AService _aService;
#Autowired
public AService(AService aService) {
_aService = aService;
}
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = _aService.getEmployeeData(date);
...
}
}
If you call a cached method from same bean it will be treated as a private method and annotations will be ignored
Yes, the caching will not happen because of the reasons that were already mentioned in the other posts. However I would solve the problem by putting that method to its own class (service in this case). With that your code will be easier to maintain/test and understand.
#Service // or #Named("aService")
public class AService {
#Autowired //or how you inject your dependencies
private EmployeeService employeeService;
public List<EmployeeData> getEmployeeData(Date date){
employeeService.getEmployeeData(date);
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = getEmployeeData(date);
...
}
}
#Service // or #Named("employeeService")
public class EmployeeService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
println("This will be called only once for same date");
...
}
}
In my Case I add variable :
#Autowired
private AService aService;
So I call the getEmployeeData method by using the aService
#Named("aService")
public class AService {
#Cacheable("employeeData")
public List<EmployeeData> getEmployeeData(Date date){
..println("Cache is not being used");
...
}
public List<EmployeeEnrichedData> getEmployeeEnrichedData(Date date){
List<EmployeeData> employeeData = aService.getEmployeeData(date);
...
}
}
It will use the cache in this case.
Better approach should be creating another service like ACachingService and call ACachingService.cachingMethod() instead of self Autowiring ( or any other approach trying to self inject). This way you do not fall into Circular dependency, which may be resulted in warning/error when upgrade to newer Spring ( Spring 2.6.6 in my case ) :
ERROR o.s.boot.SpringApplication - Application run failed
org.springframework.beans.factory.BeanCurrentlyInCreationException:
Error creating bean with name 'webSecurityConfig':
Requested bean is currently in creation: Is there an unresolvable circular reference?
We looked at all the solutions here and decided to use a separate class for the cached methods because Spring 5 doesn't like circular dependencies.
Use static weaving to create proxy around your bean. In this case even 'internal' methods would work correctly
I use internal inner bean (FactoryInternalCache) with real cache for this purpose:
#Component
public class CacheableClientFactoryImpl implements ClientFactory {
private final FactoryInternalCache factoryInternalCache;
#Autowired
public CacheableClientFactoryImpl(#Nonnull FactoryInternalCache factoryInternalCache) {
this.factoryInternalCache = factoryInternalCache;
}
/**
* Returns cached client instance from cache.
*/
#Override
public Client createClient(#Nonnull AggregatedConfig aggregateConfig) {
return factoryInternalCache.createClient(aggregateConfig.getClientConfig());
}
/**
* Returns cached client instance from cache.
*/
#Override
public Client createClient(#Nonnull ClientConfig clientConfig) {
return factoryInternalCache.createClient(clientConfig);
}
/**
* Spring caching feature works over AOP proxies, thus internal calls to cached methods don't work. That's why
* this internal bean is created: it "proxifies" overloaded {#code #createClient(...)} methods
* to real AOP proxified cacheable bean method {#link #createClient}.
*
* #see Spring Cache #Cacheable - not working while calling from another method of the same bean
* #see Spring cache #Cacheable method ignored when called from within the same class
*/
#EnableCaching
#CacheConfig(cacheNames = "ClientFactoryCache")
static class FactoryInternalCache {
#Cacheable(sync = true)
public Client createClient(#Nonnull ClientConfig clientConfig) {
return ClientCreationUtils.createClient(clientConfig);
}
}
}
I would like to share what I think is the easiest approach:
Autowire the controller and use to call the method it instead of using the class context this.
The updated code would look like:
#Controller
public class TestController {
#Autowired TestController self;
#RequestMapping("/test")
public String testView(){
self.expensiveMethod();
return "test";
}
#Cacheable("ones")
public void expensiveMethod(){
System.out.println("Cache is not being used");
}
}
The default advice mode for processing caching annotation is “proxy”. At the startup of an application, all the caching annotations like #Caching, #Cacheable, #CacheEvict etc. are scanned and a target proxy class is generated for all of these classes. The proxy allows for intercepting the calls to these cacheable methods, which adds the caching advice/behavior.
So when we invoke the cacheable methods from the same class, as shown below, calls from the clients don’t get intercepted in a way that allows for caching advice to be added to them. Hence, every single time there is an unexpected cache miss.
Solution: Invoke the Cacheable methods from a different bean to use proxy class with caching advice.

Implement caching framework(ehcache) to cache the Lookupcode and Location dropdown values during the server startup

I want to load both the LookupCode and Location data from database
into cache memory using Spring ehCache when the application starts i.e
when the server starts before any other method is called. In future
few more dropdowns will be added. So there should be a common method
to cache whatever datas comes in based on the criteria of the dropdown
data.
There is a Entity, Repository and Service already written for
Lookupcode and Location
I have written the below for implementing caching framework:
ehcache.xml
<cache name= "LookupCodeRepository.getDropdownValues"/> <cache name= "LocationRepository.getDropdownValues"/>
application.properties
spring.jpa.properties.hibernate.cache.use_second_level_cache = false
spring.jpa.properties.hibernate.cache.use_query_cache = false
spring.jpa.properties.hibernate.cache.region.factory_class =
org.hibernate.cache.ehcache.EhCacheRegionFactory
spring.jpa.properties.hibernate.cache.provider_class =
org.hibernate.cache.EhCacheProvider
spring.jpa.properties.hibernate.cache.use_structured_entries = true
spring.jpa.properties.hibernate.cache.region_prefix =
spring.jpa.properties.hibernate.cache.provider_configuration_file_resource_path
= ehcache.xml spring.jpa.properties.hibernate.cache.use_second_level_cache
and using hibernate-ehcache jar in pom.xml
WebConfig.java
#Configuration public class WebConfig implements
ServletContextInitializer{
#Autowired CustomCache cache;
#Override public void onStartup ( ServletContext servletContext)
throws ServletException{
cache.loadCache();
}
CustomCache.java
public class CustomCache {
#Autowired private LookupCodeService lkupSer;
#Autowired private LocationService locSer;
public void loadCache(){
List<LookupCode> lkup = lkupServ.getDropdownValues();
List<Location> locat = locSer.getDropdownValues();
}
So here in loadCache() method instead of calling each individual
service it should be like, automatic. Whatever service is created
it should automatically be cached. So there should be a common method
to cache whatever datas comes in based on the criteria of the
dropdown data.
How to implement that?
The services you want to work with have a common method. Define an interface for that method:
interface ProvidesDropdownValues<T> {
List<T> getDropdownValues();
}
Now you can do:
class DropdownValuesService {
#Autowired ApplicationContext context;
#Cacheable List getDropdownValues(String beanName) {
ProvidesDropdownValues<?> bean = ((ProvidesDropdownValues) context.getBean(beanName));
return bean.getDropdownValues();
}
}
If your services don't have bean names you could work with class names instead.
For load on startup you could do:
class StartupWarmupService {
#Autowired ApplicationContext context;
#Autowired DropdownValuesService dropDowns;
#PostConstruct void startup() {
for (String n : context.getBeanNamesForType(ProvidesDropdownValues.class)) {
dropDowns.getDropdownValues(n);
}
}
}
I suggest that the load code only runs in the production application. That is why it makes sense to keep it separate from the general caching logic. For testing a single service you don't want to load everything. Startup times for developers should be fast.
Disclaimer: I am not a heavy Spring user, so details may be wrong but the basic approach should work out.

rollback transaction in test with apache camel

I am struggling to make a working junit test that rolls back actions that the occurred during the camel routing.
I have a camel route setup that listens on a directory. It is expecting a csv file. When the csv file appears it then creates new SearchAnalytics data. It adds a new row into a table per each line in the csv file.
The default spring transaction methods that I have put do not seem to apply to actions that occur on the camel routing.
The code below works. However it saves the data permanently and does not rollback the insert. This means that the test will only pass once unless I manually delete the data.
Given my example code how do I make it roll back the transaction?
my route looks like this
from("ftp://some__remote__ftp_dir_path")
.routeId("searchAnalyticsImport")
.choice()
.when(simple("${in.header.CamelFileName} contains '.csv'"))
.split().method("csvSplitter", "iterator").streaming() // reads the csv file returns data objects
.processRef("searchAnalyticsProcesser") // this some dao saves
.to(Queues.SOME_REQUEST)
.end();
Junit test
#TransactionConfiguration(defaultRollback = true, transactionManager = "transactionManager")
#RunWith(SpringJUnit4ClassRunner.class)
#ContextConfiguration(locations = { TestAppConfig.class})
public class searchAnalyticsImportTest {
#EndpointInject(uri = "mock:sippmatcher.requestqueue?preserveMessageQos=true")
private MockEndpoint mockEndpointRequest;
#Before
public void setup() throws Exception {
camelContext.getRouteDefinition("searchAnalyticsImport").adviceWith(camelContext, new AdviceWithRouteBuilder() {
#Override
public void configure() throws Exception {
replaceFromWith("file://"+this.getClass().getResource("path to folder etc...")+"?noop=true");
interceptSendToEndpoint(Queues.SOME_REQUEST)
.skipSendToOriginalEndpoint()
.to(mockEndpointRequest);
}
});
}
#Test
public void simpleTest() throws Exception{
// there are 2 results in the test csv file.. need to poll the results till it completes
PollWithTimeout.run("keep polling until route has been statisfied", 15000, new PollWithTimeout.Attempt() {
#Override
public boolean complete() {
Date dateTime1MinuteAgo = new DateTime().minusMinutes(1).toDate();
Integer newSearchCount = searchAnalysiticDao.findBySearchStartedAfter(dateTime1MinuteAgo).size();
System.out.println("Recently added count: " + newSearchCount);
return (newSearchCount == 2);
}
});
mockEndpointRequest.expectedMessageCount(2);
mockEndpointRequest.assertIsSatisfied();
}
}
Add bean to Context (will add javaconfig option to this)
As mentioned in the comment section by Andreas you can add .transacted to the route and ensure that your transaction manager bean is injected in your context file.
Route
from("ftp://some__remote__ftp_dir_path")
.routeId("searchAnalyticsImport")
.end()
.transacted("PROPAGATION_REQUIRED")
etc....
Context Bean Config
<bean id="jmsTransactionManager"
class="org.springframework.jms.connection.JmsTransactionManager">
<property name="connectionFactory" ref="pooledConnectionFactory" />
<property name="defaultTimeout" value="30"/>
</bean>
<bean id="PROPAGATION_REQUIRED" class="org.apache.camel.spring.spi.SpringTransactionPolicy">
<property name="transactionManager" ref="jmsTransactionManager" />
<property name="propagationBehaviorName" value="PROPAGATION_REQUIRED" />
</bean>
Alternatively add transaction to dao
you can use the below annotation at the dao method that is being called in the searchAnalyticsProcesser. A transaction maanger bean will still be required but you can specify it by name in the annotation.
#Transactional(
propagation = Propagation.REQUIRED,
readOnly = false,
value="transactionManager",
rollbackFor = {
Exception.class
})
public void insertStuff()

Keeping the session Open in JUnit/JPA/Hibernate/Struts and Spring integration test - No Session or session closed - LazyInitialization Exception

My application uses Struts2(mvc), Spring (Dependency Injection), JPA with Hibernate, JUnit along with struts2-junit plugin and struts2 spring plugin.
Here is my test class:
#RunWith(SpringJUnit4ClassRunner.class)
public class CustomerSearchIntegrationTest extends StrutsSpringTestCase {
#Test
#Transactional
public void testGetActionProxy() throws Exception {
ActionProxy proxy;
String result;
ActionMapping mapping = getActionMapping("userInfo");
assertNotNull(mapping);
..... // Some JUnit init code..
ActionProxy proxy = getActionProxy("userInfo");
UserInfo user = (UserInfo) proxy.getAction();
result = proxy.execute();
assertEquals("details", result);
System.out.prinltn("Username:" + user.getFirstName());
}
}
GetUserInfo
public class UserInfo extends ActionSupport {
User user; // Entity
UDetails details; // Entity
public String getUserDetails() { //Action method
user = userMgmt.getUser(usrId);
if (user != null ) {
for(UDetails det : user.getUDetails()) { // user.getUDetails() there is where exception gets thrown.
if(det.getStreet().equals(street)){
details = det;
break;
}
}
}
...
...
}
}
User and UDetails are entities. UDetalis is ManyToMany with User and Lazily fetched. All entities are annotated.
UserMgmt
public class UserMgmt {
public User getUser(String userId) {
return userDao.getUser(userId);
}
}
UserDAO
public class UserDAO extends AbstractJPAImpl{
public User getUser(String userId) {
User user = (User) getSession().get(User.class, userId);
return user;
}
}
AbstractJPAImpl
#Transactional
public abstract class AbstractJPAImpl {
private EntityManager em;
#PersistenceContext
protected void setEntityManager(EntityManager em) {
this.em = em;
}
#Transactional
protected Session getSession() {
return (Session) em.getDelegate();
}
....
}
jpaContext.xml
<bean class="org.springframework.orm.jpa.support.PersistenceAnnotationBeanPostProcessor"/>
<bean id="entityManagerFactory" class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
// other config stuff
</bean>
<bean id="transactionManager" class="org.springframework.orm.jpa.JpaTransactionManager">
<property name="entityManagerFactory" ref="entityManagerFactory"/>
</bean>
<tx:annotation-driven transaction-manager="transactionManager"/>
All configuration/context files are loading fine. Struts.xml, jpacontext.xml, beans.xml, etc. all are loaded.
But I get an exception:
failed to lazily initialize a collection of role: com.my.data.User.udetails, no session or session was closed
org.hibernate.LazyInitializationException: failed to lazily initialize a collection of role:
at the following line:
for(UDetails det : user.getUDetails())
Obviously, its trying to load UDetails lazily, then exception is throwing.
However, this application works fine when deployed in a AppServer (WebSphere).
What could I be doing wrong? How do I keep session open?
Update: More info
I am using OpenEntityManagerInViewFilter. My web.xml below
<filter>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<filter-class>
org.springframework.orm.jpa.support.OpenEntityManagerInViewFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>OpenEntityManagerInViewFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
Update:
This finally works, if I pass the parameter to #PersistenceContext annotation like below:
#PersistenceContext(type=PersistenceContextType.EXTENDED)
So I guess, the transaction is getting closed, but the entities are operable outside of transaction, because of the EXTENDED context type. However, I can't modify the code like above and leave it permanently. So I need to remove it.
So I guess, I have these options, but not sure if these are doable and how:
Get the persistence context from spring application context and pass the parameter. Not sure if this is relevant and possible.
Get the session/entity manager from application context and add another layer of transaction. That ways, the the session runs in two transactions. One is started from my testing code, and another one is in the existing code, which is automatically getting closed, while mine remains open until my test code completes execution.
For the 2nd one, I tried annotatting the method with #Transactional. But that did not work. Not sure why.
Any help?
Update
Looks like, struts-junit plugin does NOT load/read web.xml, which has the OpenEntityManagerInViewFilter. So it did not get loaded.
Any other work around or setup this filter?
If it helps anyone, I couldn't get the #Transaction to work. But I put this:
#PersistenceContext(type=PersistenceContextType.EXTENDED)
and it works now!
.....

Logging bean id into log4j logfile without BeanNameAware interface

Given a set of classes wired together by spring. There are several classes that are used with different configuration in multiple instances in the environment. They have different beanid of course.
The problems:
When they make log entries, we dont know exactly which bean made the log, since the log4j displays the classname only
I know that I could use logger instantiated by spring InitializationBean+BeanNameAware interface methods, but I do not want to do it, since I do not want to implement them in all classes
The solution could be:
Having some effect on bean factory to store the id of the beans in a map with the bean reference (key is the ref, name is the value)
Creating an aspect to be applied on every method, that would set an "BeanName" MDC entry in Log4j before the call, and would restore it to the previous value after the call. Meanwhile the previous beannames could be stored in a threadlocal in a stack.
The questions:
How can I change/configure the bean factory to do this trick for me? Is there any customization point I could use to this aim?
How can I avoid memory leaks in the map in the beanid registry? Maybe the registry is not needed at all, if somehow spring can look up the id for a reference.
Do you have any better idea, that would not result in changing ten thousand classes?
Thanks in advance.
UPDATE:
- Does anyone have solution for the prototype beans?
I have managed to hack something together based on this Spring AOP Example.
I am not yet up to speed with Spring 3 so I have implemented this using Spring 2.5 - I dare say there are more elegant ways of achieving what you want. I have implemented this using System.out's for simplicity but these could easily be converted to log4j calls.
Initially I create a map between the Spring's bean names and the string representation of the object (InitBean). This map is used inside the MethodInterceptor - I did try making the MethodInterceptor an InitializingBean but the MethodInterceptor stopped working for some reason.
Performing an equals between the bean passed in via the MethodInterceptor and the other beans in the application context did not work. e.g. by using something like "ctx.getBeansOfType(GoBean.class)" inside the MethodInterceptor. I presume this is because the object passed in via the MethodInvocation was a GoBean whereas objects obtained from the application context at this point are proxied (e.g. something like example.GoBean$$EnhancerByCGLIB$$bd27d40e).
This is why I had to resort to a comparison of object string representations (which is not ideal). Also I specifically do not want to activate the MethodInterceptor logic when calling the "toString" method on an object (as since I'm using toString elsewhere leads to infinite loops and StackOverflow).
I hope this is useful,
applicationContext.xml
<beans>
<bean name="initBean" class="example.InitBean"/>
<bean name="methodLoggingInterceptor" class="example.MethodLoggingInterceptor">
<property name="initBean" ref="initBean"/>
</bean>
<bean name="proxyCreator" class="org.springframework.aop.framework.autoproxy.BeanNameAutoProxyCreator">
<property name="beanNames">
<list>
<value>go*</value>
</list>
</property>
<property name="interceptorNames">
<list>
<value>methodLoggingInterceptor</value>
</list>
</property>
</bean>
<bean name="goBean1" class="example.GoBean" />
<bean name="goBean2" class="example.GoBean" />
<bean name="goBean3" class="example.GoBean" />
</beans>
GoBean.java
public class GoBean {
public void execute(){
System.out.println(new Date());
}
}
SimpleTestClass.java
public static void main( String[] args ){
ApplicationContext ctx = new ClassPathXmlApplicationContext("applicationContext.xml");
ArrayList<GoBean> goBeans = new ArrayList<GoBean>();
goBeans.add((GoBean) ctx.getBean("goBean1"));
goBeans.add((GoBean) ctx.getBean("goBean2"));
goBeans.add((GoBean) ctx.getBean("goBean3"));
for(GoBean g: goBeans){
g.execute();
}
}
InitBean.java
public class InitBean implements ApplicationContextAware, InitializingBean {
private ApplicationContext ctx;
private Map<String, String> beanMap = new HashMap<String,String>();
public void setApplicationContext(ApplicationContext ac) throws BeansException {
ctx = ac;
}
public void afterPropertiesSet() throws Exception {
for(String beanName: ctx.getBeanNamesForType(GoBean.class)){
beanMap.put(ctx.getBean(beanName).toString(), beanName);
}
}
public Map<String,String> getBeanMap(){
return beanMap;
}
}
MethodLoggingInterceptor.java
public class MethodLoggingInterceptor implements MethodInterceptor{
private InitBean initBean;
public Object invoke(MethodInvocation method) throws Throwable {
if (!"toString".equals(method.getMethod().getName())) {
StringBuilder sb = new StringBuilder();
Object obj = method.getThis();
if (obj instanceof GoBean) {
Map<String,String> beanMap = initBean.getBeanMap();
String objToString = obj.toString();
if (beanMap.containsKey(objToString)) {
System.out.println(beanMap.get(objToString));
sb.append("bean: ");
sb.append(beanMap.get(objToString));
sb.append(" : ");
}
}
sb.append(method.getMethod().getDeclaringClass());
sb.append('.');
sb.append(method.getMethod().getName());
System.out.println(sb.toString() + " starts");
Object result = method.proceed();
System.out.println(sb.toString() + " finished");
return result;
} else {
return method.proceed();
}
}
public void setInitBean(InitBean ib) {
this.initBean = ib;
}
}

Categories