package dev.peerat.framework; import java.io.BufferedReader; import java.io.InputStream; import java.io.InputStreamReader; import java.lang.reflect.Constructor; import java.lang.reflect.Method; import java.net.ServerSocket; import java.net.Socket; import java.util.ArrayList; import java.util.List; import javax.net.ssl.SSLServerSocket; import javax.net.ssl.SSLServerSocketFactory; import dev.peerat.framework.auth.Authenticator; public class Router{ public static void main(String[] args) throws Exception{} private Locker logger; private Locker exceptions; private RouteMapper[] mappers; private List interceptors; private Response noFileFound; private Authenticator auth; private String[][] headers; private ServerSocket serverSocket; public Router(){ this.logger = new Locker<>(); this.exceptions = new Locker<>(); int types = RequestType.values().length; this.mappers = new RouteMapper[types]; this.interceptors = new ArrayList<>(); for(RequestType type : RequestType.values()) this.mappers[type.ordinal()] = new RouteMapper<>(this); this.headers = new String[types][0]; } public Router setAuthenticator(Authenticator auth){ this.auth = auth; return this; } public Router addDefaultHeaders(RequestType type, String... headers){ String[] origin = this.headers[type.ordinal()]; String[] copy = new String[origin.length+headers.length]; System.arraycopy(origin, 0, copy, 0, origin.length); System.arraycopy(headers, 0, copy, origin.length, headers.length); this.headers[type.ordinal()] = copy; return this; } public Router register(Response response){ try{ Method method = response.getClass().getDeclaredMethod("exec", Response.class.getDeclaredMethods()[0].getParameterTypes()); Route route = method.getAnnotation(Route.class); this.mappers[route.type().ordinal()].register(response, method, route); System.out.println("Registered route "+method+" ["+route.type()+" "+route.path()+"]"); }catch(Exception e){ throw new IllegalArgumentException(e); } return this; } public Router registerClass(Class clazz, Object... injections) throws Exception{ return registerClass(clazz, new DependencyInjector().of(injections)); } public Router registerClass(Class clazz, DependencyInjector injector) throws Exception{ Object instance = null; for(Method method : clazz.getDeclaredMethods()){ Route route = method.getAnnotation(Route.class); if(route == null) continue; if(instance == null){ Constructor[] constructors = clazz.getDeclaredConstructors(); if(constructors.length != 1) throw new IllegalArgumentException("Class with multiple constructor. Not supported by this framework for the moment."); instance = constructors[0].newInstance(injector.apply(constructors[0])); } this.mappers[route.type().ordinal()].register(instance, method, route); System.out.println("Registered route "+method+" ["+route.type()+" "+route.path()+"]"); } return this; } public Router registerPackages(Object... injections){ return registerPackages(new DependencyInjector().of(injections)); } public Router registerPackages(DependencyInjector injector){ String clazz = Thread.currentThread().getStackTrace()[2].getClassName(); if(clazz.startsWith("dev.peerat.framework")) clazz = Thread.currentThread().getStackTrace()[3].getClassName(); String pack = clazz.substring(0, clazz.lastIndexOf('.')); return registerPackages(pack, injector); } public Router registerPackages(String name, Object... injections){ return registerPackages(name, new DependencyInjector().of(injections)); } public Router registerPackages(String name, DependencyInjector injector){ InputStream stream = ClassLoader.getSystemClassLoader().getResourceAsStream(name.replace(".", "/")); try{ BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); String line; while((line = reader.readLine()) != null){ if(line.endsWith(".class")){ Class clazz = Class.forName(name+"."+line.substring(0, line.length()-6)); registerClass(clazz, injector); continue; } if(!line.contains(".")) registerPackages(name+"."+line, injector); } reader.close(); }catch(Exception e){ System.err.println("Failed to read "+name); e.printStackTrace(); } return this; } public Router setDefault(Response response){ this.noFileFound = response; return this; } public Router addInterceptor(RouteInterceptor interceptor){ this.interceptors.add(interceptor); return this; } public Router bind(RouteFilter filter, Class... types){ for(RouteMapper mapper : this.mappers) mapper.bind(filter, types); return this; } private void bindAll(){ for(RouteMapper mapper : this.mappers) mapper.bindAll(); } public Router activeReOrdering(){ for(RouteMapper mapper : this.mappers) mapper.activeReOrdering(); return this; } String[] getDefaultHeaders(RequestType type){ return this.headers[type.ordinal()]; } public Authenticator getAuthenticator(){ return this.auth; } void exec(Context context, HttpReader reader, HttpWriter writer) throws Exception{ if(this.mappers[context.getType().ordinal()].exec(context, reader, writer, this.interceptors)) return; if(noFileFound != null) noFileFound.exec(null, context, reader, writer); } public Router configureSSL(String keyStore, String keyStorePassword){ System.setProperty("javax.net.ssl.keyStore", keyStore); System.setProperty("javax.net.ssl.keyStorePassword", keyStorePassword); return this; } public U getUser(RequestType type, HttpReader reader) throws Exception{ return this.auth != null ? this.auth.getUser(type, reader) : null; } public U getUser(String data) throws Exception{ return this.auth != null ? this.auth.getUser(data) : null; } public String createAuthUser(U user) throws Exception{ return this.auth.createAuthUser(user); } public Locker getLogger(){ return this.logger; } public Locker getExceptionLogger(){ return this.exceptions; } public void listen(int port, boolean ssl) throws Exception{ bindAll(); if (ssl) { // Not needed with the use of a proxy try { SSLServerSocketFactory ssf = (SSLServerSocketFactory) SSLServerSocketFactory.getDefault(); serverSocket = (SSLServerSocket) ssf.createServerSocket(port); while (!serverSocket.isClosed()) { Socket socket = serverSocket.accept(); Client client = new Client<>(socket, this); client.start(); } } catch (Exception e) { e.printStackTrace(); } finally { stop(); } } else { try{ serverSocket = new ServerSocket(port); while (!serverSocket.isClosed()) { Socket socket = serverSocket.accept(); Client client = new Client<>(socket, this); client.start(); } } catch (Exception e) { e.printStackTrace(); } finally { stop(); } } } public void stop(){ if(serverSocket == null) return; try { serverSocket.close(); serverSocket = null; }catch(Exception e){ e.printStackTrace(); } } }