Can i remote RPC calls through proxied spring beans?
Is it possible to mix my current spring app controllers with GWT
Widgets?
It is not really clear to me if i can use my current WEB-INF/web.xml
structure loading my servlets combining with the GWT architecture?
gr,
Marcel
you can start your gwt servlet in the springserver.. ie:
<bean id="gwtController"
class="org.springframework.web.servlet.mvc.ServletWrappingController">
<property name="servletClass">
<value>com.google.gwt.dev.shell.GWTShellServlet</value>
</property>
</bean>
instead of starting it in web.xml
Did you integrate spring with gwt with success?
my idea is quite simple, write one servlet wrapper of
RemoteServiceServlet and make a bridge to spring bean container. GWT
client invokes the servlet (ServiceEntryPoint in gwt term) using RPC.
So i think we can manager these services by spring.
I wouldn't go with the wrapper approach for RemoteServiceServlet, as
you'll see that there's probably a more extensible way to implement
this solution.
I have a suggestion about improving the RPC server's ability to
integrate with Spring Framework. GWT would need no dependencies on
Spring, but the change would let a Spring support class (or any other
IoC container) inject a reference to the object representing the
service implementation. Before I go forth and implement my own POC, I
wanted to get some feedback from others.
Currently, the concrete subclass overriding the RemoteServiceServlet
must contain the concrete service implementation. While not
necessarily a problem, this inheritance hierarchy causes two
limitations:
* Developers cannot reuse existing web service implementations if the
implementations inherit from another type of base class, and
* Developers cannot apply transaction control to individual service
methods in an aspect-oriented fashion (provided by Spring)
>From looking at the code, it appears that a few simple changes would
provide the option that would let the RemoteServiceServlet delegate
service implementation to a separate POJO service class.
This solution would require addition of a "service" property on
RemoteServiceServlet as a reference to an optional delegate object. On
RemoteServiceServlet invocation, if the property is null, then the
class would continue to behave as it currently does (using the
servlet's concrete class as the service implementation). If the
property is not null, then the RemoteServiceServlet would interrogate
and invoke methods on the referenced object.
With this hook, Spring (or another container/developer) can "inject"
the service into the servlet. (marcelpanse: I'm using a variation of
your suggestion. Good observation about the ServletWrappingController,
by the way.)
<bean id="myServiceViaGWT"
class="org.springframework.web.servlet.mvc.ServletWrappingController">
<property name="servletClass">
<value>com.google.gwt.user.server.RemoteServiceServlet</value>
</property>
<property name="service" ref="myService"/>
</bean>
This simple change buys us is the ability to have declarative
transaction control on a method-by-method basis, along with any other
AOP goodies Spring provides. It lets us reuse existing services,
published through GWT.
With that said, what does everyone else think?
Thanks,
Curt
Don't forget to rate! :-)
I bet it not so easy. Your solution seems ok the application is
deployed.
But another question how can we start hosted browser along with spring
application together for development?
/Jack
Now i can create a service bean in my xml.. Inject some managers/dao
other stuff into it and use the bean in the servletWrappingController!
I already submitted an improvement in spring's jira about this issue.
The hosted browser should still work as normal, now using the
springbeans.. (just create a servlet the normal way with spring)
Hi Marcel,
Can you define servlet instances in Spring? What would the
applicationContext look like?
Another approach I thought may work is to use the following code in
your servlet:
WebApplicationContext appContext =
WebApplicationContextUtils.getApplicationContext(this.getServletContext());
UserService userService = (UserService)
appContext.getBean("userService");
... some code which uses UserService
Its far from ideal, as you have to lookup the beans rather than have
them injected, but it should work.
It would be great to have a Spring class that could automatically
expose your service beans, much like the HessianServiceExporter, or the
HttpInvokerServiceExporter...
Regards,
Mike.
<bean id="gwtController"
class="org.springframework.web.servlet.mvc.ServletWrappingController">
<property name="servletClass">
<value>com.google.gwt.dev.shell.GWTShellServlet</value>
</property>
</bean>
<bean id="UserService"
class="web.service.ServletBeanWrappingController">
<property name="beanName">
<value>userService</value>
</property>
<property name="servletClass">
<value>web.service.UserServiceImpl</value>
</property>
<property name="servletInstance">
<ref bean="userService" />
</property>
</bean>
<bean id="userService" class="web.service.UserServiceImpl">
<property name="productManager">
<ref bean="productManager" />
</property>
</bean>
map gwt in SimpleUrlHandlerMapping like:
<prop key="/**/*">gwtController</prop>
But not that the web.service.ServletBeanWrappingController does not
exists in spring. The ServletWrappingController DOES exists but it
doesnt implement the setInstance method .. What i did is copyed the
servletWrappingController and added a setInstance method and also added
this line in afterPropertiesSet method just before the init();
if (servletInstance == null)
servletInstance = (Servlet) servletClass.newInstance();
handleRequestInternal() {
if (instanceServlet != null)
instanceServlet.service(request, response); // uses
injected property
else
super.handleRequestInternal(request, response); // uses
superclass property
}
afterPropertiesSet() ;
destroy()
Now you can inject the property as you would normally do. I beleive
this is a cleaner implementation.
2. To generalize the solution further actual service code could be
implemented in separate service beans (say, implementing some
interface). The GWT RPC processing servlet (derived from
RemoteServiceServlet class) can be injected with these service beans.
It serves client requests for services delegating processing to the
appropriate service bean and calling the specified method on it (using
reflection). Now implement your services and inject them into the
processing servlet. From the client side it will look like this:
invokeService(String serviceName, String[] params);
3. I've tried linking the GWT front end with Spring's MVC and this is
strightforward.
Regards, Alex
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.Controller;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
public class GwtServiceExporter implements Controller{
private RemoteServiceServlet service;
public RemoteServiceServlet getService() {
return service;
}
public void setService(RemoteServiceServlet service) {
this.service = service;
}
public ModelAndView handleRequest(HttpServletRequest request,
HttpServletResponse response) throws Exception {
service.doPost(request, response);
return null;
}
}
and in web.xml:
<servlet>
<servlet-name>service</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>2</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>service</servlet-name>
<url-pattern>*.do</url-pattern>
</servlet-mapping>
then in spring(service-servlet.xml):
<beans>
<bean name="/login.do"
class="server.spring.GwtServiceExporter">
<property name="service" ref="LoginService" />
</bean>
<bean name="/register.do"
class="server.spring.GwtServiceExporter">
<property name="service" ref="RegisterService" />
</bean>
<bean name="LoginService" class="server.LoginServiceImpl"></bean>
<bean name="RegisterService"
class="server.register.RegisterServiceImpl"></bean>
</beans>
So, using the ideas here, I wrote a very simple dispatcher that seems
to achieve the same effect as the Spring DispatcherServlet:
public class ServiceDispatcherServlet extends HttpServlet {
protected void doGet(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
doPost(req, resp);
}
protected void doPost(HttpServletRequest req, HttpServletResponse
resp)
throws ServletException, IOException {
// we use a custom way to get the ApplicationContext, instead of
assuming WebApplicationContext
ApplicationContext context =
ContextLoader.getApplicationContext(getServletContext());
// treat the Request URI as a bean name, and dispatch the request to
that RemoteServiceServlet
RemoteServiceServlet servlet =
(RemoteServiceServlet)context.getBean(req.getRequestURI());
if (servlet != null) {
servlet.doPost(req, resp);
}
}
}
In the web.xml, I added a definition:
<servlet>
<servlet-name>serviceDispatcher</servlet-name>
<servlet-class>com.everstream.server.ServiceDispatcherServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>serviceDispatcher</servlet-name>
<url-pattern>/service/*</url-pattern>
</servlet-mapping>
Then in my Spring configuration, I defined my services. For example:
<bean name="/service/userlist"
class="com.blah.blah.blah.server.UserListServiceImpl">
<property name="userService"><ref bean="userService"/></property>
</bean>
Did I miss anything here?
There is one interesting thing.... using this approach, it appears
unnecessary to define my remote services using the <servlet> tag in the
module xml. In fact, this strategy depends on Spring to do the remote
service servlet instantiation. Is this consistent with others'
experience... that in hosted mode, you can use the web.xml OR the
module xml to define the servlet entry points?