Socle V004 – Janino

Socle V004 - Janino

29 – Janino (Compilateur Java Dynamique)

Version : 4.0.0 Date : 2026-01-17

1. Introduction

Janino est un compilateur Java embarqué qui permet de compiler du code source Java en bytecode JVM à la volée. Contrairement au ScriptEngine existant (interprété), Janino offre des performances natives car le code est réellement compilé.

Positionnement

┌─────────────────────────────────────────────────────────────────┐
│                      Socle V004 Scripts                          │
├─────────────────────────────────────────────────────────────────┤
│                                                                  │
│  ┌─────────────────────┐       ┌─────────────────────┐          │
│  │    ScriptEngine     │       │   JaninoEngine      │          │
│  │    (existant)       │       │   (NOUVEAU)         │          │
│  ├─────────────────────┤       ├─────────────────────┤          │
│  │ - JavaScript        │       │ - Java pur          │          │
│  │ - BeanShell         │       │ - Bytecode natif    │          │
│  │ - Interprété        │       │ - Haute performance │          │
│  │ - Typage dynamique  │       │ - Typage statique   │          │
│  └─────────────────────┘       └─────────────────────┘          │
│                                                                  │
└─────────────────────────────────────────────────────────────────┘

Cas d’usage

Situation Recommandation
Calculs financiers (frais, taxes) Janino
Validations métier complexes Janino
Transformations haute performance Janino
Scripts simples, prototypage ScriptEngine
Formules configurables Janino

2. Architecture

2.1 Composants

eu.lmvi.socle.janino/
├── JaninoEngine.java              # Moteur principal
├── JaninoWorker.java              # Worker de gestion
├── JaninoScript.java              # Script compilé
├── JaninoClassLoader.java         # ClassLoader sécurisé
├── JaninoConfiguration.java       # Configuration Spring
├── JaninoCompilationException.java # Exception
└── interfaces/
    ├── Calculator.java            # Interface calculateur
    ├── Executable.java            # Interface exécutable
    ├── Validator.java             # Interface validateur
    └── ValidationResult.java      # Résultat validation

2.2 Diagramme de classes

┌───────────────────────┐
│   JaninoWorker        │  (implements Worker)
│   @Component          │
├───────────────────────┤
│ - janinoEngine        │
│ - config              │
│ - techDb              │
│ - supervisor          │
├───────────────────────┤
│ + execute()           │
│ + compileScript()     │
│ + forceReload()       │
│ + getEngine()         │
└───────────┬───────────┘
            │
            ▼
┌───────────────────────┐
│   JaninoEngine        │
│   @Component          │
├───────────────────────┤
│ - scriptCache         │
│ - classLoader         │
├───────────────────────┤
│ + compile()           │
│ + execute()           │
│ + reload()            │
│ + getStats()          │
└───────────┬───────────┘
            │
            ▼
┌───────────────────────┐
│   JaninoScript        │
├───────────────────────┤
│ - name                │
│ - compiledClass       │
│ - executionCount      │
│ - avgExecutionTimeNs  │
└───────────────────────┘

3. Configuration

3.1 application.yml

socle:
  janino:
    # Activer Janino (défaut: false)
    enabled: ${JANINO_ENABLED:false}

    # Répertoire des scripts Java
    scripts-path: ${JANINO_SCRIPTS_PATH:./repository/scripts/java}

    # Intervalle de rechargement (défaut: 5 minutes)
    reload-interval-ms: ${JANINO_RELOAD_INTERVAL:300000}

    # Nombre max de classes en cache
    max-cached-classes: ${JANINO_MAX_CACHED:100}

    # Sécurité
    security:
      # Packages bloqués dans les scripts
      blocked-packages:
        - java.io
        - java.net
        - java.lang.reflect
        - java.lang.invoke
        - sun.
        - com.sun.

      # Timeout d'exécution max
      max-execution-time-ms: ${JANINO_MAX_EXEC_TIME:5000}

3.2 Variables d’environnement

Variable Description Défaut
JANINO_ENABLED Activer Janino false
JANINO_SCRIPTS_PATH Répertoire scripts ./repository/scripts/java
JANINO_RELOAD_INTERVAL Intervalle reload (ms) 300000
JANINO_MAX_CACHED Max classes en cache 100
JANINO_MAX_EXEC_TIME Timeout exécution (ms) 5000

