diff --git a/404.html b/404.html index de7e16c..142111c 100644 --- a/404.html +++ b/404.html @@ -17,7 +17,7 @@
- + \ No newline at end of file diff --git a/assets/banner-cards/flash-handler-default-implementations.png b/assets/banner-cards/flash-handler-default-implementations.png new file mode 100644 index 0000000..f8c2a64 Binary files /dev/null and b/assets/banner-cards/flash-handler-default-implementations.png differ diff --git a/assets/banner-cards/flash-static-file-server.png b/assets/banner-cards/flash-static-file-server.png new file mode 100644 index 0000000..4512122 Binary files /dev/null and b/assets/banner-cards/flash-static-file-server.png differ diff --git a/assets/flash_handler-default-implementations.md.BEJ-A1cJ.js b/assets/flash_handler-default-implementations.md.BEJ-A1cJ.js new file mode 100644 index 0000000..19f2c26 --- /dev/null +++ b/assets/flash_handler-default-implementations.md.BEJ-A1cJ.js @@ -0,0 +1,89 @@ +import{_ as i,c as a,a0 as n,o as t}from"./chunks/framework.p2VkXzrt.js";const g=JSON.parse(`{"title":"⚡ Handler Default Implementations (HDI)","description":"","frontmatter":{"banner_title":"Flash - Handler Default Implementations","banner_description":"Leverage HDI's for cleaner and more maintainable route logic.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-handler-default-implementations.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-handler-default-implementations.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/handler-default-implementations.md","filePath":"flash/handler-default-implementations.md"}`),h={name:"flash/handler-default-implementations.md"};function e(l,s,k,p,r,d){return t(),a("div",null,s[0]||(s[0]=[n(`

⚡ Handler Default Implementations (HDI)

HDI's provide an elegant and reliable way to standardize common behaviors across multiple request handlers. By defining base handlers that extend RequestHandler (or even chaining multiple base handlers), you can modularize your logic for aspects like authentication, user data retrieval, and rate limiting.

🔗 How It Works

Instead of implementing repeated logic in every handler, you create abstract handler classes that encapsulate common functionality. Your actual route handlers then extend these base classes, inheriting their behavior while focusing purely on request-specific logic.

🛠 Example: API Key Authentication

Imagine you want to protect API endpoints with an authentication key by checking it against a database. You can create an abstract APIKeyProtectedHandler that extends RequestHandler and implements the authentication logic:

java
public abstract class APIKeyProtectedHandler extends RequestHandler {
+    protected String apiKey;
+
+    public APIKeyProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        apiKey = req.header("X-API-Key");
+
+        if (apiKey == null || !isValidApiKey(apiKey)) {
+            res.status(403);
+            return "{\\"error\\":\\"Invalid API Key\\"}";
+        }
+
+        return handleAuthorized();
+    }
+
+    // Implement this method in your actual handlers
+    protected abstract Object handleAuthorized();
+
+    private boolean isValidApiKey(String key) {
+        // Implement key validation logic, e.g., checking against a database
+        // ...
+        return true;
+    }
+}

Now, your actual API handlers only need to extend APIKeyProtectedHandler, ensuring every request has a valid API key before executing its logic:

java
@RouteInfo(method = HttpMethod.GET, path = "/data")
+public class GetDataHandler extends APIKeyProtectedHandler {
+    public GetDataHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthorized() {
+        return "{\\"data\\":\\"Your API response here\\"}";
+    }
+}

🏗️ Chaining HDIs for Modular Logic

HDIs can be chained together to compose multiple layers of behavior. For example, imagine you want to authenticate users with a token and fetch their data from a database. You can create two abstract handlers that extend one another:

java
public abstract class ProtectedHandler extends RequestHandler {
+    protected String authToken;
+
+    public ProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        authToken = req.header("Authorization");
+        if (authToken == null || !isValidToken(authToken)) {
+            res.status(401);
+            res.type("application/json");
+            return "{\\"error\\":\\"Unauthorized\\"}";
+        }
+        return handleAuthenticated();
+    }
+
+    protected abstract Object handleAuthenticated();
+}
java
public abstract class AuthenticatedHandler extends ProtectedHandler {
+    protected User user;
+    private String authToken; // inherited from ProtectedHandler
+
+    public AuthenticatedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthenticated() {
+        user = getUserFromDatabase(authToken);
+        if (user == null) {
+            res.status(403);
+            return "{\\"error\\":\\"User not found\\"}";
+        }
+        return handleWithUser();
+    }
+
+    protected abstract Object handleWithUser();
+}
java
@RouteInfo(endpoint = "/profile", method = HttpMethod.GET)
+public class UserProfileHandler extends AuthenticatedHandler {
+    // inherited from AuthenticatedHandler
+    private String username = user.getUsername();
+    
+    public UserProfileHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleWithUser() {
+        return "{\\"username\\":\\"" + username + "\\"}";
+    }
+}
`,17)]))}const y=i(h,[["render",e]]);export{g as __pageData,y as default}; diff --git a/assets/flash_handler-default-implementations.md.BEJ-A1cJ.lean.js b/assets/flash_handler-default-implementations.md.BEJ-A1cJ.lean.js new file mode 100644 index 0000000..19f2c26 --- /dev/null +++ b/assets/flash_handler-default-implementations.md.BEJ-A1cJ.lean.js @@ -0,0 +1,89 @@ +import{_ as i,c as a,a0 as n,o as t}from"./chunks/framework.p2VkXzrt.js";const g=JSON.parse(`{"title":"⚡ Handler Default Implementations (HDI)","description":"","frontmatter":{"banner_title":"Flash - Handler Default Implementations","banner_description":"Leverage HDI's for cleaner and more maintainable route logic.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-handler-default-implementations.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-handler-default-implementations.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/handler-default-implementations.md","filePath":"flash/handler-default-implementations.md"}`),h={name:"flash/handler-default-implementations.md"};function e(l,s,k,p,r,d){return t(),a("div",null,s[0]||(s[0]=[n(`

⚡ Handler Default Implementations (HDI)

HDI's provide an elegant and reliable way to standardize common behaviors across multiple request handlers. By defining base handlers that extend RequestHandler (or even chaining multiple base handlers), you can modularize your logic for aspects like authentication, user data retrieval, and rate limiting.

🔗 How It Works

Instead of implementing repeated logic in every handler, you create abstract handler classes that encapsulate common functionality. Your actual route handlers then extend these base classes, inheriting their behavior while focusing purely on request-specific logic.

🛠 Example: API Key Authentication

Imagine you want to protect API endpoints with an authentication key by checking it against a database. You can create an abstract APIKeyProtectedHandler that extends RequestHandler and implements the authentication logic:

java
public abstract class APIKeyProtectedHandler extends RequestHandler {
+    protected String apiKey;
+
+    public APIKeyProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        apiKey = req.header("X-API-Key");
+
+        if (apiKey == null || !isValidApiKey(apiKey)) {
+            res.status(403);
+            return "{\\"error\\":\\"Invalid API Key\\"}";
+        }
+
+        return handleAuthorized();
+    }
+
+    // Implement this method in your actual handlers
+    protected abstract Object handleAuthorized();
+
+    private boolean isValidApiKey(String key) {
+        // Implement key validation logic, e.g., checking against a database
+        // ...
+        return true;
+    }
+}

Now, your actual API handlers only need to extend APIKeyProtectedHandler, ensuring every request has a valid API key before executing its logic:

java
@RouteInfo(method = HttpMethod.GET, path = "/data")
+public class GetDataHandler extends APIKeyProtectedHandler {
+    public GetDataHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthorized() {
+        return "{\\"data\\":\\"Your API response here\\"}";
+    }
+}

🏗️ Chaining HDIs for Modular Logic

HDIs can be chained together to compose multiple layers of behavior. For example, imagine you want to authenticate users with a token and fetch their data from a database. You can create two abstract handlers that extend one another:

java
public abstract class ProtectedHandler extends RequestHandler {
+    protected String authToken;
+
+    public ProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        authToken = req.header("Authorization");
+        if (authToken == null || !isValidToken(authToken)) {
+            res.status(401);
+            res.type("application/json");
+            return "{\\"error\\":\\"Unauthorized\\"}";
+        }
+        return handleAuthenticated();
+    }
+
+    protected abstract Object handleAuthenticated();
+}
java
public abstract class AuthenticatedHandler extends ProtectedHandler {
+    protected User user;
+    private String authToken; // inherited from ProtectedHandler
+
+    public AuthenticatedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthenticated() {
+        user = getUserFromDatabase(authToken);
+        if (user == null) {
+            res.status(403);
+            return "{\\"error\\":\\"User not found\\"}";
+        }
+        return handleWithUser();
+    }
+
+    protected abstract Object handleWithUser();
+}
java
@RouteInfo(endpoint = "/profile", method = HttpMethod.GET)
+public class UserProfileHandler extends AuthenticatedHandler {
+    // inherited from AuthenticatedHandler
+    private String username = user.getUsername();
+    
+    public UserProfileHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleWithUser() {
+        return "{\\"username\\":\\"" + username + "\\"}";
+    }
+}
`,17)]))}const y=i(h,[["render",e]]);export{g as __pageData,y as default}; diff --git a/assets/flash_static-file-server.md.C3QKsWSO.js b/assets/flash_static-file-server.md.C3QKsWSO.js new file mode 100644 index 0000000..16c6060 --- /dev/null +++ b/assets/flash_static-file-server.md.C3QKsWSO.js @@ -0,0 +1,28 @@ +import{_ as i,c as a,a0 as e,o as t}from"./chunks/framework.p2VkXzrt.js";const o=JSON.parse('{"title":"📁 Static File Server","description":"","frontmatter":{"banner_title":"Flash - Static File Server","banner_description":"Learn how to serve static files in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-static-file-server.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-static-file-server.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/static-file-server.md","filePath":"flash/static-file-server.md"}'),n={name:"flash/static-file-server.md"};function l(h,s,p,r,k,d){return t(),a("div",null,s[0]||(s[0]=[e(`

📁 Static File Server

Flash provides a built-in static file server that allows you to serve static files such with autoresolving MIME types and caching.

WARNING

The static file server pre-registers literal routes for every file in the specified target directory. Creating/deleting files in the target directory will trigger the internal route registry to update accordingly. If you are planning to serve a compiled frontend application with a client-side router (think of react-router-dom), it is reccomended to use the Dynamic File Server instead,

Usage

To serve static files in Flash, you need to call the server.serveStatic() method with the endpoint path and an instance of StaticFileServerConfig. The configuration object is instanced like so :

java
public StaticFileServerConfiguration(
+    boolean enableFileWatcher,
+    boolean enableIndexRedirect,
+    String destinationPath,
+    SourceType sourceType
+)

Registering the static file server is as simple as calling the server.serveStatic() method with the desired path and configuration object:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.serveStatic("/static", new StaticFileServerConfiguration(
+            true,
+            true,
+            "path/to/static/files",
+            SourceType.FILESYSTEM
+        ));
+    }
+}

Now you can access the files in the specified directory by navigating to http://localhost:8080/static/<file-name>.

Similarly, you can serve files from the jar's resources folder by setting the sourceType to RESOURCESTREAM:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.serveStatic("/static", new StaticFileServerConfiguration(
+            true,
+            true,
+            "path/to/static/files",
+            SourceType.RESOURCESTREAM
+        ));
+    }
+}
`,12)]))}const E=i(n,[["render",l]]);export{o as __pageData,E as default}; diff --git a/assets/flash_static-file-server.md.C3QKsWSO.lean.js b/assets/flash_static-file-server.md.C3QKsWSO.lean.js new file mode 100644 index 0000000..16c6060 --- /dev/null +++ b/assets/flash_static-file-server.md.C3QKsWSO.lean.js @@ -0,0 +1,28 @@ +import{_ as i,c as a,a0 as e,o as t}from"./chunks/framework.p2VkXzrt.js";const o=JSON.parse('{"title":"📁 Static File Server","description":"","frontmatter":{"banner_title":"Flash - Static File Server","banner_description":"Learn how to serve static files in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-static-file-server.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-static-file-server.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/static-file-server.md","filePath":"flash/static-file-server.md"}'),n={name:"flash/static-file-server.md"};function l(h,s,p,r,k,d){return t(),a("div",null,s[0]||(s[0]=[e(`

📁 Static File Server

Flash provides a built-in static file server that allows you to serve static files such with autoresolving MIME types and caching.

WARNING

The static file server pre-registers literal routes for every file in the specified target directory. Creating/deleting files in the target directory will trigger the internal route registry to update accordingly. If you are planning to serve a compiled frontend application with a client-side router (think of react-router-dom), it is reccomended to use the Dynamic File Server instead,

Usage

To serve static files in Flash, you need to call the server.serveStatic() method with the endpoint path and an instance of StaticFileServerConfig. The configuration object is instanced like so :

java
public StaticFileServerConfiguration(
+    boolean enableFileWatcher,
+    boolean enableIndexRedirect,
+    String destinationPath,
+    SourceType sourceType
+)

Registering the static file server is as simple as calling the server.serveStatic() method with the desired path and configuration object:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.serveStatic("/static", new StaticFileServerConfiguration(
+            true,
+            true,
+            "path/to/static/files",
+            SourceType.FILESYSTEM
+        ));
+    }
+}

Now you can access the files in the specified directory by navigating to http://localhost:8080/static/<file-name>.

Similarly, you can serve files from the jar's resources folder by setting the sourceType to RESOURCESTREAM:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.serveStatic("/static", new StaticFileServerConfiguration(
+            true,
+            true,
+            "path/to/static/files",
+            SourceType.RESOURCESTREAM
+        ));
+    }
+}
`,12)]))}const E=i(n,[["render",l]]);export{o as __pageData,E as default}; diff --git a/assets/flash_websockets.md.CYiVOCzA.js b/assets/flash_websockets.md.CYiVOCzA.js new file mode 100644 index 0000000..e61d735 --- /dev/null +++ b/assets/flash_websockets.md.CYiVOCzA.js @@ -0,0 +1,33 @@ +import{_ as i,c as e,a0 as a,o as t}from"./chunks/framework.p2VkXzrt.js";const c=JSON.parse('{"title":"🌐 Websockets","description":"","frontmatter":{"banner_title":"Flash - Websockets","banner_description":"Learn how to create and manage server-side Websockets in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-websockets.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-websockets.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/websockets.md","filePath":"flash/websockets.md"}'),n={name:"flash/websockets.md"};function h(l,s,k,p,o,d){return t(),e("div",null,s[0]||(s[0]=[a(`

🌐 Websockets

In this section, we illustrate how to create and manage Websockets in Flash, which are used to establish a bidirectional communication channel between the client and the server.

Understanding Websockets

Websockets are a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is a request-response protocol, Websockets allow for real-time, low-latency communication between the client and the server.

Imagine a scenario where you need to send real-time updates to the client, such as a chat application or a live feed: if you were to use HTTP, you would need to poll the server at regular intervals to check for updates, which is inefficient and resource-intensive.

Creating a Websocket

To create a Websocket in Flash, you need to extend the WebsocketHandler class and override the onOpen, onMessage, onClose, and onError methods. These methods are called when the Websocket connection is opened, a message is received, the connection is closed, and an error occurs, respectively, and they provide you with an instance of the WebSocketSession object to be able to interact with it.

java
public class MyWebsocketHandler extends WebsocketHandler {
+    @Override
+    public void onOpen(WebSocketSession session) {
+        System.out.println("WebSocket connection opened");
+    }
+
+    @Override
+    public void onClose(WebSocketSession session, int statusCode, String reason) {
+        System.out.println("WebSocket connection closed");
+    }
+
+    @Override
+    public void onMessage(WebSocketSession session, String message) {
+        System.out.println("Received message: " + message);
+
+        //optionally send a reponse back to the client
+        session.sendMessage("I received your message!");
+    }
+
+    @Override
+    public void onError(WebSocketSession session, Throwable error) {
+        System.out.println("WebSocket error: " + error.getMessage());
+    }
+}

To register your Websocket handler with the server, you can use the server.ws() method:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.ws("/ws")
+            .register(new MyWebsocketHandler());
+
+        server.start();
+    }
+}

Interacting with Websockets sessions

The WebSocketSession object provides methods to interact with the Websocket session, such as sending messages, closing the connection, and getting the remote address and session ID.

MethodParamsDescription
getChannel()noneReturns an instance of AsynchronousSocketChannel useful for retrieving info about the client .
getRequestInfo()noneReturns an instance of RequestInfo containing all sorts of information about the request (headers, method, path etc.) .
getPath()noneReturns the path to the websocket endpoint as a String.
getId()noneReturns the id of the websocket session as a String, useful if you want to keep track of the connected clients in a custom manager.
getBuffer()noneReturns the ByteBuffer for that session.
sendMessage()String messageSends the message to the client as a String. it's up to the developer to stringify and de-stringify any data you want to send back and forth
close()noneCloses the websocket session.

NOTE

WebsocketHandler includes a setId(String id) method for overriding the default session ID. Unless you have a specific reason to change it, it's best to leave it as is.

Similarly, the setBuffer(ByteBuffer buffer) method allows you to override the default buffer. If you're unsure about this, it's recommended to keep the default setting.

`,14)]))}const E=i(n,[["render",h]]);export{c as __pageData,E as default}; diff --git a/assets/flash_websockets.md.CYiVOCzA.lean.js b/assets/flash_websockets.md.CYiVOCzA.lean.js new file mode 100644 index 0000000..e61d735 --- /dev/null +++ b/assets/flash_websockets.md.CYiVOCzA.lean.js @@ -0,0 +1,33 @@ +import{_ as i,c as e,a0 as a,o as t}from"./chunks/framework.p2VkXzrt.js";const c=JSON.parse('{"title":"🌐 Websockets","description":"","frontmatter":{"banner_title":"Flash - Websockets","banner_description":"Learn how to create and manage server-side Websockets in Flash.","head":[["meta",{"name":"twitter:image","content":"/assets/banner-cards/flash-websockets.png"}],["meta",{"name":"twitter:image:src","content":"https://docs.pixel-services.com/assets/banner-cards/flash-websockets.png"}],["meta",{"name":"twitter:card","content":"summary_large_image"}],["meta",{"name":"twitter:image:height","content":"1280"}],["meta",{"name":"twitter:image:width","content":"669"}],["meta",{"name":"twitter:description","content":""}]]},"headers":[],"relativePath":"flash/websockets.md","filePath":"flash/websockets.md"}'),n={name:"flash/websockets.md"};function h(l,s,k,p,o,d){return t(),e("div",null,s[0]||(s[0]=[a(`

🌐 Websockets

In this section, we illustrate how to create and manage Websockets in Flash, which are used to establish a bidirectional communication channel between the client and the server.

Understanding Websockets

Websockets are a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is a request-response protocol, Websockets allow for real-time, low-latency communication between the client and the server.

Imagine a scenario where you need to send real-time updates to the client, such as a chat application or a live feed: if you were to use HTTP, you would need to poll the server at regular intervals to check for updates, which is inefficient and resource-intensive.

Creating a Websocket

To create a Websocket in Flash, you need to extend the WebsocketHandler class and override the onOpen, onMessage, onClose, and onError methods. These methods are called when the Websocket connection is opened, a message is received, the connection is closed, and an error occurs, respectively, and they provide you with an instance of the WebSocketSession object to be able to interact with it.

java
public class MyWebsocketHandler extends WebsocketHandler {
+    @Override
+    public void onOpen(WebSocketSession session) {
+        System.out.println("WebSocket connection opened");
+    }
+
+    @Override
+    public void onClose(WebSocketSession session, int statusCode, String reason) {
+        System.out.println("WebSocket connection closed");
+    }
+
+    @Override
+    public void onMessage(WebSocketSession session, String message) {
+        System.out.println("Received message: " + message);
+
+        //optionally send a reponse back to the client
+        session.sendMessage("I received your message!");
+    }
+
+    @Override
+    public void onError(WebSocketSession session, Throwable error) {
+        System.out.println("WebSocket error: " + error.getMessage());
+    }
+}

To register your Websocket handler with the server, you can use the server.ws() method:

java
public class Example {
+    public static void main(String[] args) {
+        FlashServer server = new FlashServer(8080);
+
+        server.ws("/ws")
+            .register(new MyWebsocketHandler());
+
+        server.start();
+    }
+}

Interacting with Websockets sessions

The WebSocketSession object provides methods to interact with the Websocket session, such as sending messages, closing the connection, and getting the remote address and session ID.

MethodParamsDescription
getChannel()noneReturns an instance of AsynchronousSocketChannel useful for retrieving info about the client .
getRequestInfo()noneReturns an instance of RequestInfo containing all sorts of information about the request (headers, method, path etc.) .
getPath()noneReturns the path to the websocket endpoint as a String.
getId()noneReturns the id of the websocket session as a String, useful if you want to keep track of the connected clients in a custom manager.
getBuffer()noneReturns the ByteBuffer for that session.
sendMessage()String messageSends the message to the client as a String. it's up to the developer to stringify and de-stringify any data you want to send back and forth
close()noneCloses the websocket session.

NOTE

WebsocketHandler includes a setId(String id) method for overriding the default session ID. Unless you have a specific reason to change it, it's best to leave it as is.

Similarly, the setBuffer(ByteBuffer buffer) method allows you to override the default buffer. If you're unsure about this, it's recommended to keep the default setting.

`,14)]))}const E=i(n,[["render",h]]);export{c as __pageData,E as default}; diff --git a/flash/handler-default-implementations.html b/flash/handler-default-implementations.html new file mode 100644 index 0000000..6833350 --- /dev/null +++ b/flash/handler-default-implementations.html @@ -0,0 +1,120 @@ + + + + + + ⚡ Handler Default Implementations (HDI) | Pixel Services Docs + + + + + + + + + + + + + + + + + + + + + +
Skip to content

