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

View file

@ -12,73 +12,61 @@ public class RouteMapper<U extends User>{
private Router<U> router;
private Response[] responses;
private Method[] methods;
private Route[] routes;
private Pattern[] patterns;
private RouteState[] states;
private int[] calls;
private int dif;
public RouteMapper(Router<U> router){
this.router = router;
this.responses = new Response[0];
this.methods = new Method[0];
this.routes = new Route[0];
this.patterns = new Pattern[0];
this.states = new RouteState[0];
}
public void register(Response response, Method method, Route route, Pattern pattern){
int length = responses.length+1;
this.responses = addElement(responses, new Response[length], response);
this.methods = addElement(methods, new Method[length], method);
this.routes = addElement(routes, new Route[length], route);
this.patterns = addElement(patterns, new Pattern[length], pattern);
public void register(Object instance, Method method, Route route){
int length = states.length+1;
this.states = addElement(states, new RouteState[length], new RouteState(instance, method, route));
if(this.calls != null) this.calls = new int[length];
}
public void activeReOrdering(){
this.calls = new int[responses.length];
this.calls = new int[states.length];
this.dif = 2;
}
public boolean exec(Context context, HttpReader reader, HttpWriter writer, List<RouteInterceptor> interceptors) throws Exception{
Response response = null;
Method method = null;
RouteState target = null;
Matcher matcher = null;
String path = context.getPath();
synchronized(responses){
for(int i = 0; i < responses.length; i++){
Route route = routes[i];
Pattern pattern = patterns[i];
matcher = pattern.matcher(path);
if(matcher.matches()){
if(route.websocket()){
synchronized(states){
for(int i = 0; i < states.length; i++){
RouteState state = states[i];
matcher = state.match(path);
if(matcher != null){
if(state.isWebSocket()){
switchToWebSocket(reader, writer);
reader = new WebSocketReader(reader);
writer = new WebSocketWriter(writer);
if(route.needLogin()){
if(state.needLogin()){
String token = reader.<JsonMap>readJson().get("token");
context.setUser(this.router.getUser(token));
}
}
if((!context.isLogged()) && route.needLogin()){
if((!context.isLogged()) && state.needLogin()){
writer.response(401, router.getDefaultHeaders(context.getType()));
return true;
}
response = responses[i];
method = methods[i];
target = state;
order(i);
break;
}
}
}
if(response != null){
if(target != null){
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 false;
@ -94,10 +82,7 @@ public class RouteMapper<U extends User>{
}
if(index < 1) return;
if(call > this.calls[index-1]){
switchElement(responses, index, index-1);
switchElement(methods, index, index-1);
switchElement(routes, index, index-1);
switchElement(patterns, index, index-1);
switchElement(states, index, index-1);
this.calls[index] = this.calls[index-1];
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());
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){
throw new IllegalArgumentException(e);
}
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){
return registerPackages(new DependencyInjector().of(injections));
}
@ -131,16 +151,10 @@ public class Router<U extends User>{
try{
BufferedReader reader = new BufferedReader(new InputStreamReader(stream));
String line;
Map<Class<?>, Object> cache = new HashMap<>();
while((line = reader.readLine()) != null){
if(line.endsWith(".class")){
Class<?> clazz = Class.forName(name+"."+line.substring(0, line.lastIndexOf('.')));
if(Response.class.isAssignableFrom(clazz)){
if(clazz.equals(Response.class)) continue;
Constructor<?>[] constructors = clazz.getDeclaredConstructors();
if(constructors.length != 1) continue;
injectDependencies(constructors[0], cache, injector);
}
registerClass(clazz, injector);
}else{
registerPackages(name+"."+line, injector);
}
@ -153,24 +167,11 @@ public class Router<U extends User>{
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();
Object[] injections = new Object[parameters.length];
for(int i = 0; i < parameters.length; i++){
Parameter parameter = parameters[i];
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();
}
for(int i = 0; i < parameters.length; i++) injections[i] = injector.applyDependency(constructor, parameters[i]);
return constructor.newInstance(injections);
}
public Router<U> setDefault(Response response){