Repository files navigation

autorouter

Automatic HTTP handlers for a controller object with specific annotations.

Assignments

  1. Published 1-3-2023 DEADLINE: 03-4-2023
  2. Published 1-3-2023 DEADLINE: 08-5-2023
  3. Published 05-5-2023 DEADLINE: 29-5-2023

Assignment 1 - Types at runtime and Reflection API

The AutoRouter library allows the automatic creation of HTTP handlers for a pt.isel.autorouter.JsonServer based on a router object with specific annotations, according to the next example of ClassroomController.

All methods annotated with @AutoRoute and returning an Optional are eligible for HTTP handlers. To avoid ambiguity with existing types of JDK we choose to prefix annotations with Ar, i.e. ArRoute, ArQuery, ArBody.

For simplicity, JsonServer is only responding with status codes of 200, 404, and 500, depending on whether the handler returns a fulfilled Optional, an empty Optional or an exception. (you may consider the use of an alternative Either, or other type to enhance responses)

  1. Implement the Java function Stream<ArHttpRoute> autorouterReflect(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

  2. Implement another example of a controller object for a different domain, such as playlist, movies, football teams, basket, moto gp, or any other of your choice. Implement the corresponding tests to validate that all routes generated with autorouterReflect for your controller class are correctly invoked for each HTTP request.

The next figure shows the resulting stream of ArHttpRoute objects for the example of a ClassroomRouter instance. The autorouterReflect can be use in Kotlin through a statement such as:

ClassroomRouter().autorouterReflect().jsonServer().start(4000)

ClassroomController example

classClassroomController {
/** * Example: http://localhost:4000/classroom/i42d?student=jo*/
@Synchronized
@AutoRoute("/classroom/{classroom}")
funsearch(@ArRoute classroom:String, @ArQuery student:String?): Optional<List<Student>> {
...
}
/** * Example: * curl --header "Content-Type: application/json" \ * --request PUT \ * --data '{"nr": "7777", "name":"Ze Gato","group":"11", "semester":"3"}' \ * http://localhost:4000/classroom/i42d/students/7777*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =PUT)
funaddStudent(
@ArRoute classroom:String,
@ArRoute nr:Int,
@ArBody s:Student
): Optional<Student> {
...
}
/** * Example: * curl --request DELETE http://localhost:4000/classroom/i42d/students/4536*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =DELETE)
funremoveStudent(@ArRoute classroom:String, @ArRoute nr:Int) : Optional<Student> {
...
}
}

Assignment 2 - Dynamic Code Generator and Performance Evaluation with JMH

In this workout we follow a different approach to invoke the functions of a controller object. Instead of using Reflection we will generate different implementations of ArHttpHandler for each function in controller object, as denoted in the next figure. Notice, these implementations (e.g. ArHttpHandlerSearch, ArHttpHandlerAddStudent, ArHttpHandlerRemoveStudent) do not use reflection to call the methods of ClassroomController.

Implement the Java function Stream<ArHttpRoute> autorouterDynamic(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

Usage

To run these benchmarks on you local machine just run:

./gradlew jmhJar

And then:

java -jar autorouter-bench/build/libs/autorouter-bench-jmh.jar -i 4 -wi 4 -f 1 -r 2 -w 2 
  • -i 4 iterations
  • -wi 4 warmup iterations
  • -f 1 fork
  • -r 2 run each iteration for 2 seconds
  • -w 2 run each warmup iteration for 2 seconds.

Assignment 3 - Lazy Sequences

Part 1 - Path.watchNewFilesContent()

Build an extension function Path.watchNewFilesContent(): Sequence<Sequence<String>> that registers a WatchService to given Path and returns an sequence with the content of new or modified files (i.e. each files's content is a Sequence<String>).

To that end, you should first create a new WatchService by using the newWatchService method of the FileSystem class, as follows:

val service = path.fileSystem.newWatchService()

After that, you may register that path as a Watchable of the WatchService through:

path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

In former example we are registering for events that create, modify or delete an entry in a directory. More details here

Next is an example of an event processing loop:

path.fileSystem.newWatchService().use { service ->// Register the path to the service and watch for events
path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
// Start the infinite polling loopwhile (true) {
val key = service.take()
// Dequeueing eventsfor (watchEvent in key.pollEvents()) {
// Get the type of the eventwhen(watchEvent.kind()) {
OVERFLOW->continue// loopENTRY_CREATE->...
ENTRY_MODIFY->...
ENTRY_DELETE->...
}
}
if (!key.reset()) {
break// loop
}
}
}

Part 2 - Testing

You should develop a unit test that checks if the resulting sequence of the watchNewFilesContent produces a new Sequence<String> with the content of new files.

Your tests, should also confirm that making a concurrent change to the content of the new file after the File has been watched and before getting the first item from iterator is visible on the iteration.

Part 3 - JsonServer

Make a proposal and modify the annotations API of the AutoRouter library to let programmers express routes that return an sequence.

You should also modify the JsonServer to support this new kind of routes that should continuously write a new HTML paragraph for each String in a sequence. To that end, you may use the underlying Javalin OutputStream through a PrintWriter, such as:

Javalin.create().also {
it.get("/") { ctx ->
ctx.res().writer.use { writer ->
writer.println("....")
writer.flush()
}
}
it.start(300)
}

Consider the response is never finished by the server, but only when the client terminates the connection.

Screen.Recording.2023-05-12.at.11.03.33.mov

About

Automatic HTTP handlers for a router object with specific annotations

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Add copy buttons to all
 blocks\n(function() {\n function addCopyButtons() {\n document.querySelectorAll('pre code').forEach(function(codeBlock) {\n if (codeBlock.parentElement.hasAttribute('data-copy-added')) return;\n codeBlock.parentElement.setAttribute('data-copy-added', 'true');\n \n var btn = document.createElement('button');\n btn.textContent = 'Copy';\n btn.style.cssText = 'position:absolute;top:4px;right:4px;padding:2px 8px;font-size:11px;background:#4ecdc4;border:none;border-radius:4px;color:#1a1a2e;cursor:pointer;opacity:0.7;transition:opacity 0.2s;';\n btn.onmouseover = function() { this.style.opacity = '1'; };\n btn.onmouseout = function() { this.style.opacity = '0.7'; };\n btn.onclick = function() {\n navigator.clipboard.writeText(codeBlock.textContent).then(function() {\n btn.textContent = 'Copied!';\n setTimeout(function() { btn.textContent = 'Copy'; }, 1500);\n });\n };\n codeBlock.parentElement.style.position = 'relative';\n codeBlock.parentElement.appendChild(btn);\n });\n }\n \n addCopyButtons();\n \n // Re-run on dynamic content\n var observer = new MutationObserver(addCopyButtons);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Add Copy Buttons to Code Blocks");
}
} catch(__e) { console.warn('[Userscript:Add Copy Buttons to Code Blocks]', __e); }
})();
(function(){
try {
var __m = "github.com";
var __re = new RegExp('^' + "github\\.com" + '
Skip to content

Repository files navigation

autorouter

Automatic HTTP handlers for a controller object with specific annotations.

Assignments

  1. Published 1-3-2023 DEADLINE: 03-4-2023
  2. Published 1-3-2023 DEADLINE: 08-5-2023
  3. Published 05-5-2023 DEADLINE: 29-5-2023

Assignment 1 - Types at runtime and Reflection API

The AutoRouter library allows the automatic creation of HTTP handlers for a pt.isel.autorouter.JsonServer based on a router object with specific annotations, according to the next example of ClassroomController.

All methods annotated with @AutoRoute and returning an Optional are eligible for HTTP handlers. To avoid ambiguity with existing types of JDK we choose to prefix annotations with Ar, i.e. ArRoute, ArQuery, ArBody.

For simplicity, JsonServer is only responding with status codes of 200, 404, and 500, depending on whether the handler returns a fulfilled Optional, an empty Optional or an exception. (you may consider the use of an alternative Either, or other type to enhance responses)

  1. Implement the Java function Stream<ArHttpRoute> autorouterReflect(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

  2. Implement another example of a controller object for a different domain, such as playlist, movies, football teams, basket, moto gp, or any other of your choice. Implement the corresponding tests to validate that all routes generated with autorouterReflect for your controller class are correctly invoked for each HTTP request.

The next figure shows the resulting stream of ArHttpRoute objects for the example of a ClassroomRouter instance. The autorouterReflect can be use in Kotlin through a statement such as:

ClassroomRouter().autorouterReflect().jsonServer().start(4000)

ClassroomController example

classClassroomController {
/** * Example: http://localhost:4000/classroom/i42d?student=jo*/
@Synchronized
@AutoRoute("/classroom/{classroom}")
funsearch(@ArRoute classroom:String, @ArQuery student:String?): Optional<List<Student>> {
...
}
/** * Example: * curl --header "Content-Type: application/json" \ * --request PUT \ * --data '{"nr": "7777", "name":"Ze Gato","group":"11", "semester":"3"}' \ * http://localhost:4000/classroom/i42d/students/7777*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =PUT)
funaddStudent(
@ArRoute classroom:String,
@ArRoute nr:Int,
@ArBody s:Student
): Optional<Student> {
...
}
/** * Example: * curl --request DELETE http://localhost:4000/classroom/i42d/students/4536*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =DELETE)
funremoveStudent(@ArRoute classroom:String, @ArRoute nr:Int) : Optional<Student> {
...
}
}

Assignment 2 - Dynamic Code Generator and Performance Evaluation with JMH

In this workout we follow a different approach to invoke the functions of a controller object. Instead of using Reflection we will generate different implementations of ArHttpHandler for each function in controller object, as denoted in the next figure. Notice, these implementations (e.g. ArHttpHandlerSearch, ArHttpHandlerAddStudent, ArHttpHandlerRemoveStudent) do not use reflection to call the methods of ClassroomController.

Implement the Java function Stream<ArHttpRoute> autorouterDynamic(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

Usage

To run these benchmarks on you local machine just run:

./gradlew jmhJar

And then:

java -jar autorouter-bench/build/libs/autorouter-bench-jmh.jar -i 4 -wi 4 -f 1 -r 2 -w 2 
  • -i 4 iterations
  • -wi 4 warmup iterations
  • -f 1 fork
  • -r 2 run each iteration for 2 seconds
  • -w 2 run each warmup iteration for 2 seconds.

Assignment 3 - Lazy Sequences

Part 1 - Path.watchNewFilesContent()

Build an extension function Path.watchNewFilesContent(): Sequence<Sequence<String>> that registers a WatchService to given Path and returns an sequence with the content of new or modified files (i.e. each files's content is a Sequence<String>).

To that end, you should first create a new WatchService by using the newWatchService method of the FileSystem class, as follows:

val service = path.fileSystem.newWatchService()

After that, you may register that path as a Watchable of the WatchService through:

path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

In former example we are registering for events that create, modify or delete an entry in a directory. More details here

Next is an example of an event processing loop:

path.fileSystem.newWatchService().use { service ->// Register the path to the service and watch for events
path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
// Start the infinite polling loopwhile (true) {
val key = service.take()
// Dequeueing eventsfor (watchEvent in key.pollEvents()) {
// Get the type of the eventwhen(watchEvent.kind()) {
OVERFLOW->continue// loopENTRY_CREATE->...
ENTRY_MODIFY->...
ENTRY_DELETE->...
}
}
if (!key.reset()) {
break// loop
}
}
}

Part 2 - Testing

You should develop a unit test that checks if the resulting sequence of the watchNewFilesContent produces a new Sequence<String> with the content of new files.

Your tests, should also confirm that making a concurrent change to the content of the new file after the File has been watched and before getting the first item from iterator is visible on the iteration.

Part 3 - JsonServer

Make a proposal and modify the annotations API of the AutoRouter library to let programmers express routes that return an sequence.

You should also modify the JsonServer to support this new kind of routes that should continuously write a new HTML paragraph for each String in a sequence. To that end, you may use the underlying Javalin OutputStream through a PrintWriter, such as:

Javalin.create().also {
it.get("/") { ctx ->
ctx.res().writer.use { writer ->
writer.println("....")
writer.flush()
}
}
it.start(300)
}

Consider the response is never finished by the server, but only when the client terminates the connection.

Screen.Recording.2023-05-12.at.11.03.33.mov

About

Automatic HTTP handlers for a router object with specific annotations

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Force GitHub README to respect dark mode\n(function() {\n var style = document.createElement('style');\n style.textContent = '\n .markdown-body {\n color-scheme: dark light;\n }\n .markdown-body pre { background: #161b22 !important; }\n .markdown-body code { background: rgba(110, 118, 129, 0.4) !important; }\n .markdown-body table th, .markdown-body table td { border-color: #30363d !important; }\n .markdown-body img { background: #0d1117; }\n .markdown-body blockquote { border-left-color: #8b949e; }\n .markdown-body hr { border-color: #30363d; }\n ';\n document.head.appendChild(style);\n})();", "GitHub Dark Mode README Fix"); } } catch(__e) { console.warn('[Userscript:GitHub Dark Mode README Fix]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

autorouter

Automatic HTTP handlers for a controller object with specific annotations.

Assignments

  1. Published 1-3-2023 DEADLINE: 03-4-2023
  2. Published 1-3-2023 DEADLINE: 08-5-2023
  3. Published 05-5-2023 DEADLINE: 29-5-2023

Assignment 1 - Types at runtime and Reflection API

The AutoRouter library allows the automatic creation of HTTP handlers for a pt.isel.autorouter.JsonServer based on a router object with specific annotations, according to the next example of ClassroomController.

All methods annotated with @AutoRoute and returning an Optional are eligible for HTTP handlers. To avoid ambiguity with existing types of JDK we choose to prefix annotations with Ar, i.e. ArRoute, ArQuery, ArBody.

For simplicity, JsonServer is only responding with status codes of 200, 404, and 500, depending on whether the handler returns a fulfilled Optional, an empty Optional or an exception. (you may consider the use of an alternative Either, or other type to enhance responses)

  1. Implement the Java function Stream<ArHttpRoute> autorouterReflect(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

  2. Implement another example of a controller object for a different domain, such as playlist, movies, football teams, basket, moto gp, or any other of your choice. Implement the corresponding tests to validate that all routes generated with autorouterReflect for your controller class are correctly invoked for each HTTP request.

The next figure shows the resulting stream of ArHttpRoute objects for the example of a ClassroomRouter instance. The autorouterReflect can be use in Kotlin through a statement such as:

ClassroomRouter().autorouterReflect().jsonServer().start(4000)

ClassroomController example

classClassroomController {
/** * Example: http://localhost:4000/classroom/i42d?student=jo*/
@Synchronized
@AutoRoute("/classroom/{classroom}")
funsearch(@ArRoute classroom:String, @ArQuery student:String?): Optional<List<Student>> {
...
}
/** * Example: * curl --header "Content-Type: application/json" \ * --request PUT \ * --data '{"nr": "7777", "name":"Ze Gato","group":"11", "semester":"3"}' \ * http://localhost:4000/classroom/i42d/students/7777*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =PUT)
funaddStudent(
@ArRoute classroom:String,
@ArRoute nr:Int,
@ArBody s:Student
): Optional<Student> {
...
}
/** * Example: * curl --request DELETE http://localhost:4000/classroom/i42d/students/4536*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =DELETE)
funremoveStudent(@ArRoute classroom:String, @ArRoute nr:Int) : Optional<Student> {
...
}
}

Assignment 2 - Dynamic Code Generator and Performance Evaluation with JMH

In this workout we follow a different approach to invoke the functions of a controller object. Instead of using Reflection we will generate different implementations of ArHttpHandler for each function in controller object, as denoted in the next figure. Notice, these implementations (e.g. ArHttpHandlerSearch, ArHttpHandlerAddStudent, ArHttpHandlerRemoveStudent) do not use reflection to call the methods of ClassroomController.

Implement the Java function Stream<ArHttpRoute> autorouterDynamic(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

Usage

To run these benchmarks on you local machine just run:

./gradlew jmhJar

And then:

java -jar autorouter-bench/build/libs/autorouter-bench-jmh.jar -i 4 -wi 4 -f 1 -r 2 -w 2 
  • -i 4 iterations
  • -wi 4 warmup iterations
  • -f 1 fork
  • -r 2 run each iteration for 2 seconds
  • -w 2 run each warmup iteration for 2 seconds.

Assignment 3 - Lazy Sequences

Part 1 - Path.watchNewFilesContent()

Build an extension function Path.watchNewFilesContent(): Sequence<Sequence<String>> that registers a WatchService to given Path and returns an sequence with the content of new or modified files (i.e. each files's content is a Sequence<String>).

To that end, you should first create a new WatchService by using the newWatchService method of the FileSystem class, as follows:

val service = path.fileSystem.newWatchService()

After that, you may register that path as a Watchable of the WatchService through:

path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

In former example we are registering for events that create, modify or delete an entry in a directory. More details here

Next is an example of an event processing loop:

path.fileSystem.newWatchService().use { service ->// Register the path to the service and watch for events
path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
// Start the infinite polling loopwhile (true) {
val key = service.take()
// Dequeueing eventsfor (watchEvent in key.pollEvents()) {
// Get the type of the eventwhen(watchEvent.kind()) {
OVERFLOW->continue// loopENTRY_CREATE->...
ENTRY_MODIFY->...
ENTRY_DELETE->...
}
}
if (!key.reset()) {
break// loop
}
}
}

Part 2 - Testing

You should develop a unit test that checks if the resulting sequence of the watchNewFilesContent produces a new Sequence<String> with the content of new files.

Your tests, should also confirm that making a concurrent change to the content of the new file after the File has been watched and before getting the first item from iterator is visible on the iteration.

Part 3 - JsonServer

Make a proposal and modify the annotations API of the AutoRouter library to let programmers express routes that return an sequence.

You should also modify the JsonServer to support this new kind of routes that should continuously write a new HTML paragraph for each String in a sequence. To that end, you may use the underlying Javalin OutputStream through a PrintWriter, such as:

Javalin.create().also {
it.get("/") { ctx ->
ctx.res().writer.use { writer ->
writer.println("....")
writer.flush()
}
}
it.start(300)
}

Consider the response is never finished by the server, but only when the client terminates the connection.

Screen.Recording.2023-05-12.at.11.03.33.mov

About

Automatic HTTP handlers for a router object with specific annotations

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Highlight search terms from Google/DuckDuckGo/Bing referrer\n(function() {\n var ref = document.referrer;\n var terms = [];\n \n if (ref.includes('google.com') || ref.includes('duckduckgo.com') || ref.includes('bing.com')) {\n var url = new URL(ref);\n var q = url.searchParams.get('q') || url.searchParams.get('p');\n if (q) {\n terms = q.split(/\\s+/).filter(function(t) { return t.length > 2; });\n }\n }\n \n if (terms.length === 0) return;\n \n var style = document.createElement('style');\n style.textContent = '.userscript-highlight { background: #fbbf24; color: #1a1a2e; padding: 1px 3px; border-radius: 2px; }';\n document.head.appendChild(style);\n \n function highlight(node) {\n if (node.nodeType === 3) { // text node\n var text = node.textContent;\n var found = false;\n terms.forEach(function(term) {\n var regex = new RegExp('(' + term.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\') + ')', 'gi');\n if (regex.test(text)) {\n found = true;\n var frag = document.createDocumentFragment();\n var parts = text.split(regex);\n parts.forEach(function(part, i) {\n if (i % 2 === 0) {\n frag.appendChild(document.createTextNode(part));\n } else {\n var span = document.createElement('span');\n span.className = 'userscript-highlight';\n span.textContent = part;\n frag.appendChild(span);\n }\n });\n node.parentNode.replaceChild(frag, node);\n }\n });\n } else if (node.nodeType === 1 && node.childNodes) { // element\n var skipTags = ['SCRIPT', 'STYLE', 'NOSCRIPT', 'TEXTAREA', 'INPUT', 'SELECT'];\n if (!skipTags.includes(node.tagName)) {\n Array.from(node.childNodes).forEach(highlight);\n }\n }\n }\n \n highlight(document.body);\n \n // Re-highlight on dynamic content\n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1 || node.nodeType === 3) highlight(node);\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Highlight Search Terms"); } } catch(__e) { console.warn('[Userscript:Highlight Search Terms]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

autorouter

Automatic HTTP handlers for a controller object with specific annotations.

Assignments

  1. Published 1-3-2023 DEADLINE: 03-4-2023
  2. Published 1-3-2023 DEADLINE: 08-5-2023
  3. Published 05-5-2023 DEADLINE: 29-5-2023

Assignment 1 - Types at runtime and Reflection API

The AutoRouter library allows the automatic creation of HTTP handlers for a pt.isel.autorouter.JsonServer based on a router object with specific annotations, according to the next example of ClassroomController.

All methods annotated with @AutoRoute and returning an Optional are eligible for HTTP handlers. To avoid ambiguity with existing types of JDK we choose to prefix annotations with Ar, i.e. ArRoute, ArQuery, ArBody.

For simplicity, JsonServer is only responding with status codes of 200, 404, and 500, depending on whether the handler returns a fulfilled Optional, an empty Optional or an exception. (you may consider the use of an alternative Either, or other type to enhance responses)

  1. Implement the Java function Stream<ArHttpRoute> autorouterReflect(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

  2. Implement another example of a controller object for a different domain, such as playlist, movies, football teams, basket, moto gp, or any other of your choice. Implement the corresponding tests to validate that all routes generated with autorouterReflect for your controller class are correctly invoked for each HTTP request.

The next figure shows the resulting stream of ArHttpRoute objects for the example of a ClassroomRouter instance. The autorouterReflect can be use in Kotlin through a statement such as:

ClassroomRouter().autorouterReflect().jsonServer().start(4000)

ClassroomController example

classClassroomController {
/** * Example: http://localhost:4000/classroom/i42d?student=jo*/
@Synchronized
@AutoRoute("/classroom/{classroom}")
funsearch(@ArRoute classroom:String, @ArQuery student:String?): Optional<List<Student>> {
...
}
/** * Example: * curl --header "Content-Type: application/json" \ * --request PUT \ * --data '{"nr": "7777", "name":"Ze Gato","group":"11", "semester":"3"}' \ * http://localhost:4000/classroom/i42d/students/7777*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =PUT)
funaddStudent(
@ArRoute classroom:String,
@ArRoute nr:Int,
@ArBody s:Student
): Optional<Student> {
...
}
/** * Example: * curl --request DELETE http://localhost:4000/classroom/i42d/students/4536*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =DELETE)
funremoveStudent(@ArRoute classroom:String, @ArRoute nr:Int) : Optional<Student> {
...
}
}

Assignment 2 - Dynamic Code Generator and Performance Evaluation with JMH

In this workout we follow a different approach to invoke the functions of a controller object. Instead of using Reflection we will generate different implementations of ArHttpHandler for each function in controller object, as denoted in the next figure. Notice, these implementations (e.g. ArHttpHandlerSearch, ArHttpHandlerAddStudent, ArHttpHandlerRemoveStudent) do not use reflection to call the methods of ClassroomController.

Implement the Java function Stream<ArHttpRoute> autorouterDynamic(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

Usage

To run these benchmarks on you local machine just run:

./gradlew jmhJar

And then:

java -jar autorouter-bench/build/libs/autorouter-bench-jmh.jar -i 4 -wi 4 -f 1 -r 2 -w 2 
  • -i 4 iterations
  • -wi 4 warmup iterations
  • -f 1 fork
  • -r 2 run each iteration for 2 seconds
  • -w 2 run each warmup iteration for 2 seconds.

Assignment 3 - Lazy Sequences

Part 1 - Path.watchNewFilesContent()

Build an extension function Path.watchNewFilesContent(): Sequence<Sequence<String>> that registers a WatchService to given Path and returns an sequence with the content of new or modified files (i.e. each files's content is a Sequence<String>).

To that end, you should first create a new WatchService by using the newWatchService method of the FileSystem class, as follows:

val service = path.fileSystem.newWatchService()

After that, you may register that path as a Watchable of the WatchService through:

path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

In former example we are registering for events that create, modify or delete an entry in a directory. More details here

Next is an example of an event processing loop:

path.fileSystem.newWatchService().use { service ->// Register the path to the service and watch for events
path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
// Start the infinite polling loopwhile (true) {
val key = service.take()
// Dequeueing eventsfor (watchEvent in key.pollEvents()) {
// Get the type of the eventwhen(watchEvent.kind()) {
OVERFLOW->continue// loopENTRY_CREATE->...
ENTRY_MODIFY->...
ENTRY_DELETE->...
}
}
if (!key.reset()) {
break// loop
}
}
}

Part 2 - Testing

You should develop a unit test that checks if the resulting sequence of the watchNewFilesContent produces a new Sequence<String> with the content of new files.

Your tests, should also confirm that making a concurrent change to the content of the new file after the File has been watched and before getting the first item from iterator is visible on the iteration.

Part 3 - JsonServer

Make a proposal and modify the annotations API of the AutoRouter library to let programmers express routes that return an sequence.

You should also modify the JsonServer to support this new kind of routes that should continuously write a new HTML paragraph for each String in a sequence. To that end, you may use the underlying Javalin OutputStream through a PrintWriter, such as:

Javalin.create().also {
it.get("/") { ctx ->
ctx.res().writer.use { writer ->
writer.println("....")
writer.flush()
}
}
it.start(300)
}

Consider the response is never finished by the server, but only when the client terminates the connection.

Screen.Recording.2023-05-12.at.11.03.33.mov

About

Automatic HTTP handlers for a router object with specific annotations

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Strip utm_, fbclid, gclid, etc. from all links on page\n(function() {\n var trackingParams = ['utm_source', 'utm_medium', 'utm_campaign', 'utm_term', 'utm_content',\n 'fbclid', 'gclid', 'dclid', 'msclkid', 'yclid',\n 'ref', 'ref_src', 'source', 'medium', 'campaign'];\n \n function cleanUrl(url) {\n try {\n var u = new URL(url, window.location.origin);\n var changed = false;\n trackingParams.forEach(function(p) {\n if (u.searchParams.has(p)) {\n u.searchParams.delete(p);\n changed = true;\n }\n });\n return changed ? u.toString() : url;\n } catch (e) {\n return url;\n }\n }\n \n function cleanLinks() {\n document.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n \n cleanLinks();\n \n var observer = new MutationObserver(function(mutations) {\n mutations.forEach(function(m) {\n m.addedNodes.forEach(function(node) {\n if (node.nodeType === 1) {\n if (node.tagName === 'A') cleanLinks();\n node.querySelectorAll('a[href]').forEach(function(a) {\n var clean = cleanUrl(a.href);\n if (clean !== a.href) a.href = clean;\n });\n }\n });\n });\n });\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "Remove Tracking Parameters from Links"); } } catch(__e) { console.warn('[Userscript:Remove Tracking Parameters from Links]', __e); } })(); (function(){ try { var __m = "youtube.com"; var __re = new RegExp('^' + "youtube\\.com" + '
Skip to content

Repository files navigation

autorouter

Automatic HTTP handlers for a controller object with specific annotations.

Assignments

  1. Published 1-3-2023 DEADLINE: 03-4-2023
  2. Published 1-3-2023 DEADLINE: 08-5-2023
  3. Published 05-5-2023 DEADLINE: 29-5-2023

Assignment 1 - Types at runtime and Reflection API

The AutoRouter library allows the automatic creation of HTTP handlers for a pt.isel.autorouter.JsonServer based on a router object with specific annotations, according to the next example of ClassroomController.

All methods annotated with @AutoRoute and returning an Optional are eligible for HTTP handlers. To avoid ambiguity with existing types of JDK we choose to prefix annotations with Ar, i.e. ArRoute, ArQuery, ArBody.

For simplicity, JsonServer is only responding with status codes of 200, 404, and 500, depending on whether the handler returns a fulfilled Optional, an empty Optional or an exception. (you may consider the use of an alternative Either, or other type to enhance responses)

  1. Implement the Java function Stream<ArHttpRoute> autorouterReflect(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

  2. Implement another example of a controller object for a different domain, such as playlist, movies, football teams, basket, moto gp, or any other of your choice. Implement the corresponding tests to validate that all routes generated with autorouterReflect for your controller class are correctly invoked for each HTTP request.

The next figure shows the resulting stream of ArHttpRoute objects for the example of a ClassroomRouter instance. The autorouterReflect can be use in Kotlin through a statement such as:

ClassroomRouter().autorouterReflect().jsonServer().start(4000)

ClassroomController example

classClassroomController {
/** * Example: http://localhost:4000/classroom/i42d?student=jo*/
@Synchronized
@AutoRoute("/classroom/{classroom}")
funsearch(@ArRoute classroom:String, @ArQuery student:String?): Optional<List<Student>> {
...
}
/** * Example: * curl --header "Content-Type: application/json" \ * --request PUT \ * --data '{"nr": "7777", "name":"Ze Gato","group":"11", "semester":"3"}' \ * http://localhost:4000/classroom/i42d/students/7777*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =PUT)
funaddStudent(
@ArRoute classroom:String,
@ArRoute nr:Int,
@ArBody s:Student
): Optional<Student> {
...
}
/** * Example: * curl --request DELETE http://localhost:4000/classroom/i42d/students/4536*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =DELETE)
funremoveStudent(@ArRoute classroom:String, @ArRoute nr:Int) : Optional<Student> {
...
}
}

Assignment 2 - Dynamic Code Generator and Performance Evaluation with JMH

In this workout we follow a different approach to invoke the functions of a controller object. Instead of using Reflection we will generate different implementations of ArHttpHandler for each function in controller object, as denoted in the next figure. Notice, these implementations (e.g. ArHttpHandlerSearch, ArHttpHandlerAddStudent, ArHttpHandlerRemoveStudent) do not use reflection to call the methods of ClassroomController.

Implement the Java function Stream<ArHttpRoute> autorouterDynamic(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

Usage

To run these benchmarks on you local machine just run:

./gradlew jmhJar

And then:

java -jar autorouter-bench/build/libs/autorouter-bench-jmh.jar -i 4 -wi 4 -f 1 -r 2 -w 2 
  • -i 4 iterations
  • -wi 4 warmup iterations
  • -f 1 fork
  • -r 2 run each iteration for 2 seconds
  • -w 2 run each warmup iteration for 2 seconds.

Assignment 3 - Lazy Sequences

Part 1 - Path.watchNewFilesContent()

Build an extension function Path.watchNewFilesContent(): Sequence<Sequence<String>> that registers a WatchService to given Path and returns an sequence with the content of new or modified files (i.e. each files's content is a Sequence<String>).

To that end, you should first create a new WatchService by using the newWatchService method of the FileSystem class, as follows:

val service = path.fileSystem.newWatchService()

After that, you may register that path as a Watchable of the WatchService through:

path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

In former example we are registering for events that create, modify or delete an entry in a directory. More details here

Next is an example of an event processing loop:

path.fileSystem.newWatchService().use { service ->// Register the path to the service and watch for events
path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
// Start the infinite polling loopwhile (true) {
val key = service.take()
// Dequeueing eventsfor (watchEvent in key.pollEvents()) {
// Get the type of the eventwhen(watchEvent.kind()) {
OVERFLOW->continue// loopENTRY_CREATE->...
ENTRY_MODIFY->...
ENTRY_DELETE->...
}
}
if (!key.reset()) {
break// loop
}
}
}

Part 2 - Testing

You should develop a unit test that checks if the resulting sequence of the watchNewFilesContent produces a new Sequence<String> with the content of new files.

Your tests, should also confirm that making a concurrent change to the content of the new file after the File has been watched and before getting the first item from iterator is visible on the iteration.

Part 3 - JsonServer

Make a proposal and modify the annotations API of the AutoRouter library to let programmers express routes that return an sequence.

You should also modify the JsonServer to support this new kind of routes that should continuously write a new HTML paragraph for each String in a sequence. To that end, you may use the underlying Javalin OutputStream through a PrintWriter, such as:

Javalin.create().also {
it.get("/") { ctx ->
ctx.res().writer.use { writer ->
writer.println("....")
writer.flush()
}
}
it.start(300)
}

Consider the response is never finished by the server, but only when the client terminates the connection.

Screen.Recording.2023-05-12.at.11.03.33.mov

About

Automatic HTTP handlers for a router object with specific annotations

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Auto-enable theater mode on YouTube\n(function() {\n function tryTheater() {\n var btn = document.querySelector('button[aria-label=\"Theater mode\"], ytd-player #player button[title=\"Theater mode\"]');\n if (btn && !btn.classList.contains('activated')) {\n btn.click();\n }\n }\n \n // Try immediately\n tryTheater();\n \n // Try after navigation (SPA)\n var lastUrl = location.href;\n setInterval(function() {\n if (location.href !== lastUrl) {\n lastUrl = location.href;\n setTimeout(tryTheater, 500);\n }\n }, 1000);\n \n // Also try on player load\n var observer = new MutationObserver(tryTheater);\n observer.observe(document.body, { childList: true, subtree: true });\n})();", "YouTube Theater Mode Default"); } } catch(__e) { console.warn('[Userscript:YouTube Theater Mode Default]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

autorouter

Automatic HTTP handlers for a controller object with specific annotations.

Assignments

  1. Published 1-3-2023 DEADLINE: 03-4-2023
  2. Published 1-3-2023 DEADLINE: 08-5-2023
  3. Published 05-5-2023 DEADLINE: 29-5-2023

Assignment 1 - Types at runtime and Reflection API

The AutoRouter library allows the automatic creation of HTTP handlers for a pt.isel.autorouter.JsonServer based on a router object with specific annotations, according to the next example of ClassroomController.

All methods annotated with @AutoRoute and returning an Optional are eligible for HTTP handlers. To avoid ambiguity with existing types of JDK we choose to prefix annotations with Ar, i.e. ArRoute, ArQuery, ArBody.

For simplicity, JsonServer is only responding with status codes of 200, 404, and 500, depending on whether the handler returns a fulfilled Optional, an empty Optional or an exception. (you may consider the use of an alternative Either, or other type to enhance responses)

  1. Implement the Java function Stream<ArHttpRoute> autorouterReflect(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

  2. Implement another example of a controller object for a different domain, such as playlist, movies, football teams, basket, moto gp, or any other of your choice. Implement the corresponding tests to validate that all routes generated with autorouterReflect for your controller class are correctly invoked for each HTTP request.

The next figure shows the resulting stream of ArHttpRoute objects for the example of a ClassroomRouter instance. The autorouterReflect can be use in Kotlin through a statement such as:

ClassroomRouter().autorouterReflect().jsonServer().start(4000)

ClassroomController example

classClassroomController {
/** * Example: http://localhost:4000/classroom/i42d?student=jo*/
@Synchronized
@AutoRoute("/classroom/{classroom}")
funsearch(@ArRoute classroom:String, @ArQuery student:String?): Optional<List<Student>> {
...
}
/** * Example: * curl --header "Content-Type: application/json" \ * --request PUT \ * --data '{"nr": "7777", "name":"Ze Gato","group":"11", "semester":"3"}' \ * http://localhost:4000/classroom/i42d/students/7777*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =PUT)
funaddStudent(
@ArRoute classroom:String,
@ArRoute nr:Int,
@ArBody s:Student
): Optional<Student> {
...
}
/** * Example: * curl --request DELETE http://localhost:4000/classroom/i42d/students/4536*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =DELETE)
funremoveStudent(@ArRoute classroom:String, @ArRoute nr:Int) : Optional<Student> {
...
}
}

Assignment 2 - Dynamic Code Generator and Performance Evaluation with JMH

In this workout we follow a different approach to invoke the functions of a controller object. Instead of using Reflection we will generate different implementations of ArHttpHandler for each function in controller object, as denoted in the next figure. Notice, these implementations (e.g. ArHttpHandlerSearch, ArHttpHandlerAddStudent, ArHttpHandlerRemoveStudent) do not use reflection to call the methods of ClassroomController.

Implement the Java function Stream<ArHttpRoute> autorouterDynamic(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

Usage

To run these benchmarks on you local machine just run:

./gradlew jmhJar

And then:

java -jar autorouter-bench/build/libs/autorouter-bench-jmh.jar -i 4 -wi 4 -f 1 -r 2 -w 2 
  • -i 4 iterations
  • -wi 4 warmup iterations
  • -f 1 fork
  • -r 2 run each iteration for 2 seconds
  • -w 2 run each warmup iteration for 2 seconds.

Assignment 3 - Lazy Sequences

Part 1 - Path.watchNewFilesContent()

Build an extension function Path.watchNewFilesContent(): Sequence<Sequence<String>> that registers a WatchService to given Path and returns an sequence with the content of new or modified files (i.e. each files's content is a Sequence<String>).

To that end, you should first create a new WatchService by using the newWatchService method of the FileSystem class, as follows:

val service = path.fileSystem.newWatchService()

After that, you may register that path as a Watchable of the WatchService through:

path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

In former example we are registering for events that create, modify or delete an entry in a directory. More details here

Next is an example of an event processing loop:

path.fileSystem.newWatchService().use { service ->// Register the path to the service and watch for events
path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
// Start the infinite polling loopwhile (true) {
val key = service.take()
// Dequeueing eventsfor (watchEvent in key.pollEvents()) {
// Get the type of the eventwhen(watchEvent.kind()) {
OVERFLOW->continue// loopENTRY_CREATE->...
ENTRY_MODIFY->...
ENTRY_DELETE->...
}
}
if (!key.reset()) {
break// loop
}
}
}

Part 2 - Testing

You should develop a unit test that checks if the resulting sequence of the watchNewFilesContent produces a new Sequence<String> with the content of new files.

Your tests, should also confirm that making a concurrent change to the content of the new file after the File has been watched and before getting the first item from iterator is visible on the iteration.

Part 3 - JsonServer

Make a proposal and modify the annotations API of the AutoRouter library to let programmers express routes that return an sequence.

You should also modify the JsonServer to support this new kind of routes that should continuously write a new HTML paragraph for each String in a sequence. To that end, you may use the underlying Javalin OutputStream through a PrintWriter, such as:

Javalin.create().also {
it.get("/") { ctx ->
ctx.res().writer.use { writer ->
writer.println("....")
writer.flush()
}
}
it.start(300)
}

Consider the response is never finished by the server, but only when the client terminates the connection.

Screen.Recording.2023-05-12.at.11.03.33.mov

About

Automatic HTTP handlers for a router object with specific annotations

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Remove or un-stick sticky/fixed headers that block content\n(function() {\n function unstick() {\n document.querySelectorAll('header, nav, [role=\"banner\"], .header, .navbar, .sticky, .fixed-top, [style*=\"position: fixed\"], [style*=\"position:sticky\"]').forEach(function(el) {\n if (el.style.position === 'fixed' || el.style.position === 'sticky' || \n getComputedStyle(el).position === 'fixed' || getComputedStyle(el).position === 'sticky') {\n el.style.position = 'static';\n el.style.top = 'auto';\n el.style.zIndex = 'auto';\n }\n });\n }\n \n unstick();\n \n var observer = new MutationObserver(unstick);\n observer.observe(document.body, { childList: true, subtree: true, attributes: true, attributeFilter: ['style', 'class'] });\n})();", "Kill Sticky Headers"); } } catch(__e) { console.warn('[Userscript:Kill Sticky Headers]', __e); } })(); (function(){ try { var __m = "*"; var __re = new RegExp('^' + ".*" + '
Skip to content

Repository files navigation

autorouter

Automatic HTTP handlers for a controller object with specific annotations.

Assignments

  1. Published 1-3-2023 DEADLINE: 03-4-2023
  2. Published 1-3-2023 DEADLINE: 08-5-2023
  3. Published 05-5-2023 DEADLINE: 29-5-2023

Assignment 1 - Types at runtime and Reflection API

The AutoRouter library allows the automatic creation of HTTP handlers for a pt.isel.autorouter.JsonServer based on a router object with specific annotations, according to the next example of ClassroomController.

All methods annotated with @AutoRoute and returning an Optional are eligible for HTTP handlers. To avoid ambiguity with existing types of JDK we choose to prefix annotations with Ar, i.e. ArRoute, ArQuery, ArBody.

For simplicity, JsonServer is only responding with status codes of 200, 404, and 500, depending on whether the handler returns a fulfilled Optional, an empty Optional or an exception. (you may consider the use of an alternative Either, or other type to enhance responses)

  1. Implement the Java function Stream<ArHttpRoute> autorouterReflect(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

  2. Implement another example of a controller object for a different domain, such as playlist, movies, football teams, basket, moto gp, or any other of your choice. Implement the corresponding tests to validate that all routes generated with autorouterReflect for your controller class are correctly invoked for each HTTP request.

The next figure shows the resulting stream of ArHttpRoute objects for the example of a ClassroomRouter instance. The autorouterReflect can be use in Kotlin through a statement such as:

ClassroomRouter().autorouterReflect().jsonServer().start(4000)

ClassroomController example

classClassroomController {
/** * Example: http://localhost:4000/classroom/i42d?student=jo*/
@Synchronized
@AutoRoute("/classroom/{classroom}")
funsearch(@ArRoute classroom:String, @ArQuery student:String?): Optional<List<Student>> {
...
}
/** * Example: * curl --header "Content-Type: application/json" \ * --request PUT \ * --data '{"nr": "7777", "name":"Ze Gato","group":"11", "semester":"3"}' \ * http://localhost:4000/classroom/i42d/students/7777*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =PUT)
funaddStudent(
@ArRoute classroom:String,
@ArRoute nr:Int,
@ArBody s:Student
): Optional<Student> {
...
}
/** * Example: * curl --request DELETE http://localhost:4000/classroom/i42d/students/4536*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =DELETE)
funremoveStudent(@ArRoute classroom:String, @ArRoute nr:Int) : Optional<Student> {
...
}
}

Assignment 2 - Dynamic Code Generator and Performance Evaluation with JMH

In this workout we follow a different approach to invoke the functions of a controller object. Instead of using Reflection we will generate different implementations of ArHttpHandler for each function in controller object, as denoted in the next figure. Notice, these implementations (e.g. ArHttpHandlerSearch, ArHttpHandlerAddStudent, ArHttpHandlerRemoveStudent) do not use reflection to call the methods of ClassroomController.

Implement the Java function Stream<ArHttpRoute> autorouterDynamic(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

Usage

To run these benchmarks on you local machine just run:

./gradlew jmhJar

And then:

java -jar autorouter-bench/build/libs/autorouter-bench-jmh.jar -i 4 -wi 4 -f 1 -r 2 -w 2 
  • -i 4 iterations
  • -wi 4 warmup iterations
  • -f 1 fork
  • -r 2 run each iteration for 2 seconds
  • -w 2 run each warmup iteration for 2 seconds.

Assignment 3 - Lazy Sequences

Part 1 - Path.watchNewFilesContent()

Build an extension function Path.watchNewFilesContent(): Sequence<Sequence<String>> that registers a WatchService to given Path and returns an sequence with the content of new or modified files (i.e. each files's content is a Sequence<String>).

To that end, you should first create a new WatchService by using the newWatchService method of the FileSystem class, as follows:

val service = path.fileSystem.newWatchService()

After that, you may register that path as a Watchable of the WatchService through:

path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

In former example we are registering for events that create, modify or delete an entry in a directory. More details here

Next is an example of an event processing loop:

path.fileSystem.newWatchService().use { service ->// Register the path to the service and watch for events
path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
// Start the infinite polling loopwhile (true) {
val key = service.take()
// Dequeueing eventsfor (watchEvent in key.pollEvents()) {
// Get the type of the eventwhen(watchEvent.kind()) {
OVERFLOW->continue// loopENTRY_CREATE->...
ENTRY_MODIFY->...
ENTRY_DELETE->...
}
}
if (!key.reset()) {
break// loop
}
}
}

Part 2 - Testing

You should develop a unit test that checks if the resulting sequence of the watchNewFilesContent produces a new Sequence<String> with the content of new files.

Your tests, should also confirm that making a concurrent change to the content of the new file after the File has been watched and before getting the first item from iterator is visible on the iteration.

Part 3 - JsonServer

Make a proposal and modify the annotations API of the AutoRouter library to let programmers express routes that return an sequence.

You should also modify the JsonServer to support this new kind of routes that should continuously write a new HTML paragraph for each String in a sequence. To that end, you may use the underlying Javalin OutputStream through a PrintWriter, such as:

Javalin.create().also {
it.get("/") { ctx ->
ctx.res().writer.use { writer ->
writer.println("....")
writer.flush()
}
}
it.start(300)
}

Consider the response is never finished by the server, but only when the client terminates the connection.

Screen.Recording.2023-05-12.at.11.03.33.mov

About

Automatic HTTP handlers for a router object with specific annotations

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

, 'i'); if (__m === '*' || __re.test(location.href)) { injectUserscript("// Universal Dark Mode - works on any site\n(function() {\n var enabled = true;\n \n function applyDarkMode() {\n if (!enabled) return;\n \n // Create style element if it doesn't exist\n var style = document.getElementById('universal-dark-mode-style');\n if (!style) {\n style = document.createElement('style');\n style.id = 'universal-dark-mode-style';\n document.head.appendChild(style);\n }\n \n // Dark mode CSS - inverts colors but preserves images/video\n style.textContent = '\n /* Invert everything except media */\n html {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #1a1a2e !important;\n }\n \n /* Restore images, videos, iframes, canvas */\n img, video, iframe, canvas, svg, picture, [style*=\"background-image\"] {\n filter: invert(1) hue-rotate(180deg) !important;\n }\n \n /* Preserve specific elements that should not be inverted */\n .no-dark-mode, .no-dark-mode *,\n [data-theme=\"light\"], [data-theme=\"light\"],\n .ace_editor, .ace_editor *,\n .CodeMirror, .CodeMirror *,\n .monaco-editor, .monaco-editor *,\n .markdown-body pre, .markdown-body pre *,\n .highlight, .highlight *,\n pre code, pre code * {\n filter: none !important;\n }\n \n /* Fix common UI elements */\n .modal, .popup, .dropdown-menu, .tooltip, .popover {\n filter: invert(1) hue-rotate(180deg) !important;\n background: #2d2d44 !important;\n border-color: #444 !important;\n }\n \n /* Scrollbars */\n ::-webkit-scrollbar { background: #1a1a2e !important; }\n ::-webkit-scrollbar-thumb { background: #444 !important; }\n ::-webkit-scrollbar-thumb:hover { background: #555 !important; }\n \n /* Selection */\n ::selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ::-moz-selection { background: #4ecdc4 !important; color: #1a1a2e !important; }\n ';\n }\n \n function removeDarkMode() {\n var style = document.getElementById('universal-dark-mode-style');\n if (style) style.remove();\n }\n \n // Toggle with Alt+Shift+D\n document.addEventListener('keydown', function(e) {\n if (e.altKey && e.shiftKey && e.key === 'D') {\n e.preventDefault();\n enabled = !enabled;\n if (enabled) {\n applyDarkMode();\n console.log('[Universal Dark Mode] Enabled');\n } else {\n removeDarkMode();\n console.log('[Universal Dark Mode] Disabled');\n }\n }\n });\n \n // Apply on load\n applyDarkMode();\n \n // Re-apply on dynamic content\n var observer = new MutationObserver(function(mutations) {\n if (enabled && !document.getElementById('universal-dark-mode-style')) {\n applyDarkMode();\n }\n });\n observer.observe(document.head, { childList: true });\n \n console.log('[Universal Dark Mode] Loaded - Press Alt+Shift+D to toggle');\n})();", "Universal Dark Mode"); } } catch(__e) { console.warn('[Userscript:Universal Dark Mode]', __e); } })(); })();
Skip to content

Repository files navigation

autorouter

Automatic HTTP handlers for a controller object with specific annotations.

Assignments

  1. Published 1-3-2023 DEADLINE: 03-4-2023
  2. Published 1-3-2023 DEADLINE: 08-5-2023
  3. Published 05-5-2023 DEADLINE: 29-5-2023

Assignment 1 - Types at runtime and Reflection API

The AutoRouter library allows the automatic creation of HTTP handlers for a pt.isel.autorouter.JsonServer based on a router object with specific annotations, according to the next example of ClassroomController.

All methods annotated with @AutoRoute and returning an Optional are eligible for HTTP handlers. To avoid ambiguity with existing types of JDK we choose to prefix annotations with Ar, i.e. ArRoute, ArQuery, ArBody.

For simplicity, JsonServer is only responding with status codes of 200, 404, and 500, depending on whether the handler returns a fulfilled Optional, an empty Optional or an exception. (you may consider the use of an alternative Either, or other type to enhance responses)

  1. Implement the Java function Stream<ArHttpRoute> autorouterReflect(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

  2. Implement another example of a controller object for a different domain, such as playlist, movies, football teams, basket, moto gp, or any other of your choice. Implement the corresponding tests to validate that all routes generated with autorouterReflect for your controller class are correctly invoked for each HTTP request.

The next figure shows the resulting stream of ArHttpRoute objects for the example of a ClassroomRouter instance. The autorouterReflect can be use in Kotlin through a statement such as:

ClassroomRouter().autorouterReflect().jsonServer().start(4000)

ClassroomController example

classClassroomController {
/** * Example: http://localhost:4000/classroom/i42d?student=jo*/
@Synchronized
@AutoRoute("/classroom/{classroom}")
funsearch(@ArRoute classroom:String, @ArQuery student:String?): Optional<List<Student>> {
...
}
/** * Example: * curl --header "Content-Type: application/json" \ * --request PUT \ * --data '{"nr": "7777", "name":"Ze Gato","group":"11", "semester":"3"}' \ * http://localhost:4000/classroom/i42d/students/7777*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =PUT)
funaddStudent(
@ArRoute classroom:String,
@ArRoute nr:Int,
@ArBody s:Student
): Optional<Student> {
...
}
/** * Example: * curl --request DELETE http://localhost:4000/classroom/i42d/students/4536*/
@Synchronized
@AutoRoute("/classroom/{classroom}/students/{nr}", method =DELETE)
funremoveStudent(@ArRoute classroom:String, @ArRoute nr:Int) : Optional<Student> {
...
}
}

Assignment 2 - Dynamic Code Generator and Performance Evaluation with JMH

In this workout we follow a different approach to invoke the functions of a controller object. Instead of using Reflection we will generate different implementations of ArHttpHandler for each function in controller object, as denoted in the next figure. Notice, these implementations (e.g. ArHttpHandlerSearch, ArHttpHandlerAddStudent, ArHttpHandlerRemoveStudent) do not use reflection to call the methods of ClassroomController.

Implement the Java function Stream<ArHttpRoute> autorouterDynamic(Object controller), which builds a stream of ArHttpRoute objects for each eligible method in given controller object parameter.

Usage

To run these benchmarks on you local machine just run:

./gradlew jmhJar

And then:

java -jar autorouter-bench/build/libs/autorouter-bench-jmh.jar -i 4 -wi 4 -f 1 -r 2 -w 2 
  • -i 4 iterations
  • -wi 4 warmup iterations
  • -f 1 fork
  • -r 2 run each iteration for 2 seconds
  • -w 2 run each warmup iteration for 2 seconds.

Assignment 3 - Lazy Sequences

Part 1 - Path.watchNewFilesContent()

Build an extension function Path.watchNewFilesContent(): Sequence<Sequence<String>> that registers a WatchService to given Path and returns an sequence with the content of new or modified files (i.e. each files's content is a Sequence<String>).

To that end, you should first create a new WatchService by using the newWatchService method of the FileSystem class, as follows:

val service = path.fileSystem.newWatchService()

After that, you may register that path as a Watchable of the WatchService through:

path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);

In former example we are registering for events that create, modify or delete an entry in a directory. More details here

Next is an example of an event processing loop:

path.fileSystem.newWatchService().use { service ->// Register the path to the service and watch for events
path.register(service, ENTRY_CREATE, ENTRY_MODIFY, ENTRY_DELETE);
// Start the infinite polling loopwhile (true) {
val key = service.take()
// Dequeueing eventsfor (watchEvent in key.pollEvents()) {
// Get the type of the eventwhen(watchEvent.kind()) {
OVERFLOW->continue// loopENTRY_CREATE->...
ENTRY_MODIFY->...
ENTRY_DELETE->...
}
}
if (!key.reset()) {
break// loop
}
}
}

Part 2 - Testing

You should develop a unit test that checks if the resulting sequence of the watchNewFilesContent produces a new Sequence<String> with the content of new files.

Your tests, should also confirm that making a concurrent change to the content of the new file after the File has been watched and before getting the first item from iterator is visible on the iteration.

Part 3 - JsonServer

Make a proposal and modify the annotations API of the AutoRouter library to let programmers express routes that return an sequence.

You should also modify the JsonServer to support this new kind of routes that should continuously write a new HTML paragraph for each String in a sequence. To that end, you may use the underlying Javalin OutputStream through a PrintWriter, such as:

Javalin.create().also {
it.get("/") { ctx ->
ctx.res().writer.use { writer ->
writer.println("....")
writer.flush()
}
}
it.start(300)
}

Consider the response is never finished by the server, but only when the client terminates the connection.

Screen.Recording.2023-05-12.at.11.03.33.mov

About

Automatic HTTP handlers for a router object with specific annotations

Resources

Stars

7 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages