Spring session tries serialize service with session scope - spring

I have a service
#Service
#Scope(value = SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyService {
public void method() throws MyException {
throw new MyException();
}
}
And a Rest Controller:
#RestController
public class MyController {
#Autowired
private MyService myService;
#RequestMapping(value = "/do", method = RequestMethod.POST)
public String do() {
myService.method();
return null;
}
#ExceptionHandler(MyException.class)
public ResponseEntity<MyException> exceptionHandler(MyException e) {
return new ResponseEntity<MyException>(e, HttpStatus.UNAUTHORIZED);
}
}
And configuration file:
#EnableRedisHttpSession
public class AppConfig {
}
When I request do method of controller, service throws MyException, ExceptionHandler catch it. But after exceptionHandler return, I have an Exception:
org.springframework.data.redis.serializer.SerializationException: Cannot serialize; nested exception is org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [my.example.MyService$$EnhancerBySpringCGLIB$$442fa3ef_5]
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:92) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
at org.springframework.data.redis.core.AbstractOperations.rawHashValue(AbstractOperations.java:171) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
at org.springframework.data.redis.core.DefaultHashOperations.putAll(DefaultHashOperations.java:129) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
at org.springframework.data.redis.core.DefaultBoundHashOperations.putAll(DefaultBoundHashOperations.java:86) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.saveDelta(RedisOperationsSessionRepository.java:778) ~[spring-session-1.3.0.RELEASE.jar:na]
at org.springframework.session.data.redis.RedisOperationsSessionRepository$RedisSession.access$000(RedisOperationsSessionRepository.java:670) ~[spring-session-1.3.0.RELEASE.jar:na]
at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:388) ~[spring-session-1.3.0.RELEASE.jar:na]
at org.springframework.session.data.redis.RedisOperationsSessionRepository.save(RedisOperationsSessionRepository.java:245) ~[spring-session-1.3.0.RELEASE.jar:na]
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.commitSession(SessionRepositoryFilter.java:245) ~[spring-session-1.3.0.RELEASE.jar:na]
at org.springframework.session.web.http.SessionRepositoryFilter$SessionRepositoryRequestWrapper.access$100(SessionRepositoryFilter.java:217) ~[spring-session-1.3.0.RELEASE.jar:na]
at org.springframework.session.web.http.SessionRepositoryFilter.doFilterInternal(SessionRepositoryFilter.java:170) ~[spring-session-1.3.0.RELEASE.jar:na]
...
Caused by: org.springframework.core.serializer.support.SerializationFailedException: Failed to serialize object using DefaultSerializer; nested exception is java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [my.example.MyService$$EnhancerBySpringCGLIB$$442fa3ef_5]
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:68) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:35) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.data.redis.serializer.JdkSerializationRedisSerializer.serialize(JdkSerializationRedisSerializer.java:90) ~[spring-data-redis-1.8.0.RELEASE.jar:na]
... 33 common frames omitted
Caused by: java.lang.IllegalArgumentException: DefaultSerializer requires a Serializable payload but received an object of type [my.example.MyService$$EnhancerBySpringCGLIB$$442fa3ef_5]
at org.springframework.core.serializer.DefaultSerializer.serialize(DefaultSerializer.java:43) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
at org.springframework.core.serializer.support.SerializingConverter.convert(SerializingConverter.java:63) ~[spring-core-4.3.6.RELEASE.jar:4.3.6.RELEASE]
... 35 common frames omitted
While debugging, I've got that session tries to serialize Myservice object to key "sessionAttr:scopedTarget.myService"
I do not whant to make MyService class Serializeble. Is there any another solutions?

