# Semantic Evaluation

**Since Camel 4.23**

The Semantic language evaluates named questions through a provider-independent adapter. Boolean questions produce decisions, choice questions produce category strings, and score questions produce numbers on an ordered rubric. Calls are synchronous and may block while inference runs. Provider errors propagate through normal Camel error handling.

## Dependencies and providers

Add `org.apache.camel:camel-semantic` and a provider, such as [TypeSafe AI](../typesafe-ai-component.md) (`camel-typesafe-ai`), using the same Camel version. The provider owns credentials, model selection, request timeout, concurrency limits and transport resources. TypeSafe AI uses `camel.component.typesafe-ai.*` settings even when a route contains no TypeSafe AI endpoint.

One advertised adapter is selected automatically. No provider, or multiple distinct providers, is an error. Select an existing bean with `camel.language.semantic.adapter=myAdapter`, or a class using its fully qualified name. Use plain names without `#bean:` or `#class:` prefixes. Registry lookup takes precedence over class resolution. Class selection uses Camel’s class resolver and injector. Created adapters are registered as `camelSemanticAdapter` and managed by the context. A collision at that name is an error. Referenced beans retain their existing lifecycle owner.

Camel Main and Camel JBang bind `camel.language.semantic.adapter` and `camel.language.semantic.default-state` to the language. Embedded applications can resolve `SemanticLanguage` from the context and call `setAdapter` and `setDefaultState` before route setup. The catalog describes the generic `language` expression model; it does not provide a dedicated semantic Spring Boot configuration class. Configure the language bean explicitly in Spring Boot until generic-language starter configuration is available.

## Named questions

The [YAML DSL](../others/yaml-dsl.md) supports declarations alongside routes, including declarations after their use:

```yaml
- semantic:
    question:
      department:
        type: choice
        instructions: Which department should handle this message?
        criteria:
          billing: Invoices, payments and refunds
          technical: Bugs, outages and technical problems
      actionable:
        type: boolean
        instructions: Does this message contain an actionable request?
        threshold: 0.8
        uncertainty: 0.1
        uncertaintyPolicy: fail
      urgency:
        type: score
        instructions: Assess urgency
        criteria:
          - Routine request
          - Time-sensitive request
          - Immediate attention needed
- route:
    from:
      uri: direct:tickets
      steps:
        - setProperty:
            name: department
            expression:
              language:
                language: semantic
                expression: ref:department
        - choice:
            when:
              - expression:
                  simple:
                    expression: "${exchangeProperty.department} == 'billing'"
                steps:
                  - to: direct:billing
              - expression:
                  simple:
                    expression: "${exchangeProperty.department} == 'technical'"
                steps:
                  - to: direct:technical
            otherwise:
              steps:
                - to: direct:review
```

Names are context-wide. Duplicate declarations across resources and unknown references fail. Reloading a resource replaces its complete set of questions, including removing declarations no longer present. Development-mode route reload also removes definitions from deleted or renamed files before parsing replacements. This replacement does not make the surrounding route reload transactional. Existing expressions resolve the current definition on their next evaluation. Loading declarations does not perform inference.

### Java declarations

Use the fluent helper from `camel-semantic` inside an ordinary `RouteBuilder`:

```java
import static org.apache.camel.semantic.SemanticQuestionsBuilder.semanticQuestions;

@Override
public void configure() {
    semanticQuestions(this)
        .question("department")
            .type("choice")
            .state("${header.myState}")
            .instructions("Which department should handle this message?")
            .criterion("billing", "Invoices, payments, and refunds")
            .criterion("technical", "Bugs, outages, and technical problems")
            .criterion("other", "Everything else")
        .register();

    from("direct:tickets")
        .setProperty("department").language("semantic", "ref:department")
        .to("direct:dispatch");
}
```

Call `.end().question("anotherName")` to add another question to the group, then `.register()` once to validate and install the entire group atomically. Register before creating expressions that refer to these names. When adding builders to a running context, add the builder declaring shared questions before builders whose routes reference them.

Use `.type("boolean")` with optional `.threshold(0.8)`, `.uncertainty(0.1)` and `.uncertaintyPolicy("non-match")` for boolean questions. Use `.type("score")` and successive `.level("description")` calls for ordered score levels. Applications can also register immutable `SemanticQuestion` definitions using `SemanticQuestions.get(context).replace(source, questions)`.

