Add Constroller system; a class that contains 0-N methods with Route's annotation; can have Matcher, Context, Reader or Write, or none of them

This commit is contained in:
jeffcheasey88 2024-09-17 13:00:52 +02:00
parent b47ffac3b1
commit 2cf4363e25
4 changed files with 122 additions and 61 deletions

View file

@ -13,10 +13,12 @@ public class DependencyInjector{
private Map<String, Object> map; private Map<String, Object> map;
private List<BiFunction<Constructor<?>, Parameter, Object>> builders; private List<BiFunction<Constructor<?>, Parameter, Object>> builders;
private Object[] injections; private Object[] injections;
private Map<Class<?>, Object> cache;
public DependencyInjector(){ public DependencyInjector(){
this.map = new HashMap<>(); this.map = new HashMap<>();
this.builders = new ArrayList<>(); this.builders = new ArrayList<>();
this.cache = new HashMap<>();
} }
public DependencyInjector of(String name, Object value){ public DependencyInjector of(String name, Object value){
@ -41,7 +43,7 @@ public class DependencyInjector{
return this; return this;
} }
Object applyDependency(Constructor<?> constructor, Parameter parameter, Map<Class<?>, Object> cache){ Object applyDependency(Constructor<?> constructor, Parameter parameter){
Injection annotation = parameter.getAnnotation(Injection.class); Injection annotation = parameter.getAnnotation(Injection.class);
if(annotation != null){ if(annotation != null){
Object result = this.map.get(annotation.value()); Object result = this.map.get(annotation.value());
@ -52,8 +54,8 @@ public class DependencyInjector{
Object result = function.apply(constructor, parameter); Object result = function.apply(constructor, parameter);
if(result != null) return result; if(result != null) return result;
} }
if(this.injections == null) return null;
Class<?> type = parameter.getType(); Class<?> type = parameter.getType();
if(this.injections == null) throw new IllegalArgumentException("No dependency for type "+type+" in constructor "+constructor);
Object result = null; Object result = null;
for(Object injection : injections){ for(Object injection : injections){
if(type.isAssignableFrom(injection.getClass())){ if(type.isAssignableFrom(injection.getClass())){
@ -65,6 +67,7 @@ public class DependencyInjector{
} }
} }
} }
if(result == null) throw new IllegalArgumentException("No dependency for type "+type+" in constructor "+constructor);
return result; return result;
} }
} }

View file