Your bean should be serializable like below:
#Service
#Scope(value = SCOPE_SESSION, proxyMode = ScopedProxyMode.TARGET_CLASS)
public class MyService implements Serializable {
public void method() throws MyException {
throw new MyException();
}
}
The reason Is that using redis your bean must be ready to be serialized
the bean have travel in the network to redis
the bean have serialized in redis data store
Probably you should think of don't use redis as session store, infact i belive that you need that a fresh instance of bean for every session and for this reaso a centralized session datastore may be not effective, a classical approach may be better for this propouse.
I hope that this reflection may be usefull for you to understand why I consider that you should be think to an anoder solutions.

Related

How to send a class annotated #Configuration as a REST response ? in springboot

I want to send this class as the response for a REST service
#Configuration
#ConfigurationProperties(prefix = "test")
public class Test {
private String str;
}
A controller has method :
#Autowired
private Test test;
#GetMapping("/url")
public Test config() {
return test;
}
The exception I am getting is:
Type definition error: [simple type, class org.springframework.beans.support.ResourceEditorRegistrar]; nested exception is com.fasterxml.jackson.databind.exc.InvalidDefinitionException: No serializer found for class org.springframework.beans.support.ResourceEditorRegistrar and no properties discovered to create BeanSerializer (to avoid exception, disable SerializationFeature.FAIL_ON_EMPTY_BEANS)

Cucumber Spring and class configuration

I'm struggling with Cucumber and Spring configuration.
I'm writing selenium framework using Page Object Pattern, with BrowserFactory.
When I use #ComponentScan, #Component and #Autowire annotations everything works fine, but when I want to create a bit more complicated bean with #Bean annotation (BrowserFactory which registers few browser drivers) in #Configuration class it does not work, during debug I'm getting nulls on every single variable I'm trying to Autowire.
I'm using Spring 4.2.4, all cucumber dependencies in version 1.2.4.
Config:
#Configuration
public class AppConfig {
#Bean
#Scope("cucumber-glue")
public BrowserFactory browserFactory() {
BrowserFactory browserFactory = new BrowserFactory();
browserFactory.registerBrowser(new ChromeBrowser());
browserFactory.registerBrowser(new FirefoxBrowser());
return browserFactory;
}
#Bean(name = "loginPage")
#Scope("cucumber-glue")
public LoginPage loginPage() throws Exception {
return new LoginPage();
}
#Bean(name = "login")
#Scope("cucumber-glue")
public Login login() {
return new Login();
}
}
POP:
public class LoginPage extends Page {
public LoginPage() throws Exception {
super();
}
...
}
Page:
public class Page {
#Autowired
private BrowserFactory browserFactory;
public Page() throws Exception{
...
}
}
Login:
public class Login {
#Autowired
private LoginPage loginPage;
public Login(){}
...
}
Steps:
#ContextConfiguration(classes = {AppConfig.class})
public class LoginSteps {
#Autowired
Login login;
public LoginSteps(){
}
#Given("^an? (admin|user) logs in$")
public void adminLogsIn(Login.User user) throws Exception {
World.currentScenario().write("Logging in as " + user + "\n");
login.as(user);
}
}
Error:
cucumber.runtime.CucumberException: Error creating bean with name 'LoginSteps': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: Login LoginSteps.login; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'login': Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private LoginPage Login.loginPage; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'loginPage' defined in AppConfig: Bean instantiation via factory method failed; nested exception is org.springframework.beans.BeanInstantiationException: Failed to instantiate [LoginPage]: Factory method 'loginPage' threw exception; nested exception is java.lang.NullPointerException
And now for the fun part...
BrowserFactory in World class is properly Autowired!!
World:
public class World {
#Autowired
private BrowserFactory browserFactory;
...
}
So I'll answer my own question:)
Issue was that I was calling BrowserFactory inside of Page constructor.
Looks like this bean was not yet created and was causing NPEs.
In order to fix that I:
Added #Lazy annotation to configuration (all elements that use this configuration, both defined in that class and those which will be found by Scan will be created as Lazy)
Moved call to Browser Factory to #PostConstruct method
Two more things to increase readability of Spring config:
Added #ComponentScan to configuration
Classes with no constructor parameters are annotated with #Component and #Scope("cucumber-glue") annotation so bean creation can be removed from AppConfig.class