4. Interfaces de Scripts

Les scripts Java doivent implémenter une des interfaces suivantes :

4.1 Calculator

Pour les calculs (frais, taxes, conversions).

package eu.lmvi.socle.janino.interfaces;

public interface Calculator<T> {
    T calculate(Map<String, Object> context);
}

Exemple :

import eu.lmvi.socle.janino.interfaces.Calculator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;

public class FeeCalculator implements Calculator<BigDecimal> {

    private static final BigDecimal FEE_RATE = new BigDecimal("0.0026");

    @Override
    public BigDecimal calculate(Map<String, Object> context) {
        BigDecimal amount = (BigDecimal) context.get("amount");
        String side = (String) context.get("side");

        BigDecimal rate = "MAKER".equals(side)
            ? new BigDecimal("0.0016")
            : FEE_RATE;

        return amount.multiply(rate).setScale(8, RoundingMode.HALF_UP);
    }
}

4.2 Executable

Pour les exécutions génériques.

package eu.lmvi.socle.janino.interfaces;

public interface Executable {
    Object execute(Map<String, Object> context);
}

Exemple :

import eu.lmvi.socle.janino.interfaces.Executable;
import java.util.Map;

public class OrderProcessor implements Executable {

    @Override
    public Object execute(Map<String, Object> context) {
        String orderId = (String) context.get("orderId");
        Double amount = (Double) context.get("amount");

        // Logique de traitement...

        return Map.of(
            "status", "PROCESSED",
            "orderId", orderId,
            "processedAmount", amount * 0.99
        );
    }
}

4.3 Validator

Pour les validations métier.

package eu.lmvi.socle.janino.interfaces;

public interface Validator {
    ValidationResult validate(Object input);
}

Exemple :

import eu.lmvi.socle.janino.interfaces.Validator;
import eu.lmvi.socle.janino.interfaces.ValidationResult;
import java.math.BigDecimal;

public class OrderValidator implements Validator {

    private static final BigDecimal MIN_AMOUNT = new BigDecimal("10.00");
    private static final BigDecimal MAX_AMOUNT = new BigDecimal("100000.00");

    @Override
    public ValidationResult validate(Object input) {
        Order order = (Order) input;
        ValidationResult result = new ValidationResult();

        if (order.getAmount().compareTo(MIN_AMOUNT) < 0) {
            result.addError("AMOUNT_TOO_LOW",
                "Amount must be >= " + MIN_AMOUNT);
        }

        if (order.getAmount().compareTo(MAX_AMOUNT) > 0) {
            result.addError("AMOUNT_TOO_HIGH",
                "Amount must be <= " + MAX_AMOUNT);
        }

        if (order.getPair() == null || order.getPair().isEmpty()) {
            result.addError("INVALID_PAIR", "Trading pair is required");
        }

        return result;
    }
}

5. Utilisation

5.1 Structure des scripts

repository/scripts/java/
├── fees/
│   ├── KrakenFeeCalculator.java
│   ├── BinanceFeeCalculator.java
│   └── CryptoComFeeCalculator.java
├── validators/
│   ├── OrderValidator.java
│   └── AmountValidator.java
└── processors/
    ├── OrderProcessor.java
    └── TradeProcessor.java

5.2 Injection dans un Worker

@Component
public class TradingWorker implements Worker {

    @Autowired
    private JaninoWorker janinoWorker;

    @Override
    public void doWork() {
        // Exécuter un calculateur
        Map<String, Object> context = Map.of(
            "amount", new BigDecimal("1000.00"),
            "side", "TAKER"
        );

        BigDecimal fee = janinoWorker.execute(
            "KrakenFeeCalculator",
            context,
            BigDecimal.class
        );

        log.info("Fee calculated: {}", fee);
    }
}

5.3 Compilation à la volée

@Autowired
private JaninoWorker janinoWorker;

public void compileCustomScript() {
    String source = """
        import eu.lmvi.socle.janino.interfaces.Calculator;
        import java.math.BigDecimal;
        import java.util.Map;

        public class CustomCalculator implements Calculator<BigDecimal> {
            @Override
            public BigDecimal calculate(Map<String, Object> context) {
                BigDecimal value = (BigDecimal) context.get("value");
                return value.multiply(new BigDecimal("1.05"));
            }
        }
        """;

    janinoWorker.compileScript("CustomCalculator", source);

    // Exécuter
    BigDecimal result = janinoWorker.execute(
        "CustomCalculator",
        Map.of("value", new BigDecimal("100")),
        BigDecimal.class
    );
}