Each Java resource owns one group. Register all of its questions together; registering again replaces that resource’s previous group. Reloading a Java resource without the helper removes its declarations. Embedded builders without a resource receive distinct generated source keys. The helper reserves `"java:" + resource.getLocation()` as the resource source key; passing that key and an empty map to `replace` removes the resource’s questions.

### XML declarations

The XML extension belongs to `camel-semantic`. Include `camel-semantic` and [XML DSL](../others/java-xml-io-dsl.md) (`camel-xml-io-dsl`). Camel discovers the extension automatically at startup: use ordinary `*.xml` files without registering a loader or changing the Camel core model. XML documents without semantic declarations are handled by the standard XML loader, including its bean and route configuration support. XML loaders registered by the application before route resource discovery retain precedence over automatic discovery. Loader discovery is refreshed when the context starts or routes are reloaded. Filenames may contain dots, such as `my.tickets.xml`. Routes retain their original resource locations and line numbers for debugging and error messages.

Both layouts are supported. To keep declarations alongside routes, use `tickets.xml`:

```xml
<routes>
  <semantic>
    <question name="department" type="choice" state="${header.myState}">
      <instructions>Which department should handle this message?</instructions>
      <criterion key="billing" value="Invoices, payments, and refunds"/>
      <criterion key="technical" value="Bugs, outages, and technical problems"/>
      <criterion key="other" value="Everything else"/>
    </question>
  </semantic>
  <route id="classify-ticket">
    <from uri="direct:tickets"/>
    <setProperty name="department">
      <language language="semantic">ref:department</language>
    </setProperty>
    <to uri="direct:dispatch"/>
  </route>
</routes>
```

To share declarations across route files, use a standalone `questions.xml`:

```xml
<semantic>
  <question name="department" type="choice" state="${header.myState}">
    <instructions>Which department should handle this message?</instructions>
    <criterion key="billing" value="Invoices, payments, and refunds"/>
    <criterion key="technical" value="Bugs, outages, and technical problems"/>
    <criterion key="other" value="Everything else"/>
  </question>
</semantic>
```

Load this together with ordinary `*.xml`, Java or YAML route resources that use `ref:department`. For example, with Camel Main, set `camel.main.routes-include-pattern=classpath:questions.xml,classpath:routes.xml`. Declarations are registered before consuming routes are configured. Reload replaces the source’s questions; removing the `semantic` block, using an empty block, or deleting the resource removes obsolete definitions. XML source keys are the resource locations.

The combined format supports a `routes` root with one optional `semantic` block and ordinary `route` elements. The standalone format uses a `semantic` root. Namespace-free documents are supported, as are documents consistently using `[http://camel.apache.org/schema/semantic](http://camel.apache.org/schema/semantic)`, `[http://camel.apache.org/schema/xml-io](http://camel.apache.org/schema/xml-io)`, or `[http://camel.apache.org/schema/spring](http://camel.apache.org/schema/spring)`. A `semantic` block can also declare `xmlns="http://camel.apache.org/schema/semantic"` inside a standard `routes` document; its question elements inherit that namespace. The extension detects the declaration block or semantic root namespace automatically.

These extensions do not validate against Camel’s standard core XSDs. Automatic discovery applies to Camel’s route resource loader; it does not extend Spring’s XML application-context parser or direct JAXB unmarshalling.

For boolean questions, `threshold`, `uncertainty` and `uncertaintyPolicy` are optional question attributes. For score questions, replace the named `criterion` elements with ordered `level` elements, such as `<level>Routine</level><level>Urgent</level><level>Critical</level>`.

The numeric `threshold` and `uncertainty` options accept property placeholders in all three DSLs. For example, Java accepts `.threshold("{{semantic.threshold:0.5}}")`, and XML accepts `threshold="{{semantic.threshold:0.5}}"`. Values are resolved and validated when declarations are registered.

Java and XML declarations use the same context-wide registry, validation, defaults and adapters as YAML. They do not require `camel-yaml-dsl`. Their questions can also be selected together using `refs:name1,name2`, as described below.

### Exporting routes

Question declarations live outside Camel’s core route model. Generic model exports and runtime route dumps such as `camel.main.dumpRoutes=yaml` contain only routes; keep declarations separately and load them before reloading an exported route.

