cq5 accessing osgi servlet through Url - aem

I am trying to access following sling servlet using http://localhost:4502/sling/test-services/planet.html
But, it is giving 404 error, not sure what I am doing wrong here.
#Component
#Service(value=javax.servlet.Servlet.class)
#Properties({
#Property(name="service.description", value="HTML renderer for Planet resources"),
#Property(name="service.vendor", value="The Apache Software Foundation"),
#Property(name="sling.servlet.resourceTypes", value="sling/test-services/planet"),
#Property(name="sling.servlet.extensions", value="html"),
#Property(name="sling.servlet.methods", value="GET")
})
#SuppressWarnings("serial")
public class PlanetResourceRenderingServlet extends SlingSafeMethodsServlet {
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
response.setContentType("text/html");
response.setCharacterEncoding("UTF-8");
final ValueMap properties = request.getResource().adaptTo(ValueMap.class);
// TODO should escape output - good enough for our tests
final PrintWriter pw = response.getWriter();
pw.println(String.format("<html><head><title>Planet at %s</title></head><body>", request.getResource().getPath()));
pw.println(String.format("<p>Name: %s</p>", properties.get("name")));
pw.println(String.format("<p>Distance: %s</p>", properties.get("distance")));
pw.println("</body></html>");
pw.flush();
}
}
Is it possible, I could access the servlet service without ".html" extension, if I remove extension property?
I appreciate any help.
Thank you!

When you want to access a servlet through an URL you need to set the sling.servlet.paths instead of the sling.servlet.resourceTypes. A similar issue has been answered here.
If you are setting the sling.servlet.resourceTypes property, then you need to access a resource whose sling:resourceType is sling/test-services/planet.
Your annotations should be
#Component
#Service(value=javax.servlet.Servlet.class)
#Properties({
#Property(name="service.description", value="HTML renderer for Planet resources"),
#Property(name="service.vendor", value="The Apache Software Foundation"),
#Property(name="sling.servlet.paths", value="/sling/test-services/planet"),
#Property(name="sling.servlet.extensions", value="html"),
#Property(name="sling.servlet.methods", value="GET")
})
Or this can be further simplified using the #SlingServlet annotation as shown below
#SlingServlet(paths="/sling/test-services/planet", methods="GET", extensions="html")
Make sure that you allow the following path is allowed in Apache Sling Servlet/Script Resolver and Error Handler configuration available in OSGi console.

Related

rest service works when deployed in Eclipse but not in Tomcat

