While much of Spring4gwt's power lies in dependency injection, it may be also useful to simply use the provided servlet to expose Spring services to your client while still looking up those services directly using GWT.create(). This is a good first step for those new to GWT RPC and may be sufficient in itself for projects with relatively few services or when another client-side framework like Google Gin is being used to manage dependencies. The following steps show how to accomplish this for a simple greeting service. Full source code and a working war for for this example is contained in the examples/simplerpc project.
- Add the spring4gwt jar to your server (war) classpath. Note that for simple RPC without DI it is not needed in the client classpath.
- Edit your server's web.xml (which should already contain a Spring context listener) to also contain the following servlet:
<!-- SpringGwt remote service servlet -->
<servlet>
<servlet-name>springGwtRemoteServiceServlet</servlet-name>
<servlet-class>org.spring4gwt.server.SpringGwtRemoteServiceServlet</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>springGwtRemoteServiceServlet</servlet-name>
<url-pattern>/springGwtServices/*</url-pattern>
</servlet-mapping>
Note that this is a single servlet that handles all requests matching the URL /springGwtServices/. Individual services are automatically exposed within this path, e.g. /springGwtServices/greetingService, but do not per-service mappings defined in web.xml.
- Identify the Spring service(s) you wish to access from your GWT client. For this example, we assume a service like the following.
@Service("greetingService")
public class GreetingServiceImpl implements GreetingService {
public String greet(String input) {
return "Hello from the server, " + input + "!";
}
}Note that the service is a "pure" Spring service and does not need to extend RemoteServlet or contain any other coupling to GWT. This is the primary difference between using spring4gwt instead of exposing Spring services as servlets directly. The only requirement for spring4gwt is that the service has a meaningful name since all client-side lookups are performed by name.
- For each service, create an interface containing the remote methods (or optionally, move an existing interface to a location that allows it to be shared by the client classpath) that extends RemooteService. Also annotate it with the @RemoteServiceRelativePath annotation using /springGwtServices/<bean name> as the path.
@RemoteServiceRelativePath("springGwtServices/greetingService")
public interface GreetingService extends RemoteService {
String greet(String name);
} - For each service, also create an async counterpart that is accessible within the client classpath. See the GWT documentation for more on the relationship between primary and async interfaces. Essentially they provide a mechanism for calling the proxied service asynchronously with a callback on success or failure.
public interface GreetingServiceAsync {
void greet(String input, AsyncCallback<String> callback);
} - In client code, access the service using GWT.create() and invoke methods.
private final GreetingServiceAsync greetingService = GWT.create(GreetingService.class);
...
greetingService.greet(nameField.getText(), new AsyncCallback<String>() {
public void onFailure(Throwable caught) {
...
}
public void onSuccess(String result) {
...
}
});
When a method on the service is called, the proxy created by GWT will access the URL gwtSpringServices/greetingService which is handled by the springGwtRemoteServiceServlet. The servlet will perform a bean lookup by name in the current web application context for "greetingService" and then invoke the appropriate method, also returning the result. An IllegalArgumentException will be thrown if the bean does not exist or does not implement the RemoteService interface. The later is a safety check to prevent accessing any bean in the server's Spring context from the client since typically only a subset should be exposed. Message serialization is not affected by sprig4gwt so refer to the GWT documentation for additional information on RPC type constraints and serialization errors that may occur.
I'm writing a Spring/GWT application, and I did not like the way I had to have the Spring service implement 'RemoteService?' (even if only by inheritance). My Spring classes are in a separate project jar file without any GWT dependencies.
I found another solution using Spring Load-Time Weaving. In your example, I would have 'GreetingServiceImpl?' not implement any interfaces at all:
@Service("greetingService") public class GreetingServiceImpl { public String greet(String input) { return "Hello from the server, " + input + "!"; } }Then, I create an 'META-INF/aop.xml' file in my GWT project that points to an introduction aspect, and I write the aspect:
@Aspect public class Introductions { @SuppressWarnings("unused") @DeclareParents("GreetingServiceImpl") private GreetingService greetingService; }Now, the Spring project need know nothing about GWT, so I can happily use it for web-services, desktop applications, or any other kind of application.
hi erjablow, may you share more about the way you implement your service that totally remove dependency from gwt?
I have a few projects, each of which compiles to jar files (using Ant).
My domain classes are in a domain project producing domain.jar and domain-src.jar.
I have a GWT module project that depends on the domain project and includes a Domain.gwt.xml file. I also include custom serializers; for some reason, GWT was unable to serialize the domain classes even though they implemented java.io.Serializable, and have no noncompliant methods. This produces a domain-module.jar file.
I have a Spring project containing the DAO classes and services. It produces a service.jar file, and depends on the domain.jar file, the various spring jar files, but not the GWT jars.
I have the web application, dependant on GWT and Spring. It has the web.xml file and the Spring xml configuration files. One of the Spring configuration files invokes load-time weaving. It also has the META-INF/aop.xml file as I discussed above, and that file points to an inroduction aspect class that causes the Spring services to retroactively adopt the GWT service interface.
I cannot simply use Spring AOP, because Spring AOP only recognizes the two argument form of the @DeclareParents annotation. I could say that my service implements an interface only if I give another class that is the implementation of the interface. Here, the service class is the implementation (even though it isn't declared to be one). I'm trying to use duck-typing, and I can only do that with Load-Time Weaving or full AspectJ.
hi erjablow, thanks for your sharing ya.
I think the approach erjablow is the right approach to make spring transparent to GWT.
In step 4, the GreetingService? interface still extends the RemoteService?. Therefore, it is not transparent for client and server integration with Spring for GWT.
I don't understand the advantage of creating Services this way? Why do you need Spring at all?
In my experience Spring doesn't simplify code, it just makes it harder to follow due to the wiring being done via annotations or XML. There really isn't anything wrong with dependency injection via 'new'.
Hello,
I would like to ask how, without directly extending the RemoteServiceServlet? in the service implementation, one can access the current http request object. So far the getThreadLocalRequest() method could be used. Thanks for any info!
Hello erjablow, We conceptualized our project structure close to how you described. Except for the fact that we have the domain and services into one project (jar). We've been looking around to figure the best way to integrated GWT with Spring and it looks like there are multiple ways. We like to keep them as loosely coupled as possible. It looks like you guys have done it already. If only you could write a blog on how you did it.. would be really helpful. Thanks
I added a blog detailing my test project integrating spring with GWT. I'm having trouble running the simple application I created. If any body can help me figure this thing out it would be great!
http://spring4gwt.blogspot.com/
Kdasika,
I think you're missing one thing. You don't have the META-INF/aop.xml file I added to my project. Spring AOP does not recognize the single-srgument form of @DeclareParents?; it only accepts the two argument form where one gives an interface together with a class that provides the implementation.
Look up SpringLoadTimeWeaving in the Spring manual. You put the bean declaration into the aop.xml file. You then need to tell Spring that you are invoking Load Time Weaving by putting a declaration in the Spring context file.
Dharezlak,
The class in spring4gwt that invokes the Spring service is a servlet, and can make the call.
Spervine,
Spring offers nice ways of working with databases, directory services, and security. Dependency injection allows me to write test configurations that mock calls to the database so they run fast.
Kdasika,
One more thing. As the load-time-weaving section of the manual says, you will need to use a special class loader or Java agent. Java needs to instrument the code, and the agent will be the hook.
Hi erjablow,
I'm also encountering the same problem as Dharezlak. In my service class, I need access to the HTTP request... However, since the spring4gwt class does not pass that on to my service, how can I get access to the request?
Thanks
mikenikles, I'm just curious why do you need access to HTTP request?
We need the http request object because we need to access the session. Currently, this call fails:
Any insight into how to get the session (we have implemented following the above example) would be appreciated.
Hello, I followed this tutorial and very glad to use Spring and GWT RPC. But, I need the HttpSession? inside my RPC server implementation. It doesn’t really work. Help !!!
Hello,
For those who still want to access http request or session from within the GWT service implementation ...
I found that spring's RequestContextFilter? and RequestContextHolder? helper classes can be used.
First, the filter needs to be configured in web.xml by adding:
And then in the code you can use:
Note that as the URL pattern I use the same value as for my spring4gwt servlet. Hope this helps...
Hello,
I tried to run the example but I get the following error
WARNING: No file found for: /ejemplo/springGwtServices/greetingService
Thanks
Hello, For those who still want to access http request or session from within the GWT service implementation ... I think there is something wrong in your design if you need access to Request/Session in your service layer. Service Layer must be competely de-coupled from the layer which uses it (front end or web tier). If your service needs some objects related to Request/Session, just get them in web-tier (I assume they are POJO or you transform it to POJO in web tier) and pass them as parameters when you call service. If your front end changes (say, you'll make use Swing application instead of web tier) you have an chance to re-use service without changes; but if you need a request/session objects in service, how do you get them in your Swing application? That is the sense of having de-coupled service tier which has no knowledge about the client which make use of it. If you introduce dependency in service on front end it is similar to scenario when you inroduce dependency in db services on whoever uses db. Should db know which client make use of it? We call desing like this as an anti-pattern.
Can anyone get this project to work. I started a new project and added the spring.jar to it in additon to what they have in this project and it gives me a 404 error. When I modify the servlet mapping I get a 500 error.
I found that I needed to change the url mapping to include the full path of the Main class:
<servlet-mapping> <servlet-name>springGwtRemoteServiceServlet</servlet-name> <url-pattern>/fully.qualified.path.Main/springGwtServices/</url-pattern> </servlet-mapping>Is there some configuration option in the GWT to not prepend the class path of Main?
The module attribute "rename-to" lets you specify a different value for the logical module name:
<module rename-to="main"> <inherits name="com.google.gwt.user.User" /> <entry-point class="fully.qualified.path.Main" /> </module>