The MCP route conversion tool rejects Java or YAML inputs containing semantic declarations because a generic route export would lose those declarations. Convert the routes separately. Extended XML documents must also be separated into declarations and ordinary routes before using the generic converter.

### Selected state

A question’s optional `state` Simple expression overrides `camel.language.semantic.default-state`, whose default is `${body}`. Selectors are compiled before evaluation; selected strings, maps and lists are passed as data and are never evaluated recursively. A missing selected header fails instead of falling back to the body. A selector must be nonblank before property placeholders are resolved. A placeholder that resolves to an empty string retains the existing Simple behavior: it selects an empty string as state. Invalid Simple syntax fails. The original message is preserved. `CamelSemanticResult` contains the latest successful normalized result and is cleared before each evaluation, including one that fails.

State must be a string, map or list. For byte arrays or stream bodies, explicitly select `$\{bodyAs(String)}`. Enable stream caching before evaluating a stream when later processors also need to read it. Unsupported state types fail instead of being implicitly converted.

YAML declarations are provided by `camel-semantic` through the YAML deserializer resolver SPI. Include both `camel-semantic` and `camel-yaml-dsl` when using them. The YAML DSL does not pull in semantic evaluation, and Java applications using `camel-semantic` do not pull in the YAML DSL.

## Results and policy

Only boolean questions can be predicates. A category string is never implicitly a boolean. A probability-based boolean uses an inclusive threshold (default `0.5`). A nonzero `uncertainty` defines an inclusive band around that threshold. `fail` (the default) raises an error within the band; `non-match` returns false. An already-boolean provider supports the default policy without inventing a probability. Additional policy requirements must be supported by the provider. Choice results must name a declared criterion. Scores range from zero to the last rubric index and may be fractional.

Probabilities, provider confidence and selected values are separate. Missing optional fields remain absent. Results can retain provider/model identity and usage metadata. These fields do not imply equivalent quality or calibration when switching providers. Timeouts, malformed answers and unsupported capabilities are errors, distinct from valid negative decisions or unmatched Choice results.

## Batching named questions

Use `refs:name1,name2` to evaluate independent questions against the same selected state. The expression returns a map of normalized decisions keyed by question name. For example, using the declarations above:

```java
from("direct:tickets")
    .setProperty("decision").language("semantic", "refs:actionable,department,urgency")
    .choice()
        .when(simple("${exchangeProperty.decision[actionable]} == true && ${exchangeProperty.decision[department]} == 'billing'"))
            .to("direct:billing")
        .otherwise()
            .to("direct:review");
```

The same expression works through the generic language integration in YAML and XML. For example, replace the route above with:

```yaml
- route:
    from:
      uri: direct:tickets
      steps:
        - setProperty:
            name: decision
            expression:
              language:
                language: semantic
                expression: refs:actionable,department,urgency
        - setHeader:
            name: urgency
            expression:
              simple:
                expression: "${exchangeProperty.decision[urgency]}"
        - choice:
            when:
              - expression:
                  simple:
                    expression: "${exchangeProperty.decision[department]} == 'billing'"
                steps:
                  - to: direct:billing
            otherwise:
              steps:
                - to: direct:review
```

A result might contain `actionable → true`, `department → "billing"` and `urgency → 1.2`. Reading the stored map does not invoke the provider again. Each question retains its own threshold and uncertainty policy. There is no implicit AND/OR: batch expressions, including a batch containing one boolean question, cannot be used as predicates.

`CamelSemanticResults` holds an immutable map of the detailed `SemanticResult` objects, keyed by the same names. These retain available probabilities, confidence and provider metadata. `CamelSemanticResult` remains the single-question diagnostic property for `ref:name`. Both diagnostic properties are cleared before every semantic evaluation; a batch publishes its decisions and diagnostics only after every answer passes validation. Missing, extra or invalid answers, operational failures and an uncertainty policy of `fail` fail the entire evaluation through Camel error handling. A valid negative decision or uncertainty handled by `non-match` remains a normal `false` result. A route property assigned by Set Property is owned by the route; clear it explicitly before reevaluation if an error handler could otherwise reuse a decision from an earlier attempt.

Batch references are comma-separated names with surrounding whitespace removed. Empty, duplicate and unknown references are rejected. There is no quoting or escaping in this syntax: names containing commas or leading/trailing whitespace require a single `ref:name` evaluation. Existing single references are unchanged.