Issue injecting #Context in resource class

I'm having difficulties injecting HttpHeaders into my rest service class.
#Path("/account")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#Transactional
#Service
public class UserServiceImpl implements UserService {
#Context
private HttpHeaders headers;
#Override
#POST #Path("/{username}/")
public User get(#PathParam(value = "username") String username)
throws UnknownUserException {
String requestId = headers.getHeaderString("requestId");
return new User();
}
}
When I try running the application I get the following exception during the spring initialisation:
Caused by: java.lang.IllegalArgumentException: Can not set javax.ws.rs.core.HttpHeaders field com.acme.service.UserServiceImpl.headers to com.sun.proxy.$Proxy50
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:167)
at sun.reflect.UnsafeFieldAccessorImpl.throwSetIllegalArgumentException(UnsafeFieldAccessorImpl.java:171)
at sun.reflect.UnsafeFieldAccessorImpl.ensureObj(UnsafeFieldAccessorImpl.java:58)
at sun.reflect.UnsafeObjectFieldAccessorImpl.set(UnsafeObjectFieldAccessorImpl.java:75)
at java.lang.reflect.Field.set(Field.java:758)
at org.apache.cxf.jaxrs.utils.InjectionUtils$1.run(InjectionUtils.java:192)
at java.security.AccessController.doPrivileged(Native Method)
at org.apache.cxf.jaxrs.utils.InjectionUtils.injectFieldValue(InjectionUtils.java:188)
at org.apache.cxf.jaxrs.utils.InjectionUtils.injectContextProxiesAndApplication(InjectionUtils.java:1058)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.injectContexts(JAXRSServerFactoryBean.java:405)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.updateClassResourceProviders(JAXRSServerFactoryBean.java:429)
at org.apache.cxf.jaxrs.JAXRSServerFactoryBean.create(JAXRSServerFactoryBean.java:162)
A bit of experimenting shows that I get the same error when trying to inject anything. I have an exception mapper that has the http headers injected in do problem what so ever, so I created a custom provider to obtain the headers but get the same problem injecting that in.
I'm pretty sure I must be missing something fundamental here.
I know that I could add what I need out of the headers as params to the method but I can't change the interface.
Adding the context to the operation
#Override
#POST #Path("/{username}/")
public User get(#Context HttpHeaders httpHeaders, #PathParam(value = "username") String username)
Gives me the following error.
java.lang.IllegalArgumentException: object is not an instance of declaring class
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:483)
at org.apache.cxf.service.invoker.AbstractInvoker.performInvocation(AbstractInvoker.java:181)
at org.apache.cxf.service.invoker.AbstractInvoker.invoke(AbstractInvoker.java:97)
I've discovered that if I drop the implements interface then the application actually starts, however nothing is actually injected in.
I've found a workaround (or possibly the expected solution). It appears that spring is using a proxy based on the interface. I created an intermediate interface and stuck on a setHttpHeaders operation on the interface and annotated the implementation with #Context. All seems fine with that.
public interface MyService {
void doStuff();
}
public interface MyServiceInt extends MyService {
void setHttpHeaders(HttpHeaders headers);
}
#Path("/")
#Produces({ MediaType.APPLICATION_JSON })
#Consumes({ MediaType.APPLICATION_JSON })
#Transactional
#Service
public class MyServiceImpl implements MyServiceInt {
private HttpHeaders httpHeaders;
#Context
void setHttpHeaders(HttpHeaders headers) {
this.httpHeaders = httpHeaders;
}
#Override
#POST #Path("/doStuff")
public void doStuff() {
}
}
Would still like to hear of a better solution if anyone knows.

Using Spring beans as a key with #Cacheable annotation

