/* Copyright (c) 2001 - 2007 TOPP - www.openplans.org. All rights reserved.
* This code is licensed under the GPL 2.0 license, availible at the root
* application directory.
*/
package org.geoserver.rest;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.geoserver.config.GeoServer;
import org.geoserver.ows.util.ResponseUtils;
import org.geoserver.platform.GeoServerExtensions;
import org.restlet.Restlet;
import org.restlet.Router;
import org.restlet.data.Request;
import org.restlet.data.Response;
import org.restlet.resource.Resource;
import org.springframework.beans.BeansException;
import org.springframework.web.servlet.ModelAndView;
import org.springframework.web.servlet.mvc.AbstractController;
import com.noelios.restlet.ext.servlet.ServletConverter;
/**
* Simple AbstractController implementation that does the translation between
* Spring requests and Restlet requests.
*/
public class RESTDispatcher extends AbstractController {
/** HTTP method "PUT" */
public static String METHOD_PUT = "PUT";
/** HTTP method "DELETE" */
public static String METHOD_DELETE = "DELETE";
/**
* logger
*/
static Logger LOG = org.geotools.util.logging.Logging.getLogger("org.geoserver.rest");
/**
* converter for turning servlet requests into resetlet requests.
*/
ServletConverter myConverter;
/**
* the root restlet router
*/
Router myRouter;
/**
* rest request callbacks
*/
List<DispatcherCallback> callbacks;
/**
* geoserver configuration
*/
GeoServer gs;
public RESTDispatcher(GeoServer gs) {
this.gs = gs;
setSupportedMethods(new String[] {
METHOD_GET, METHOD_POST, METHOD_PUT, METHOD_DELETE, METHOD_HEAD
});
}
protected void initApplicationContext() throws BeansException {
super.initApplicationContext();
myConverter = new GeoServerServletConverter(getServletContext());
myConverter.setTarget(createRoot());
callbacks =
GeoServerExtensions.extensions(DispatcherCallback.class, getApplicationContext());
}
protected ModelAndView handleRequestInternal(HttpServletRequest req, HttpServletResponse resp)
throws Exception {
try {
myConverter.service(req, resp);
}
catch( Exception e ) {
RestletException re = null;
if ( e instanceof RestletException ) {
re = (RestletException) e;
}
if ( re == null && e.getCause() instanceof RestletException ) {
re = (RestletException) e.getCause();
}
if ( re != null ) {
resp.setStatus( re.getStatus().getCode() );
String reStr = re.getRepresentation().getText();
if ( reStr != null ) {
LOG.severe( reStr );
resp.setContentType("text/plain");
resp.getOutputStream().write(reStr.getBytes());
}
//log the full exception at a higher level
LOG.log( Level.SEVERE, "", re );
}
else {
LOG.log( Level.SEVERE, "", e );
resp.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
if ( e.getMessage() != null ) {
resp.getOutputStream().write( e.getMessage().getBytes() );
}
}
resp.getOutputStream().flush();
}
return null;
}
public void addRoutes(Map m, Router r){
Iterator it = m.entrySet().iterator();
while (it.hasNext()){
Map.Entry entry = (Map.Entry) it.next();
// LOG.info("Found mapping: " + entry.getKey().toString());
Restlet restlet =
(getApplicationContext().getBean(entry.getValue().toString()) instanceof Resource)
? new BeanResourceFinder(getApplicationContext(), entry.getValue().toString())
: new BeanDelegatingRestlet(getApplicationContext(), entry.getValue().toString());
String path = entry.getKey().toString();
r.attach(path, restlet);
if (path.indexOf("?") == -1){
r.attach(path + "?{q}", restlet);
} else LOG.fine("Query string already listed in restlet mapping: " + path);
}
}
public Restlet createRoot() {
if (myRouter == null){
myRouter = new Router() {
@Override
protected synchronized void init(Request request,
Response response) {
super.init(request, response);
//set the page uri's
// http://host:port/appName
String baseURL = request.getRootRef().getParentRef().toString();
String rootPath = request.getRootRef().toString().substring(baseURL.length());
String pagePath = request.getResourceRef().toString().substring(baseURL.length());
String basePath = null;
if ( request.getResourceRef().getBaseRef() != null ) {
basePath = request.getResourceRef().getBaseRef().toString().substring(baseURL.length());
}
//strip off the extension
String extension = ResponseUtils.getExtension(pagePath);
if ( extension != null ) {
pagePath = pagePath.substring(0, pagePath.length() - extension.length() - 1);
}
//trim leading slash
if ( pagePath.endsWith( "/" ) ) {
pagePath = pagePath.substring(0, pagePath.length()-1);
}
//create a page info object and put it into a request attribute
PageInfo pageInfo = new PageInfo();
pageInfo.setBaseURL(baseURL);
pageInfo.setRootPath(rootPath);
pageInfo.setBasePath(basePath);
pageInfo.setPagePath(pagePath);
pageInfo.setExtension( extension );
request.getAttributes().put( PageInfo.KEY, pageInfo );
for (DispatcherCallback callback : callbacks) {
callback.init(request, response);
}
}
@Override
public Restlet getNext(Request request, Response response) {
Restlet next = super.getNext(request, response);
if (next != null) {
for (DispatcherCallback callback : callbacks) {
callback.dispatched(request, response, next);
}
}
return next;
};
@Override
public void handle(Request request, Response response) {
try {
super.handle(request, response);
}
catch(Exception e) {
//execute the exception callback
for (DispatcherCallback callback : callbacks) {
callback.exception(request, response, e);
}
if (e instanceof RuntimeException) {
throw (RuntimeException) e;
}
throw new RuntimeException(e);
}
finally {
//execute the finished callback
for (DispatcherCallback callback : callbacks) {
callback.finished(request, response);
}
}
};
};
//load all the rest mappings and register them with the router
Iterator i =
GeoServerExtensions.extensions(RESTMapping.class).iterator();
while (i.hasNext()){
RESTMapping rm = (RESTMapping)i.next();
addRoutes(rm.getRoutes(), myRouter);
}
//create a root mapping
myRouter.attach("", new IndexRestlet(myRouter));
}
return myRouter;
}
}