Copy cat > /tmp/AdvLab12.java << 'JAVAEOF'
import java.lang.annotation.*;
import java.lang.reflect.*;
import java.util.*;
import java.util.stream.*;
public class AdvLab12 {
// Custom HTTP-like annotations
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.TYPE)
@interface RestController { String path(); }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface GetMapping { String value(); }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface PostMapping { String value(); }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.PARAMETER)
@interface PathVar { String value(); }
@Retention(RetentionPolicy.RUNTIME) @Target(ElementType.METHOD)
@interface Validate { String message() default "Validation failed"; }
// Annotated controller
@RestController(path = "/products")
static class ProductController {
private final Map<Integer,String> db = new LinkedHashMap<>(
Map.of(1,"Surface Pro",2,"Surface Pen",3,"Office 365"));
@GetMapping("/")
List<Map<String,Object>> list() {
return db.entrySet().stream()
.map(e -> Map.<String,Object>of("id", e.getKey(), "name", e.getValue()))
.toList();
}
@GetMapping("/{id}")
Map<String,Object> get(@PathVar("id") int id) {
if (!db.containsKey(id)) throw new RuntimeException("Product " + id + " not found");
return Map.of("id", id, "name", db.get(id));
}
@PostMapping("/")
Map<String,Object> create(@PathVar("name") String name) {
int newId = db.size() + 1;
db.put(newId, name);
return Map.of("id", newId, "name", name, "status", "created");
}
@Validate(message = "DB must not be empty")
boolean dbNotEmpty() { return !db.isEmpty(); }
}
// Route descriptor
record Route(String httpMethod, String path, Object controller, Method handler) {
String paramsSummary() {
return Arrays.stream(handler.getParameters())
.map(p -> {
var pv = p.getAnnotation(PathVar.class);
return pv != null ? "@PathVar(\"" + pv.value() + "\") " + p.getType().getSimpleName()
: p.getType().getSimpleName();
})
.collect(Collectors.joining(", "));
}
}
// Router — discovers all routes via reflection
static class Router {
private final List<Route> routes = new ArrayList<>();
private final Map<Object, List<String>> validationErrors = new LinkedHashMap<>();
void register(Object controller) throws Exception {
var cls = controller.getClass();
var rc = cls.getAnnotation(RestController.class);
if (rc == null) throw new IllegalArgumentException("Not a @RestController");
String base = rc.path();
for (var method : cls.getDeclaredMethods()) {
var get = method.getAnnotation(GetMapping.class);
var post = method.getAnnotation(PostMapping.class);
if (get != null) routes.add(new Route("GET", base + get.value(), controller, method));
if (post != null) routes.add(new Route("POST", base + post.value(), controller, method));
}
routes.sort(Comparator.comparing(r -> r.path()));
}
Object dispatch(String httpMethod, String path, Map<String,Object> params) throws Exception {
var route = routes.stream()
.filter(r -> r.httpMethod().equals(httpMethod) && pathMatches(r.path(), path))
.findFirst()
.orElseThrow(() -> new RuntimeException("No route: " + httpMethod + " " + path));
route.handler().setAccessible(true);
var args = buildArgs(route.handler(), params);
return route.handler().invoke(route.controller(), args);
}
private boolean pathMatches(String template, String actual) {
if (template.equals(actual)) return true;
// Simple {var} matching
var tp = template.split("/"); var ap = actual.split("/");
if (tp.length != ap.length) return false;
for (int i = 0; i < tp.length; i++) {
if (!tp[i].startsWith("{") && !tp[i].equals(ap[i])) return false;
}
return true;
}
private Object[] buildArgs(Method method, Map<String,Object> params) {
return Arrays.stream(method.getParameters())
.map(p -> {
var pv = p.getAnnotation(PathVar.class);
if (pv == null) return null;
var val = params.get(pv.value());
if (val == null) return null;
if (p.getType() == int.class) return Integer.parseInt(val.toString());
return val;
}).toArray();
}
void printRoutes() {
System.out.println(" Registered routes (" + routes.size() + "):");
routes.forEach(r -> System.out.printf(" %-6s %-30s -> %s(%s)%n",
r.httpMethod(), r.path(), r.handler().getName(), r.paramsSummary()));
}
// Validation scanning
List<String> validate(Object controller) throws Exception {
var errors = new ArrayList<String>();
for (var m : controller.getClass().getDeclaredMethods()) {
var v = m.getAnnotation(Validate.class);
if (v == null) continue;
m.setAccessible(true);
if (Boolean.FALSE.equals(m.invoke(controller))) errors.add(v.message());
}
return errors;
}
}
public static void main(String[] args) throws Exception {
var controller = new ProductController();
var router = new Router();
router.register(controller);
System.out.println("=== Route Registration ===");
router.printRoutes();
System.out.println("\n=== Route Dispatch ===");
// GET /products/
var list = router.dispatch("GET", "/products/", Map.of());
System.out.println("GET /products/ -> " + list);
// GET /products/1
var product = router.dispatch("GET", "/products/1", Map.of("id", "1"));
System.out.println("GET /products/1 -> " + product);
// POST /products/
var created = router.dispatch("POST", "/products/", Map.of("name", "USB-C Hub"));
System.out.println("POST /products/ -> " + created);
// Error: not found
try { router.dispatch("GET", "/products/99", Map.of("id", "99")); }
catch (Exception e) { System.out.println("GET /products/99 -> " + e.getCause().getMessage()); }
// Validation
System.out.println("\n=== @Validate Scan ===");
var errors = router.validate(controller);
System.out.println(" Validation errors: " + (errors.isEmpty() ? "none ✓" : errors));
// Schema inspection
System.out.println("\n=== Annotation Introspection ===");
var cls = ProductController.class;
System.out.println(" Class: " + cls.getSimpleName());
System.out.println(" @RestController path: " + cls.getAnnotation(RestController.class).path());
Arrays.stream(cls.getDeclaredMethods())
.filter(m -> m.isAnnotationPresent(GetMapping.class) || m.isAnnotationPresent(PostMapping.class))
.forEach(m -> {
var get = m.getAnnotation(GetMapping.class);
var post = m.getAnnotation(PostMapping.class);
System.out.printf(" @%-11s %-10s -> %s%n",
get != null ? "GetMapping" : "PostMapping",
get != null ? get.value() : post.value(), m.getName());
});
}
}
JAVAEOF
docker run --rm -v /tmp/AdvLab12.java:/tmp/AdvLab12.java zchencow/innozverse-java:latest sh -c "javac /tmp/AdvLab12.java -d /tmp && java -cp /tmp AdvLab12"