All selected questions must use the same effective Simple state selector, after applying `camel.language.semantic.default-state` and resolving property placeholders. Selectors must match exactly; different expressions that happen to return equal values are not interchangeable. The selector is evaluated once per batch. Missing or unsupported state fails before inference. One snapshot of the question definitions is used throughout each batch, including validation and decision policies; the next evaluation sees replacements from resource reload. Questions that need a previous answer require separate evaluations.

`SemanticAdapter.evaluateBatch(Map<String, SemanticQuestion>, Object)` has a default implementation that invokes the existing single-question method sequentially, preserving compatibility with existing adapters. TypeSafe AI overrides it to send all questions in one HTTP request using the component’s existing timeout and concurrency limits. No results are cached or combined across exchanges.

## EIP integration

Use `language("semantic", "ref:name")` wherever an expression or boolean predicate is accepted.

<table class="tableblock frame-all grid-all stretch"><colgroup><col> <col></colgroup><tbody><tr><td class="tableblock halign-left valign-top">EIP / integration point</td><td class="tableblock halign-left valign-top">Usage</td></tr><tr><td class="tableblock halign-left valign-top">Choice</td><td class="tableblock halign-left valign-top">Store a category with Set Property, then compare that property in ordinary when predicates.</td></tr><tr><td class="tableblock halign-left valign-top">Filter</td><td class="tableblock halign-left valign-top">Use a boolean question as the filter predicate.</td></tr><tr><td class="tableblock halign-left valign-top">Validate</td><td class="tableblock halign-left valign-top">Use a boolean question; false follows normal validation failure handling.</td></tr><tr><td class="tableblock halign-left valign-top">Set Header / Set Property</td><td class="tableblock halign-left valign-top">Store a category or score for explicit reuse in later steps.</td></tr><tr><td class="tableblock halign-left valign-top">Aggregate correlation</td><td class="tableblock halign-left valign-top">Use a category as a correlation expression, retaining tenant or case identifiers where needed.</td></tr><tr><td class="tableblock halign-left valign-top">Aggregate completion</td><td class="tableblock halign-left valign-top">Evaluate a boolean question against the accumulated body, with a size or timeout limit. <code>eagerCheckCompletion</code> instead sees the incoming exchange.</td></tr><tr><td class="tableblock halign-left valign-top">Recipient List</td><td class="tableblock halign-left valign-top">Map a category to a configured list of recipient URIs.</td></tr><tr><td class="tableblock halign-left valign-top">Routing Slip</td><td class="tableblock halign-left valign-top">Map a category to a predefined processing sequence.</td></tr><tr><td class="tableblock halign-left valign-top">Enrich</td><td class="tableblock halign-left valign-top">Map a category to a configured resource URI and use an ordinary aggregation strategy.</td></tr><tr><td class="tableblock halign-left valign-top">Loop</td><td class="tableblock halign-left valign-top">Reevaluate a boolean question on updated state, with an explicit iteration or time budget.</td></tr><tr><td class="tableblock halign-left valign-top">Sort</td><td class="tableblock halign-left valign-top">Score each item once, store the scores, then sort using a deterministic comparator.</td></tr><tr><td class="tableblock halign-left valign-top">On Exception / retryWhile</td><td class="tableblock halign-left valign-top">Evaluate whether another attempt is worthwhile, with an explicit retry budget checked before inference.</td></tr><tr><td class="tableblock halign-left valign-top">Contextual action validation</td><td class="tableblock halign-left valign-top">Use Validate after ordinary permission checks and before executing the action.</td></tr></tbody></table>

Destination mappings belong to trusted route configuration; provider output should select known labels rather than supply unrestricted endpoint URIs. Expressions reevaluate on each invocation. There is no implicit exchange-wide inference cache. Store results explicitly when reuse is intended, and reevaluate after relevant input changes.

### Predicates and explicit reuse

```java
from("direct:actionable")
    .filter().language("semantic", "ref:actionable")
    .to("direct:accepted");

from("direct:validate")
    .validate().language("semantic", "ref:actionable")
    .to("direct:valid");

from("direct:tag")
    .setHeader("department").language("semantic", "ref:department")
    .setProperty("urgency").language("semantic", "ref:urgency")
    .choice()
        .when(header("department").isEqualTo("billing")).to("direct:billing")
        .otherwise().to("direct:technical");
```

