> ## Documentation Index
> Fetch the complete documentation index at: https://agenticbanking.backbase.io/llms.txt
> Use this file to discover all available pages before exploring further.

# Reference artifacts and rules

> Maven artifacts on JFrog, and the rules and anti-patterns every connector must follow

## JFrog reference artifacts

Backbase publishes every Grand Central artifact to the internal JFrog repository, including the BOM, SDK, kamelets, API specs, and Maven plugins. Use the repository as your single source of truth for available versions when you don't have GitHub access to the source repositories.

Browse the tree: [Grand Central artifacts on JFrog](https://repo.backbase.com/ui/native/repo/com/backbase/gc).

| Artifact (Maven `groupId:artifactId`)                    | What it provides                                                                                                                                                                                                                                                                                 | Where to use it                                                                      |
| -------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | ------------------------------------------------------------------------------------ |
| `com.backbase.gc:grandcentral-bom`                       | Parent POM that pins versions for Camel, Quarkus, Camel-K, the kamel/helm plugins, and the Grand Central linter                                                                                                                                                                                  | `<parent>` in your connector's `pom.xml`                                             |
| `com.backbase.gc:grandcentral-connectors-sdk`            | Cross-vendor utilities: `GrandCentralUtil`, `GrandCentralTestSupportUtil`, `JunitUtil`, `RemoveNullOrEmptyJsonSerializer`, `HeaderRemovalPolicy`, `PredicatesValidatingProcessor`, `GrandCentralServiceExceptionProcessor`, `EndpointCaller`, exception classes, error constants, holiday parser | The BOM manages the version; use the SDK directly in connector code                  |
| `com.backbase.gc.generic:grandcentral-platform-kamelets` | All platform kamelets, including `gc-http-caller`, `gc-asb-producer-caller`, all exception-handler kamelets, `pgp-transform`, `determine-offset-array`, and `handle-no-records`                                                                                                                  | The BOM manages the version; reference kamelets by name in routes (`kamelet:<name>`) |
| `com.backbase.gc.api:grandcentral-{domain}-api`          | The Grand Central OpenAPI spec for a given domain such as `payment-order`, `deposit`, `party`, `batch-payment`, `fraud-detection`, `device-administration`, or `generic`                                                                                                                         | `api.artifact-id` / `api.artifact-version` in `pom.xml` `<properties>`               |
| `com.backbase.gc:{vendor}-connector-sdk`                 | Vendor-specific SDK for SOAP-style vendors only. Provides the `{vendor}-api-caller` and `{vendor}-properties` kamelets and any vendor-common Java utilities                                                                                                                                      | `<dependency>` in the connector's `pom.xml`                                          |
| `com.backbase.gc:kamel-maven-plugin`                     | Maven plugin that builds Camel-K integrations (`mvn kamel:dev`, `mvn kamel:run`)                                                                                                                                                                                                                 | Provided by the `grandcentral-bom` parent                                            |
| `com.backbase.gc:maven-kamel-helm-plugin`                | Maven plugin that generates the Helm chart and values for a connector at package time                                                                                                                                                                                                            | Provided by the `grandcentral-bom` parent                                            |
| `com.backbase.gc:gc-linter-maven-plugin`                 | Maven plugin that lints JOLT, XSLT, and JSON-validator files at the verify phase                                                                                                                                                                                                                 | Provided by the `grandcentral-bom` parent                                            |

## Important rules and anti-patterns

The following table lists the conventions every connector must follow, along with the rationale for each rule. Apply them when you author or review connector code.

| Rule                                                                                    | Rationale                                                                                                                      |
| --------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------ |
| Never use `.routeId(...)`                                                               | Camel-K assigns route IDs; manual IDs cause Kubernetes deployment conflicts                                                    |
| Never use Spring annotations (`@Component`, `@Autowired`, `@Value`)                     | The runtime is Quarkus, not Spring Boot                                                                                        |
| Use `@PropertyInject` (Camel), not `@Value` (Spring)                                    | Quarkus Camel uses Camel's property injection                                                                                  |
| Don't annotate the connector class                                                      | Quarkus Camel registers `RouteBuilder` automatically                                                                           |
| Promote all string literals to `private static final String` constants                  | Prevents typos in kamelet URIs and route keys                                                                                  |
| Use `{{?propertyName}}` for optional properties                                         | The `?` prefix prevents `PropertiesException` when a property is absent                                                        |
| Use exchange properties for side-channel data                                           | `HeaderRemovalPolicy` strips headers, but exchange properties survive every step                                               |
| Set the URL on the `gc-http-caller-url` exchange property                               | The kamelet reads the URL from this property, not from a header                                                                |
| Set query params on the `gc-http-caller-queryParams` property                           | The value must be a `Map<String, Object>`                                                                                      |
| Place JOLT, XSLT, and schema files in `src/main/resources/`                             | Camel resolves them from the classpath                                                                                         |
| Pin the BOM version explicitly per connector                                            | Different connectors target different BOM versions; align with your team's choice rather than copying from a sibling connector |
| Pin the API spec version explicitly per connector                                       | Set `<api.artifact-version>` to the version your team has chosen for this connector; never leave it at a stale default         |
| After converting XML to JSON, call `correctNumericAndBooleanValuesInJsonFromProperties` | Fixes string-to-number/boolean coercion from Jackson's XML deserializer                                                        |
| Use `doTry`/`doCatch` for optional data extraction                                      | Prevents route failure when an optional JSON or XML field is absent                                                            |
| Use `GrandCentralTestSupportUtil` and `JunitUtil` for all JUnit tests                   | Both are from `grandcentral-connectors-sdk` (managed by the BOM); never write custom base test classes                         |

## SDK utilities and processors with examples

The following examples cover commonly used utilities and processors from `grandcentral-connectors-sdk`. Use them as a starting point; for the full method surface, browse the [SDK source](https://github.com/bb-ecos-core/grandcentral-connectors-sdk/tree/develop/src/main/java/com/backbase/grandcentral/sdk).

### Load test resources

#### `JunitUtil.loadJsonFromResource(String resourcePath)`

A static helper that loads a JSON file from `src/test/resources/` and returns it as a Jackson `JsonNode`. Throws `GrandCentralException` if the resource is missing or cannot be parsed as JSON.

```java theme={"system"}
import com.backbase.grandcentral.sdk.util.JunitUtil;
import com.fasterxml.jackson.databind.JsonNode;

JsonNode expected = JunitUtil.loadJsonFromResource("fixtures/create-party-expected.json");
assertEquals(expected, actual);
```

#### `JunitUtil.loadXmlFromResource(String resourcePath)`

Works like the JSON variant, but returns the file as a UTF-8 `String`. Use it for SOAP request and response fixtures.

```java theme={"system"}
String soapResponse = JunitUtil.loadXmlFromResource("fixtures/get-account-response.xml");
mockEndpoint.whenAnyExchangeReceived(e -> e.getIn().setBody(soapResponse));
```

#### `JunitUtil.mockRoutes(ModelCamelContext context, List<String> uris, String patternToSkip)`

Walks all routes in the context, applies `AdviceWith` to each route whose input URI contains one of the supplied substrings, and mocks-and-skips endpoints matching `patternToSkip`. Use it in a `@BeforeEach` to isolate a route under test.

```java theme={"system"}
import org.apache.camel.model.ModelCamelContext;

@BeforeEach
void setUp() throws Exception {
    JunitUtil.mockRoutes(
        context.adapt(ModelCamelContext.class),
        List.of("direct:create-party"),
        "kamelet:*"
    );
    context.start();
}
```

#### `GrandCentralTestSupportUtil`

An abstract base class. Extend it to inherit `isUseAdviceWith()` and a `@BeforeEach` that mocks-and-skips all `kamelet:*` endpoints, plus sensible JVM defaults for XSLT.

```java theme={"system"}
import com.backbase.grandcentral.sdk.util.GrandCentralTestSupportUtil;

class CreatePartyRouteTest extends GrandCentralTestSupportUtil {

    @Override
    protected RoutesBuilder createRouteBuilder() {
        return new CreatePartyRouteBuilder();
    }

    @Test
    void shouldCreatePartySuccessfully() throws Exception {
        // template.sendBodyAndHeaders(...); more assertions go here
    }
}
```

### JSON manipulation (`GrandCentralUtil`)

Most `GrandCentralUtil` methods are instance methods. Bind a single instance to the Camel registry once, then call it using `bean("grandCentralUtil", "methodName")` in routes or inject it into a `Processor`. A small number of methods (marked *(static)* below) are static and can be called directly from a `Processor`.

```java theme={"system"}
getContext().getRegistry().bind("grandCentralUtil", new GrandCentralUtil());
```

#### `convertStringToJsonNode(String string)`

Parses a JSON string into a Jackson `JsonNode`. Wraps `ObjectMapper.readTree` and is a common entry point when the route body is a `String`.

```java theme={"system"}
.bean("grandCentralUtil", "convertStringToJsonNode(${body})")
```

#### `appendAttribute(JsonNode json, String key, String value)`

Adds a single `key:value` to a JSON object (or to every element if `json` is an array). Returns the mutated node.

```java theme={"system"}
.setBody().method("grandCentralUtil", "appendAttribute(${body}, 'channel', 'web')")
```

#### `appendMultipleAttributes(JsonNode jsonNode, Map<String, String> valuesToAppend)`

Like `appendAttribute`, but takes a map for several key/value pairs at once.

```java theme={"system"}
Map<String, String> extras = Map.of("channel", "web", "source", "online");

.process(exchange -> {
    JsonNode body = exchange.getIn().getBody(JsonNode.class);
    exchange.getIn().setBody(grandCentralUtil.appendMultipleAttributes(body, extras));
})
```

#### `appendJsonNode(JsonNode json, String key, JsonNode value)`

Sets a nested JSON node under `key`. Use when the value is itself an object, not a scalar.

```java theme={"system"}
.setBody().method("grandCentralUtil", "appendJsonNode(${body}, 'address', ${exchangeProperty.addressNode})")
```

#### `convertHeadersToJsonBody(Exchange exchange, String headersStr)`

Reads the listed message headers (colon-separated, for example `"X-User:X-Channel"`), assembles them into a JSON object, and sets it as the new exchange body.

```java theme={"system"}
.bean("grandCentralUtil", "convertHeadersToJsonBody(${exchange}, 'X-User:X-Channel:X-Tenant')")
```

#### `correctNumericAndBooleanValuesInJsonFromProperties(Exchange exchange)` *(static)*

After Jackson converts XML to JSON, every value becomes a quoted string. This method rewrites quoted numerics and booleans into their native JSON types. It reads the skip list from the `typeConversion.skipAttributes` property in `microprofile-config.properties`. **Call this method immediately after every XML-to-JSON conversion.**

```java theme={"system"}
import com.backbase.grandcentral.sdk.util.GrandCentralUtil;

.process(GrandCentralUtil::correctNumericAndBooleanValuesInJsonFromProperties)
```

Set `typeConversion.skipAttributes` in `microprofile-config.properties` to control which fields stay quoted:

```properties theme={"system"}
typeConversion.skipAttributes = accountNumber,phoneNumber
```

For per-route overrides where you need to specify the skip set explicitly, use the parameterized form:

#### `correctNumericAndBooleanValuesInJson(Exchange exchange, Set<String> skipAttributes)` *(static)*

```java theme={"system"}
import com.backbase.grandcentral.sdk.util.GrandCentralUtil;
import java.util.Set;

.process(exchange ->
    GrandCentralUtil.correctNumericAndBooleanValuesInJson(exchange, Set.of("accountNumber"))
)
```

#### `replaceValuesForJson(Exchange exchange)`

Reads `fieldsToBeReplaced` from properties, then replaces each matching JSON path in the body with the corresponding property placeholder value. Handles both single-object and array bodies.

```properties theme={"system"}
# microprofile-config.properties
# Comma-separated JSON paths whose current values should be replaced
fieldsToBeReplaced = $.tenantId,$.channel
# Optional: map JsonPath to a shorter mapping-field key
mappingField.$.tenantId = tenantId
mappingField.$.channel  = channel
# <mappingField>.<currentValue> = <newValue>
tenantId.LEGACY_ID = TENANT_42
channel.web        = WEB
```

```java theme={"system"}
.bean("grandCentralUtil", "replaceValuesForJson(${exchange})")
```

### Error handling

#### `transformCoreErrorToGCError(Exchange exchange)`

Reads an array of core (vendor) errors from the body. It maps each one through your connector's error-code property mapping, applies `skipErrorCodes`, then writes the canonical Grand Central error array back to the body. Use it on the failure branch of vendor calls.

```java theme={"system"}
.onException(GrandCentralServiceException.class)
    .handled(true)
    .bean("grandCentralUtil", "transformCoreErrorToGCError(${exchange})")
.end()
```

#### `GrandCentralServiceExceptionProcessor(String errorCode, int statusCode)`

Throws a `GrandCentralServiceException` with the supplied error code and HTTP status. Use it as a one-line "throw a typed error" processor inside a route.

```java theme={"system"}
import com.backbase.grandcentral.sdk.constants.ErrorConstants;
import com.backbase.grandcentral.sdk.processor.GrandCentralServiceExceptionProcessor;
import org.apache.hc.core5.http.HttpStatus;

.choice()
    .when(simple("${body} == null"))
        .process(new GrandCentralServiceExceptionProcessor(
            ErrorConstants.INVALID_JSON_SYNTAX_ERROR_CODE,
            HttpStatus.SC_BAD_REQUEST))
.end();
```

### Reusable processors

#### `ResponseHeadersFilterProcessor`

Removes response headers based on the `headerFilterMode` exchange property:

* `REMOVE_PROHIBITED` → strips headers listed in the `prohibitedHeaders` property.
* `KEEP_WHITELISTED` → keeps only headers listed in the `acceptedHeaders` property; removes everything else.

```java theme={"system"}
.setProperty("headerFilterMode", constant("KEEP_WHITELISTED"))
.process(new ResponseHeadersFilterProcessor())
```

```properties theme={"system"}
acceptedHeaders = Content-Type,X-Request-Id,X-Tenant
```

#### `PredicatesValidatingProcessor(Map<Predicate, ErrorMessage> predicates)`

Evaluates a map of Camel `Predicate`s against the exchange. Every failing predicate contributes an `ErrorMessage` to an aggregated `GC025` `GrandCentralServiceException` (HTTP 400). The two-argument constructor adds the header name and value to each error string.

```java theme={"system"}
import com.backbase.grandcentral.sdk.dto.ErrorMessage;
import com.backbase.grandcentral.sdk.processor.PredicatesValidatingProcessor;
import org.apache.camel.builder.PredicateBuilder;

Map<Predicate, ErrorMessage> rules = Map.of(
    PredicateBuilder.isNotNull(header("X-Tenant")), new ErrorMessage("X-Tenant header is required"),
    simple("${header.amount} > 0"),                  new ErrorMessage("amount must be greater than 0")
);

.process(new PredicatesValidatingProcessor(rules))
```

### Miscellaneous utilities

#### `getGeneratedUUID(Exchange exchange)`

Generates a UUID and stores it on the exchange as the `generatedUuid` property. Use it for correlation IDs when the upstream system doesn't provide one.

```java theme={"system"}
.bean("grandCentralUtil", "getGeneratedUUID(${exchange})")
.setHeader("X-Correlation-Id", exchangeProperty("generatedUuid"))
```