⚡ Handler Default Implementations (HDI)

HDI's provide an elegant and reliable way to standardize common behaviors across multiple request handlers. By defining base handlers that extend RequestHandler (or even chaining multiple base handlers), you can modularize your logic for aspects like authentication, user data retrieval, and rate limiting.

🔗 How It Works

Instead of implementing repeated logic in every handler, you create abstract handler classes that encapsulate common functionality. Your actual route handlers then extend these base classes, inheriting their behavior while focusing purely on request-specific logic.

🛠 Example: API Key Authentication

Imagine you want to protect API endpoints with an authentication key by checking it against a database. You can create an abstract APIKeyProtectedHandler that extends RequestHandler and implements the authentication logic:

java
public abstract class APIKeyProtectedHandler extends RequestHandler {
+    protected String apiKey;
+
+    public APIKeyProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        apiKey = req.header("X-API-Key");
+
+        if (apiKey == null || !isValidApiKey(apiKey)) {
+            res.status(403);
+            return "{\"error\":\"Invalid API Key\"}";
+        }
+
+        return handleAuthorized();
+    }
+
+    // Implement this method in your actual handlers
+    protected abstract Object handleAuthorized();
+
+    private boolean isValidApiKey(String key) {
+        // Implement key validation logic, e.g., checking against a database
+        // ...
+        return true;
+    }
+}