@ -12,73 +12,61 @@ public class RouteMapper<U extends User>{
private Router<U> router; private Router<U> router;
private Response[] responses; private RouteState[] states;
private Method[] methods;
private Route[] routes;
private Pattern[] patterns;
private int[] calls; private int[] calls;
private int dif; private int dif;
public RouteMapper(Router<U> router){ public RouteMapper(Router<U> router){
this.router = router; this.router = router;
this.responses = new Response[0]; this.states = new RouteState[0];
this.methods = new Method[0];
this.routes = new Route[0];
this.patterns = new Pattern[0];
} }
public void register(Response response, Method method, Route route, Pattern pattern){ public void register(Object instance, Method method, Route route){
int length = responses.length+1; int length = states.length+1;
this.responses = addElement(responses, new Response[length], response); this.states = addElement(states, new RouteState[length], new RouteState(instance, method, route));
this.methods = addElement(methods, new Method[length], method);
this.routes = addElement(routes, new Route[length], route);
this.patterns = addElement(patterns, new Pattern[length], pattern);
if(this.calls != null) this.calls = new int[length]; if(this.calls != null) this.calls = new int[length];
} }
public void activeReOrdering(){ public void activeReOrdering(){
this.calls = new int[responses.length]; this.calls = new int[states.length];
this.dif = 2; this.dif = 2;
} }
public boolean exec(Context context, HttpReader reader, HttpWriter writer, List<RouteInterceptor> interceptors) throws Exception{ public boolean exec(Context context, HttpReader reader, HttpWriter writer, List<RouteInterceptor> interceptors) throws Exception{
Response response = null; RouteState target = null;
Method method = null;
Matcher matcher = null; Matcher matcher = null;
String path = context.getPath(); String path = context.getPath();
synchronized(responses){ synchronized(states){
for(int i = 0; i < responses.length; i++){ for(int i = 0; i < states.length; i++){
Route route = routes[i]; RouteState state = states[i];
Pattern pattern = patterns[i]; matcher = state.match(path);
matcher = pattern.matcher(path); if(matcher != null){
if(matcher.matches()){ if(state.isWebSocket()){
if(route.websocket()){
switchToWebSocket(reader, writer); switchToWebSocket(reader, writer);
reader = new WebSocketReader(reader); reader = new WebSocketReader(reader);
writer = new WebSocketWriter(writer); writer = new WebSocketWriter(writer);
if(route.needLogin()){ if(state.needLogin()){
String token = reader.<JsonMap>readJson().get("token"); String token = reader.<JsonMap>readJson().get("token");
context.setUser(this.router.getUser(token)); context.setUser(this.router.getUser(token));
} }
} }
if((!context.isLogged()) && route.needLogin()){ if((!context.isLogged()) && state.needLogin()){
writer.response(401, router.getDefaultHeaders(context.getType())); writer.response(401, router.getDefaultHeaders(context.getType()));
return true; return true;
} }
response = responses[i]; target = state;
method = methods[i];
order(i); order(i);
break; break;
} }
} }
} }
if(response != null){ if(target != null){
for(RouteInterceptor interceptor : interceptors){ for(RouteInterceptor interceptor : interceptors){
if(!interceptor.intercept(matcher, context, reader, writer, method)) return true; if(!interceptor.intercept(matcher, context, reader, writer, target.getMethod())) return true;
} }
response.exec(matcher, context, reader, writer); target.getMethod().invoke(target.getInstance(), target.bindMethod(matcher, context, reader, writer));
return true; return true;
} }
return false; return false;
@ -94,10 +82,7 @@ public class RouteMapper<U extends User>{
} }
if(index < 1) return; if(index < 1) return;
if(call > this.calls[index-1]){ if(call > this.calls[index-1]){
switchElement(responses, index, index-1); switchElement(states, index, index-1);
switchElement(methods, index, index-1);
switchElement(routes, index, index-1);
switchElement(patterns, index, index-1);
this.calls[index] = this.calls[index-1]; this.calls[index] = this.calls[index-1];
this.calls[index-1] = call; this.calls[index-1] = call;

View file

@ -0,0 +1,72 @@
package dev.peerat.framework;
import java.lang.reflect.Method;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
public class RouteState {
private Object instance;
private Method method;
private Route route;
private Pattern pattern;
private int[] bindMethod;
public RouteState(Object instance, Method method, Route route){
this.instance = instance;
this.method = method;
this.route = route;
this.pattern = Pattern.compile(route.path());
Class<?>[] parameters = method.getParameterTypes();
this.bindMethod = new int[4];
this.bindMethod[0] = findIndex(parameters, Matcher.class);
this.bindMethod[1] = findIndex(parameters, Context.class);
this.bindMethod[2] = findIndex(parameters, HttpReader.class);
this.bindMethod[3] = findIndex(parameters, HttpWriter.class);
}
public Matcher match(String path){
Matcher matcher = pattern.matcher(path);
return matcher.matches() ? matcher : null;
}
public boolean isWebSocket(){
return route.websocket();
}
public boolean needLogin(){
return route.needLogin();
}
public Method getMethod(){
return this.method;
}
public Object getInstance(){
return this.instance;
}
public Object[] bindMethod(Matcher matcher, Context context, HttpReader reader, HttpWriter writer){
Object[] result = new Object[method.getParameterCount()];
setElement(result, bindMethod[0], matcher);
setElement(result, bindMethod[1], context);
setElement(result, bindMethod[2], reader);
setElement(result, bindMethod[3], writer);
return result;
}
private void setElement(Object[] array, int position, Object element){
if(position < 0) return;
array[position] = element;
}
private int findIndex(Class<?>[] array, Class<?> type){
for(int i = 0; i < array.length; i++){
Class<?> clazz = array[i];
if(type.isAssignableFrom(clazz)) return i;
}
return -1;
}
}

View file

@ -105,13 +105,33 @@ public class Router<U extends User>{
Response.class.getDeclaredMethods()[0].getParameterTypes()); Response.class.getDeclaredMethods()[0].getParameterTypes());
Route route = method.getAnnotation(Route.class); Route route = method.getAnnotation(Route.class);
this.mappers[route.type().ordinal()].register(response, method, route, Pattern.compile(route.path())); this.mappers[route.type().ordinal()].register(response, method, route);
}catch(Exception e){ }catch(Exception e){
throw new IllegalArgumentException(e); throw new IllegalArgumentException(e);
} }
return this; return this;
} }
public Router<U> registerClass(Class<?> clazz, Object... injections) throws Exception{
return registerClass(clazz, new DependencyInjector().of(injections));
}
public Router<U> registerClass(Class<?> clazz, DependencyInjector injector) throws Exception{
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if(constructors.length != 1) throw new IllegalArgumentException("Class with multiple constructor. Not supported by this framework for the moment.");
Object instance = injectDependencies(constructors[0], injector);
for(Method method : clazz.getDeclaredMethods()){
Route route = method.getAnnotation(Route.class);
if(route == null) continue;
this.mappers[route.type().ordinal()].register(instance, method, route);
System.out.println("Registered route "+method+" ["+route.type()+" "+route.path()+"]");
}
return this;
}
public Router<U> registerPackages(Object... injections){ public Router<U> registerPackages(Object... injections){
return registerPackages(new DependencyInjector().of(injections)); return registerPackages(new DependencyInjector().of(injections));
} }
@ -131,16 +151,10 @@ public class Router<U extends User>{
try{ try{
BufferedReader reader = new BufferedReader(new InputStreamReader(stream)); BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line; String line;
Map<Class<?>, Object> cache = new HashMap<>();
while((line = reader.readLine()) != null){ while((line = reader.readLine()) != null){
if(line.endsWith(".class")){ if(line.endsWith(".class")){
Class<?> clazz = Class.forName(name+"."+line.substring(0, line.lastIndexOf('.'))); Class<?> clazz = Class.forName(name+"."+line.substring(0, line.lastIndexOf('.')));
if(Response.class.isAssignableFrom(clazz)){ registerClass(clazz, injector);
if(clazz.equals(Response.class)) continue;
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if(constructors.length != 1) continue;
injectDependencies(constructors[0], cache, injector);
}
}else{ }else{
registerPackages(name+"."+line, injector); registerPackages(name+"."+line, injector);
} }
@ -153,24 +167,11 @@ public class Router<U extends User>{
return this; return this;
} }
private void injectDependencies(Constructor<?> constructor, Map<Class<?>, Object> cache, DependencyInjector injector){ private Object injectDependencies(Constructor<?> constructor, DependencyInjector injector) throws Exception{
Parameter[] parameters = constructor.getParameters(); Parameter[] parameters = constructor.getParameters();
Object[] injections = new Object[parameters.length]; Object[] injections = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++){ for(int i = 0; i < parameters.length; i++) injections[i] = injector.applyDependency(constructor, parameters[i]);
Parameter parameter = parameters[i]; return constructor.newInstance(injections);
Class<?> type = parameter.getType();
Object dependency = cache.get(type);
if(dependency != null) injections[i] = dependency;
else if((injections[i] = injector.applyDependency(constructor, parameter, cache)) == null) throw new IllegalArgumentException("No dependency for type "+type+" in constructor "+constructor);
}
try {
Response response = (Response) constructor.newInstance(injections);
register(response);
System.out.println("registerd "+response.getClass());
} catch (Exception e){
e.printStackTrace();
}
} }
public Router<U> setDefault(Response response){ public Router<U> setDefault(Response response){