Add comprehensive Javadoc documentation to server components, including annotations, request/response handling, routing, and WebSocket support.

This commit is contained in:
CodingPhoenixx
2026-05-29 08:50:05 +02:00
parent f00a1098b4
commit 5d6e8622bf
33 changed files with 1938 additions and 53 deletions
@@ -9,15 +9,63 @@ import java.lang.invoke.MethodHandles;
import java.lang.invoke.MethodType;
import java.lang.reflect.Method;
/**
* Reflective registrar that wires the routing annotations on a controller object into a
* {@link Router}.
*
* <p>Given a controller instance, the scanner reads the optional {@link Controller} annotation
* to determine a path prefix, then walks every declared method looking for one of the
* supported route annotations ({@link Route}, {@link GET}, {@link POST}, {@link PUT},
* {@link DELETE}, {@link PATCH} or {@link CUSTOM}). For each matching method it:</p>
* <ol>
* <li>validates that the method has the required {@code (Request, Response)} signature and
* a {@code void} return type;</li>
* <li>creates a {@link MethodHandle} bound to the controller instance for fast,
* reflection-free invocation;</li>
* <li>registers a {@link Router.Handler} that delegates to that handle under the resolved
* HTTP method and full path.</li>
* </ol>
*
* <p>This class is a stateless utility and cannot be instantiated.</p>
*
* @see Controller
* @see Router
*/
public final class AnnotationScanner {
/**
* Shared lookup used to unreflect controller methods into {@link MethodHandle}s. A single
* lookup is sufficient because the scanner forces accessibility on each method before
* unreflecting it.
*/
private static final MethodHandles.Lookup LOOKUP = MethodHandles.lookup();
/**
* The exact method type every handler must conform to: {@code void (Request, Response)}.
* Used as documentation of the contract enforced by {@link #validateSignature(Method)}.
*/
private static final MethodType HANDLER_TYPE =
MethodType.methodType(void.class, Request.class, Response.class);
/**
* Private constructor preventing instantiation of this stateless utility class.
*/
private AnnotationScanner() {
}
/**
* Scans the given controller for route annotations and registers every discovered handler
* with the supplied router.
*
* <p>If the controller class is annotated with {@link Controller}, its value is used as a
* path prefix for all routes. Methods without a recognised route annotation are ignored.
* A line describing each registered route is printed to standard output.</p>
*
* @param router the router to register the discovered handlers with
* @param controller the controller instance whose annotated methods should be registered
* @throws IllegalArgumentException if an annotated method has an invalid signature
* @throws RuntimeException if a method cannot be made accessible or unreflected
*/
public static void register(Router router, Object controller) {
Class<?> clazz = controller.getClass();
Controller ctrlAnno = clazz.getAnnotation(Controller.class);
@@ -55,11 +103,27 @@ public final class AnnotationScanner {
}
}
/**
* Normalizes a controller-level path prefix by ensuring it starts with a single leading
* slash.
*
* @param p the raw prefix from the {@link Controller} annotation, may be {@code null} or empty
* @return the normalized prefix, or an empty string if {@code p} is {@code null} or empty
*/
private static String normalizePrefix(String p) {
if (p == null || p.isEmpty()) return "";
return p.startsWith("/") ? p : "/" + p;
}
/**
* Extracts route metadata (HTTP method and path) from a method by inspecting the supported
* route annotations in priority order. {@link Route} is checked first, followed by the
* verb-specific annotations and finally {@link CUSTOM}.
*
* @param m the method to inspect
* @return a {@link RouteInfo} describing the route, or {@code null} if the method carries
* no recognised route annotation
*/
private static RouteInfo extractRoute(Method m) {
Route r = m.getAnnotation(Route.class);
if (r != null) return new RouteInfo(r.method(), r.path());
@@ -75,16 +139,24 @@ public final class AnnotationScanner {
DELETE del = m.getAnnotation(DELETE.class);
if (del != null) return new RouteInfo("DELETE", del.value());
PATCH patch = m.getAnnotation(PATCH.class);
if (patch != null) return new RouteInfo("PATCH", patch.value());
CUSTOM custom = m.getAnnotation(CUSTOM.class);
if (custom != null) return new RouteInfo(custom.method(), custom.value());
return null;
}
/**
* Validates that a handler method conforms to the required {@code void (Request, Response)}
* contract.
*
* @param m the method to validate
* @throws IllegalArgumentException if the method does not take exactly a {@link Request}
* and a {@link Response}, or does not return {@code void}
*/
private static void validateSignature(Method m) {
Class<?>[] params = m.getParameterTypes();
if (params.length != 2 || params[0] != Request.class || params[1] != Response.class) {
@@ -95,11 +167,23 @@ public final class AnnotationScanner {
}
}
/**
* Normalizes a route-level path by ensuring it starts with a single leading slash.
*
* @param p the raw path from a route annotation, may be {@code null} or empty
* @return the normalized path, or an empty string if {@code p} is {@code null} or empty
*/
private static String normalizePath(String p) {
if (p == null || p.isEmpty()) return "";
return p.startsWith("/") ? p : "/" + p;
}
/**
* Immutable carrier for the HTTP method and path extracted from a route annotation.
*
* @param method the HTTP method name (e.g. {@code "GET"})
* @param path the route path relative to the controller prefix
*/
private record RouteInfo(String method, String path) {
}
}
}