Spring - context scan fail with requestfactory (gwt) - spring

in a gwt web application i use spring.
if i use in my applicationContext.xml file
<context:component-scan base-package="com.test.**"/>
my bean is not found, i need to manually declare it in the applicationContext file.
my web.xml file
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>gwtRequest</servlet-name>
<servlet-class>com.google.web.bindery.requestfactory.server.RequestFactoryServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gwtRequest</servlet-name>
<url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>welcomeGWT.html</welcome-file>
</welcome-file-list>
Is there any reason why component scan fail?
Locator class
public class AccountLocator extends Locator<Account, Long> {
#Autowired
private AccountDAO accountDAO;
...
}
Spring service locator
public class SpringServiceLocator implements ServiceLocator {
#Override
public Object getInstance(Class<?> clazz) {
HttpServletRequest request = RequestFactoryServlet.getThreadLocalRequest();
ServletContext servletContext = request.getSession().getServletContext();
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(servletContext);
return context.getBean(clazz);
}
}
Server layer
#Service
public class AccountServiceImpl implements AccountService{
#Autowired
private AccountDAO accountDAO;
...
}
Dao layer
#Repository
public class AccountDAOImpl implements AccountDAO{
...
}

Related

Autowiring bean in Session Listener

I need to Autowire a service class annotated with #Service annotation in my session listener class as I need to perform some DB operation on session destroyed method. I am not able to autowire the service class as I have added the listener in my web.xml and it is no longer spring managed. I have tried several options(workarounds) like getting a bean from application context via servlet context but I am not getting any beans in that way.
Following are my classes:-
MyService:
#Service
#Transactional
public class FxTransactionService{
//some autowirings
public void performDBoperation(Long id)
{
//business logic
}
}
Session Listener:
public class SessionHandler implements HttpSessionListener {
private final Logger logger = LoggerFactory.getLogger(this.getClass());
#Autowired
private MyService myService;
#Override
public void sessionCreated(HttpSessionEvent arg0) {
System.out.println("Session created");
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(arg0.getSession()
.getServletContext());
System.out.println(Arrays.toString(context.getBeanDefinitionNames()));
//This gives me empty list
}
#Override
public void sessionDestroyed(HttpSessionEvent arg0) {
Long id = (Long) arg0.getSession().getAttribute("Id");
myService.performDBoperation(id);
}
}
web.xml:
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE web-app
PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/xml/ns/j2ee/web-app_2_5.xsd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<listener>
<listener-class>com.abc.controller.SessionHandler</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.util.Log4jConfigListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>/WEB-INF/classes/log4j.properties</param-value>
</context-param>
<filter>
<filter-name>preAuthHeaderAdditionFilter</filter-name>
<filter-class>com.abc.filter.PreAuthHeaderAdditionFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>preAuthHeaderAdditionFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- <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> -->
</web-app>
First install the Spring listener ContextLoaderListener.
In your own listener you can access the context using WebApplicationContextUtils.
It is not autowiring though, you have to fetch the required bean/service yourself.

Spring autowiring fails in RESTeasy service

