spring-session Session implementation not getting CGLIB Enhanced Proxy Bean - spring

I'm attempting to drop spring-session into an existing web application that uses session scoped beans. This application is running on Wildfly 10.1.0.Final
When I run the application uses wildfly's internal session management, everything works fine. Attempting to put in a custom implementation of Spring-Session is losing the session bean information. When I debug the application, there is a call for scopedTarget.userSessionData, however it is of the underlying class UserSessionData, and not the UserSessionData$$EnhancedBySpringCGLIB$$1234 that has the actual data.
configuration-spring.xml
<?xml version="1.0" encoding="UTF-8"?>
<beans>
<bean id="userSessionData" scope="session" class="UserSessionData">
<aop:scoped-proxy/>
</bean>
<context:annotation-config />
<bean class="my.MyHttpSessionConfiguration" />
</beans>
This configuration properly creates and registers a springSessionRepositoryFilter using MyHttpSessionConfiguration. However the problem is when MySession object has a session scoped bean of UserSessionData, I get the raw proxied class instance and not the decorated one which has the session values. I was able to verify this by debugging the below SesssionImpl on the #setAttribute method
class UserSessionData {
private String _myValue;
public void setMyValue(String value) {
_myValue = value;
}
public String getMyValue() {
return _myValue;
}
}
final class MySession implements ExpiringSession {
private final MapSession _delegate;
private final Map<String, Object> _delta = new HashMap<>();
private boolean _isNew;
MySession() {
this(new MapSession());
_isNew = true;
flushImmediateIfNecessary();
}
MySession(MapSession session) {
if (session == null) {
throw new IllegalArgumentException("MapSession cannot be null");
}
_isNew = false;
_delegate = session;
}
public boolean isNew() {
return _isNew;
}
#Override
public String getId() {
return _delegate.getId();
}
#Override
public long getCreationTime() {
return _delegate.getCreationTime();
}
#Override
public long getLastAccessedTime() {
return _delegate.getLastAccessedTime();
}
#Override
public void setLastAccessedTime(long lastAccessedTime) {
_delegate.setLastAccessedTime(lastAccessedTime);
flushImmediateIfNecessary();
}
#Override
public void setMaxInactiveIntervalInSeconds(int interval) {
_delegate.setMaxInactiveIntervalInSeconds(interval);
flushImmediateIfNecessary();
}
#Override
public int getMaxInactiveIntervalInSeconds() {
return _delegate.getMaxInactiveIntervalInSeconds();
}
#Override
public boolean isExpired() {
return _delegate.isExpired();
}
#SuppressWarnings("unchecked")
#Override
public <T> T getAttribute(String attributeName) {
return (T) _delegate.getAttribute(attributeName);
}
#Override
public Set<String> getAttributeNames() {
return _delegate.getAttributeNames();
}
#Override
public void setAttribute(String attributeName, Object attributeValue) {
_delegate.setAttribute(attributeName, attributeValue);
putAndFlush(attributeName, attributeValue);
}
#Override
public void removeAttribute(String attributeName) {
_delegate.removeAttribute(attributeName);
putAndFlush(attributeName, null);
}
Map<String, Object> getDelta() {
return _delta;
}
private void putAndFlush(String attr, Object value) {
_delta.put(attr, value);
flushImmediateIfNecessary();
}
private void flushImmediateIfNecessary() {
if (MySessionRepository.this._flushMode == MyFlushMode.IMMEDIATE) {
MySessionRepository.this.save(this);
}
}
}
I feel like I'm missing either a configuration or something because the native WildFly SessionImpl has the correct Enhanced object for serialization when I remove the springSessionRepositoryFilter and let the container handle the session object. Additionally the Spring Controllers have the correct enhanced object. It's just MySession that does not appear to be getting it. Any thoughts?
EDIT:
Looks like the core issue is that my spring-security is using a different version of the proxy class when setting members, so I am not getting them in the MySession object. Attached are the configurations for Spring-Security
public class WebSessionInitializer extends AbstractHttpSessionInitializer {
}
#Configuration
#EnableSpringHttpSession
public class MySessionConfiguration {
#Bean
public SessionRepository sessionRepository() {
return new MySessionRepository();
}
}
<?xml version="1.0" encoding="utf-8"?>
<web-app xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:web="http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"
id="imb"
version="2.5">
<distributable />
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>
/WEB-INF/spring/configuration-spring.xml
</param-value>
</context-param>
The SpringSession filter is correctly added with my implementation. It appears the root problem is when I get into spring-security, it is setting values on a different instance of UserSessionData than the one in the MySession object. When I use #Autowired in MVC Controllers, I get the correct one.
Library Versions:
Spring: 3.2.14
Spring Session: 1.3.2
WildFly 10.1.0.Final