I implemented rest web services with Spring. When I deployed it in Eclipse as a Spring Boot Application, it works. However when I deployed it in Tomcat 7 on the same machine, it does not work. The error message is as follows:
XMLHttpRequest cannot load http://localhost:8080/ristoreService/oauth/token. Response to preflight request doesn't pass access control check: No 'Access-Control-Allow-Origin' header is present on the requested resource. Origin 'http://127.0.0.1:8081' is therefore not allowed access.
My CORS filter looks like this:
#Component
#Order(Ordered.HIGHEST_PRECEDENCE)
public class CORSFilter implements Filter {
#Override
public void doFilter(ServletRequest req, ServletResponse res, FilterChain chain) throws IOException, ServletException {
HttpServletRequest request = (HttpServletRequest) req;
HttpServletResponse response = (HttpServletResponse) res;
response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8081");
response.setHeader("Access-Control-Allow-Credentials", "true");
response.setHeader("Access-Control-Allow-Methods", "POST, GET, OPTIONS, DELETE");
response.setHeader("Access-Control-Max-Age", "3600");
response.setHeader("Access-Control-Allow-Headers", "Content-Type, Accept, X-Requested-With, remember-me, "
+ "Origin,Access-Control-Request-Method, Access-Control-Request-Headers, Authorization");
if ("OPTIONS".equalsIgnoreCase(request.getMethod())) {
response.setStatus(HttpServletResponse.SC_OK);
} else {
chain.doFilter(req, res);
}
}
If I comment out response.setHeader("Access-Control-Allow-Origin", "http://127.0.0.1:8081");, I still get the same error. It wouldn't work without this line even if I deploy in Eclipse. Why does it act differently being deployed under different environment on the same ip?
EDIT:
I tested the url http://localhost:8080/ristoreService/oauth/tokenwith rest client tester "CocoaRestClient" and got 404. So I made up a url which apparently does not exist http://localhost:8080/xxxxx and run it in UI (angularjs) and again got the CORS error. I think the error is kind of misleading, it is after all a 404. But why does it complain not found when the war was deployed successfully with the name ristoreService.war under webapps in Tomcat?
Try using a FilterRegistrationBean. Looks like this in Java Config:
#Bean
public FilterRegistrationBean authorizationFilter(){
FilterRegistrationBean filterRegBean = new FilterRegistrationBean();
filterRegBean.setFilter(authorizationFilter);
List<String> urlPatterns = new ArrayList<String>();
urlPatterns.add("/v1/*");
filterRegBean.setUrlPatterns(urlPatterns);
return filterRegBean;
}
Any reason why you're not using Spring Boot's CORS capabilities? It's already supported out of the box, you just gotta configure it. You can enable it globally like this:
#Bean
public WebMvcConfigurer corsConfigurer() {
return new WebMvcConfigurerAdapter() {
#Override
public void addCorsMappings(CorsRegistry registry) {
registry.addMapping("/**").allowedOrigins("*");
}
};
}
According to How to deploy Spring Boot application, I have to make main application to extend SpringBootServletInitializer. Once I added that, it works.
To solve CORS issue I used #CrossOrigin. And I did not implement my own CORS filter. Any way spring already have provided few addition solutions for CORS issue.
If you need only your filter you could use it in this way:
#Override
protected void configure(HttpSecurity http) throws Exception {
http.addFilterBefore(yourFilter);
...
}

Swagger documentation with JAX-RS Jersey 2 and Grizzly

I have implementated a Rest web service (the function is not relevant) using JAX-RS. Now I want to generate its documentation using Swagger. I have followed these steps:
1) In build.gradle I get all the dependencies I need:
compile 'org.glassfish.jersey.media:jersey-media-moxy:2.13'
2) I documentate my code with Swagger annotations
3) I hook up Swagger in my Application subclass:
public class ApplicationConfig extends ResourceConfig {
/**
* Main constructor
* #param addressBook a provided address book
*/
public ApplicationConfig(final AddressBook addressBook) {
register(AddressBookService.class);
register(MOXyJsonProvider.class);
register(new AbstractBinder() {
#Override
protected void configure() {
bind(addressBook).to(AddressBook.class);
}
});
register(io.swagger.jaxrs.listing.ApiListingResource.class);
register(io.swagger.jaxrs.listing.SwaggerSerializers.class);
BeanConfig beanConfig = new BeanConfig();
beanConfig.setVersion("1.0.2");
beanConfig.setSchemes(new String[]{"http"});
beanConfig.setHost("localhost:8282");
beanConfig.setBasePath("/");
beanConfig.setResourcePackage("rest.addressbook");
beanConfig.setScan(true);
}
}
However, when going to my service in http://localhost:8282/swagger.json, I get this output.
You can check my public repo here.
It's times like this (when there is no real explanation for the problem) that I throw in an ExceptionMapper<Throwable>. Often with server related exceptions, there are no mappers to handle the exception, so it bubbles up to the container and we get a useless 500 status code and maybe some useless message from the server (as you are seeing from Grizzly).
import javax.ws.rs.WebApplicationException;
import javax.ws.rs.core.Response;
import javax.ws.rs.ext.ExceptionMapper;
public class DebugMapper implements ExceptionMapper<Throwable> {
#Override
public Response toResponse(Throwable exception) {
exception.printStackTrace();
if (exception instanceof WebApplicationException) {
return ((WebApplicationException)exception).getResponse();
}
return Response.serverError().entity(exception.getMessage()).build();
}
}
Then just register with the application
public ApplicationConfig(final AddressBook addressBook) {
...
register(DebugMapper.class);
}
When you run the application again and try to hit the endpoint, you will now see a stacktrace with the cause of the exception
java.lang.NullPointerException
at io.swagger.jaxrs.listing.ApiListingResource.getListingJson(ApiListingResource.java:90)
If you look at the source code for ApiListingResource.java:90, you will see
Swagger swagger = (Swagger) context.getAttribute("swagger");
The only thing here that could cause the NPE is the context, which scrolling up will show you it's the ServletContext. Now here's the reason it's null. In order for there to even be a ServletContext, the app needs to be run in a Servlet environment. But look at your set up:
HttpServer server = GrizzlyHttpServerFactory
.createHttpServer(uri, new ApplicationConfig(ab));
This does not create a Servlet container. It only creates an HTTP server. You have the dependency required to create the Servlet container (jersey-container-grizzly2-servlet), but you just need to make use of it. So instead of the previous configuration, you should do
ServletContainer sc = new ServletContainer(new ApplicationConfig(ab));
HttpServer server = GrizzlyWebContainerFactory.create(uri, sc, null, null);
// you will need to catch IOException or add a throws clause
See the API for GrizzlyWebContainerFactory for other configuration options.
Now if you run it and hit the endpoint again, you will see the Swagger JSON. Do note that the response from the endpoint is only the JSON, it is not the documentation interface. For that you need to use the Swagger UI that can interpret the JSON.
Thanks for the MCVE project BTW.
Swagger fixed this issue in 1.5.7. It was Issue 1103, but the fix was rolled in last February. peeskillet's answer will still work, but so will OP's now.

