Modèles de développement tinystruct
Architecture et modèles d'implémentation pour la construction de modules avec le framework tinystruct Java – un framework léger et haute performance qui traite la CLI et HTTP comme des citoyens de premier ordre, sans nécessiter de méthode main() ni de configuration minimale.
Principe fondamental
La CLI et HTTP sont des citoyens égaux. Chaque méthode annotée avec @Action doit idéalement pouvoir être exécutée à la fois depuis un terminal et un navigateur web sans modification. Cette capacité « double mode » est la philosophie de conception centrale de tinystruct.
Quand activer
Quand l'utiliser
- Créer de nouveaux modules
Applicationen étendantAbstractApplication. - Définir des routes et des actions en ligne de commande à l'aide de
@Action. - Gérer l'état par requête via
Context. - Effectuer la sérialisation JSON à l'aide des composants natifs
BuilderetBuilders. - Travailler avec la persistance de base de données via les POJOs
AbstractData. - Générer des POJOs à partir de tables de base de données à l'aide de la commande
generate. - Implémenter des Server-Sent Events (SSE) pour des push en temps réel.
- Gérer les téléchargements de fichiers via des données multipart.
- Effectuer des requêtes HTTP sortantes avec
URLRequestetHTTPHandler. - Configurer les connexions de base de données ou les paramètres système dans
application.properties. - Déboguer les conflits de routage (Actions) ou l'analyse des arguments CLI.
Comment ça fonctionne
Le framework tinystruct traite toute méthode annotée avec @Action comme un point de terminaison routable pour les environnements terminal et web. Les applications sont créées en étendant AbstractApplication, qui fournit des hooks de cycle de vie de base comme init() et l'accès au Context de la requête.
Le routage est géré par ActionRegistry, qui mappe automatiquement les segments de chemin aux arguments de méthode et injecte les dépendances. Pour les services de données uniquement, les composants natifs Builder et Builders doivent être utilisés pour la sérialisation JSON afin de maintenir une empreinte zéro dépendance. La couche de base de données utilise des POJOs AbstractData associés à des fichiers de mapping XML pour les opérations CRUD sans bibliothèques ORM externes.
Exemples
Application de base (MyService)
public class MyService extends AbstractApplication {
@Override
public void init() {
this.setTemplateRequired(false); // Désactiver la recherche .view pour les applications de données/API
}
@Override public String version() { return "1.0.0"; }
@Action("greet")
public String greet() {
return "Bonjour depuis tinystruct !";
}
// Paramètre de chemin : GET /?q=greet/James OU bin/dispatcher greet/James
@Action("greet")
public String greet(String name) {
return "Bonjour, " + name + " !";
}
}
Désambiguïsation du mode HTTP (login)
@Action(value = "login", mode = Mode.HTTP_POST)
public String doLogin(Request<?, ?> request) throws ApplicationException {
request.getSession().setAttribute("userId", "42");
return "Connecté";
}
Gestion native des données JSON (Builder + Builders)
import org.tinystruct.data.component.Builder;
import org.tinystruct.data.component.Builders;
@Action("api/data")
public String getData() throws ApplicationException {
Builders dataList = new Builders();
Builder item = new Builder();
item.put("id", 1);
item.put("name", "James");
dataList.add(item);
Builder response = new Builder();
response.put("status", "success");
response.put("data", dataList);
return response.toString(); // {"status":"success","data":[{"id":1,"name":"James"}]}
}
SSE (Server-Sent Events)
import org.tinystruct.http.SSEPushManager;
@Action("sse/connect")
public String connect() {
return "{\"type\":\"connect\",\"message\":\"Connecté au SSE\"}";
}
// Pousser vers un client spécifique
String sessionId = getContext().getId();
Builder msg = new Builder();
msg.put("text", "Bonjour, utilisateur !");
SSEPushManager.getInstance().push(sessionId, msg);
// Diffuser à tous
// Diffuser à tous
SSEPushManager.getInstance().broadcast(msg);
Téléchargement de fichier
import org.tinystruct.data.FileEntity;
@Action(value = "upload", mode = Mode.HTTP_POST)
public String upload(Request<?, ?> request) throws ApplicationException {
List<FileEntity> files = request.getAttachments();
if (files != null) {
for (FileEntity file : files) {
System.out.println("Téléchargé : " + file.getFilename());
}
}
return "Téléchargement OK";
}
Intégration du serveur MCP et des outils
tinystruct offre un support natif du Model Context Protocol (MCP) à partir de la version SDK 1.7.26.
Les API MCP (par ex., org.tinystruct.mcp.MCPTool, org.tinystruct.mcp.MCPServer, org.tinystruct.mcp.MCPException) sont incluses directement dans la dépendance de base :
<dependency>
<groupId>org.tinystruct</groupId>
<artifactId>tinystruct</artifactId>
<version>1.7.26</version>
</dependency>
AVERTISSEMENT DE SÉCURITÉ (Injection de prompt) : Les valeurs de retour des outils sont directement réinjectées dans le contexte du modèle d'IA. Vous DEVEZ valider et assainir tous les arguments fournis par l'appelant avant de les inclure dans la chaîne de retour de l'outil. Ne pas assainir les entrées peut permettre à un attaquant d'injecter des instructions adverses (Injection de prompt) qui outrepassent le comportement du modèle. Validez toujours la longueur, les jeux de caractères et la nullité.
Pour créer un outil MCP :
- Étendez
org.tinystruct.mcp.MCPTool. - Annotez les opérations avec
@Actionet déclarez les paramètres à l'aide de@Argumentdans le tableauarguments. - Acceptez les paramètres comme arguments de méthode explicites correspondant aux clés dans
@Argument. (N'utilisez pasgetContext().getAttribute(...)pour les arguments de l'outil).
import org.tinystruct.mcp.MCPTool;
import org.tinystruct.mcp.MCPException;
import org.tinystruct.system.annotation.Action;
import org.tinystruct.system.annotation.Argument;
public class MyCustomTool extends MCPTool {
public MyCustomTool() {
super("custom", "Un outil personnalisé pour démontrer MCP");
}
@Action(
value = "custom/hello",
description = "Dire bonjour à quelqu'un",
arguments = {
@Argument(key = "name", description = "Le nom à saluer", type = "string", optional = false)
}
)
public String hello(String name) throws MCPException {
// SÉCURITÉ : Valider/assainir les entrées de l'outil avant de les retourner au modèle
// pour prévenir les vulnérabilités d'injection de prompt.
if (name == null || name.length() > 50 || !name.matches("^[a-zA-Z0-9 ]+$")) {
throw new MCPException("Nom invalide fourni");
}
return "Bonjour, " + name + " !";
}
}
Pour déployer un serveur MCP :
- Étendez
org.tinystruct.mcp.MCPServer. - Surchargez
init()et enregistrez vos outils à l'aide dethis.registerTool(). Le framework analyse et mappe automatiquement les méthodes@Action.
import org.tinystruct.mcp.MCPServer;
public class MyMCPServer extends MCPServer {
@Override
public void init() {
super.init();
this.registerTool(new MyCustomTool());
}
@Override
public String version() {
return "1.0.0";
}
}
Exécutez le serveur via le répartiteur :
bin/dispatcher start --import org.tinystruct.system.HttpServer --import com.example.MyMCPServer
Configuration
Les paramètres sont gérés dans src/main/resources/application.properties.
# Base de données
driver=org.h2.Driver
database.url=jdbc:h2:~/mydb
database.user=sa
database.password=
# Serveur
default.home.page=hello
server.port=8080
# Langue
default.language=en_US
# Session (Redis pour environnements en cluster)
# default.session.repository=org.tinystruct.http.RedisSessionRepository
# redis.host=127.0.0.1
# redis.port=6379
Accédez aux valeurs de configuration dans votre application :
String port = this.getConfiguration("server.port");
Drapeaux rouges et anti-modèles
| Symptôme | Modèle correct |
|---|---|
Importer com.google.gson ou com.fasterxml.jackson | Utiliser org.tinystruct.data.component.Builder / Builders. |
Utiliser List<Builder> pour les tableaux JSON | Utiliser Builders pour éviter les problèmes d'effacement de type générique. |
ApplicationRuntimeException: template not found | Appeler setTemplateRequired(false) dans init() pour les applications API uniquement. |
Annoter des méthodes private avec @Action | Les actions doivent être public pour être enregistrées par le framework. |
Coder en dur main(String[] args) dans les apps | Utiliser bin/dispatcher comme point d'entrée pour tous les modules. |
Enregistrement manuel de ActionRegistry | Préférer l'annotation @Action pour la découverte automatique. |
| Action introuvable lors de l'exécution | Assurez-vous que la classe est importée via --import ou listée dans application.properties. |
| Argument CLI non visible | Passer avec --key value ; accéder via getContext().getAttribute("--key"). |
| Deux méthodes même chemin, la mauvaise se déclenche | Définir un mode explicite (par ex., HTTP_GET vs HTTP_POST) pour désambiguïser. |
Bonnes pratiques
- Applications granulaires : Divisez la logique en applications plus petites et ciblées plutôt qu'une seule classe monolithique.
- Configuration dans
init(): Exploitezinit()pour la configuration (config, BD) plutôt que le constructeur. N'appelez PASsetAction()— utilisez l'annotation@Action. - Conscience du mode : Utilisez le paramètre
Modedans@Actionpour restreindre les opérations sensibles à la CLI uniquement ou à des méthodes HTTP spécifiques. - Contexte plutôt que paramètres : Pour les indicateurs CLI optionnels, utilisez
getContext().getAttribute("--flag")plutôt que d'ajouter des paramètres à la signature de la méthode. - Événements asynchrones : Pour les tâches lourdes déclenchées par des événements, utilisez
CompletableFuture.runAsync()dans le gestionnaire d'événements.
Référence technique
Des guides détaillés sont disponibles dans le répertoire references/ :
- Architecture et configuration — Abstractions, Plan du package, Propriétés
- Routage et @Action — Détails d'annotation, Modes, Paramètres
- Gestion des données — Builder, Builders, sérialisation et analyse JSON
- Persistance de base de données — POJOs AbstractData, CRUD, XML de mapping, génération de POJO
- Système et utilisation — Contexte, Sessions, SSE, Téléchargements de fichiers, Événements, Réseautage
- Modèles de test — Tests unitaires JUnit 5 et d'intégration HTTP
Fichiers sources de référence (interne)
src/main/java/org/tinystruct/AbstractApplication.java— Classe de base centrale avec hooks de cycle de viesrc/main/java/org/tinystruct/system/annotation/Action.java— Annotation et modessrc/main/java/org/tinystruct/application/ActionRegistry.java— Moteur de routagesrc/main/java/org/tinystruct/data/component/Builder.java— Sérialiseur d'objet JSONsrc/main/java/org/tinystruct/data/component/Builders.java— Sérialiseur de tableau JSONsrc/main/java/org/tinystruct/data/component/AbstractData.java— Classe POJO de base avec CRUDsrc/main/java/org/tinystruct/data/Mapping.java— Analyseur XML de mappingsrc/main/java/org/tinystruct/data/tools/MySQLGenerator.java— Référence pour le générateur de POJOsrc/main/java/org/tinystruct/data/component/FieldType.java— Mappages SQL vers Javasrc/main/java/org/tinystruct/data/component/Condition.java— Constructeur de requête SQL fluidesrc/main/java/org/tinystruct/http/SSEPushManager.java— Gestion des connexions SSEsrc/test/java/org/tinystruct/application/ActionRegistryTest.java— Exemples de test du registresrc/test/java/org/tinystruct/system/HttpServerHttpModeTest.java— Modèles de test d'intégration HTTP