Now, your actual API handlers only need to extend APIKeyProtectedHandler, ensuring every request has a valid API key before executing its logic:

java
@RouteInfo(method = HttpMethod.GET, path = "/data")
+public class GetDataHandler extends APIKeyProtectedHandler {
+    public GetDataHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthorized() {
+        return "{\"data\":\"Your API response here\"}";
+    }
+}

🏗️ Chaining HDIs for Modular Logic

HDIs can be chained together to compose multiple layers of behavior. For example, imagine you want to authenticate users with a token and fetch their data from a database. You can create two abstract handlers that extend one another:

  • ProtectedHandler ensures authentication.
java
public abstract class ProtectedHandler extends RequestHandler {
+    protected String authToken;
+
+    public ProtectedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    public Object handle() {
+        authToken = req.header("Authorization");
+        if (authToken == null || !isValidToken(authToken)) {
+            res.status(401);
+            res.type("application/json");
+            return "{\"error\":\"Unauthorized\"}";
+        }
+        return handleAuthenticated();
+    }
+
+    protected abstract Object handleAuthenticated();
+}
  • AuthenticatedHandler extends ProtectedHandler to fetch user data from the database, and overrides handleAuthenticated to ensure the user is authenticated before proceeding.
java
public abstract class AuthenticatedHandler extends ProtectedHandler {
+    protected User user;
+    private String authToken; // inherited from ProtectedHandler
+
+    public AuthenticatedHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleAuthenticated() {
+        user = getUserFromDatabase(authToken);
+        if (user == null) {
+            res.status(403);
+            return "{\"error\":\"User not found\"}";
+        }
+        return handleWithUser();
+    }
+
+    protected abstract Object handleWithUser();
+}
  • Your actual implementation handler UserProfileHandler extends AuthenticatedHandler and implements handleWithUser to ensure the user is authenticated and their profile data has been fetched before proceeding.