How to make following to work:
- a spring bean that has a method that should be cached with #Cacheable annotation
- another spring bean that creates keys for the cache (KeyCreatorBean).
So the code looks something like this.
#Inject
private KeyCreatorBean keyCreatorBean;
#Cacheable(value = "cacheName", key = "{#keyCreatorBean.createKey, #p0}")
#Override
public List<Examples> getExamples(ExampleId exampleId) {
...
However the above code doesn't work: it gives following exception:
Caused by: org.springframework.expression.spel.SpelEvaluationException:
EL1057E:(pos 2): No bean resolver registered in the context to resolve access to bean 'keyCreatorBean'
I checked the underlying cache resolution implementation, there doesn't appear to be a simple way to inject in a BeanResolver which is required for resolving the beans and evaluating expressions like #beanname.method.
So I would also recommend a somewhat hacky way along the lines of one which #micfra has recommended.
Along what he has said, have a KeyCreatorBean along these lines, but internally delegate it to the keycreatorBean that you registered in your application:
package pkg.beans;
import org.springframework.stereotype.Repository;
public class KeyCreatorBean implements ApplicationContextAware{
private static ApplicationContext aCtx;
public void setApplicationContext(ApplicationContext aCtx){
KeyCreatorBean.aCtx = aCtx;
}
public static Object createKey(Object target, Method method, Object... params) {
//store the bean somewhere..showing it like this purely to demonstrate..
return aCtx.getBean("keyCreatorBean").createKey(target, method, params);
}
}
In the case you have a static class function, it will work like this
#Cacheable(value = "cacheName", key = "T(pkg.beans.KeyCreatorBean).createKey(#p0)")
#Override
public List<Examples> getExamples(ExampleId exampleId) {
...
}
with
package pkg.beans;
import org.springframework.stereotype.Repository;
public class KeyCreatorBean {
public static Object createKey(Object o) {
return Integer.valueOf((o != null) ? o.hashCode() : 53);
}
}

Spring #Scheduled Annotation for Inherited Classes

I am trying to use #Scheduled annotation for a service layer class. The class also gets monitored by a logging service through AOP.
When I make the service class implement an interface, Spring throws the error
Error creating bean with name 'dummyService' defined in file
......
......
Caused by: java.lang.IllegalStateException: failed to prepare task
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor$1.doWith(ScheduledAnnotationBeanPostProcessor.java:114)
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:452)
at org.springframework.util.ReflectionUtils.doWithMethods(ReflectionUtils.java:430)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor.postProcessAfterInitialization(ScheduledAnnotationBeanPostProcessor.java:98)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.applyBeanPostProcessorsAfterInitialization(AbstractAutowireCapableBeanFactory.java:407)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.initializeBean(AbstractAutowireCapableBeanFactory.java:1426)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:519)
... 11 more
Caused by: java.lang.NoSuchMethodException: $Proxy23.run()
at java.lang.Class.getMethod(Class.java:1605)
at org.springframework.util.MethodInvoker.prepare(MethodInvoker.java:178)
at org.springframework.scheduling.annotation.ScheduledAnnotationBeanPostProcessor$1.doWith(ScheduledAnnotationBeanPostProcessor.java:111)
... 17 more
This is the service layer class:
package com.mydomain.web.myapp.service;
#Service
public class DummyService implements DummyI{
#Scheduled(cron = "${some.cron.time}")
public void run() {
}
}
If I remove the inheritance, it works without problem. Why is that?
This is what I have for the logging service :
#Component
#Aspect
public class LoggingServiceImpl implements LoggingService {
private final Logger log = LoggerFactory.getLogger(this.getClass());
#Around("execution(* com.mydomain.web.myapp..*.*(..))")
public Object log(ProceedingJoinPoint joinPoint) throws Throwable {
.....
You have several options here:
Add run() to DummyI interface
Create another interface having only run() method (or use java.lang.Runnable) and implemented that interface as well:
public class DummyService implements DummyI, Runnable
Enable class based (cglib) proxies

Resources