5.4 Accès direct au moteur

@Autowired
private JaninoWorker janinoWorker;

public void advancedUsage() {
    JaninoEngine engine = janinoWorker.getEngine();

    // Vérifier si un script est compilé
    boolean ready = engine.isCompiled("FeeCalculator");

    // Liste des scripts compilés
    Set<String> scripts = engine.getCompiledScripts();

    // Statistiques détaillées
    Map<String, Map<String, Object>> stats = engine.getScriptStats();

    // Forcer le rechargement
    janinoWorker.forceReload();
}

6. Hot-Reload

Le JaninoWorker surveille automatiquement les modifications des fichiers .java dans le répertoire configuré.

Fonctionnement

  1. À chaque cycle (reload-interval-ms), le worker scanne le répertoire
  2. Pour chaque fichier modifié (timestamp changé), le script est recompilé
  3. Le nouveau bytecode remplace l’ancien dans le cache
  4. Les prochaines exécutions utilisent la nouvelle version

Logs

[exec:xxx][step:janino_reloaded] Reloaded script: FeeCalculator
[exec:xxx][step:janino_reload_cycle] Reload cycle completed (5 scripts)

Forcer le rechargement

// Recharger tous les scripts
janinoWorker.forceReload();

7. Securite

7.1 Validation des Imports (Source-Level)

Le JaninoEngine valide les imports avant la compilation pour bloquer l’acces aux packages dangereux. Cette approche (validation au niveau du code source) est plus compatible avec les fat-jars Spring Boot que la precedente approche ClassLoader.

Package bloque Raison
java.io Acces fichiers
java.net Acces reseau
java.lang.reflect Reflection
java.lang.invoke MethodHandles
sun.* Classes internes
com.sun.* Classes internes

7.2 Fonctionnement

Source Java → validateSourceSecurity() → Compilation Janino → Execution
                     ↓
              Analyse des imports
                     ↓
            Blocage si package interdit

Le moteur analyse les declarations import dans le code source et rejette le script si un package bloque est detecte.

7.3 Tentative d’acces bloque

// Ce script sera bloque AVANT compilation
import java.io.File;  // BLOQUE!

public class MaliciousScript implements Executable {
    @Override
    public Object execute(Map<String, Object> context) {
        File file = new File("/etc/passwd");
        return null;
    }
}

Erreur :

JaninoCompilationException: Security violation in script 'MaliciousScript':
Import of blocked package 'java.io' is not allowed. Blocked import: java.io.File

7.4 Configuration personnalisee

socle:
  janino:
    security:
      blocked-packages:
        - java.io
        - java.net
        - java.lang.reflect
        - java.lang.invoke
        - sun.
        - com.sun.
        - com.mycompany.internal  # Packages internes custom

8. Métriques et Monitoring

8.1 Stats du Worker

Map<String, Object> stats = janinoWorker.getStats();

// Résultat:
{
    "name": "janino_worker",
    "running": true,
    "healthy": true,
    "scripts_path": "./repository/scripts/java",
    "reload_interval_ms": 300000,
    "reload_count": 15,
    "last_reload_at": "2026-01-17T10:30:00Z",
    "scripts_compiled": 5,
    "compilation_count": 8,
    "compilation_errors": 0,
    "execution_count": 1234,
    "execution_errors": 2
}

8.2 Stats par script

Map<String, Map<String, Object>> scriptStats = janinoWorker.getEngine().getScriptStats();

// Résultat:
{
    "KrakenFeeCalculator": {
        "class": "KrakenFeeCalculator",
        "compiled_at": 1705487400000,
        "execution_count": 500,
        "avg_execution_us": 12
    },
    "BinanceFeeCalculator": {
        "class": "BinanceFeeCalculator",
        "compiled_at": 1705487400000,
        "execution_count": 300,
        "avg_execution_us": 8
    }
}

8.3 TechDB

Le worker persiste ses stats dans la table janino_stats :

SELECT * FROM janino_stats;

-- worker_name | scripts_count | compilation_count | execution_count | reload_count | last_reload
-- janino_worker | 5 | 8 | 1234 | 15 | 2026-01-17 10:30:00