I have a simple service which fails to autowire bean.
Although getting the same bean through context succeeds.
So the bean creation and registration in repository is working, but autowiring does not.
Changing class of the field in the service (MyRepository -> YourRepository), there is an error thrown that such bean does not exists, so the autowiring mechanism is working.
Any ideas what might be missing?
#Component
#Path("/")
public class RestService {
#Autowired
private MyRepository myRepository; // is not autowired and is null
#GET
#Path("/{param}")
#Produces(MediaType.APPLICATION_JSON)
public Response printMessage(#PathParam("param") String msg) {
return Response.ok(
AppContext.getContext().getBean("myRepository") == myRepository)
.build(); // false
}
public void setMyRepository(MyRepository myRepository) {
this.myRepository = myRepository;
}
}
AppContext above is my simple implementation of ApplicationContextAware
Repository
#Repository
public interface MyRepository extends MongoRepository<MyEntity, String> {
}
There is no .xml configuration and spring is initialized through
public class MyInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
container.addListener(new ResteasyBootstrap());
final AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
ContextLoaderListener springListener = new ContextLoaderListener(rootContext);
rootContext.register(MyConfiguration.class);
container.addListener(springListener);
}
}
And configuration class
#Configuration
#EnableMongoRepositories("my.package.repository")
#ComponentScan("my.package")
public class MyConfiguration {
#Bean
public MongoTemplate mongoTemplate() throws UnknownHostException {
return new MongoTemplate(new MongoClient("localhost"), "db");
}
}
EDIT after 2 comments
I'm using the following library for RESTeasy - spring integration.
Do I need some other?
<dependency>
<groupId>org.jboss.resteasy</groupId>
<artifactId>resteasy-spring</artifactId>
<version>3.0.8.Final</version>
</dependency>
I think JAX-RS and RESTeasy is working correctly, because service is working and I can access it through web when deployed on JBoss
EDIT for workaround
Service is initialized correctly if I create the following constructor, but it feels more like a workaround
public RestService() {
this.myRepository = MyContext.getContext().getBean(MyRepository.class);
}
And MyContext class for more clarity
#Component
public class MyContext implements ApplicationContextAware {
private static ApplicationContext context;
#Override
public void setApplicationContext(ApplicationContext applicationContext) throws BeansException {
context = applicationContext;
}
public static ApplicationContext getContext() {
return context;
}
}
Spring might be setup correctly, but that doesn't necessary mean that Spring+RestEasy integration is setup correctly.
The code I am posting is the web.xml configuration that I have used (with RestEasy 3.0.6 and Spring 3.2.8) and correctly sets up the integration between RestEasy and Spring and also sets up Spring MVC (everything under /api is handled by RestEasy, everything else is handled by Spring MVC).
<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>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</context-param>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>my.package.config.ApplicationConfig</param-value>
</context-param>
<context-param>
<param-name>spring.profiles.active</param-name>
<param-value>web</param-value>
</context-param>
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/api</param-value>
</context-param>
<!-- Spring + RESTEasy -->
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.jboss.resteasy.plugins.spring.SpringContextLoaderListener</listener-class>
</listener>
<!-- RESTEasy Servlet-->
<servlet>
<servlet-name>Resteasy</servlet-name>
<servlet-class>org.jboss.resteasy.plugins.server.servlet.HttpServletDispatcher</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>Resteasy</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
<!-- Spring MVC Servlet -->
<servlet>
<servlet-name>mvc-dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextClass</param-name>
<param-value>org.springframework.web.context.support.AnnotationConfigWebApplicationContext</param-value>
</init-param>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>my.package.config.MvcConfig</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>mvc-dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file></welcome-file>
</welcome-file-list>
<error-page>
<error-code>404</error-code>
<location>/WEB-INF/jsp/error404.jsp</location>
</error-page>
</web-app>
Managed to solve the issue myself.
The correct solution is either to replace
public class MyInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
container.addListener(new ResteasyBootstrap());
final AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
ContextLoaderListener springListener = new ContextLoaderListener(rootContext);
rootContext.register(MyConfiguration.class);
container.addListener(springListener);
}
}
with
public class MyInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
container.addListener(new ResteasyBootstrap());
container.addListener(new SpringContextLoaderListener());
}
}
but in this case I'm losing possibility to use AnnotationConfigWebApplicationContext.
Also the initializer can be changed as follows to preserve annotation context.
public class MyInitializer implements WebApplicationInitializer {
#Override
public void onStartup(ServletContext container) throws ServletException {
container.addListener(new ResteasyBootstrap());
final AnnotationConfigWebApplicationContext rootContext = new AnnotationConfigWebApplicationContext();
ContextLoaderListener springListener = new ContextLoaderListener(rootContext) {
#Override
protected ContextLoader createContextLoader() {
return new SpringContextLoader();
}
};
rootContext.register(MyConfiguration.class);
container.addListener(springListener);
}
}

Spring 2.5.5 and jersey with autowired

I am trying to integrate jersey to an existing Spring application (Spring 2.5.5).
Jersey is working fine, but however when I AutoWire an existing spring bean, the object is null.
Below is my web.xml
<servlet>
<servlet-name>fs3web</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet>
<servlet-name>jersey-servlet</servlet-name>
<servlet-class>com.sun.jersey.spi.spring.container.servlet.SpringServlet</servlet-class>
<init-param>
<param-name>com.sun.jersey.config.property.packages</param-name>
<param-value>com.fl.fs3.api;org.codehaus.jackson.jaxrs</param-value>
</init-param>
<init-param>
<param-name>com.sun.jersey.api.json.POJOMappingFeature</param-name>
<param-value>true</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>fs3web</servlet-name>
<url-pattern>/fs3/*</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>jersey-servlet</servlet-name>
<url-pattern>/api/*</url-pattern>
</servlet-mapping>
And, here my application context xml (obviously this is not complete, since this is a huge application, there is much more bean definitions):
TestPojo is my bean I would like to autowire to my jersey resource.
<context:annotation-config />
<aop:aspectj-autoproxy/>
<context:component-scan base-package="com.fl.fs3.api,com.fl.fs3.integration.*.web"/>
Both my jersey resource class and POJO class is in package com.fl.fs3.api
#Component
#Path("/v1/site")
public class SitesApiControllerV1 {
#Autowired TestPojo testPojo;
#GET
#Path("/{folderName}")
#Produces(MediaType.APPLICATION_JSON)
public Response getSite(#PathParam("folderName") String folderName) {
System.out.println("pojo obj:" + testPojo);
return Response.ok("info for " + folderName).build();
}
}
#Component
public class TestPojo {
}
When I start my tomcat, I do not see the expected line in logs:
INFO: Registering Spring bean, hello, of type ..... as a root resource class
When I invoke my service /v1/site/xyz, testPojo object is null.
However, before integrating this to my existing project, I did a sample jersey+spring application, and it worked perfectly. I was able to see 'Registering Spring bean' line in logs.
Any help is appreciated.
Try this, it may be more simplified:
Load spring through web.xml like shown below as normal spring confifuration:
<servlet>
<servlet-name>project-spring</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<init-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath:project-spring-servlet.xml</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>project-spring</servlet-name>
<url-pattern>*.htm</url-pattern>
</servlet-mapping>
Now load your jersey Resources through Application as shown below:
#ApplicationPath("/rest")
public class ResourceLoader extends Application
{
/* (non-Javadoc)
* #see javax.ws.rs.core.Application#getClasses()
*/
#Override
public Set<Class<?>> getClasses()
{
Set<Class<?>> classes = new HashSet<Class<?>>();
loadResourceClasses(classes);
return classes;
}
private void loadResourceClasses(Set<Class<?>> classes)
{
classes.add(StudentResource.class);
}
}
Then in your resource:
#Path("student")
class StudentResource
{
private StudentService studentService;
StudentResource(#Context ServletContext servletContext)
{
ApplicationContext applicationContext = WebApplicationContextUtils.getWebApplicationContext(servletContext);
this.transactionService = applicationContext.getBean(StudentService .class);
}
}
There you go.
Spring has been configured with all dependency injections with Jersey!

RestEasy with Spring renders no answer

I can see that the FilterDispatcher is called (by debugger), but it doesn't seem to find the service to call. I've got trouble grasping how RestEasy actually maps between resources defined in Spring and RestEasy.
Main story: Getting http://my.local.no:8087/rest/typeaheads/h only renders 404
web.xml:
...
<context-param>
<param-name>resteasy.servlet.mapping.prefix</param-name>
<param-value>/rest</param-value>
</context-param>
<listener>
<listener-class>org.jboss.resteasy.plugins.server.servlet.ResteasyBootstrap</listener-class>
</listener>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<filter>
<filter-name>restFilterDispatcher</filter-name>
<filter-class>org.jboss.resteasy.plugins.server.servlet.FilterDispatcher</filter-class>
</filter>
<filter-mapping>
<filter-name>restFilterDispatcher</filter-name>
<url-pattern>/rest/*</url-pattern>
</filter-mapping>
...
resteasy resource is set up by bean:
#Configuration
#ComponentScan(basePackageClasses = TypeaheadsRestService.class)
public class SpringConfig {
}
TypeaheadsRestService.java:
#Resource
#Path("/typeaheads")
public class TypeaheadsRestService {
#GET
#Path("/{search}")
#Produces(MediaType.APPLICATION_JSON)
public List<NameUrl> get(#PathParam("search") String search) {
...
}
}
The RestEasy SpringContextLoaderListener seem to be the missing part. I created a stripped down problem from RestEasy example and used it. For my somewhat more complex application however it would not work. That is probably because it overrides the deprecated createContextLoader-method. In Spring 3 ContextLoaderListener is an instance of ContextLoader. So I reimplemented it like this:
public class MyContextLoaderListener extends ContextLoaderListener {
private SpringContextLoaderSupport springContextLoaderSupport = new SpringContextLoaderSupport();
#Override
protected void customizeContext(ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) {
super.customizeContext(servletContext, applicationContext);
this.springContextLoaderSupport.customizeContext(servletContext, applicationContext);
}
}
I originally tried to do the customizeContext(...) in a bean initialisation. That worked in RestEasy 2.2.1.GA, but not in 2.3.4.FINAL.

NoSuchBeanDefinitionException with spring and gwt (requestFactory)

I get this error with a gwt (using requestfactory) and spring
org.springframework.beans.factory.NoSuchBeanDefinitionException: No unique bean of type [org.calibra.server.service.AccountService] is defined: expected single bean but found 0:
at org.springframework.beans.factory.support.DefaultListableBeanFactory.getBean(DefaultListableBeanFactory.java:271)
at org.springframework.context.support.AbstractApplicationContext.getBean(AbstractApplicationContext.java:1101)
at org.calibra.server.SpringServiceLocator.getInstance(SpringServiceLocator.java:24)
at com.google.web.bindery.requestfactory.server.LocatorServiceLayer.createServiceInstance(LocatorServiceLayer.java:56)
My service locator
public class SpringServiceLocator implements ServiceLocator {
#Override
public Object getInstance(Class<?> clazz) {
ApplicationContext context = WebApplicationContextUtils.getWebApplicationContext(
RequestFactoryServlet.getThreadLocalServletContext());
return context.getBean(clazz);
}
}
My spring service
#Service
public class AccountServiceImpl implements AccountService{
#Override
public void addNewAccount(Account account) {
...
}
#Override
public List<Account> loadAllAccounts() {
...
}
}
Gwt requestContext, reference my spring service
#Service(value=AccountService.class, locator=SpringServiceLocator.class)
public interface AccountRequest extends RequestContext {
Request<Void> addNewAccount(AccountProxy account);
Request<List<AccountProxy>> loadAllAccounts();
}
my web.xml
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>gwtRequest</servlet-name>
<servlet-class>com.google.web.bindery.requestfactory.server.RequestFactoryServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>gwtRequest</servlet-name>
<url-pattern>/gwtRequest</url-pattern>
</servlet-mapping>
<welcome-file-list>
<welcome-file>welcomeGWT.html</welcome-file>
</welcome-file-list>
I don't understand how i can have 0 AccountService beans ?
i tried to add in the dispatcher-servlet
<bean id="accountService" class="org.calibra.server.service.AccountServiceImpl"/>
I got the same result
Any idea?
edit: if somebody have a full complete example, that could be useful.
I think using the ContextLoaderListener alone is not enough as you don't seem to have the DispatcherServlet in use (have you?).
The following lines work for me:
<filter>
<filter-name>springRequestContextFilter</filter-name>
<filter-class>org.springframework.web.filter.RequestContextFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>springRequestContextFilter</filter-name>
<url-pattern>/gwtRequest</url-pattern>
</filter-mapping>
I've seen this question in a couple of other places. You should try explicity defining the AccountServiceImpl as a bean in your applicationContext.xml (not the dispatch-servlet.xml) first and see if you still get the error, if you don't then you know it's that you're missing the component-scan in your application context xml which is what I think is the case.
hope this helps

Resources