Storing the category before [Choice](../eips/choice-eip.md) performs one semantic evaluation each time execution reaches that Set Header or Set Property step. The branches compare the stored result without calling the provider again. Place that step inside a loop when the decision must be refreshed on each iteration. Nested decisions can use separate properties to retain their own results. Boolean questions can also be used directly as ordinary when predicates. These patterns use the existing EIP model and work with the Java, XML and YAML DSLs.

### Aggregation and destinations

For [Aggregate](../eips/aggregate-eip.md) and other Java APIs accepting an `Expression`, use `new LanguageExpression("semantic", "ref:department")` from `org.apache.camel.model.language`. For example, group messages with that expression and a `GroupedBodyAggregationStrategy`, using `completionSize(10)` and `completionTimeout(5000)` to bound the group. Include a trusted tenant or case identifier in the correlation key when messages must remain isolated.

A completion predicate can be obtained with `context.resolveLanguage("semantic").createPredicate("ref:actionable")`. The aggregation strategy must first put the accumulated conversation in the selected state. Use `completionPredicate(predicate).completionSize(10)` to retain a deterministic size limit.

Classify once, then map the stored label to destinations supplied by the route author:

```java
from("direct:dispatch")
    .setProperty("department").language("semantic", "ref:department")
    .process(exchange -> {
        String department = exchange.getProperty("department", String.class);
        exchange.getMessage().setHeader("recipients",
            Map.of("billing", "direct:billing,direct:audit",
                   "technical", "direct:technical").get(department));
        exchange.getMessage().setHeader("slip",
            Map.of("billing", "direct:invoice,direct:archive",
                   "technical", "direct:diagnose,direct:archive").get(department));
        exchange.getMessage().setHeader("resource",
            Map.of("billing", "direct:billingKnowledge",
                   "technical", "direct:technicalKnowledge").get(department));
    })
    .recipientList(header("recipients")).end()
    .routingSlip(header("slip"))
    .enrich().header("resource").aggregationStrategy((original, resource) -> {
        original.getMessage().setHeader("knowledge", resource.getMessage().getBody());
        return original;
    });
```

### Changed state and sorting

A loop must have a finite budget in addition to its semantic predicate. Combine the predicate with a check of `CamelLoopIndex`, and let the loop body update the selected state. Each new iteration evaluates the current content. For example, a predicate can first check that `exchange.getProperty(Exchange.LOOP_INDEX, 0, Integer.class) < 5` and then call the boolean semantic predicate, before `loopDoWhile` invokes a refinement processor.

For sorting, evaluate `ref:urgency` once for each item and build a list containing each item and its score. Use `.sort(body(), Comparator.comparingDouble(ScoredItem::score))` with that stored score. The comparator must not call the provider: sorting can compare an item multiple times and in an implementation-dependent order.

### Bounded semantic retry

A boolean question can supply the [retryWhile predicate](../../../manual/exception-advanced.md) for an exception clause. Ask whether another attempt is worthwhile using the current failure and selected request context. Restrict this to operations that are safe to retry.

> **Important**
> `retryWhile` replaces the `maximumRedeliveries` decision. Check the retry budget inside the predicate, before calling the provider. A provider that always returns true must not cause unlimited retries or evaluations.

Declare a boolean question named `retryable` with `state: $\{exchangeProperty.retryState}`. In this example, the request body is a string. `onExceptionOccurred` prepares the state before the retry predicate runs; `onRedelivery` runs later and is too late for this purpose. Include only the failure details needed by the question, excluding credentials and sensitive data.

```java
Predicate retryable = context.resolveLanguage("semantic").createPredicate("ref:retryable");

onException(IOException.class)
    .onExceptionOccurred(exchange -> {
        Exception failure = exchange.getProperty(Exchange.EXCEPTION_CAUGHT, Exception.class);
        exchange.setProperty("retryState", Map.of(
            "request", exchange.getMessage().getBody(String.class),
            "failureType", failure.getClass().getSimpleName(),
            "attempt", exchange.getMessage().getHeader(Exchange.REDELIVERY_COUNTER, Integer.class)));
    })
    .retryWhile(exchange ->
        exchange.getMessage().getHeader(Exchange.REDELIVERY_COUNTER, 0, Integer.class) <= 3
            && retryable.matches(exchange))
    .redeliveryDelay(1000)
    .handled(true)
    .to("direct:escalate");
```