Related

How to have jersey inject a param based on the http header?

I have an interface and would like to open it as a REST API.
Interface:
string createToken(String username, String scopes);
REST web API:
#GET
#Path("/createToken")
#Override
public string createToken(#InjectParam String username, String scopes) {
...
}
As a simple Java API,, the interface itself makes sense - creating an access token by a specific (unique) user.
But, as an REST web API, I need a previous step to retrieve the username, based on some user data that is passed in the http header, like an SSO key.
How do I inject a value into the username - extracted from the HTTP header? Thanks.
Created a Provider to inject the value into a custom annotation. See small working example here. See source inline below as well.
The example extracts the username from an sso token. It's a dummy extraction.
* I didn't use #InjectParam.
Invocation example:
curl -X POST -H "ssoToken: 1234" http://localhost:8080/JerseyCustomParamInjection-1.0-SNAPSHOT/oauth2/createAccessToken
Custom annotation:
#Target({ElementType.PARAMETER})
#Retention(RetentionPolicy.RUNTIME)
public #interface LoggedUser {
}
Provider to do the injection:
#Provider
public class LoggedUserProvider implements
Injectable<String>,
InjectableProvider<LoggedUser, Parameter> {
#Context
private HttpServletRequest request;
public LoggedUserProvider() {
}
#Override
public Injectable<String> getInjectable(ComponentContext cc, LoggedUser a, com.sun.jersey.api.model.Parameter c) {
return this;
}
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public String getValue() {
String sso = request.getHeader("ssoToken");
if (sso == null) {
throw new WebApplicationException(Response.Status.UNAUTHORIZED);
}
// Retreive username from soo
String username = " <extracted username from sso="+sso+">";
return username;
}
}
Resource that defines the wants to inject the value:
#Path("/oauth2")
public class Resource {
#POST
#Path("/createAccessToken")
public String createAccessToken(
#LoggedUser String username
) {
return username + " <created access token using the logged in injected username>";
}
}
Servlet configuration (web.xml):
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">
<display-name>Restful Web Application</display-name>
<servlet>
<servlet-name>jersey-serlvet</servlet-name>
<servlet-class>
com.sun.jersey.spi.container.servlet.ServletContainer
</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>info.fastpace.jerseycustomparaminjection</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
</servlet>
<servlet-mapping>
<servlet-name>jersey-serlvet</servlet-name>
<url-pattern>/*</url-pattern>
</servlet-mapping>
</web-app>
You should use HeaderParam. See this
#GET
#Path("/createToken")
#Override
public string createToken(#HeaderParam("username") String username, String scopes) {
...
}
If you have to extract username and have to inject it, you will have to implement a provider:
#Provider
public class UsernameProvider
extends AbstractHttpContextInjectable<Locale>
implements InjectableProvider<Context, Type> {
#Override
public Injectable<E> getInjectable(ComponentContext compCntxt, Context cntxt, Type typ) {
if (typ.equals(String.class)) {
return this;
}
return null;
}
#Override
public ComponentScope getScope() {
return ComponentScope.PerRequest;
}
#Override
public String getValue(HttpContext httpCntxt) {
final Request rqst = httpCntxt.getRequest();
String username = null;
//Extract 'username' from Headers
return username;
}
}
Detailed explanation here

Failed startup of context com.google.appengine.tools.development.DevAppEngineWebAppContext

I am using Spring MVC,google app engine, admin sdk, cloud sql.
I want to access (preferncesDao) dao class into Filter.
Below is my filter
public class NameSpaceGoogleSecurityFilter implements Filter
{
#Autowired
IPreferencesDao preferncesDao;
public void init( FilterConfig filterConfig ) throws ServletException{
SpringUtils.init(filterConfig.getServletContext());
preferncesDao = SpringUtils.getPreferncesDao();
}
}
Below is my SpringUtils class.
public class SpringUtils {
private static ApplicationContext appContext;
private static IPreferencesDao preferncesDao = null;
public static void init(final ServletConfig config) {
init(config.getServletContext());
}
public static void init(final ServletContext context) {
if(appContext==null){
appContext =
(ApplicationContext) context.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
}
}
public static IPreferencesDao getPreferncesDao() {
if(preferncesDao==null){
preferncesDao=(IPreferencesDao) appContext.getBean("preferncesDao");
}
return preferncesDao;
}
protected SpringUtils() {
throw new UnsupportedOperationException();
}
}
When I start build process, It is throwing below exception
Failed startup of context com.google.appengine.tools.development.DevAppEngineWebAppContext
java.lang.NullPointerException.
Nullpointer at line preferncesDao=(IPreferencesDao) appContext.getBean("preferncesDao");
How can i resolve above error ? is it right way to get dao object into filter ? if not what is correct way.?
It is required to add below tag in web.xml
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
That purely indicate that ContextLoaderListener missing.
so add below code in web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>
org.springframework.web.context.ContextLoaderListener
</listener-class>
</listener>
For more details refer this link

Liferay 6.2 + Spring #Autowired not working in a hook

I am using Spring 4.0.6 with Liferay 6.2. Spring isn't able to inject autowired components in to the hook, object comes as null. I have also tried with spring version 3.1 that comes with liferay. Same code works in portlets but not in hooks.
private ApplicationEventPublisher publisher in ActivityEventPublisher.java is null.
web.xml
<?xml version="1.0"?>
<web-app version="2.4" xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee http://java.sun.com/xml/ns/j2ee/web- app_2_4.xsd">
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/spring/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>com.liferay.portal.kernel.servlet.SecurePluginContextListener</listener- class>
</listener>
<listener>
<listener-class>com.liferay.portal.kernel.servlet.PortletContextListener</listener-class>
</listener>
<servlet>
<servlet-name>ViewRendererServlet</servlet-name>
<servlet-class>org.springframework.web.servlet.ViewRendererServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ViewRendererServlet</servlet-name>
<url-pattern>/WEB-INF/servlet/view</url-pattern>
</servlet-mapping>
</web-app>
ActivityEventPublisher.java
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.context.ApplicationEventPublisherAware;
import org.springframework.stereotype.Component;
import connect.activity.solr.document.ActivityData;
#Component
public class ActivityEventPublisher implements ApplicationEventPublisherAware {
private ApplicationEventPublisher publisher;
#Override
public void setApplicationEventPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public ApplicationEventPublisher getPublisher() {
return publisher;
}
public void setPublisher(ApplicationEventPublisher publisher) {
this.publisher = publisher;
}
public void publish(ActivityData data) {
ActivityEvent event = new ActivityEvent(this);
event.setActivityData(data);
this.publisher.publishEvent(event);
}
}
Any help will be much appreciated.
Thanks
Unfortunately, autowired mechanisms are not allowed in hooks, wrappers or startup actions at Liferay.
You can implement an ApplicationContextProvider, it's so easy and useful:
#Component("applicationContextProvider")
public class ApplicationContextProvider implements ApplicationContextAware {
private static ApplicationContext ctx = null;
public static ApplicationContext getApplicationContext() {
return ctx;
}
#Override
public void setApplicationContext(ApplicationContext ac) throws BeansException {
ctx = ac;
}
}
An example of use in a Hook will be the following:
public class PostLoginActionHook extends Action {
// That code "replaces" #Autowired annotation
private final UserProxyService userProxyService = (UserProxyService) ApplicationContextProvider.
getApplicationContext().getBean(UserProxyService.class);
#Override
public void run(HttpServletRequest request, HttpServletResponse response) throws ActionException {
UserVO myCustomUser = userProxyService.getCustomUserByLiferayUser(user.getUserId());
{...}
}
Hope it helps!

Inject CDI bean into ContainerRequestFilter results into UnsatisfiedDependencyException

I'm trying to delegate the work of a request filter to a CDI bean. But Glassfish 4.0 gives me an exceptioon when I call the RestURL.
The filter definition
#Provider
#Authenticated
public class AuthenticationFilter implements ContainerRequestFilter{
#Inject
private AuthenticationService authenticationService;
#Override
public void filter(ContainerRequestContext containerRequestContext) throws IOException {
String token = containerRequestContext.getHeaderString("x-authentication-token");
if (!authenticationService.isAuthenticated(token)) {
containerRequestContext.abortWith(Response.serverError().entity("No authentication").build());
}
}
}
Defintion of the NameBinding so that we can tag specific resource URL's.
#NameBinding
#Target({ ElementType.TYPE, ElementType.METHOD })
#Retention(value = RetentionPolicy.RUNTIME)
public #interface Authenticated {
}
And the CDI bean definition.
#ApplicationScoped
public class AuthenticationService {
public boolean isAuthenticated(String token) {
boolean result = false;
if (token != null && token.length() > 0) {
....
}
return result;
}
}
And I do have a bean.xml file in the WEB-INF directory
<beans
xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee
http://xmlns.jcp.org/xml/ns/javaee/beans_1_1.xsd"
bean-discovery-mode="all">
</beans>
Thanks
Rudy

Using Spring's LocalValidatorFactoryBean with JSF

I am trying to get a bean injected into a custom ConstraintValidator. I have come across some things:
CDI is supported in validation-api-1.1.0 (Beta available)
Hibernate Validator 5 seems to implement validation-api-1.1.0 (Alpha available)
Use Seam validation module
Use Spring's LocalValidatorFactoryBean
The last one seems most appropriate for my situation since we're already using Spring (3.1.3.Release).
I have added the validator factory to the XML application context and annotations are enabled:
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.1.xsd">
<context:component-scan base-package="com.example" />
<bean id="validator" class="org.springframework.validation.beanvalidation.LocalValidatorFactoryBean" />
</beans>
The validator:
public class UsernameUniqueValidator implements
ConstraintValidator<Username, String>
{
#Autowired
private PersonManager personManager;
#Override
public void initialize(Username constraintAnnotation)
{
}
#Override
public boolean isValid(String value, ConstraintValidatorContext context)
{
if (value == null) return true;
return personManager.findByUsername(value.trim()) != null;
}
}
The validation is applied to a Person:
public class Person
{
#Username
private String username;
}
And the backing bean:
#Named
#Scope("request")
public class PersonBean
{
private Person person = new Person();
#Inject
private PersonManager personManager;
public create()
{
personManager.create(person);
}
}
And in the JSF page I have:
<p:inputText value="#{personBean.person.username}" />
The validator is invoked but the field is not autowired/injected and stays null. This of course trows a NullPointerException.
I am testing this with Hibernate validator 4.2 (since LocalValidatorFactoryBean should be able to do this I think).
I also faced the same issue. In my case Spring+MyFaces+RichFaces are used. During the application startup Spring creates it's LocalValidatorFactoryBean, but MyFaces doesn't use that bean as a validation factory. Instead MyFaces and RichFaces both used their own validators even with spring-faces module.
To figure out how to make faces use LocalValidatorFactoryBean I looked inside javax.faces.validator.BeanValidator createValidatorFactory method. This method is used by MyFaces to create ValidatorFactory every time when validation is required. Inside of that method you can see the following:
Map<String, Object> applicationMap = context.getExternalContext().getApplicationMap();
Object attr = applicationMap.get(VALIDATOR_FACTORY_KEY);
if (attr instanceof ValidatorFactory)
{
return (ValidatorFactory) attr;
}
else
{
synchronized (this)
{
if (_ExternalSpecifications.isBeanValidationAvailable())
{
ValidatorFactory factory = Validation.buildDefaultValidatorFactory();
applicationMap.put(VALIDATOR_FACTORY_KEY, factory);
return factory;
}
else
{
throw new FacesException("Bean Validation is not present");
}
}
}
So as you can see it first tries to load ValidatorFactory from context before creating a new instance. So I implemented the following solution to make faces use Spring LocalValidatorFactoryBean: I created a SystemEventListener which runs on PostConstructApplicationEvent. This listener get's a Spring WebApplicationContext from servlet context, retrieves instance of LocalValidatorFactoryBean from it and stores it in ExternalContext ApplicationMap.
public class SpringBeanValidatorListener implements javax.faces.event.SystemEventListener {
private static final long serialVersionUID = -1L;
private final Logger logger = LoggerFactory.getLogger(SpringBeanValidatorListener.class);
#Override
public boolean isListenerForSource(Object source) {
return true;
}
#Override
public void processEvent(SystemEvent event) {
if (event instanceof PostConstructApplicationEvent) {
FacesContext facesContext = FacesContext.getCurrentInstance();
onStart(facesContext);
}
}
private void onStart(FacesContext facesContext) {
logger.info("--- onStart ---");
if (facesContext == null) {
logger.warn("FacesContext is null. Skip further steps.");
return;
}
ServletContext context = getServletContext(facesContext);
if (context == null) {
logger.warn("ServletContext is not available. Skip further steps.");
return;
}
WebApplicationContext webApplicationContext = (WebApplicationContext) context.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE);
if (webApplicationContext == null) {
logger.warn("Spring WebApplicationContext was not set in ServletContext. Skip further steps.");
return;
}
LocalValidatorFactoryBean validatorFactory = null;
try {
validatorFactory = webApplicationContext.getBean(LocalValidatorFactoryBean.class);
} catch (BeansException ex){
logger.warn("Cannot get LocalValidatorFactoryBean from spring context.", ex);
}
logger.info("LocalValidatorFactoryBean loaded from Spring context.");
Map<String, Object> applicationMap = facesContext.getExternalContext().getApplicationMap();
applicationMap.put(BeanValidator.VALIDATOR_FACTORY_KEY, validatorFactory);
logger.info("LocalValidatorFactoryBean set to faces context.");
}
private ServletContext getServletContext(FacesContext facesContext) {
return (ServletContext) facesContext.getExternalContext().getContext();
}
}
So when MyFaces try to get ValidatorFactory for the first time, LocalValidatorFactoryBean is already there and MyFaces don't create a new instance.
It is definately the way to go to add your own custom ValidatorFactory to the application map using the key BeanValidator.VALIDATOR_FACTORY_KEY.
But instead of using a javax.faces.event.SystemEventListener, you could also approach it from the spring side. Registering your ValidatorFactory as an attribute in the ServletContext will be enough for it to be picked up and added to the application map (which is an abstraction for either the ServletContext or PortletContext, whatever you are using).
So the question is: how to add a spring bean as an attribute to the ServletContext. My solution was to use a helper bean that implements ServletContextAware:
public class ServletContextAttributePopulator implements ServletContextAware {
Map<String,Object> attributes;
public Map<String, Object> getAttributes() {
return attributes;
}
public void setAttributes(Map<String, Object> attributes) {
this.attributes = attributes;
}
#Override
public void setServletContext(ServletContext servletContext) {
for (Map.Entry<String,Object> entry : attributes.entrySet()) {
servletContext.setAttribute(entry.getKey(), entry.getValue());
}
}
}
Note that you could use this class for any type of bean you want to add to the ServletContext.
In your spring context, you would then add:
<bean id="servletContextPopulator" class="my.package.ServletContextAttributePopulator">
<property name="attributes">
<map>
<entry key="javax.faces.validator.beanValidator.ValidatorFactory" value-ref="validator"/>
</map>
</property>
</bean>
where "validator" is the id of your LocalValidatorFactoryBean.
I am not a spring expert, but I would expect that you either define a PersonManager in beans.xml as well or that you annotate it with #Component. See also Autowiring Unmanaged Beans Annotated With #Component

Resources