Is it possible to place variables into a resource path within a sling servlet?

We are trying to provide a clean URI structure for external endpoints to pull json information from CQ5.
For example, if you want to fetch information about a particular users history (assuming you have permissions etc), ideally we would like the endpoint to be able to do the following:
/bin/api/user/abc123/phone/555-klondike-5/history.json
In the URI, we would specifying /bin/api/user/{username}/phone/{phoneNumber}/history.json so that it is very easy to leverage the dispatcher to invalidate caching changes etc without invalidating a broad swath of cached information.
We would like to use a sling servlet to handle the request, however, I am not aware as to how to put variables into the path.
It would be great if there were something like #PathParam from JaxRS to add to the sling path variable, but I suspect it's not available.
The other approach we had in mind was to use a selector to recognise when we are accessing the api, and thus could return whatever we wanted to from the path, but it would necessitate a singular sling servlet to handle all of the requests, and so I am not happy about the approach as it glues a lot of unrelated code together.
Any help with this would be appreciated.
UPDATE:
If we were to use a OptingServlet, then put some logic inside the accepts function, we could stack a series of sling servlets on and make the acceptance decisions from the path with a regex.
Then during execution, the path itself can be parsed for the variables.
If the data that you provide comes from the JCR repository, the best is to structure it exactly as you want the URLs to be, that's the recommended way of doing things with Sling.
If the data is external you can create a custom Sling ResourceProvider that you mount on the /bin/api/user path and acquires or generates the corresponding data based on the rest of the path.
The Sling test suite's PlanetsResourceProvider is a simple example of that, see http://svn.apache.org/repos/asf/sling/trunk/launchpad/test-services/src/main/java/org/apache/sling/launchpad/testservices/resourceprovider/
The Sling resources docs at https://sling.apache.org/documentation/the-sling-engine/resources.html document the general resource resolution mechanism.
It is now possible to integrate jersy(JAX-RS) with CQ. We are able to create primitive prototype to say "Hello" to the world.
https://github.com/hstaudacher/osgi-jax-rs-connector
With this we can use the #PathParam to map the requests
Thanks and Regards,
San
There is no direct way to create such dynamic paths. You could register servlet under /bin/api/user.json and provide the rest of the path as a suffix:
/bin/api/user.json/abc123/phone/555-klondike-5/history
^ ^
| |
servlet path suffix starts here
then you could parse the suffix manually:
#SlingServlet(paths = "/bin/api/user", extensions = "json")
public class UserServlet extends SlingSafeMethodsServlet {
public void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) {
String suffix = request.getRequestPathInfo().getSuffix();
String[] split = StringUtils.split(suffix, '/');
// parse split path and check if the path is valid
// if path is not valid, send 404:
// response.sendError(HttpURLConnection.HTTP_NOT_FOUND);
}
}
The RESTful way to approach this would be to have the information stored in the structure that you want to use. i.e. /content/user/abc123/phone/555-klondike-5/history/ would contain all the history nodes for that path.
In that usage. you can obtain an out of the box json response by simply calling
/content/user/abc123/phone/555-klondike-5/history.json
Or if you need something in a specific json format you could use the sling resource resolution to use a custom json response.
Excited to share this! I've worked ~ a week solving this, finally have the best Answer.
First: Try to use Jersey
The osgi-jax-rs-connector suggested by kallada is best, but I couldn't get it working on Sling 8. I lost a full day trying, all I have to show for it are spooky class not found errors and dependency issues.
Solution: The ResourceProvider
Bertrand's link is for Sling 9 only, which isn't released. So here's how you do it in Sling 8 and older!
Two Files:
ResourceProvider
Servlet
The ResourceProvider
The purpose of this is only to listen to all requests at /service and then produce a "Resource" at that virtual path, which doesn't actually exist in the JCR.
#Component
#Service(value=ResourceProvider.class)
#Properties({
#Property(name = ResourceProvider.ROOTS, value = "service/image"),
#Property(name = ResourceProvider.OWNS_ROOTS, value = "true")
})
public class ImageResourceProvider implements ResourceProvider {
#Override
public Resource getResource(ResourceResolver resourceResolver, String path) {
AbstractResource abstractResource;
abstractResource = new AbstractResource() {
#Override
public String getResourceType() {
return TypeServlet.RESOURCE_TYPE;
}
#Override
public String getResourceSuperType() {
return null;
}
#Override
public String getPath() {
return path;
}
#Override
public ResourceResolver getResourceResolver() {
return resourceResolver;
}
#Override
public ResourceMetadata getResourceMetadata() {
return new ResourceMetadata();
}
};
return abstractResource;
}
#Override
public Resource getResource(ResourceResolver resourceResolver, HttpServletRequest httpServletRequest, String path) {
return getResource(resourceResolver , path);
}
#Override
public Iterator<Resource> listChildren(Resource resource) {
return null;
}
}
The Servlet
Now you just write a servlet which handles any of the resources coming from that path - but this is accomplished by handling any resources with the resource type which is produced by the ResourceProvider listening at that path.
#SlingServlet(
resourceTypes = TypeServlet.RESOURCE_TYPE,
methods = {"GET" , "POST"})
public class TypeServlet extends SlingAllMethodsServlet {
static final String RESOURCE_TYPE = "mycompany/components/service/myservice";
#Override
protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws ServletException, IOException {
final String [] pathParts = request.getResource().getPath().split("/");
final String id = pathParts[pathParts.length-1];
response.setContentType("text/html");
PrintWriter out = response.getWriter();
try {
out.print("<html><body>Hello, received this id: " + id + "</body></html>");
} finally {
out.close();
}
}
}
Obviously your servlet would do something much more clever, such as process the "path" String more intelligently and probably produce JSON.