The counter starts at one when deciding the first redelivery. This permits at most three redeliveries after the initial attempt. Each failure refreshes the selected state; redelivery restarts at the failed processor, not at the beginning of the route. A negative decision or an exhausted budget sends the message to `direct:escalate` through normal exception handling.

If the retry predicate throws (such as a provider timeout, or a malformed or uncertain result), Camel logs the evaluation failure at `WARN` level and regards the predicate as `false`. The message is not redelivered and is sent to `direct:escalate` through normal exception handling, the same as for a negative decision. The original exception is kept as the caught exception, with the evaluation failure attached as a suppressed exception. To tell an evaluation failure apart from a negative decision in the escalation route, check `getSuppressed()` of the `Exchange.EXCEPTION_CAUGHT` exception. Bound provider timeouts as well as the retry count.

### Contextual action validation

Use a semantic question for an additional check such as "Does this proposed action serve the approved task?" after normal identity, permission and tenant checks have succeeded. The semantic decision must not grant permissions that those checks denied.

For example, declare this question alongside routes:

```yaml
- semantic:
    question:
      withinScope:
        type: boolean
        instructions: Does the proposed action serve the approved task?
        state: ${body}
        threshold: 0.8
        uncertainty: 0.05
        uncertaintyPolicy: fail
```

The selected state should contain the approved task and the proposed action. Obtain the approved task from trusted application state; do not let the proposed action redefine it. Treat the proposed action as untrusted input: its text can attempt to steer the provider (prompt injection). A positive semantic decision must never override the application’s authorization checks. In this example, `direct:checkPermissions` performs the application’s existing authorization and rejects unauthorized requests before semantic evaluation:

```java
from("direct:action")
    .to("direct:checkPermissions")
    .validate().language("semantic", "ref:withinScope")
    .to("direct:performAction");
```

A negative decision raises normal validation failure. With this question’s `fail` policy, an uncertain decision raises an evaluation error. Timeouts and malformed responses also fail, and none of these outcomes should execute the protected action. Failure handling may reject the request or send it for human review; do not use `continued(true)` to resume at the action. The example’s threshold is a decision policy, not an accuracy guarantee.

This pattern uses Validate and existing authorization services. It does not add a semantic `AuthorizationPolicy` implementation or replace a component’s internal guardrail interfaces.

## Implementing an adapter

Implement `org.apache.camel.semantic.SemanticAdapter`. Advertise the implementation in `META-INF/services/org/apache/camel/semantic-adapter` using Camel’s `FactoryFinder` format:

```properties
class=com.example.MySemanticAdapter
```

Camel components can generate this descriptor with `@JdkService("semantic-adapter")`. Discovery checks the declarations for ambiguity before resolving the implementation through `FactoryFinder`. Repeated declarations of the same class are accepted; different classes require explicit selection. Only the selected adapter is constructed, through Camel’s `Injector`. Explicit bean or class selection also works when packaging does not expose discovery resources.

`validate` rejects unsupported questions and decision policies before traffic. `evaluate` must be thread-safe, bound time and resource consumption, honor interruption and propagate operational failures without disclosing input or credentials. Implement `CamelContextAware` for context injection and Camel `Service` interfaces when the adapter owns resources. Created service adapters are stopped and shut down by Camel; registry references are not started or stopped again by the language. Provider-specific component services remain under normal Camel lifecycle management.

For TypeSafe AI, boolean, choice and score map to Noul, Choice and Score respectively. The adapter reuses the component endpoint’s managed HTTP client, including timeout, concurrency and cancellation. Routine integration tests use deterministic adapters and local HTTP fixtures; they do not establish a model’s classification quality.

The Semantic Evaluation language supports the following options which are listed below.

   
| Name | Default | Java Type | Description |
| --- | --- | --- | --- |
| **language** (common) |  | `String` | **Required** The name of the language to use. |
| **trim** (advanced) | `true` | `Boolean` | Whether to trim the source code to remove leading and trailing whitespaces and line breaks. |
| **resolveResource** (advanced) | `false` | `Boolean` | Whether a result of the expression that is a String starting with resource: is loaded as a resource and its content becomes the result, e.g. a script that returns resource:file:order.json or resource:classpath:templates/order.json (a name without a scheme is a classpath resource). Off by default; the resource: prefix on the expression text itself is always resolved. Applies to the expression used as a value, not as a predicate. |