9. Comparaison avec ScriptEngine

Critère ScriptEngine JaninoEngine
Langages JavaScript, BeanShell Java pur
Exécution Interprétée Compilée (bytecode)
Performance ~1000x plus lent Native JVM
Typage Dynamique Statique
IDE Support Limité Complet (Java)
Debug Difficile Standard Java
Hot-reload Non Oui
Sécurité Sandbox complexe ClassLoader isolation
Courbe apprentissage Nouveau langage Java existant

Quand utiliser quoi ?

Situation Recommandation
Calculs critiques (frais, taxes) JaninoEngine
Validations complexes JaninoEngine
Scripts simples, one-liners ScriptEngine
Prototypage rapide ScriptEngine
Logique métier complexe JaninoEngine
Transformations JSON basiques ScriptEngine

10. Dépannage

10.1 Script non compilé

Erreur :

IllegalStateException: Script not compiled: MyScript

Solution :

  • Vérifier que le fichier existe dans scripts-path
  • Vérifier l’extension .java
  • Consulter les logs pour les erreurs de compilation

10.2 Erreur de compilation

Erreur :

JaninoCompilationException: Failed to compile script: MyScript

Solution :

  • Vérifier la syntaxe Java
  • Vérifier les imports
  • Vérifier que la classe implémente une interface valide

10.3 Classe non trouvée

Erreur :

Cannot find class name in source

Solution :

  • Le source doit contenir public class NomDeLaClasse
  • Le nom de la classe doit correspondre au nom du fichier

10.4 Package bloque

Erreur :

JaninoCompilationException: Security violation in script 'MyScript':
Import of blocked package 'java.io' is not allowed

Solution :

  • Utiliser uniquement les packages autorises
  • Si necessaire, modifier la config blocked-packages

10.5 Cannot load simple types (Fat-Jar Spring Boot)

Erreur :

org.codehaus.commons.compiler.CompileException: Cannot load simple types

Cause : Ce probleme survient dans les fat-jars Spring Boot car le classloader personnalise casse la resolution des modules Java 9+.

Solution : Cette erreur a ete corrigee dans le socle V4. Le JaninoEngine utilise maintenant le context classloader directement :

// JaninoEngine.java - ligne 117-121
ClassLoader contextLoader = Thread.currentThread().getContextClassLoader();
if (contextLoader == null) {
    contextLoader = getClass().getClassLoader();
}
compiler.setParentClassLoader(contextLoader);

Si vous rencontrez encore cette erreur, verifiez que vous utilisez la derniere version du socle.

10.6 Assignment conversion not possible from Object

Erreur :

Line 35, Column 41: Assignment conversion not possible from type "java.lang.Object" to type "java.math.BigDecimal"

Cause : Janino ne supporte pas l’inference de type pour Map.get().

Solution : Ajouter un cast explicite :

// AVANT (erreur)
BigDecimal rate = myMap.get(key);

// APRES (correct)
BigDecimal rate = (BigDecimal) myMap.get(key);

10.7 Invalid escape sequence

Erreur :

Line 27, Column 35: Invalid escape sequence

