package edu.neu.ccs.demeterf.http.server; import java.lang.reflect.Method; import edu.neu.ccs.demeterf.lib.*; import edu.neu.ccs.demeterf.http.classes.*; import java.net.Socket; public class ServerDispatch { static void p(String s){ Factory.p(s); } private Map dispatch; private Object target; private ServerDispatch(Map dis, Object targ){ dispatch = dis; target = targ; } /** Create a dispatcher, selecting methods with the default argument types. */ public static ServerDispatch create(Object handler){ return create(handler,DefaultFormals); } /** Create a dispatcher, selecting methods with the given formal argument types. */ public static ServerDispatch create(Object handler, final Class[] formals){ Class c = handler.getClass(); if (!c.isAnnotationPresent(Server.class)) { throw Factory.error("Cannot Create Server for unannotated class '" + c.getCanonicalName() + "'"); } Server s = c.getAnnotation(Server.class); return new ServerDispatch(List.create(c.getDeclaredMethods()).fold( new List.Fold>(){ public Map fold(Method m, Map res){ if (!m.isAnnotationPresent(Path.class)) { return res; } String p = check(m, formals); m.setAccessible(true); p("Mapping '" + p + "' to " + m.getName()); return res.put(p, m); } }, Map. create()), handler); } /** Get the target/server handler object */ public Object getTarget(){ return target; } /** Handle a given HTTP request using this dispatcher (no socket) */ public HTTPResp handle(HTTPReq req){ return handle(req,null); } /** Handle a given HTTP request using this dispatcher, passing the * server's socket */ public HTTPResp handle(HTTPReq req, Socket sock){ try { String path = req.trimmedUrl(); p("HTTP Path: " + path); HTTPResp res; if (!dispatch.containsKey(path)){ p("Unbound Path '"+path+"' Trying Default"); path = Path.EMPTY; } Object[] args; if (!dispatch.containsKey(path)){ p("No Default Path"); res = HTTPResp.textError("Path Not Found"); } else { Method m = dispatch.get(path); switch(m.getParameterTypes().length){ case 0:args = new Object[] {};break; case 1:args = new Object[] { req };break; default: if(sock == null) throw Factory.error("Missing Expected Socket Argument"); args = new Object[] { req, sock };break; } res = (HTTPResp) m.invoke(target, args); } return res; }catch(IllegalAccessException e){ return HTTPResp.textError(HTTPResp.MIN_SERVER_ERROR, "Server Error", "Inaccessable Request Handler: "+e); }catch(java.lang.reflect.InvocationTargetException e){ return HTTPResp.textError(HTTPResp.MIN_SERVER_ERROR, "Handler Exception", "Exception in Request Handler: "+e.getCause()); } } /** Minimal Parameters... */ static Class[] MinimalFormals = { HTTPReq.class }; /** Allowed Parameters... */ static Class[] DefaultFormals = { HTTPReq.class, Socket.class }; /** Do a sanity check of the types of an annotated method */ public static String check(Method m, Class[] formals){ Path p = m.getAnnotation(Path.class); if (!HTTPResp.class.equals(m.getReturnType())) { throw Factory.error("Return Type is incorrect: '" + m.getReturnType().getClass() + "'"); } Class[] params = m.getParameterTypes(); if (params.length > formals.length) { throw Factory.error("Too many Parameters for Server Method '" + m.getName() + "'"); } checkParams(m.getName(), 0, params, formals); if(p == null)return null; return p.value(); } /** Check that the parameters of the method are fine... */ private static void checkParams(String method, int i, Class[] ps, Class[] fmls){ if (i >= ps.length || i >= fmls.length)return; if (!fmls[i].isAssignableFrom(ps[i])) { throw Factory.error("Parameter #" + i + " of " + method + " is of an incorrect type. " + "Expecting '" + fmls[i].getName() + "'"); } checkParams(method, i+1, ps, fmls); } }