Re: Mapping REST requests across multiple app contexts

2010-08-25 Thread Ken Fox
Charles Caldarale recommended UrlRewriteFilter and after experimenting with
it, I agree it's very nice: great performance, very flexible and handles
cross-context forwarding. The custom Valve option is still attractive
because it has slightly better performance, slightly better cross-context
support, and much simpler configuration--at the expense of generality of
course.

Can someone look at the following Valve logic and let me know if this is
safe on Tomcat 6? It has been working fine and is stable under load, but I'm
totally new to the Tomcat code base and would really appreciate another pair
of eyes. Thanks.


public class MapRESTRequest extends ValveBase {

public void invoke(Request request, Response response) {
if (/v1.equals(request.getContextPath())) {
// map the in-bound REST URI to the app handling it
String newRequestURI = /new-app/some/derived/uri;

org.apache.coyote.Request req = request.getCoyoteRequest();
req.requestURI().setString(newRequestURI);
req.decodedURI().setString(newRequestURI);

MessageBytes uriMB = MessageBytes.newInstance();
uriMB.duplicate(req.decodedURI());

MessageBytes hostMB = MessageBytes.newInstance();
hostMB.setString(request.getHost().getName());

MappingData mappingData = request.getMappingData();
mappingData.recycle();
request.getConnector().getMapper().map(hostMB, uriMB, mappingData);

request.setContext((Context) mappingData.context);
request.setWrapper((Wrapper) mappingData.wrapper);
}

getNext().invoke(request, response);
}


If anyone is interested in this, I can share the source. What I have allows
you to implement a REST resource name space as a collection of web apps (I
mainly use Jersey for the apps). The public URLs are mapped onto the web
apps however you want by configuring each app's web.xml. Here's a web.xml
fragment that shows the configuration (this servlet is only used for Valve
configuration; it doesn't handle any requests itself):


servlet
servlet-classcom.vulpes.tomcat.MapResources/servlet-class
load-on-startup2/load-on-startup
init-param
param-nameresourcePaths/param-name

param-value/game/**;/profile/*/tokens;/profile/*/awards/param-value
/init-param
/servlet


That mapping accepts request URLs like /v1/foo/bar/game/123 and forwards
them to /a-service/game/123.

Collisions between resources are resolved with deepest-match wins
(individual path elements are resolved with longest-match wins). A trie is
used for the mapping, so it is reasonably fast for large numbers of
patterns.

- Ken




On Sat, Aug 21, 2010 at 2:04 PM, Ken Fox k...@vulpes.com wrote:

 I'm looking for advice on the best way to map REST requests onto a
 collection of Tomcat apps all running in the same JVM. The REST name space
 was designed for client use and doesn't reflect how the apps implement it.
 For example, the resource /v1/x/123 is implemented by app X, but the
 resource /v1/x/123/y is implemented by app Y.

 A proxy (e.g. Apache mod_proxy or Squid) in front of Tomcat can rewrite the
 URLs to go to the correct app, but this gives us some pretty ugly proxy
 configurations which have to be kept in lock-step with the Tomcat apps.
 Relying on a proxy also makes it a bit harder to use Amazon's load balancer
 because it doesn't do rewrites (I think we'd have to run a proxy on each
 Tomcat instance).

 I'm trying to implement the rewrite as a Valve (code outline below)
 registered with the Engine which will run before any Hosts or Contexts. This
 seems like a good approach and may even let me grab the JAX-RS annotations
 from the apps to dynamically build the rewrite rules.

 Does anyone have advice for REST name spaces in Tomcat in general?

 Has anyone had good experiences with a rewrite proxy in front of Tomcat on
 Amazon EC2 with Amazon's ELB?

 Has anybody tried a rewrite Valve similar to this? It has to modify the
 CoyoteRequest and generate new Request.mappingData which seems kind of
 risky. (Though I think it will work in Tomcat 7, I've only tried Tomcat 6.)
 This is my favorite approach so far.

 Thanks,

 - Ken


 public void invoke(Request request, Response response) {
 if (/v1.equals(request.getContextPath())) {
 // map the in-bound REST URI to the app handling it
 String newRequestURI = /new-app/some/derived/uri;

 org.apache.coyote.Request req = request.getCoyoteRequest();
 req.requestURI().setString(newRequestURI);
 req.decodedURI().setString(newRequestURI);

 MessageBytes uriMB = MessageBytes.newInstance();
 uriMB.duplicate(req.decodedURI());

 MessageBytes hostMB = MessageBytes.newInstance();
 hostMB.setString(request.getHost().getName());

 MappingData mappingData = request.getMappingData();
 mappingData.recycle();
 request.getConnector().getMapper().map(hostMB, uriMB, mappingData);


Re: Mapping REST requests across multiple app contexts

2010-08-22 Thread Ken Fox
chuck.caldar...@unisys.com wrote:

 If you place the standard rewrite filter in the ROOT context, you can catch
 any requests that do not directly map to the appropriate webapp and forward
 or redirect them appropriately.


I looked at UrlRewriteFilter and it seemed designed for forwarding within a
context, not between contexts. Can it forward from ROOT to a context loaded
by a Host finding the app in its appBase? I don't have Context declarations
for my apps and I'd really like to maintain isolation between them.

The advantage of doing context mapping in a Valve is that the context switch
can happen very early in the request and before any host-specific code runs.
I'm not concerned about portability--this is a very small amount of code and
it's fine to rewrite it entirely if I switch containers.

- Ken


Re: Mapping REST requests across multiple app contexts

2010-08-22 Thread André Warnier

Ken Fox wrote:

chuck.caldar...@unisys.com wrote:


If you place the standard rewrite filter in the ROOT context, you can catch
any requests that do not directly map to the appropriate webapp and forward
or redirect them appropriately.



I looked at UrlRewriteFilter and it seemed designed for forwarding within a
context, not between contexts. Can it forward from ROOT to a context loaded
by a Host finding the app in its appBase? I don't have Context declarations
for my apps and I'd really like to maintain isolation between them.

The advantage of doing context mapping in a Valve is that the context switch
can happen very early in the request and before any host-specific code runs.
I'm not concerned about portability--this is a very small amount of code and
it's fine to rewrite it entirely if I switch containers.


Just suggesting other scenarios here.
You could also run Tomcat behind a front-end Apache httpd, and let the front-end do the 
rewriting before forwarding to Tomcat.  This could have the advantage, should the load on 
your site become heavier, to also use the one front-end with several back-end Tomcats, 
whether load-balancing between them or just splitting the sites.
The other advantage is that you do not need to write any code.  URL-rewriting and proxying 
are already part of the standard Apache httpd.
And, this is a configuration already used at tens of thousands of sites, so one could say 
that the relevant code is pretty stable.

And it would remain perfectly portable.




-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



RE: Mapping REST requests across multiple app contexts

2010-08-22 Thread Caldarale, Charles R
 From: Ken Fox [mailto:k...@vulpes.com]
 Subject: Re: Mapping REST requests across multiple app contexts
 
 I looked at UrlRewriteFilter and it seemed designed for forwarding
 within a context, not between contexts.

Not true; forwards may cross contexts - look at the context attribute on the 
to element.

 Can it forward from ROOT to a context loaded by a Host finding 
 the app in its appBase?

Yes; just set crossContext in the global conf/context.xml file.

 I don't have Context declarations for my apps and I'd really like
 to maintain isolation between them.

Only the filter in ROOT would need to know the targets.

Using the filter would be much less complex (and less overhead) than setting up 
httpd for the sole purpose of URL rewriting.

 - Chuck


THIS COMMUNICATION MAY CONTAIN CONFIDENTIAL AND/OR OTHERWISE PROPRIETARY 
MATERIAL and is thus for use only by the intended recipient. If you received 
this in error, please contact the sender and delete the e-mail and its 
attachments from all computers.


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org



Mapping REST requests across multiple app contexts

2010-08-21 Thread Ken Fox
I'm looking for advice on the best way to map REST requests onto a
collection of Tomcat apps all running in the same JVM. The REST name space
was designed for client use and doesn't reflect how the apps implement it.
For example, the resource /v1/x/123 is implemented by app X, but the
resource /v1/x/123/y is implemented by app Y.

A proxy (e.g. Apache mod_proxy or Squid) in front of Tomcat can rewrite the
URLs to go to the correct app, but this gives us some pretty ugly proxy
configurations which have to be kept in lock-step with the Tomcat apps.
Relying on a proxy also makes it a bit harder to use Amazon's load balancer
because it doesn't do rewrites (I think we'd have to run a proxy on each
Tomcat instance).

I'm trying to implement the rewrite as a Valve (code outline below)
registered with the Engine which will run before any Hosts or Contexts. This
seems like a good approach and may even let me grab the JAX-RS annotations
from the apps to dynamically build the rewrite rules.

Does anyone have advice for REST name spaces in Tomcat in general?

Has anyone had good experiences with a rewrite proxy in front of Tomcat on
Amazon EC2 with Amazon's ELB?

Has anybody tried a rewrite Valve similar to this? It has to modify the
CoyoteRequest and generate new Request.mappingData which seems kind of
risky. (Though I think it will work in Tomcat 7, I've only tried Tomcat 6.)
This is my favorite approach so far.

Thanks,

- Ken


public void invoke(Request request, Response response) {
if (/v1.equals(request.getContextPath())) {
// map the in-bound REST URI to the app handling it
String newRequestURI = /new-app/some/derived/uri;

org.apache.coyote.Request req = request.getCoyoteRequest();
req.requestURI().setString(newRequestURI);
req.decodedURI().setString(newRequestURI);

MessageBytes uriMB = MessageBytes.newInstance();
uriMB.duplicate(req.decodedURI());

MessageBytes hostMB = MessageBytes.newInstance();
hostMB.setString(request.getHost().getName());

MappingData mappingData = request.getMappingData();
mappingData.recycle();
request.getConnector().getMapper().map(hostMB, uriMB, mappingData);

request.setContext((Context) mappingData.context);
request.setWrapper((Wrapper) mappingData.wrapper);
}

getNext().invoke(request, response);
}


RE: Mapping REST requests across multiple app contexts

2010-08-21 Thread Caldarale, Charles R
 From: Ken Fox [mailto:k...@vulpes.com]
 Subject: Mapping REST requests across multiple app contexts
 
 I'm trying to implement the rewrite as a Valve

If you place the standard rewrite filter in the ROOT context, you can catch any 
requests that do not directly map to the appropriate webapp and forward or 
redirect them appropriately.

http://www.tuckey.org/urlrewrite/

No reason to reinvent the wheel, especially in a fashion that's not very 
portable.

 - Chuck


THIS COMMUNICATION MAY CONTAIN CONFIDENTIAL AND/OR OTHERWISE PROPRIETARY 
MATERIAL and is thus for use only by the intended recipient. If you received 
this in error, please contact the sender and delete the e-mail and its 
attachments from all computers.


-
To unsubscribe, e-mail: users-unsubscr...@tomcat.apache.org
For additional commands, e-mail: users-h...@tomcat.apache.org