Ask Questions and Find Answers
Important:
Ask is now read-only. You can review any existing questions and answers, but not add anything new.
But - don't panic! While ask is no more, we've replaced it with discuss - the new Liferay Discussion Forum! Read more here here or just visit the site here:
discuss.liferay.com
RE: service builder + spring servlet in liferay 6
Hi!
In Liferay 6.0.5, I am implementing a GWT portlet that uses servlets on the server side. I am using spring to inject e.g. dao beans into the servlets. The relevant part of web.xml:
Lately, I have extended the portlet by a service using the service builder. As a consequence, my web.xml gets modified during a deploy, causing my servlets not to work anymore.
What happens is that a definition of a PortletContextLoaderListener, needed by the service, is appended to the web.xml. I get an error during deployment in this case.
This is a bit mysterious, nevertheless the deploy succeeds. The service, however, does not work. I get the following 'BeanLocator is null' error when I first try to use the service.
After searching the forums, I got the impression that the ContextLoaderListener of spring and the PortletContextLoaderLister of Liferay might be in conflict. Therefore, I changed the ContextLoaderLister to a PortletContextLoaderLister in my web.xml:
I have even tried to add a 'contextClass' parameter (in comments above), but the result was the same: deploy succeeds, but injection does not work in my servlets. Spring does not find the application context.
It seems like the PortletContextLoaderListener does not load my bean definitions from my applicationContext.xml. Or could it be possible, that this context is not accessible for my servlets?
Any idea?
Tamás
In Liferay 6.0.5, I am implementing a GWT portlet that uses servlets on the server side. I am using spring to inject e.g. dao beans into the servlets. The relevant part of web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/conf/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<!-- SpringGwt remote service servlet -->
<servlet>
<servlet-name>springGwtRemoteServiceServlet</servlet-name>
<servlet-class>org.spring4gwt.server.SpringGwtRemoteServiceServlet</servlet-class>
</servlet>
Lately, I have extended the portlet by a service using the service builder. As a consequence, my web.xml gets modified during a deploy, causing my servlets not to work anymore.
What happens is that a definition of a PortletContextLoaderListener, needed by the service, is appended to the web.xml. I get an error during deployment in this case.
15:34:53,120 INFO [[/modelexploration-portlet]] Initializing Spring root WebApplicationContext
15:34:54,796 ERROR [PortalClassLoaderServletContextListener] java.lang.IllegalStateException: Cannot initialize context because there is already
a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!
java.lang.IllegalStateException: Cannot initialize context because there is already a root application context present - check whether you have m
ultiple ContextLoader* definitions in your web.xml!
at org.springframework.web.context.ContextLoader.initWebApplicationContext(ContextLoader.java:179)
at org.springframework.web.context.ContextLoaderListener.contextInitialized(ContextLoaderListener.java:47)
at com.liferay.portal.spring.context.PortletContextLoaderListener.contextInitialized(PortletContextLoaderListener.java:71)
at com.liferay.portal.kernel.servlet.PortalClassLoaderServletContextListener.doPortalInit(PortalClassLoaderServletContextListener.java:91)
at com.liferay.portal.kernel.util.BasePortalLifecycle.portalInit(BasePortalLifecycle.java:42)
at com.liferay.portal.kernel.util.PortalLifecycleUtil.register(PortalLifecycleUtil.java:52)
at com.liferay.portal.kernel.util.BasePortalLifecycle.registerPortalLifecycle(BasePortalLifecycle.java:50)
at com.liferay.portal.kernel.servlet.PortalClassLoaderServletContextListener.contextInitialized(PortalClassLoaderServletContextListener.java:52)
at org.apache.catalina.core.StandardContext.listenerStart(StandardContext.java:3910)
at org.apache.catalina.core.StandardContext.start(StandardContext.java:4393)
at org.jboss.web.tomcat.service.deployers.TomcatDeployment.performDeployInternal(TomcatDeployment.java:310)
at org.jboss.web.tomcat.service.deployers.TomcatDeployment.performDeploy(TomcatDeployment.java:142)
at org.jboss.web.deployers.AbstractWarDeployment.start(AbstractWarDeployment.java:461)
at org.jboss.web.deployers.WebModule.startModule(WebModule.java:118)
at org.jboss.web.deployers.WebModule.start(WebModule.java:97)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
...
This is a bit mysterious, nevertheless the deploy succeeds. The service, however, does not work. I get the following 'BeanLocator is null' error when I first try to use the service.
5:04:25,413 ERROR [PortletBeanLocatorUtil] BeanLocator is null
15:04:25,429 ERROR [[/modelexploration-portlet]] Exception while dispatching incoming RPC call
com.google.gwt.user.server.rpc.UnexpectedException: Service method 'public abstract java.lang.String ai.aitia.meme.mep.client.Billing.startBankin
g(int,ai.aitia.meme.mep.shared.Address) throws ai.aitia.meme.mep.shared.BillingException' threw an unexpected exception: com.liferay.portal.kerne
l.bean.BeanLocatorException: BeanLocator has not been set
at com.google.gwt.user.server.rpc.RPC.encodeResponseForFailure(RPC.java:378)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:581)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:544)
at org.spring4gwt.server.SpringGwtRemoteServiceServlet.processCall(SpringGwtRemoteServiceServlet.java:37)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:224)
at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
...
Caused by: com.liferay.portal.kernel.bean.BeanLocatorException: BeanLocator has not been set
at com.liferay.portal.kernel.bean.PortletBeanLocatorUtil.locate(PortletBeanLocatorUtil.java:40)
at ai.aitia.meme.mep.server.banktransaction.service.BankTransactionLocalServiceUtil.getService(BankTransactionLocalServiceUtil.java:255)
at ai.aitia.meme.mep.server.banktransaction.service.BankTransactionLocalServiceUtil.createBankTransaction(BankTransactionLocalServiceUtil.java:5
9)
at ai.aitia.meme.mep.server.BillingImpl.startBanking(BillingImpl.java:205)
at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
at java.lang.reflect.Method.invoke(Method.java:597)
at com.google.gwt.user.server.rpc.RPC.invokeAndEncodeResponse(RPC.java:562)
... 30 more
After searching the forums, I got the impression that the ContextLoaderListener of spring and the PortletContextLoaderLister of Liferay might be in conflict. Therefore, I changed the ContextLoaderLister to a PortletContextLoaderLister in my web.xml:
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>WEB-INF/conf/applicationContext.xml</param-value>
</context-param>
<!-- Spring XmlWebApplicationContext class
<context-param>
<param-name>contextClass</param-name>
<param-value>com.liferay.portal.spring.context.PortletApplicationContext</param-value>
</context-param>
-->
<listener>
<listener-class>com.liferay.portal.kernel.spring.context.PortletContextLoaderListener</listener-class>
</listener>
<!-- SpringGwt remote service servlet -->
<servlet>
<servlet-name>springGwtRemoteServiceServlet</servlet-name>
<servlet-class>org.spring4gwt.server.SpringGwtRemoteServiceServlet</servlet-class>
</servlet>
I have even tried to add a 'contextClass' parameter (in comments above), but the result was the same: deploy succeeds, but injection does not work in my servlets. Spring does not find the application context.
15:47:11,741 ERROR [[/modelexploration-portlet]] Exception while dispatching incoming RPC call
java.lang.IllegalStateException: No Spring web application context found
at org.spring4gwt.server.SpringGwtRemoteServiceServlet.getBean(SpringGwtRemoteServiceServlet.java:92)
at org.spring4gwt.server.SpringGwtRemoteServiceServlet.getBean(SpringGwtRemoteServiceServlet.java:55)
at org.spring4gwt.server.SpringGwtRemoteServiceServlet.processCall(SpringGwtRemoteServiceServlet.java:31)
at com.google.gwt.user.server.rpc.RemoteServiceServlet.processPost(RemoteServiceServlet.java:224)
at com.google.gwt.user.server.rpc.AbstractRemoteServiceServlet.doPost(AbstractRemoteServiceServlet.java:62)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:637)
at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.springframework.web.filter.RequestContextFilter.doFilterInternal(RequestContextFilter.java:83)
at org.springframework.web.filter.OncePerRequestFilter.doFilter(OncePerRequestFilter.java:76)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.jboss.web.tomcat.filters.ReplyHeaderFilter.doFilter(ReplyHeaderFilter.java:96)
at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:235)
at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
at org.jboss.web.tomcat.security.SecurityAssociationValve.invoke(SecurityAssociationValve.java:190)
at org.jboss.web.tomcat.security.JaccContextValve.invoke(JaccContextValve.java:92)
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.process(SecurityContextEstablishmentValve.java:126)
at org.jboss.web.tomcat.security.SecurityContextEstablishmentValve.invoke(SecurityContextEstablishmentValve.java:70)
at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
at org.jboss.web.tomcat.service.jca.CachedConnectionValve.invoke(CachedConnectionValve.java:158)
at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:330)
at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:829)
at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:598)
at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:447)
at java.lang.Thread.run(Thread.java:619)
It seems like the PortletContextLoaderListener does not load my bean definitions from my applicationContext.xml. Or could it be possible, that this context is not accessible for my servlets?
Any idea?
Tamás
Hi Tamás,
the configuration you had in the first place is correct, at least according to this issue report , I'm getting exactly the same error as you do:
Have you figured that out ?
the configuration you had in the first place is correct, at least according to this issue report , I'm getting exactly the same error as you do:
Cannot initialize context because there is already a root application context present - check whether you have multiple ContextLoader* definitions in your web.xml!
Have you figured that out ?
On deployment Liferay adds a lot of entries to your web.xml one of which unfortunately is a spring servletcontext listener so when you define it again you'll get that error
One way to solve it is to leave your web.xml untouched, do not add your own listener.
And instead in docroot/WEB-INF/ create a file called applicationContext.xml that holds your beans. Liferay's listener will automatically pick that file up
One way to solve it is to leave your web.xml untouched, do not add your own listener.
And instead in docroot/WEB-INF/ create a file called applicationContext.xml that holds your beans. Liferay's listener will automatically pick that file up
Hi Jelmer,
so that WEB-INF/applicationContext.xml becomes part of the root portletApplicationContext (that is loaded by both classloaders) containing beans from serviceBuilder generated bean definitions. As I can see it, when looking into source code of PortletApplicationContext.java, this bean definition applicationContext.xml is loaded exactly as it would be if it was declared in service.properties....
And each portet from the plugin gets its own root app context loaded from portlet.xml definition -( init-param contextConfigLocation )
To summarize it, if serviceBuilder is used -> service.properties file exists -> web.xml gets portletContextLoaderListener that is used instead of my own contextListener that I would use if I didn't use servicebuilder ->
-> the problem is that there is no parent context for the particular portlet contexts, which makes it hard for programmer to set it all up
Without serviceBuilder, there would be the web.xml-contextConfigLocation context Parent context for all those particular portlet contexts....I'm not sure by this now :-) it looks like that from the samples without servicebuilder
One way is to put infrastructure stuff into WEB-INF/applicationContext.xml and do <import resource="../../applicationContext.xml"/> in portlet's beans definitions, but then it would all be multiple times in portlet contexts
I hope I made it clear... Now there's no place to define ViewResolver, ExceptionResolver etc., that would be in parent context for portlet's child contexts
so that WEB-INF/applicationContext.xml becomes part of the root portletApplicationContext (that is loaded by both classloaders) containing beans from serviceBuilder generated bean definitions. As I can see it, when looking into source code of PortletApplicationContext.java, this bean definition applicationContext.xml is loaded exactly as it would be if it was declared in service.properties....
And each portet from the plugin gets its own root app context loaded from portlet.xml definition -( init-param contextConfigLocation )
To summarize it, if serviceBuilder is used -> service.properties file exists -> web.xml gets portletContextLoaderListener that is used instead of my own contextListener that I would use if I didn't use servicebuilder ->
-> the problem is that there is no parent context for the particular portlet contexts, which makes it hard for programmer to set it all up
Without serviceBuilder, there would be the web.xml-contextConfigLocation context Parent context for all those particular portlet contexts....I'm not sure by this now :-) it looks like that from the samples without servicebuilder
One way is to put infrastructure stuff into WEB-INF/applicationContext.xml and do <import resource="../../applicationContext.xml"/> in portlet's beans definitions, but then it would all be multiple times in portlet contexts
I hope I made it clear... Now there's no place to define ViewResolver, ExceptionResolver etc., that would be in parent context for portlet's child contexts