package javango.core.servers;
import javango.api.MethodObjectParams;
import javango.api.Urls;
import java.lang.annotation.Annotation;
import java.util.ArrayList;
import java.util.List;
import java.util.ListIterator;
import javango.core.AnonymousUser;
import javango.core.LoginRequired;
import javango.core.RoleRequired;
import javango.api.Url;
import javango.core.contextprocessors.ContextProcessor;
import javango.http.Http404;
import javango.http.HttpException;
import javango.http.HttpRequest;
import javango.http.HttpResponse;
import javango.http.SimpleHttpResponse;
import javango.middleware.Middleware;
import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;
import com.google.inject.Inject;
import com.google.inject.Injector;
import com.google.inject.Singleton;
import java.lang.reflect.InvocationTargetException;
import javango.conf.RuntimeSettings;
@Singleton
public class RequestProcessor {
public static interface Listener {
public void stopRequestProcessor(RequestProcessor requestProcessor);
}
private final static Log log = LogFactory.getLog(RequestProcessor.class);
protected List<Listener> listeners = new ArrayList<Listener>();
private RuntimeSettings settings;
private Injector injector;
/**
* Cached list of urls, each url will have its members injected by Guice once and only once when this list is first initizlized by the first call to getUrls().
*/
private Urls urls;
@Inject
public RequestProcessor(RuntimeSettings settings, Injector injector) throws Exception {
this.settings = settings;
this.injector = injector;
}
public void stop() {
for (Listener l : listeners) {
l.stopRequestProcessor(null);
}
}
/**
* Returns a list of urls for this application, the list is cached on first access, all
* urls in the list will be injected by guice.
*
* @return
*/
protected Urls getUrls() {
if (urls == null) {
urls = injector.getInstance(settings.getSettings().getUrlsClass());
for(Url u : urls.getUrlPatterns()) {
injector.injectMembers(u);
}
}
return urls;
}
public HttpResponse service(final HttpRequest request) throws HttpException {
String path = request.getPath();
if (log.isDebugEnabled()) log.debug("Processing path '" + path + "'" );
ListIterator<Middleware> middlewareIterator=settings.getMiddlewares().listIterator();
for (; middlewareIterator.hasNext(); ) {
Middleware m = middlewareIterator.next();
HttpResponse r = m.processRequest(request);
if (r != null) {
return r;
}
}
HttpResponse response = null;
Urls urls = getUrls();
for(Url url: urls.getUrlPatterns()) {
if (url.matches(path)) {
try {
MethodObjectParams mop = url.getMethodObjectParams(path, request);
for (Middleware m : settings.getMiddlewares()) {
m.processMethodAndParams(request, mop);
}
// TODO Move somewhere else
for (Annotation annotation : mop.getMethod().getAnnotations()) {
// TODO Should this return the response, or let it fall through to the middleware?
if (annotation instanceof LoginRequired) {
if (request.getUser() == null || request.getUser() instanceof AnonymousUser) {
response = new SimpleHttpResponse("Login Required");
}
} else if (annotation instanceof RoleRequired) {
RoleRequired rr = (RoleRequired)annotation;
if (request.getUser() == null || request.getUser() instanceof AnonymousUser || !request.getUser().hasRole(rr.role())) {
response = new SimpleHttpResponse("Not Authorized");
}
}
}
if (response == null ) response = invoke(mop, injector);
break;
} catch (Exception e) {
for (; middlewareIterator.hasPrevious(); ) {
Middleware m = middlewareIterator.previous();
response = m.processException(request, response, e);
}
// got to be a better solution to this...
//middlewareIterator=settings.getMiddlewares().listIterator();
for (; middlewareIterator.hasNext(); middlewareIterator.next()) ;
if( response == null) {
if (e instanceof HttpException) {
throw (HttpException)e;
}
throw new HttpException(e);
}
}
}
}
if (response != null && response.getContext() != null) {
// TODO move message handling to template context processor
// should be an object that will automatically remove messages/errors if they are read from the template
response.getContext().put("messages", request.getSession().getAndClearMessages());
response.getContext().put("errors", request.getSession().getAndClearErrors());
response.getContext().put("absolute_path", request.getContext());
String media_url = settings.getSettings().get("MEDIA_URL");
if (media_url != null) {
response.getContext().put("MEDIA_URL", media_url);
}
for (ContextProcessor rc : settings.getContextProcessors() ) {
response.getContext().putAll(rc.evaluate(request));
}
}
for (; middlewareIterator.hasPrevious(); ) {
Middleware m = middlewareIterator.previous();
response = m.processResponse(request, response);
}
if (response != null) {
return response;
}
throw new Http404("No URLs matched: '" + path + "'");
}
public static HttpResponse invoke(MethodObjectParams m, Injector injector) throws HttpException {
try {
Object object = injector.getInstance(m.getClazz());
return (HttpResponse) m.getMethod().invoke(object, m.getArguments());
} catch (InvocationTargetException e) {
if (e.getTargetException() instanceof HttpException) {
throw (HttpException)e.getTargetException();
} else if (e.getTargetException() instanceof Exception) {
e.printStackTrace();
throw new HttpException(e.getTargetException());
}
throw new HttpException(e);
} catch (Exception e) {
throw new HttpException(e);
}
}
}