java
@RouteInfo(endpoint = "/profile", method = HttpMethod.GET)
+public class UserProfileHandler extends AuthenticatedHandler {
+    // inherited from AuthenticatedHandler
+    private String username = user.getUsername();
+    
+    public UserProfileHandler(Request req, Response res) {
+        super(req, res);
+    }
+
+    @Override
+    protected Object handleWithUser() {
+        return "{\"username\":\"" + username + "\"}";
+    }
+}
+ + + + \ No newline at end of file diff --git a/flash/index.html b/flash/index.html index b3d8473..e2c4e89 100644 --- a/flash/index.html +++ b/flash/index.html @@ -25,8 +25,8 @@ -
Skip to content

⚡ Flash

Flash is a simple, modern and fast expressive web framework written in Java. The project is maintained by Pixel Services and the open-source community.

Our Team

Say hello to the Pixel Services team !

Relism

Relism

Developer

Sieadev

Sieadev

Developer

- +
Skip to content

⚡ Flash

Flash is a simple, modern and fast expressive web framework written in Java. The project is maintained by Pixel Services and the open-source community.

Our Team

Say hello to the Pixel Services team !

Relism

Relism

Developer

Sieadev

Sieadev

Developer

+ \ No newline at end of file diff --git a/flash/installation.html b/flash/installation.html index 446faa4..1055508 100644 --- a/flash/installation.html +++ b/flash/installation.html @@ -25,7 +25,7 @@ -
Skip to content