Xpage REST service control and service bean

I am trying to implement REST Service using XPage REST Service Control. I have opted for "customRESTService".
I would like to emit JSON when this service is requested. I can write logic in Server Side Java Script.
But I noticed that this customRESTService also supports "serviceBean", meaning I can write whole logic in pure JAVA.
I have given below code of the bean. I have declared it in faces-config.xml as well. But it throws exception while rendering. Has anyone used "serviceBean" in customRESTService?
I appreciate any help!! Thanks!!
public class GetApproverJSON{
public GetApproverJSON(){
System.out.println("Instantiating Bean");
}
public String doGet() throws NotesException{
JSONObject mainObj = new JSONObject();;
JSONObject itemObj;
try{
mainObj.put("label", "name");
mainObj.put("identifier", "abbr");
itemObj = new JSONObject();
itemObj.put("name", "");
itemObj.put("abbr", "");
mainObj.accumulate("items", itemObj);
return mainObj.toString();
}catch(Exception e){
System.out.println("Exception occured while generating JSON ");
e.printStackTrace();
return mainObj.toString();
}finally{
}
}
Error :
com.ibm.domino.services.ServiceException: Error while rendering service
at com.ibm.xsp.extlib.component.rest.CustomService$ScriptServiceEngine.renderService(CustomService.java:304)
at com.ibm.domino.services.HttpServiceEngine.processRequest(HttpServiceEngine.java:167)
at com.ibm.xsp.extlib.component.rest.UIBaseRestService._processAjaxRequest(UIBaseRestService.java:252)
at com.ibm.xsp.extlib.component.rest.UIBaseRestService.processAjaxRequest(UIBaseRestService.java:229)
at com.ibm.xsp.util.AjaxUtilEx.renderAjaxPartialLifecycle(AjaxUtilEx.java:206)
at com.ibm.xsp.webapp.FacesServletEx.renderAjaxPartial(FacesServletEx.java:221)
at com.ibm.xsp.webapp.FacesServletEx.serviceView(FacesServletEx.java:166)
at com.ibm.xsp.webapp.FacesServlet.service(FacesServlet.java:160)
at com.ibm.xsp.webapp.FacesServletEx.service(FacesServletEx.java:137)
at com.ibm.xsp.webapp.DesignerFacesServlet.service(DesignerFacesServlet.java:103)
at com.ibm.designer.runtime.domino.adapter.ComponentModule.invokeServlet(ComponentModule.java:576)
at com.ibm.domino.xsp.module.nsf.NSFComponentModule.invokeServlet(NSFComponentModule.java:1267)
at com.ibm.designer.runtime.domino.adapter.ComponentModule$AdapterInvoker.invokeServlet(ComponentModule.java:847)
at com.ibm.designer.runtime.domino.adapter.ComponentModule$ServletInvoker.doService(ComponentModule.java:796)
at com.ibm.designer.runtime.domino.adapter.ComponentModule.doService(ComponentModule.java:565)
at com.ibm.domino.xsp.module.nsf.NSFComponentModule.doService(NSFComponentModule.java:1251)
at com.ibm.domino.xsp.module.nsf.NSFService.doServiceInternal(NSFService.java:598)
at com.ibm.domino.xsp.module.nsf.NSFService.doService(NSFService.java:421)
at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.doService(LCDEnvironment.java:341)
at com.ibm.designer.runtime.domino.adapter.LCDEnvironment.service(LCDEnvironment.java:297)
at com.ibm.domino.xsp.bridge.http.engine.XspCmdManager.service(XspCmdManager.java:272)
Caused by: com.ibm.xsp.FacesExceptionEx: Bean getApproverJSON is not a CustomServiceBean
at com.ibm.xsp.extlib.component.rest.CustomService.findBeanInstance(CustomService.java:226)
at com.ibm.xsp.extlib.component.rest.CustomService$ScriptServiceEngine.renderService(CustomService.java:255)
... 20 more
You need to change your code to:
public class GetApproverJSON{ ...}
to:
public class GetApproverJSON extends CustomServiceBean {
#Override
public void renderService(CustomService service, RestServiceEngine engine) throws ServiceException {
HttpServletRequest request = engine.getHttpRequest();
HttpServletResponse response = engine.getHttpResponse();
response.setHeader("Content-Type", "application/json; charset=UTF-8");
// Here goes your code, get the response writer or stream
}
since that's the interface the REST service is expecting. You will need to implement just renderService. You can get the method (GET, POST etc.) from the request object
I've never used the service bean before, I usually create my own parser with a static doGet method very similar to yours and in the doGet property of the custom REST service make a call to the static doGet method I create. But I think (I'm probably wrong on this count) if you use the service bean it has to be an entire servlet like if you wrote your own actual REST Service, and not just the parser portion.
I've created quite a few of the parsers and have found that a list of maps:
List>
is usually the best approach for building the initial data. I then loop through the list to build my JSON. In the Extension Library there is a class called JsonWriter which makes it very easy to build a JSON Object. Use the JsonWriter like:
StringWriter sw = new StringWriter();
JsonWriter jw = new JsonWriter(sw);
jw.startObject();
jw.startProperty("SomeProperty");
jw.outStringLiteral("SomeValue");
jw.endProperty();
jw.endObject();
return sw.toString();
For a full on example you can take a look at the REST service I built for my JQuery FullCalendar demo. While none of the methods are static (I need to track a couple of properties) you should get the basic idea. But what kicks the whole thing off is a call to the writeJson() method. That is invoked in this custom control.
Those examples should get you going on building your own custom JSON parser and emitting that JSON back to your application.

Problem with GWT behind a reverse proxy - either nginx or apache

I'm having this problem with GWT when it's behind a reverse proxy. The backend app is deployed within a context - let's call it /context.
The GWT app works fine when I hit it directly:
http://host:8080/context/
I can configure a reverse proxy in front it it. Here's my nginx example:
upstream backend {
server 127.0.0.1:8080;
}
...
location / {
proxy_pass http://backend/context/;
}
But, when I run through the reverse proxy, GWT gets confused, saying:
2009-10-04 14:05:41.140:/:WARN: Login: ERROR: The serialization policy file '/C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc' was not found; did you forget to include it in this deployment?
2009-10-04 14:05:41.140:/:WARN: Login: WARNING: Failed to get the SerializationPolicy 'C7F5ECA5E3C10B453290DE47D3BE0F0E' for module 'https://hostname:444/'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.
2009-10-04 14:05:41.292:/:WARN: StoryService: ERROR: The serialization policy file '/0445C2D48AEF2FB8CB70C4D4A7849D88.gwt.rpc' was not found; did you forget to include it in this deployment?
2009-10-04 14:05:41.292:/:WARN: StoryService: WARNING: Failed to get the SerializationPolicy '0445C2D48AEF2FB8CB70C4D4A7849D88' for module 'https://hostname:444/'; a legacy, 1.3.3 compatible, serialization policy will be used. You may experience SerializationExceptions as a result.
In other words, GWT isn't getting the word that it needs to prepend /context/ hen look for C7F5ECA5E3C10B453290DE47D3BE0F0E.gwt.rpc, but only when the request comes throught proxy. A workaround is to add the context to the url for the web site:
location /context/ {
proxy_pass http://backend/context/;
}
but that means the context is now part of the url that the user sees, and that's ugly.
Anybody know how to make GWT happy in this case?
Software versions:
GWT - 1.7.0 (same problem with 1.7.1)
Jetty - 6.1.21 (but the same problem existed under tomcat)
nginx - 0.7.62 (same problem under apache 2.x)
I've looked at the traffic between the proxy and the backend using DonsProxy, but there's nothing noteworthy there.
I have the same problem, and I opened a bug report:
http://code.google.com/p/google-web-toolkit/issues/detail?id=4817
The problem is that it was marked "As Design", so I don't think it will be fixed.
I found this solution for me. I extended the class RemoteServiceServlet and I forced GWT to load serialization policy file starting from ContextName instead of URL.
Then I extended my service my class instead of RemoteServiceServlet class.
In this way the application will be unlinked from the url from where it will be called.
Here there is my custom class:
import java.io.IOException;
import java.io.InputStream;
import java.net.MalformedURLException;
import java.net.URL;
import java.text.ParseException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import com.google.gwt.user.server.rpc.RemoteServiceServlet;
import com.google.gwt.user.server.rpc.SerializationPolicy;
import com.google.gwt.user.server.rpc.SerializationPolicyLoader;
public class MyRemoteServiceServlet extends RemoteServiceServlet
{
#Override
protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName)
{
return MyRemoteServiceServlet.loadSerializationPolicy(this, request, moduleBaseURL, strongName);
}
/**
* Used by HybridServiceServlet.
*/
static SerializationPolicy loadSerializationPolicy(HttpServlet servlet,
HttpServletRequest request, String moduleBaseURL, String strongName) {
// The serialization policy path depends only by contraxt path
String contextPath = request.getContextPath();
SerializationPolicy serializationPolicy = null;
String contextRelativePath = contextPath + "/";
String serializationPolicyFilePath = SerializationPolicyLoader.getSerializationPolicyFileName(contextRelativePath
+ strongName);
// Open the RPC resource file and read its contents.
InputStream is = servlet.getServletContext().getResourceAsStream(
serializationPolicyFilePath);
try {
if (is != null) {
try {
serializationPolicy = SerializationPolicyLoader.loadFromStream(is,
null);
} catch (ParseException e) {
servlet.log("ERROR: Failed to parse the policy file '"
+ serializationPolicyFilePath + "'", e);
} catch (IOException e) {
servlet.log("ERROR: Could not read the policy file '"
+ serializationPolicyFilePath + "'", e);
}
} else {
String message = "ERROR: The serialization policy file '"
+ serializationPolicyFilePath
+ "' was not found; did you forget to include it in this deployment?";
servlet.log(message);
}
} finally {
if (is != null) {
try {
is.close();
} catch (IOException e) {
// Ignore this error
}
}
}
return serializationPolicy;
}
}
Michele,
Thank you for the example servlet to handle this problem. However when I tried to use your approach it worked in the reverse proxy environment but not in my dev mode eclipse environment.
I took an approach that would let me seamlessly move between my dev and prod environments.
As you did I overwrote RemoteServiceServlet but I only replaced following...
#Override
protected SerializationPolicy doGetSerializationPolicy(
HttpServletRequest request, String moduleBaseURL, String strongName) {
//get the base url from the header instead of the body this way
//apache reverse proxy with rewrite on the header can work
String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");
if(moduleBaseURLHdr != null){
moduleBaseURL = moduleBaseURLHdr;
}
return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
}
In my apache config I added...
ProxyPass /app/ ajp://localhost:8009/App-0.0.1-SNAPSHOT/
RequestHeader edit X-GWT-Module-Base ^(.*)/app/(.*)$ $1/App-0.0.1-SNAPSHOT/$2
This approach works in all scenarios and delegates the url "mucking" to apache's proxy settings which is the approach I've always taken.
Comments on this approach are appreciated
I'm fairly sure the correct answer here is to patch the source and submit a bug report. Another option would be to run the GWT app at / on your backend.
I'd prefer the former, but the latter should work too. If you really needed things separated out into multiple contexts, use a different port number?
I've run into a similar problem, a successful workaround was to make all serialized objects implement GWT's IsSerializable interface (in addition to the standard Serializable interface). If you read the message, it states that 'a legacy, 1.3.3 compatible, serialization policy will be used' - the 1.3.3 compatible policy requires all of your serialized objects implement the IsSerializable interface, so by adding it, everything worked.
I do have concerns that the legacy policy will be desupported in future versions of GWT, so i am also in search for a better workaround myself.
KC's answer is good. For those that do not want to muck around with apache configs, or need a quick and dirty way to test, here is a code only solution.
protected SerializationPolicy doGetSerializationPolicy(final HttpServletRequest request, String moduleBaseURL, final String strongName) {
final String moduleBaseURLHdr = request.getHeader("X-GWT-Module-Base");
if (moduleBaseURLHdr != null) {
moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar");
}
return super.doGetSerializationPolicy(request, moduleBaseURL, strongName);
}
The application is on http://server/bar, the proxy is serving it at http://proxy/foo/bar
Hence moduleBaseURL = moduleBaseURLHdr.replace("foo/bar", "bar"); makes GWT happy.
Likewise if the application is at http://server/bar and the proxy is serving at http://proxy/, you need to add bar to the moduleBaseURL (right before the package name).
This can be generalized through the use of getServletContext().getContextPath() etc...
My goal was to avoid additional header(s) which would make deployment and configuration harder. I solved this problem by overriding RemoteServiceServlet.doGetSerializationPolicy():
#Override
protected SerializationPolicy doGetSerializationPolicy(HttpServletRequest request, String moduleBaseURL, String strongName) {
String localServerAddress = "http://127.0.0.1:" + getThreadLocalRequest().getLocalPort();
String localContextPath = getServletConfig().getServletContext().getContextPath();
String moduleName = extractGwtModuleName(moduleBaseURL);
String localModuleBaseURL = joinPaths(localServerAddress, localContextPath, moduleName, "/");
return super.doGetSerializationPolicy(request, localModuleBaseURL, strongName);
}
In above code:
extractGwtModuleName() extracts last string prefixed and/or followed by slash
joinPaths() joins multiple url parts, removes unnecessary slashes
Use restful JSON for your RPC calls instead of GWT-RPC.
This solves the reverse-proxy problem since no serialization files are required.