Cause : Les sequences d’echappement regex (\s, \d, \{) doivent etre double-echappees.

Solution :

// AVANT (erreur)
Pattern p = Pattern.compile("\s+");

// APRES (correct)
Pattern p = Pattern.compile("\\s+");

10.8 Invocation of static interface methods

Erreur :

Invocation of static interface methods only available for target version 8+

Cause : Janino ne supporte pas Map.of(), Set.of(), List.of() (Java 9+).

Solution : Utiliser des blocs static :

// AVANT (erreur)
private static final Map<String, String> DATA = Map.of("a", "b");

// APRES (correct)
private static final Map<String, String> DATA = new HashMap<>();
static {
    DATA.put("a", "b");
}

11. Bonnes Pratiques

DO

  • Implémenter une des interfaces (Calculator, Executable, Validator)
  • Utiliser des types explicites (pas de var)
  • Gérer les exceptions dans le script
  • Tester les scripts avant déploiement
  • Utiliser des noms de classes descriptifs

DON’T

  • Ne pas utiliser java.io, java.net, java.lang.reflect
  • Ne pas stocker d’état entre les exécutions (stateless)
  • Ne pas faire d’opérations bloquantes longues
  • Ne pas utiliser de dépendances externes non disponibles

12. Limitations Janino et Compatibilite

Janino est un compilateur Java simplifie qui ne supporte pas toutes les fonctionnalites du langage Java moderne. Cette section documente les limitations et les solutions.

12.1 Fonctionnalites Non Supportees

Fonctionnalite Version Java Statut Janino
Methodes generiques <T> Java 5+ NON SUPPORTE
Map.of(), Set.of(), List.of() Java 9+ NON SUPPORTE
switch expressions Java 14+ NON SUPPORTE
var (inference de type) Java 10+ NON SUPPORTE
Records Java 16+ NON SUPPORTE
Pattern matching Java 16+ NON SUPPORTE
Text blocks """ Java 15+ NON SUPPORTE

12.2 Solutions et Contournements

A. Pas de methodes generiques

// INTERDIT - Ne compile pas
private <T extends Number> T extractValue(String json, String key, Class<T> type) {
    // ...
}

// CORRECT - Methodes specifiques par type
private Integer extractIntValue(String json, String key) {
    // implementation pour Integer
}

private Double extractDoubleValue(String json, String key) {
    // implementation pour Double
}

B. Pas de Map.of() / Set.of() / List.of()

// INTERDIT - Ne compile pas
private static final Map<String, BigDecimal> RATES = Map.of(
    "FR", new BigDecimal("0.015"),
    "DE", new BigDecimal("0.018")
);

// CORRECT - Bloc static avec HashMap
private static final Map<String, BigDecimal> RATES = new HashMap<>();
static {
    RATES.put("FR", new BigDecimal("0.015"));
    RATES.put("DE", new BigDecimal("0.018"));
}

// CORRECT - Pour Set
private static final Set<String> COUNTRIES = new HashSet<>();
static {
    COUNTRIES.add("FR");
    COUNTRIES.add("DE");
}

C. Cast explicite pour Map.get()

// INTERDIT - "Assignment conversion not possible from Object to BigDecimal"
BigDecimal rate = RATES.get(country);

// CORRECT - Cast explicite
BigDecimal rate = (BigDecimal) RATES.get(country);
if (rate == null) rate = BigDecimal.ZERO;

D. Pas de switch expressions

// INTERDIT - Ne compile pas
String result = switch (status) {
    case "A" -> "Active";
    case "I" -> "Inactive";
    default -> "Unknown";
};

// CORRECT - Switch statement classique
String result;
switch (status) {
    case "A": result = "Active"; break;
    case "I": result = "Inactive"; break;
    default: result = "Unknown";
}

E. Double echappement des regex

// INTERDIT - "Invalid escape sequence"
Pattern p = Pattern.compile("\s+");
Pattern p2 = Pattern.compile("\{.*\}");

// CORRECT - Double echappement
Pattern p = Pattern.compile("\\s+");
Pattern p2 = Pattern.compile("\\{.*\\}");

12.3 Template de Script Compatible

Voici un template de script qui fonctionne avec Janino :

import eu.lmvi.socle.janino.interfaces.Calculator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.HashMap;
import java.util.Set;
import java.util.HashSet;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

public class MyCalculator implements Calculator<BigDecimal> {

    // Collections statiques avec bloc static
    private static final Map<String, BigDecimal> RATES = new HashMap<>();
    private static final Set<String> VALID_CODES = new HashSet<>();

    static {
        RATES.put("A", new BigDecimal("0.10"));
        RATES.put("B", new BigDecimal("0.20"));

        VALID_CODES.add("X");
        VALID_CODES.add("Y");
    }

    @Override
    public BigDecimal calculate(Map<String, Object> context) {
        // Extraction avec cast explicite
        BigDecimal amount = extractAmount(context.get("amount"));
        String code = (String) context.get("code");
        if (code == null) code = "A";

        // Acces Map avec cast
        BigDecimal rate = (BigDecimal) RATES.get(code);
        if (rate == null) rate = new BigDecimal("0.15");

        return amount.multiply(rate).setScale(2, RoundingMode.HALF_UP);
    }

    // Methode d'extraction type-safe (pas de generiques)
    private BigDecimal extractAmount(Object value) {
        if (value == null) return BigDecimal.ZERO;
        if (value instanceof BigDecimal) return (BigDecimal) value;
        if (value instanceof Number) {
            return BigDecimal.valueOf(((Number) value).doubleValue());
        }
        try {
            return new BigDecimal(value.toString());
        } catch (NumberFormatException e) {
            return BigDecimal.ZERO;
        }
    }
}

12.4 Checklist de Compatibilite

Avant de deployer un script Janino, verifiez :

  • [ ] Pas de <T> dans les signatures de methodes
  • [ ] Pas de Map.of(), Set.of(), List.of()
  • [ ] Pas de var pour les declarations
  • [ ] Pas de switch expressions (fleches ->)
  • [ ] Cast explicite (Type) pour tous les Map.get()
  • [ ] Double echappement \\ dans les regex
  • [ ] Pas de text blocks """
  • [ ] Imports explicites (pas de wildcards import java.util.*)

13. Exemple Complet

13.1 Script : TradingFeeCalculator.java

import eu.lmvi.socle.janino.interfaces.Calculator;
import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.Map;
import java.util.HashMap;

/**
 * Calculateur de frais de trading multi-exchange.
 * Compatible Janino (pas de switch expressions, pas de Map.of)
 */
public class TradingFeeCalculator implements Calculator<BigDecimal> {

    // Taux MAKER par exchange
    private static final Map<String, BigDecimal> MAKER_RATES = new HashMap<>();
    // Taux TAKER par exchange
    private static final Map<String, BigDecimal> TAKER_RATES = new HashMap<>();

    static {
        MAKER_RATES.put("KRAKEN", new BigDecimal("0.0016"));
        MAKER_RATES.put("BINANCE", new BigDecimal("0.0010"));
        MAKER_RATES.put("COINBASE", new BigDecimal("0.0040"));

        TAKER_RATES.put("KRAKEN", new BigDecimal("0.0026"));
        TAKER_RATES.put("BINANCE", new BigDecimal("0.0010"));
        TAKER_RATES.put("COINBASE", new BigDecimal("0.0060"));
    }

    private static final BigDecimal DEFAULT_RATE = new BigDecimal("0.0025");

    @Override
    public BigDecimal calculate(Map<String, Object> context) {
        String exchange = (String) context.get("exchange");
        BigDecimal amount = (BigDecimal) context.get("amount");
        String side = (String) context.get("side");

        if (exchange == null) exchange = "DEFAULT";
        if (side == null) side = "TAKER";

        BigDecimal feeRate = getFeeRate(exchange.toUpperCase(), side.toUpperCase());

        return amount.multiply(feeRate).setScale(8, RoundingMode.HALF_UP);
    }

    private BigDecimal getFeeRate(String exchange, String side) {
        boolean isMaker = "MAKER".equals(side);

        // Cast explicite requis par Janino
        BigDecimal rate;
        if (isMaker) {
            rate = (BigDecimal) MAKER_RATES.get(exchange);
        } else {
            rate = (BigDecimal) TAKER_RATES.get(exchange);
        }

        if (rate == null) {
            rate = DEFAULT_RATE;
        }
        return rate;
    }
}

13.2 Worker utilisant le script

Note : Ce Worker est du code Java standard compile par javac/Maven, pas un script Janino. Il peut donc utiliser List.of(), Map.of() et les fonctionnalites Java modernes.

@Component
public class FeeWorker implements Worker {

    private static final Logger log = LoggerFactory.getLogger(FeeWorker.class);

    @Autowired
    private JaninoWorker janinoWorker;

    @Override
    public String getName() {
        return "fee-worker";
    }

    @Override
    public void doWork() {
        // Calculer les frais pour différents exchanges
        List<String> exchanges = List.of("KRAKEN", "BINANCE", "COINBASE");
        BigDecimal amount = new BigDecimal("10000.00");

        for (String exchange : exchanges) {
            Map<String, Object> context = Map.of(
                "exchange", exchange,
                "amount", amount,
                "side", "TAKER"
            );

            BigDecimal fee = janinoWorker.execute(
                "TradingFeeCalculator",
                context,
                BigDecimal.class
            );

            log.info("Fee for {} on amount {}: {}",
                exchange, amount, fee);
        }
    }

    // ... autres méthodes Worker
}

14. References

Commentaires

Laisser un commentaire

Votre adresse e-mail ne sera pas publiée. Les champs obligatoires sont indiqués avec *