package edu.neu.ccs.demeterf.http.server; import java.io.IOException; import java.net.Socket; import java.lang.reflect.Field; import edu.neu.ccs.demeterf.lib.List; import java.lang.reflect.Method; /** Web Server Factory class */ public class Factory { private Factory() {} static boolean verbose = false; /** Set to true in order to get Output for Server/Dispatch descisions. */ public static void setVerbose(boolean v){ verbose = v; } static void p(String s){ if (verbose) { System.err.println(" ** " + s); } } /** * Create a new Server using the given Handler. The {@link edu.neu.ccs.demeterf.http.server.Server Server} * annotation is used to tag a class as an HTTP Server/Handler. Within the class, * {@link edu.neu.ccs.demeterf.http.server.Port Port} Annotation is used to mark the port (an instance variable) * the Server will listen on, which is bound (read) at Server creation time. Handler methods are annotated with * {@link edu.neu.ccs.demeterf.http.server.Path Path} to describe responses to various Path * requests. See {@link edu.neu.ccs.demeterf.http.Test Test} for an Example. */ public static ServerThread create(Object handler) throws IOException{ return create(handler, false); } /** Create a Single/Multi-threaded Server */ public static ServerThread create(Object handler, boolean single) throws IOException{ return create(handler, single, ServerThread.DEFAULT_BACKLOG); } /** Create a Single/Multi-threaded Server */ public static ServerThread create(Object handler, boolean single, int backlog) throws IOException{ Class c = handler.getClass(); if (!c.isAnnotationPresent(Server.class)) { throw error("Cannot Create Server for unannotated class '" + c.getCanonicalName() + "'"); } int port = getPort(c, handler); long max = getMaxMsgSize(c, handler); p("Server Port = " + port); p("Max MsgSize = " + max); List except = List.create(c.getDeclaredMethods()).filter(new Except()); if(!except.isEmpty()) ServerDispatch.check(except.top(), new Class[]{Exception.class, Socket.class}); return new ServerThread(port, single, ServerDispatch.create(handler), max, except.isEmpty()?null:except.top(), backlog); } private static class Except extends List.Pred{ public boolean huh(Method m) { return m.isAnnotationPresent(ExceptionHandler.class); } } /** Create a ServerDispatch for direct testing */ public ServerDispatch localDispatcher(Object handler){ return ServerDispatch.create(handler, ServerDispatch.MinimalFormals); } /** Get the Port number of the server handler instance */ private static int getPort(Class c, Object server){ for(Field ff:c.getDeclaredFields()){ if(ff.isAnnotationPresent(Port.class)) try{ ff.setAccessible(true); if(ff.getType().equals(int.class) || ff.getType().equals(Integer.class)) return (Integer)ff.get(server); }catch(IllegalAccessException e){ throw error("Port Field '"+ff.getName()+"' is not accessible in " + c.getCanonicalName()); } } throw error("No Port Field found in " + c.getCanonicalName()); } /** Get the MaxBytes (request size) of the server handler instance */ private static long getMaxMsgSize(Class c, Object server){ for(Field ff:c.getDeclaredFields()){ if(ff.isAnnotationPresent(MaxMessageSize.class)) try{ ff.setAccessible(true); if(ff.getType().equals(int.class) || ff.getType().equals(Integer.class)) return (Integer)ff.get(server); if(ff.getType().equals(long.class) || ff.getType().equals(Long.class)) return (Long)ff.get(server); throw error("Incorrect Type for MaxRequestSize ("+ff.getType().getSimpleName()+")"); }catch(IllegalAccessException e){ throw error("MaxRequestSize Field '"+ff.getName()+"' is not accessible in " + c.getCanonicalName()); } } return 0; } /** Create a RuntimeException to throw with the given Message */ public static RuntimeException error(String s){ return new RuntimeException(s); } /** Create a RuntimeException to throw with the given Cause */ public static RuntimeException error(Throwable t){ return new RuntimeException(t); } }