📲 Installation

This page provides installation instructions for the latest version of the flash library from Pixel Services.

Installation

Maven (pom.xml)

  1. Add the repository :

    xml
    <repositories>
    +    
    Skip to content

    📲 Installation

    This page provides installation instructions for the latest version of the flash library from Pixel Services.

    Installation

    Maven (pom.xml)

    1. Add the repository :

      xml
      <repositories>
         <repository>
           <id>pixel-services</id>
           <name>Pixel Services</name>
      @@ -44,7 +44,7 @@
       }
    2. And the dependency :

      groovy
      dependencies {
           implementation 'com.pixelservices:flash:{{ latestVersion }}'
       }
    ⚡ Latest version:
    - + \ No newline at end of file diff --git a/flash/request-handler.html b/flash/request-handler.html index 6446f70..050d220 100644 --- a/flash/request-handler.html +++ b/flash/request-handler.html @@ -25,7 +25,7 @@ -
    Skip to content

    ⚙️ Request Handler

    In this section, we illustrate the powerful concept of RequestHandler in Flash, which are used to handle incoming requests and generate responses.

    Creating a Request Handler

    To create a custom request handler, you need to extend the RequestHandler class and annotate the class with the RouteInfo annotation, specifying the HTTP method that the handler will respond to and the relative path that the handler will be registered to. After that, you must override the handle method; The handle method is where you define the logic for processing the request and generating the response. The req (request) and res (response) objects are available in the handler to access the request data and send the response back to the client.

    You must call the super constructor with the req and res objects to initialize the handler.

    java
    @RouteInfo(method = HttpMethod.GET, path = "/hello")
    +    
    Skip to content

    ⚙️ Request Handler

    In this section, we illustrate the powerful concept of RequestHandler in Flash, which are used to handle incoming requests and generate responses.

    Creating a Request Handler

    To create a custom request handler, you need to extend the RequestHandler class and annotate the class with the RouteInfo annotation, specifying the HTTP method that the handler will respond to and the relative path that the handler will be registered to. After that, you must override the handle method; The handle method is where you define the logic for processing the request and generating the response. The req (request) and res (response) objects are available in the handler to access the request data and send the response back to the client.

    You must call the super constructor with the req and res objects to initialize the handler.

    java
    @RouteInfo(method = HttpMethod.GET, path = "/hello")
     public class MyHandler extends RequestHandler {
         public MyHandler(Request req, Response res) {
             super(req, res);
    @@ -37,7 +37,7 @@
             return response;
         }
     }

    WARNING

    Any logic that needs to be executed before the request handler is registered must be done within the constructor.

    Request Handler methods

    The RequestHandler class provides several methods that can be used to interact with the request and response objects easily and safely. Following are listed the methods available in the RequestHandler class, with a brief description of their purpose:

    MethodParamsDescription
    getRequestBody()noneReturns a JSONObject representation of the request body.
    getSpecification()noneReturns an instance of HandlerSpecification containing all sorts of information about the handler.
    expectedRequestParameter()String name, descriptionReturns an instance of ExpectedRequestParameter for the specified parameter name.
    expectedBodyField()String name, descriptionReturns an instance of ExpectedBodyField for the specified field name.
    expectedBodyFile()String name, descriptionReturns an instance of ExpectedBodyFile for the specified file name.

    (More on the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile classes in the next section).

    - + \ No newline at end of file diff --git a/flash/request-response.html b/flash/request-response.html index 82a77b8..8a4cf70 100644 --- a/flash/request-response.html +++ b/flash/request-response.html @@ -25,7 +25,7 @@ -
    Skip to content

    📥 Request and Response

    Generally speaking, in an HTTP request-response cycle, the client sends a request to the server, and the server generates a response based on the request. In this section, we illustrate the Request and Response objects in Flash, which are used to interact with the request data and generate responses. We will learn to read and interpret the request data, and model the correct response to send back to the client.

    Request and Response Objects

    The Request and Response objects are passed to the handler constructor and provide access to the request data and response methods. Under the hood, these objects are provided by the RouteController during the registration stage of the handler, and they are continously updated for each request on that specific handler's instance, but for now, you can consider them as magic.

    These objects are a very powerful tool to interact with the lifecycle of the server's response, and they provide a wide range of methods that makes our lives as developers easier.

    RequestHandler context

    Inside a RequestHandler class, you can access the Request and Response objects inside the handle method by simply typing req and res, respectively. You can use the methods provided by these objects both outside of a handler, and inside of it, to interact with the request and response objects. Although, the advantages of being inside a handler are that Flash supports out-of-the-box methods that can significantly clean up your code and improve the overall readability of your handlers.

    Specifically, the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile objects are used to get the expected properties of the request, and they are used by Flash to validate the request data before the handle method is even executed.

    The developer can then simply assume that all the expected parameters are present and valid, without having to write a single line of validation code: Flash will do it for you.

    The three objects mentioned above are fairly similar in their usage, providing getter methods which safely return the data in the expected format and type, thanks to Flash's built-in validation and casting system.

    NOTE

    Flash will take care of informing the client of any missing or invalid parameters, parameters that are not in the expected format, or any other kind of error that might occur during the validation process.

    REMEMBER

    The ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile instances are ONLY supposed to be retrieved by calling the respective expectedRequestParameter(), expectedBodyField(), and expectedBodyFile() methods INSIDE of the super constructor of your handler class.

    Example Usage

    • ExpectedRequestParameter
    Click to expand

    The ExpectedRequestParameter object is used to get the expected parameters of the request. You can use the getter methods to safely get the parameter value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the parameter to the expected type.

    java
    @RouteInfo(method = HttpMethod.GET, path = "/hello")
    +    
    Skip to content

    📥 Request and Response

    Generally speaking, in an HTTP request-response cycle, the client sends a request to the server, and the server generates a response based on the request. In this section, we illustrate the Request and Response objects in Flash, which are used to interact with the request data and generate responses. We will learn to read and interpret the request data, and model the correct response to send back to the client.

    Request and Response Objects

    The Request and Response objects are passed to the handler constructor and provide access to the request data and response methods. Under the hood, these objects are provided by the RouteController during the registration stage of the handler, and they are continously updated for each request on that specific handler's instance, but for now, you can consider them as magic.

    These objects are a very powerful tool to interact with the lifecycle of the server's response, and they provide a wide range of methods that makes our lives as developers easier.

    RequestHandler context

    Inside a RequestHandler class, you can access the Request and Response objects inside the handle method by simply typing req and res, respectively. You can use the methods provided by these objects both outside of a handler, and inside of it, to interact with the request and response objects. Although, the advantages of being inside a handler are that Flash supports out-of-the-box methods that can significantly clean up your code and improve the overall readability of your handlers.

    Specifically, the ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile objects are used to get the expected properties of the request, and they are used by Flash to validate the request data before the handle method is even executed.

    The developer can then simply assume that all the expected parameters are present and valid, without having to write a single line of validation code: Flash will do it for you.

    The three objects mentioned above are fairly similar in their usage, providing getter methods which safely return the data in the expected format and type, thanks to Flash's built-in validation and casting system.

    NOTE

    Flash will take care of informing the client of any missing or invalid parameters, parameters that are not in the expected format, or any other kind of error that might occur during the validation process.

    REMEMBER

    The ExpectedRequestParameter, ExpectedBodyField, and ExpectedBodyFile instances are ONLY supposed to be retrieved by calling the respective expectedRequestParameter(), expectedBodyField(), and expectedBodyFile() methods INSIDE of the super constructor of your handler class.

    Example Usage

    • ExpectedRequestParameter
    Click to expand

    The ExpectedRequestParameter object is used to get the expected parameters of the request. You can use the getter methods to safely get the parameter value, such as getString, getInt, getDouble, and getBoolean methods to safely cast the parameter to the expected type.

    java
    @RouteInfo(method = HttpMethod.GET, path = "/hello")
     public class MyHandler extends RequestHandler {
         // Store the expected parameter in a private field
         private final ExpectedRequestParameter myExpectedReqParam;
    @@ -92,7 +92,7 @@
             return "File saved at: " + myFile.getAbsolutePath();
         }
     }

    This time, you will need to use a tool like Postman to send a POST request to /helloFile with a multipart form data body containing a file named myFile. You should receive a response like File saved at: <path> where <path> is the location where the server saved the file.

    - + \ No newline at end of file diff --git a/flash/server-router.html b/flash/server-router.html index 6fddc28..4eff413 100644 --- a/flash/server-router.html +++ b/flash/server-router.html @@ -25,7 +25,7 @@ -
    Skip to content

    🛣️ Server Router

    In this section, we discuss how to use the FlashServer router to manage our RequestHandler instances. The router is used to define route endpoints and their corresponding handler, which are executed when a request is made to the server.

    The FlashServer router is an instance of the RouteController class, each server instance has its own router instance. To access the router instance, you can call the route() method on the FlashServer instance.

    Creating a Route

    To create a route, you need to call the route() method on your server's instance (in this case for simplicity, on the InternalFlashServer) and specify the base path of the route, followed by your handler class,

    java
    // Example.java
    +    
    Skip to content

    🛣️ Server Router

    In this section, we discuss how to use the FlashServer router to manage our RequestHandler instances. The router is used to define route endpoints and their corresponding handler, which are executed when a request is made to the server.

    The FlashServer router is an instance of the RouteController class, each server instance has its own router instance. To access the router instance, you can call the route() method on the FlashServer instance.

    Creating a Route

    To create a route, you need to call the route() method on your server's instance (in this case for simplicity, on the InternalFlashServer) and specify the base path of the route, followed by your handler class,

    java
    // Example.java
     public class Example {
         public static void main(String[] args) {
             FlashServer server = new FlashServer(8080);
    @@ -51,8 +51,8 @@
             String response = "Hello, world!";
             return response;
         }
    -}

    In the example above, we create a route /api and register the MyHandler class to handle requests on the /api/hello endpoint.

    This is because the path property of the RouteInfo annotation is relative to the base path of the route, which in this case is /api.

    Visiting /api/hello from your browser will result in the response Hello, world!.

    - +}

    In the example above, we create a route /api and register the MyHandler class to handle requests on the /api/hello endpoint.

    This is because the path property of the RouteInfo annotation is relative to the base path of the route, which in this case is /api.

    Visiting /api/hello from your browser will result in the response Hello, world!.

    + \ No newline at end of file diff --git a/flash/static-file-server.html b/flash/static-file-server.html new file mode 100644 index 0000000..09d88e5 --- /dev/null +++ b/flash/static-file-server.html @@ -0,0 +1,59 @@ + + + + + + 📁 Static File Server | Pixel Services Docs + + + + + + + + + + + + + + + + + + + + + +
    Skip to content

    📁 Static File Server

    Flash provides a built-in static file server that allows you to serve static files such with autoresolving MIME types and caching.

    WARNING

    The static file server pre-registers literal routes for every file in the specified target directory. Creating/deleting files in the target directory will trigger the internal route registry to update accordingly. If you are planning to serve a compiled frontend application with a client-side router (think of react-router-dom), it is reccomended to use the Dynamic File Server instead,

    Usage

    To serve static files in Flash, you need to call the server.serveStatic() method with the endpoint path and an instance of StaticFileServerConfig. The configuration object is instanced like so :

    java
    public StaticFileServerConfiguration(
    +    boolean enableFileWatcher,
    +    boolean enableIndexRedirect,
    +    String destinationPath,
    +    SourceType sourceType
    +)
    • enableFileWatcher : If set to true, the server will watch for changes in the served files and reload them automatically.
    • enableIndexRedirect : If set to true, the server will redirect requests to directories to the index.html file.
    • destinationPath : The path to the directory containing the files to be served.
    • sourceType : The type of source to serve files from. It can be either FILESYSTEM or RESOURCESTREAM.

    Registering the static file server is as simple as calling the server.serveStatic() method with the desired path and configuration object:

    java
    public class Example {
    +    public static void main(String[] args) {
    +        FlashServer server = new FlashServer(8080);
    +
    +        server.serveStatic("/static", new StaticFileServerConfiguration(
    +            true,
    +            true,
    +            "path/to/static/files",
    +            SourceType.FILESYSTEM
    +        ));
    +    }
    +}

    Now you can access the files in the specified directory by navigating to http://localhost:8080/static/<file-name>.

    Similarly, you can serve files from the jar's resources folder by setting the sourceType to RESOURCESTREAM:

    java
    public class Example {
    +    public static void main(String[] args) {
    +        FlashServer server = new FlashServer(8080);
    +
    +        server.serveStatic("/static", new StaticFileServerConfiguration(
    +            true,
    +            true,
    +            "path/to/static/files",
    +            SourceType.RESOURCESTREAM
    +        ));
    +    }
    +}
    + + + + \ No newline at end of file diff --git a/flash/websockets.html b/flash/websockets.html index 3e8ef85..c073204 100644 --- a/flash/websockets.html +++ b/flash/websockets.html @@ -13,7 +13,7 @@ - + @@ -25,7 +25,7 @@ -
    Skip to content

    🌐 Websockets

    In this section, we illustrate how to create and manage Websockets in Flash, which are used to establish a bidirectional communication channel between the client and the server.

    Understanding Websockets

    Websockets are a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is a request-response protocol, Websockets allow for real-time, low-latency communication between the client and the server.

    Imagine a scenario where you need to send real-time updates to the client, such as a chat application or a live feed: if you were to use HTTP, you would need to poll the server at regular intervals to check for updates, which is inefficient and resource-intensive.

    Creating a Websocket

    To create a Websocket in Flash, you need to extend the WebsocketHandler class and override the onOpen, onMessage, onClose, and onError methods.

    java
    public class MyWebsocketHandler extends WebsocketHandler {
    +    
    Skip to content

    🌐 Websockets

    In this section, we illustrate how to create and manage Websockets in Flash, which are used to establish a bidirectional communication channel between the client and the server.

    Understanding Websockets

    Websockets are a communication protocol that provides full-duplex communication channels over a single TCP connection. Unlike HTTP, which is a request-response protocol, Websockets allow for real-time, low-latency communication between the client and the server.

    Imagine a scenario where you need to send real-time updates to the client, such as a chat application or a live feed: if you were to use HTTP, you would need to poll the server at regular intervals to check for updates, which is inefficient and resource-intensive.

    Creating a Websocket

    To create a Websocket in Flash, you need to extend the WebsocketHandler class and override the onOpen, onMessage, onClose, and onError methods. These methods are called when the Websocket connection is opened, a message is received, the connection is closed, and an error occurs, respectively, and they provide you with an instance of the WebSocketSession object to be able to interact with it.

    java
    public class MyWebsocketHandler extends WebsocketHandler {
         @Override
         public void onOpen(WebSocketSession session) {
             System.out.println("WebSocket connection opened");
    @@ -39,6 +39,8 @@
         @Override
         public void onMessage(WebSocketSession session, String message) {
             System.out.println("Received message: " + message);
    +
    +        //optionally send a reponse back to the client
             session.sendMessage("I received your message!");
         }
     
    @@ -51,12 +53,12 @@
             FlashServer server = new FlashServer(8080);
     
             server.ws("/ws")
    -            .register(MyWebsocketHandler.class);
    +            .register(new MyWebsocketHandler());
     
             server.start();
         }
    -}

    Interacting with Websockets sessions

    The WebSocketSession object provides methods to interact with the Websocket session, such as sending messages, closing the connection, and getting the remote address and session ID.

    - +}

    Interacting with Websockets sessions

    The WebSocketSession object provides methods to interact with the Websocket session, such as sending messages, closing the connection, and getting the remote address and session ID.

    MethodParamsDescription
    getChannel()noneReturns an instance of AsynchronousSocketChannel useful for retrieving info about the client .
    getRequestInfo()noneReturns an instance of RequestInfo containing all sorts of information about the request (headers, method, path etc.) .
    getPath()noneReturns the path to the websocket endpoint as a String.
    getId()noneReturns the id of the websocket session as a String, useful if you want to keep track of the connected clients in a custom manager.
    getBuffer()noneReturns the ByteBuffer for that session.
    sendMessage()String messageSends the message to the client as a String. it's up to the developer to stringify and de-stringify any data you want to send back and forth
    close()noneCloses the websocket session.

    NOTE

    WebsocketHandler includes a setId(String id) method for overriding the default session ID. Unless you have a specific reason to change it, it's best to leave it as is.

    Similarly, the setBuffer(ByteBuffer buffer) method allows you to override the default buffer. If you're unsure about this, it's recommended to keep the default setting.

    + \ No newline at end of file diff --git a/hashmap.json b/hashmap.json index 23a986d..8ac41fc 100644 --- a/hashmap.json +++ b/hashmap.json @@ -1 +1 @@ -{"flash_handler-default-implementation.md":"CL5VF6xH","flash_index.md":"E5zPbZtg","flash_installation.md":"6Deie__C","flash_request-handler.md":"D-YKsT8g","flash_request-response.md":"AHTHQW77","flash_server-router.md":"bhWEamQT","flash_websockets.md":"DzzW1sdE","index.md":"CgmTRI0Q","mobot_index.md":"Be0Zoetq","serverlibraries_index.md":"CeIqSPIs"} +{"flash_handler-default-implementations.md":"BEJ-A1cJ","flash_index.md":"E5zPbZtg","flash_installation.md":"6Deie__C","flash_request-handler.md":"D-YKsT8g","flash_request-response.md":"AHTHQW77","flash_server-router.md":"bhWEamQT","flash_static-file-server.md":"C3QKsWSO","flash_websockets.md":"CYiVOCzA","index.md":"CgmTRI0Q","mobot_index.md":"Be0Zoetq","serverlibraries_index.md":"CeIqSPIs"} diff --git a/index.html b/index.html index 2db4caa..52b6c8f 100644 --- a/index.html +++ b/index.html @@ -26,7 +26,7 @@ - + \ No newline at end of file diff --git a/mobot/index.html b/mobot/index.html index 1d26384..e4f2456 100644 --- a/mobot/index.html +++ b/mobot/index.html @@ -26,7 +26,7 @@
    Skip to content
    - + \ No newline at end of file diff --git a/serverlibraries/index.html b/serverlibraries/index.html index 78d5ae7..1156c19 100644 --- a/serverlibraries/index.html +++ b/serverlibraries/index.html @@ -26,7 +26,7 @@
    Skip to content
    - + \ No newline at end of file