Tags
I’ve been testing out the Vaadin framework lately on some personal projects and was very impressed with the look and feel and also as to how easy it was to get it up and running. If you are a Java developer the learning curve is almost non-existent as all the work is done purely on the server side and Vaadin takes care of all the client side stuff for you.
I also love the productivity gains that the Spring Framework offers but unfortunately all the resources I found on integrating both of these frameworks seemed to be either outdated or quite incomplete. Because of these reasons I thought I’ll get a simple demo application going and document the things that had to be done to integrate both of these frameworks together.
The Demo Application
A sample project demonstrating Vaadin and Spring working together can be found in my GitHub repository here.
The demo application consists of the following classes:
VaadinSpringDemoApplication: extension ofcom.vaadin.Application, the entry point of the application.MainWindow: extension ofcom.vaadin.ui.Windowused to demonstrate session scoped object injection.TextLabelService: used to demonstrate service object injection.
The VaadinSpringDemoApplication class uses Spring Autowiring to inject an instance of MainWindow, which in turn uses Autowiring to inject an instance of TextLabelService.
Running the Demo Application
The demo application can be run by issuing the command:
mvn jetty:run
You can then view the application by visiting the URL localhost:8080
Dependency Injection of Vaadin Components
Using dependency injection with Vaadin components aren’t as straight forward as it is with usual Spring components due to the following two reasons:
- One issue here is that the Vaadin components are not managed by the Spring container. The
VaadinSpringDemoApplicationclass is instantiated and managed by the servlet container and since the Spring container does not take part in the management of this class, it cannot inject any of the dependencies required by it or any of the classes used by it. We need to get the AspectJ compiler (or loadtimeweaver) involved in this process so that these dependencies can be injected as part of the instantiation of the mainApplicationclass. - We would like some of the injected components to be instantiated for each new session so that state associated with one user is not shared or affected by that of another. Normally its good practice for components to not contain user or session specific state so that the same component instances can be shared between sessions, but this becomes unavoidable when it comes to UI components and such. We get around this issue by scoping such components to the current session.
Configuring the Application
Configuring Maven
First we need to configure Maven by adding the following dependencies:
<dependencies>
<dependency>
<groupId>com.vaadin</groupId>
<artifactId>vaadin</artifactId>
<version>${vaadin.version}</version>
</dependency>
<!-- Spring -->
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-web</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
<version>${spring.version}</version>
</dependency>
<dependency>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
<version>${spring.version}</version>
</dependency>
<!-- Required due to a bug in Spring, see https://jira.springsource.org/browse/SPR-6819 -->
<dependency>
<groupId>javax.persistence</groupId>
<artifactId>persistence-api</artifactId>
<version>1.0</version>
<optional>true</optional>
</dependency>
<!-- AspectJ dependencies -->
<dependency>
<groupId>org.aspectj</groupId>
<artifactId>aspectjrt</artifactId>
<version>${aspectj.version}</version>
</dependency>
</dependencies>
In addition to the spring dependencies we also need to configure the AspectJ plugin so that we can get compile time weaving going so as to enable dependency injection:
<build>
<plugins>
<plugin>
<groupId>org.codehaus.mojo</groupId>
<artifactId>aspectj-maven-plugin</artifactId>
<version>${aspectj-maven-plugin.version}</version>
<configuration>
<!-- Required as the plugin does not resolve this by default -->
<source>${maven.compiler.source}</source>
<target>${maven.compiler.target}</target>
<aspectLibraries>
<aspectLibrary>
<groupId>org.springframework</groupId>
<artifactId>spring-aspects</artifactId>
</aspectLibrary>
</aspectLibraries>
</configuration>
<executions>
<execution>
<goals>
<goal>compile</goal>
<goal>test-compile</goal>
</goals>
</execution>
</executions>
</plugin>
</plugins>
</build>
Configuring Spring
Now that we’ve configured maven, we have to tell Spring where to find our components so that they can be injected by Spring. We do this by introducing a spring context file with the following content:
<?xml version="1.0" encoding="UTF-8"?>
<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.0.xsd
http://www.springframework.org/schema/context http://www.springframework.org/schema/context/spring-context-3.0.xsd">
<context:spring-configured/>
<context:component-scan base-package="com.navinpeiris.vaadin_spring"/>
</beans>
Configuring the Servlet Container
Since we want Spring to know when the servlet container instantiates the resources we want configured, we need to add the following to the web.xml file:
...
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>classpath*:applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
...
Since we also want Spring to be aware of when a new sessions are started, we also need to add the following:
...
<listener>
<listener-class>org.springframework.web.context.request.RequestContextListener</listener-class>
</listener>
...
Annotating the Required Classes
Firstly, we want to annotate the main Application class with the @Configurable annotation so that Spring knows that this class is eligible for Spring driven configuration:
@Configurable
public class VaadinSpringDemoApplication extends Application {
Service Objects
Next, we need to annotate the necessary component classes so that they become eligible for dependency injection.
We would like some components to be shared across all the user sessions, for example, those providing common services, management functions, or data access objects. The TextLabelService is used as a demonstration of such a class in our example application.
We make service level objects applicable for injection by simply annotating the class with the @Component annotation, for example:
@Component
public class TextLabelService {
Session Scoped Objects
In comparison, we would like some components to be instantiated per session, for example Vaadin UI components that contain session specific state. We can achieve this by annotating such classes with the @Scope("session") annotation. The RequestContextListener that we added to web.xml will then help us instantiate a new instance of such objects per session. An example of such a class in our demo application is the MainWindow class which is annotated as follows:
@Component
@Scope("session")
public class MainWindow extends Window {
Injecting the Components
We can now inject the components configured in above using the @Autowire annotation as follows:
Now that we’ve got all this setup, we can inject the MainWindow class into any other class using the @Autowire annotation as follows:
@Autowired
private MainWindow mainWindow;
....
@Autowired
private TextLabelService textLabelService;
Demonstrating Spring Integration
If we now startup our demo application and navigate to localhost:8080 we should be able to see the following text in the browser:
Vaading-Spring Demo MainWindow instanceId: 1 TextLabelService instanceId: 1
This shows us that both of the required components were successfully injected, and that everything works as expected.
If we now open up another browser (to force a new session to be started) and go to the same URL, we should see text similar to:
Vaading-Spring Demo MainWindow instanceId: 2 TextLabelService instanceId: 1
This shows us that a new instance of the MainWindow was created for this session, but we are still using the same instance of TextLabelService as the previous session.
If you use that approach for Custom Components as well you might run into the problem that the same component cannot be used twice in the same session. So it limits the re-usability of components.
We usually have a SpringUtils bean which is defined as a bean and has static properties. The static properties contain references to spring beans. So components can call SpringUtils.userManager. Components themselves are not Spring beans. Of course its not IOC for components then.
Example:
class SpringUtils {
public static TextForFieldService textForFieldService;
@Required
public void setTextForFieldService(TextForFieldService textForFieldService) {
SpringUtils.textForFieldService = textForFieldService;
}
}
That’s interesting, I haven’t yet come across this problem. I’m not a big fan of the approach you’ve shown, but have used the same before as a temporary fix when I couldn’t get a Spring managed bean injected into an JPA managed event listener. I still manage to reuse components such as various services and managers by not including the
@Scope("session")annotation. Maybe I’m missing something, I’ll check it out and update the post.Pingback: Spring integration suggestions - Forum - vaadin.com
Great post
off:
The project can’t be imported by “Existing project into Workspace.”
and mvn eclipse:eclipse could not get over the source.
what is the best way to try it in action ?
Just tried this again from scratch and works fine for me. What’s the error you get?
Hello, sorry for the late reply, here comes the pic.
http://img26.imageshack.us/img26/8201/image000il.png
Anyway, I have rebuilt the project from the scratch.
Seems like mvn eclipse:eclipse hasn’t generated the .project and .classpath files properly. Have you tried doing a “import as maven project” instead?
No, because I managed it, and the project is up and running. But I will have another questions about your solution later.
Thanks! Very helpful!!
Hi, I tried this example w/o success.
The AOP is being used but the injection simply does not work.
Could you please post your web.xml ? I guess I’m missing something there.
Regards
All the code for the project is available at https://github.com/navinpeiris/vaadin-spring The web.xml is available at https://github.com/navinpeiris/vaadin-spring/blob/master/src/main/webapp/WEB-INF/web.xml
Good luck with it, please let us know how it goes.
Hi,
I’ll take a closer look at the full example, thanks for the quick response.
As soon as I make some progress I return to post my experiences.
Regards
I created a new vaadin project. put the code and jar files into project.
I start the server, eclipse console display errors:
org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘vaadinSpringDemoApplication’: Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.navinpeiris.vaadin_spring.MainWindow com.navinpeiris.vaadin_spring.VaadinSpringDemoApplication.mainWindow; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘mainWindow’: Scope ‘session’ is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:287)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:549)
at org.mortbay.jetty.servlet.Context.startContext(Context.java:136)
at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1282)
at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:518)
at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:499)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
at org.mortbay.jetty.Server.doStart(Server.java:224)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
at runjettyrun.Bootstrap.main(Bootstrap.java:82)
2012-01-24 16:12:22.749:WARN::Nested in org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘vaadinSpringDemoApplication’: Injection of autowired dependencies failed; nested exception is org.springframework.beans.factory.BeanCreationException: Could not autowire field: private com.navinpeiris.vaadin_spring.MainWindow com.navinpeiris.vaadin_spring.VaadinSpringDemoApplication.mainWindow; nested exception is org.springframework.beans.factory.BeanCreationException: Error creating bean with name ‘mainWindow’: Scope ‘session’ is not active for the current thread; consider defining a scoped proxy for this bean if you intend to refer to it from a singleton; nested exception is java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.:
java.lang.IllegalStateException: No thread-bound request found: Are you referring to request attributes outside of an actual web request, or processing a request outside of the originally receiving thread? If you are actually operating within a web request and still receive this message, your code is probably running outside of DispatcherServlet/DispatcherPortlet: In this case, use RequestContextListener or RequestContextFilter to expose the current request.
at org.springframework.web.context.request.RequestContextHolder.currentRequestAttributes(RequestContextHolder.java:131)
at org.springframework.web.context.request.SessionScope.get(SessionScope.java:90)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:328)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.findAutowireCandidates(DefaultListableBeanFactory.java:848)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.doResolveDependency(DefaultListableBeanFactory.java:790)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.resolveDependency(DefaultListableBeanFactory.java:707)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor$AutowiredFieldElement.inject(AutowiredAnnotationBeanPostProcessor.java:478)
at org.springframework.beans.factory.annotation.InjectionMetadata.inject(InjectionMetadata.java:87)
at org.springframework.beans.factory.annotation.AutowiredAnnotationBeanPostProcessor.postProcessPropertyValues(AutowiredAnnotationBeanPostProcessor.java:284)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.populateBean(AbstractAutowireCapableBeanFactory.java:1106)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.doCreateBean(AbstractAutowireCapableBeanFactory.java:517)
at org.springframework.beans.factory.support.AbstractAutowireCapableBeanFactory.createBean(AbstractAutowireCapableBeanFactory.java:456)
at org.springframework.beans.factory.support.AbstractBeanFactory$1.getObject(AbstractBeanFactory.java:294)
at org.springframework.beans.factory.support.DefaultSingletonBeanRegistry.getSingleton(DefaultSingletonBeanRegistry.java:225)
at org.springframework.beans.factory.support.AbstractBeanFactory.doGetBean(AbstractBeanFactory.java:291)
at org.springframework.beans.factory.support.AbstractBeanFactory.getBean(AbstractBeanFactory.java:193)
at org.springframework.beans.factory.support.DefaultListableBeanFactory.preInstantiateSingletons(DefaultListableBeanFactory.java:585)
at org.springframework.context.support.AbstractApplicationContext.finishBeanFactoryInitialization(AbstractApplicationContext.java:913)
at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:464)
at org.springframework.web.context.ContextLoader.configureAndRefreshWebApplicationContext(ContextLoader.java:384)
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:283)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:111)
at org.mortbay.jetty.handler.ContextHandler.startContext(ContextHandler.java:549)
at org.mortbay.jetty.servlet.Context.startContext(Context.java:136)
at org.mortbay.jetty.webapp.WebAppContext.startContext(WebAppContext.java:1282)
at org.mortbay.jetty.handler.ContextHandler.doStart(ContextHandler.java:518)
at org.mortbay.jetty.webapp.WebAppContext.doStart(WebAppContext.java:499)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
at org.mortbay.jetty.handler.HandlerWrapper.doStart(HandlerWrapper.java:130)
at org.mortbay.jetty.Server.doStart(Server.java:224)
at org.mortbay.component.AbstractLifeCycle.start(AbstractLifeCycle.java:50)
at runjettyrun.Bootstrap.main(Bootstrap.java:82)
2012-01-24 16:12:22.785:INFO::Started SelectChannelConnector@0.0.0.0:8080
How to solve this problem? Thanks!
Sorry, can’t be of much help on this one without having access to your project configuration and such, and I’ve migrated away from Eclipse towards IntelliJ too. I did however fire it up again using the jetty:run command through maven, and it all worked fine. If you’re still unable to sort it out, tar or zip up your eclipse project and send it over, and I’ll take a look at it for you. Good luck!
Do you hava a email?
So I can send eclipse project to you.
Hi,
I got an error, could you help me about this please? Thanks
javax.servlet.ServletException: javax.servlet.ServletException: No window found. Did you remember to setMainWindow()?
com.vaadin.terminal.gwt.server.AbstractApplicationServlet.handleServiceException(AbstractApplicationServlet.java:1004)
com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:548)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
Hi Jan,
This normally means that the spring configuration for the application did not take place. Since spring wasn’t involved the
mainWindowinstance inVaadinSpringDemoApplicationwill not be autowired, and hence benullwhen the init method runs. Are you running the build through maven? It’s essential that theaspectjcompiltation process takes place so that the instantiation of these classes occur through spring.aop aspectj-autoproxy was missing in source. so i was getting servletException
hi navinpeiris,
i have the same problem, plz help if u hv any idea to resolve it.
Hi k veerababu,
i m also getting the same exception:-
javax.servlet.ServletException: javax.servlet.ServletException: No window found. Did you remember to setMainWindow()?
com.vaadin.terminal.gwt.server.AbstractApplicationServlet.handleServiceException(AbstractApplicationServlet.java:1004)
com.vaadin.terminal.gwt.server.AbstractApplicationServlet.service(AbstractApplicationServlet.java:548)
javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
plz help me to solve this, if possible post the code whatever u added.
thanks
Mantu
resolved……!
Nice and can you tell us how? Please… because I still have also the same issue.
Okay, I found it by myself:
was missing in the applicationContext.xml.
How did you guys solve this javax.servlet.ServletException: javax.servlet.ServletException: No window found. Did you remember to setMainWindow()? . Can you please be specific.
Hi Navin,
Does your tutorial work for tomcat and eclipse because you mentioned to use jetty:run which is specific to jetty. Can you please give an alternative for tomcat and eclipse as I know atleast 60% of the developers will be using tomcat and eclipse. It will be of great help.
Hi Ravi,
You would be able to deploy the war on any app server and it would work fine. It’s a good point about eclipse + tomcat though, I’ll update the post to include a section on that this weekend.
Hi Navin,
Thanks for the info. But, to take it a step further, have you tried to integrate Vaadin with Spring Security? My main interest here is to see a vaadin application with a login form built in Vaadin and with Spring Security in the mix. I’ve tried the jsp approach but i don’t like it, it just doesn’t feel wright.
Hi Marius,
Unfortunately haven’t had a chance to try that out. I’ll try to update the post some point soon after I’ve had a chance to play around with it, and let u know if I do.
Thank you. I’m trying it out now, if i get some good results i’ll share them.
helo. i tried ur all settings without maven but included all jar files, i still not figured it out as its showing
javax.servlet.ServletException: javax.servlet.ServletException: No window found. Did you remember to setMainWindow()?
please somebody help me. i want to know why this is happening with me. Post me the complete working example to my mail sherupanda@gmail.com
Pingback: Vaadin Spring Integration |
How did you solve this javax.servlet.ServletException: javax.servlet.ServletException: No window found. Did you remember to setMainWindow()?
what was missing in the applicationContext.xml. Can you please be specific!?
Hi Wassel,
Please see my comment at: http://navinpeiris.com/2011/06/13/vaadin-6-spring-3-integration-2/#comment-195
i added a helper class
import com.vaadin.Application;
import com.vaadin.terminal.gwt.server.WebApplicationContext;
import javax.servlet.ServletContext;
import org.springframework.context.ApplicationContext;
import org.springframework.web.context.support.WebApplicationContextUtils;
public class SpringContextHelper {
private ApplicationContext context;
public SpringContextHelper(Application application) {
ServletContext servletContext = ((WebApplicationContext) application.getContext()).getHttpSession().getServletContext();
context = WebApplicationContextUtils.getRequiredWebApplicationContext(servletContext);
}
public Object getBean(final String beanRef) {
return context.getBean(beanRef);
}
}
then i modified the init method
public void init() {
SpringContextHelper helper = new SpringContextHelper(this);
setMainWindow((MainWindow) helper.getBean(“mainWindow”));
}
Now it works!!
When vaadin dell road add on i am facing a problem with singleton pattern i.e., when i login to the application in one browser it is going to home page correctly, again when i login in another browser it is directly going to home page.
plz help me how to resolve this problem.
Thanks,
Ravi teja.