Suggest optimization for apache camel cartridge code
CartridgeException.java
package com.example.transformation.cartridge;
public class CartridgeException extends RuntimeException {
public CartridgeException(String message) {
super(message);
}
public CartridgeException(String message, Throwable cause) {
super(message, cause);
}
}
CartridgeYamlRouteRegistrar.java
package com.example.transformation.cartridge;
import java.io.InputStream;
import org.apache.camel.CamelContext;
import org.apache.camel.RoutesBuilder;
import org.apache.camel.dsl.yaml.YamlRoutesBuilderLoader;
import org.apache.camel.support.ResourceHelper;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.stereotype.Component;
/**
* Ensures cartridge YAML routes are loaded at runtime.
*
* We intentionally do not rely on Spring Boot property-based route discovery here,
* because it's easy to misconfigure and then "direct:" endpoints won't exist.
*
* Plug-and-play rule remains:
* - add `cartridges//-route.yaml` and `cartridges//-mapping.yaml`
* - the route must start with `direct:`
*/
@Component
public class CartridgeYamlRouteRegistrar implements ApplicationRunner {
private final CamelContext camelContext;
public CartridgeYamlRouteRegistrar(CamelContext camelContext) {
this.camelContext = camelContext;
}
@Override
@SuppressWarnings("resource")
public void run(ApplicationArguments args) throws Exception {
Resource[] cartridgeResources = new PathMatchingResourcePatternResolver()
.getResources("classpath*:cartridges/*/*-route.yaml");
Resource[] sharedResources = new PathMatchingResourcePatternResolver()
.getResources("classpath*:routes/*.yaml");
if (cartridgeResources.length == 0 && sharedResources.length == 0) {
return;
}
YamlRoutesBuilderLoader loader = new YamlRoutesBuilderLoader();
loader.setCamelContext(camelContext);
loader.start();
try {
for (Resource r : concat(sharedResources, cartridgeResources)) {
try (InputStream is = r.getInputStream()) {
byte[] bytes = is.readAllBytes();
org.apache.camel.spi.Resource camelResource = ResourceHelper.fromBytes(r.getDescription(), bytes);
RoutesBuilder rb = (RoutesBuilder) loader.loadRoutesBuilder(camelResource);
camelContext.addRoutes(rb);
}
}
} finally {
loader.stop();
}
}
private static Resource[] concat(Resource[] a, Resource[] b) {
Resource[] out = new Resource[a.length + b.length];
System.arraycopy(a, 0, out, 0, a.length);
System.arraycopy(b, 0, out, a.length, b.length);
return out;
}
}
src/main/java/com/example/transformation/cartridge/JsonPathMini.java
package com.example.transformation.cartridge;
import java.util.List;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
/**
* Minimal JSONPath-ish accessor with optimized path parsing.
*
* Supported:
* - $.a.b.c (maps)
* - $.a.0.b (lists by numeric segment)
*
* Not supported: filters, wildcards, predicates.
*
* Performance optimizations:
* - Pre-parsed path segments are cached
* - Integer indices are pre-parsed for list access
* - No regex compilation at runtime
*/
public final class JsonPathMini {
private JsonPathMini() {}
// Cache for parsed JSONPath segments (source paths like $.a.b.c)
private static final ConcurrentHashMap SOURCE_PATH_CACHE = new ConcurrentHashMap<>(64);
// Cache for parsed dot-path segments (target paths like a.b.c)
private static final ConcurrentHashMap TARGET_PATH_CACHE = new ConcurrentHashMap<>(64);
/**
* Pre-parsed path representation for fast traversal.
*/
private static final class ParsedPath {
final String[] segments;
final int[] indices; // Pre-parsed integer indices, -1 if not numeric
ParsedPath(String[] segments) {
this.segments = segments;
this.indices = new int[segments.length];
for (int i = 0; i < segments.length; i++) {
this.indices[i] = parseIndex(segments[i]);
}
}
private static int parseIndex(String seg) {
if (seg.isEmpty()) return -1;
// Fast path: check if all chars are digits
for (int i = 0; i < seg.length(); i++) {
char c = seg.charAt(i);
if (c < '0' || c > '9') return -1;
}
try {
return Integer.parseInt(seg);
} catch (NumberFormatException e) {
return -1;
}
}
}
private static ParsedPath parsePath(String path) {
return SOURCE_PATH_CACHE.computeIfAbsent(path, JsonPathMini::doParseSourcePath);
}
private static ParsedPath doParseSourcePath(String path) {
if (path == null || path.isEmpty()) {
return new ParsedPath(new String[0]);
}
String p = path.charAt(0) == ' ' ? path.trim() : path;
if ("$".equals(p)) {
return new ParsedPath(new String[0]);
}
if (p.length() < 3 || p.charAt(0) != '$' || p.charAt(1) != '.') {
throw new IllegalArgumentException("Only paths starting with '$.' are supported. Got: " + path);
}
// Manual split for performance (avoids regex)
return new ParsedPath(splitByDot(p, 2));
}
private static String[] parseTargetPath(String dotPath) {
return TARGET_PATH_CACHE.computeIfAbsent(dotPath, JsonPathMini::doParseTargetPath);
}
private static String[] doParseTargetPath(String dotPath) {
if (dotPath == null || dotPath.isEmpty()) {
return new String[0];
}
return splitByDot(dotPath, 0);
}
/**
* Split string by '.' starting from given offset. Avoids regex overhead.
*/
private static String[] splitByDot(String s, int startOffset) {
// Count dots first
int dotCount = 0;
for (int i = startOffset; i < s.length(); i++) {
if (s.charAt(i) == '.') dotCount++;
}
String[] result = new String[dotCount + 1];
int segStart = startOffset;
int segIdx = 0;
for (int i = startOffset; i < s.length(); i++) {
if (s.charAt(i) == '.') {
result[segIdx++] = s.substring(segStart, i);
segStart = i + 1;
}
}
result[segIdx] = s.substring(segStart);
return result;
}
public static Object get(Object root, String path) {
if (path == null || path.isEmpty()) {
return null;
}
// Fast path for blank check without creating new string
if (isBlank(path)) {
return null;
}
ParsedPath parsed = parsePath(path);
if (parsed.segments.length == 0) {
return root;
}
Object cur = root;
String[] segments = parsed.segments;
int[] indices = parsed.indices;
for (int i = 0; i < segments.length; i++) {
if (cur == null) {
return null;
}
if (cur instanceof Map, ?> m) {
cur = m.get(segments[i]);
} else if (cur instanceof List> list) {
int idx = indices[i];
if (idx < 0) {
// Not a valid index - try map lookup failed
return null;
}
cur = (idx < list.size()) ? list.get(idx) : null;
} else {
return null;
}
}
return cur;
}
@SuppressWarnings("unchecked")
public static void put(Map root, String dotPath, Object value) {
if (dotPath == null || dotPath.isEmpty()) {
return;
}
String[] segments = parseTargetPath(dotPath);
if (segments.length == 0) {
return;
}
Map cur = root;
int lastIdx = segments.length - 1;
for (int i = 0; i < lastIdx; i++) {
String seg = segments[i];
Object existing = cur.get(seg);
if (existing instanceof Map, ?>) {
cur = (Map) existing;
} else {
Map next = new java.util.LinkedHashMap<>(4);
cur.put(seg, next);
cur = next;
}
}
cur.put(segments[lastIdx], value);
}
private static boolean isBlank(String s) {
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) > ' ') return false;
}
return true;
}
}
src/main/java/com/example/transformation/cartridge/MappingDefinition.java
package com.example.transformation.cartridge;
import java.util.ArrayList;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
/**
* YAML model for a cartridge mapping definition.
*
* Example:
* cartridgeId: cjson-to-rest
* output:
* type: json
* validations:
* - path: $.msgId
* required: true
* mappings:
* - source: $.msgId
* target: messageId
*/
public class MappingDefinition {
public String cartridgeId;
public Output output = new Output();
public List validations = new ArrayList<>();
public List mappings = new ArrayList<>();
public Map metadata = new LinkedHashMap<>();
public static class Output {
/** json or xml */
public String type = "json";
/** Only used for xml. */
public String root = "message";
}
public static class ValidationRule {
/** JSONPath-like: $.a.b.c */
public String path;
public boolean required = false;
/** Optional: if set, the field must equal this value (string compare). */
public String equals;
/** Optional: minimum string length (applied to String.valueOf(value)). */
public Integer minLength;
/** Optional: maximum string length (applied to String.valueOf(value)). */
public Integer maxLength;
/** Optional: regex pattern (Java Pattern). Applied to String.valueOf(value). */
public String pattern;
/** Optional: numeric minimum (applied if value is numeric or numeric string). */
public Double min;
/** Optional: numeric maximum (applied if value is numeric or numeric string). */
public Double max;
}
public static class MappingRule {
/** JSONPath-like: $.a.b.c */
public String source;
/** Output field path using dot-notation: a.b.c */
public String target;
/** If true and source missing/blank => fail. */
public boolean required = false;
/** Default string to use when source missing (and not required). */
public String defaultValue;
}
}
/Users/dtale/Documents/transformationlayer/src/main/java/com/example/transformation/cartridge/MappingEngine.java
package com.example.transformation.cartridge;
import com.fasterxml.jackson.dataformat.xml.XmlMapper;
import java.util.LinkedHashMap;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.regex.Pattern;
import org.springframework.stereotype.Component;
@Component
public class MappingEngine {
private final XmlMapper xmlMapper = new XmlMapper();
// Cache compiled regex patterns to avoid recompilation on every validation
private static final ConcurrentHashMap PATTERN_CACHE = new ConcurrentHashMap<>(32);
public Result transform(Object input, MappingDefinition def) {
validate(input, def);
// Pre-size map based on number of mappings for better performance
Map out = new LinkedHashMap<>(Math.max(def.mappings.size(), 4));
for (MappingDefinition.MappingRule rule : def.mappings) {
Object v = JsonPathMini.get(input, rule.source);
if (v == null || (v instanceof String s && s.isEmpty()) || (v instanceof String s2 && isBlank(s2))) {
if (rule.required) {
throw new CartridgeException("Required mapping source missing: " + rule.source + " -> " + rule.target);
}
if (rule.defaultValue != null) {
v = rule.defaultValue;
} else {
continue;
}
}
JsonPathMini.put(out, rule.target, v);
}
String type = (def.output != null && def.output.type != null) ? def.output.type : "json";
if ("xml".equalsIgnoreCase(type)) {
String root = (def.output.root != null && !def.output.root.isEmpty() && !isBlank(def.output.root))
? def.output.root : "message";
try {
String xml = xmlMapper.writer().withRootName(root).writeValueAsString(out);
return Result.xml(xml);
} catch (Exception e) {
throw new CartridgeException("Failed to serialize XML output", e);
}
}
return Result.json(out);
}
private void validate(Object input, MappingDefinition def) {
if (def.validations == null || def.validations.isEmpty()) {
return;
}
for (MappingDefinition.ValidationRule v : def.validations) {
Object value = JsonPathMini.get(input, v.path);
if (v.required) {
if (value == null || (value instanceof String s && isBlank(s))) {
throw new CartridgeException("Validation failed: required field missing at " + v.path);
}
}
if (v.equals != null) {
String actual = (value == null) ? null : String.valueOf(value);
if (!v.equals.equals(actual)) {
throw new CartridgeException("Validation failed: " + v.path + " must equal '" + v.equals + "' but was '" + actual + "'");
}
}
if (value != null) {
// String validations
if (v.minLength != null || v.maxLength != null || v.pattern != null) {
String s = String.valueOf(value);
int len = s.length();
if (v.minLength != null && len < v.minLength) {
throw new CartridgeException("Validation failed: " + v.path + " length must be >= " + v.minLength);
}
if (v.maxLength != null && len > v.maxLength) {
throw new CartridgeException("Validation failed: " + v.path + " length must be <= " + v.maxLength);
}
if (v.pattern != null) {
Pattern p = getCompiledPattern(v.pattern);
if (!p.matcher(s).matches()) {
throw new CartridgeException("Validation failed: " + v.path + " must match pattern " + v.pattern);
}
}
}
// Numeric validations
if (v.min != null || v.max != null) {
Double n = toNumberOrNull(value);
if (n == null) {
throw new CartridgeException("Validation failed: " + v.path + " must be a number");
}
double nv = n; // Unbox once
if (v.min != null && nv < v.min) {
throw new CartridgeException("Validation failed: " + v.path + " must be >= " + v.min);
}
if (v.max != null && nv > v.max) {
throw new CartridgeException("Validation failed: " + v.path + " must be <= " + v.max);
}
}
}
}
}
/**
* Get cached compiled pattern, compiling only once per unique pattern string.
*/
private static Pattern getCompiledPattern(String regex) {
return PATTERN_CACHE.computeIfAbsent(regex, Pattern::compile);
}
private static Double toNumberOrNull(Object value) {
if (value instanceof Number n) {
return n.doubleValue();
}
if (value instanceof String s) {
int len = s.length();
if (len == 0) return null;
// Fast trim check - avoid creating new string if not needed
int start = 0;
int end = len;
while (start < end && s.charAt(start) <= ' ') start++;
while (end > start && s.charAt(end - 1) <= ' ') end--;
if (start == end) return null;
String t = (start == 0 && end == len) ? s : s.substring(start, end);
try {
return Double.parseDouble(t);
} catch (NumberFormatException ignored) {
return null;
}
}
return null;
}
/**
* Fast blank check without creating new string objects.
*/
private static boolean isBlank(String s) {
for (int i = 0; i < s.length(); i++) {
if (s.charAt(i) > ' ') return false;
}
return true;
}
public record Result(String contentType, Object body) {
public static Result json(Object body) {
return new Result("application/json", body);
}
public static Result xml(String xml) {
return new Result("application/xml", xml);
}
}
}
/Users/dtale/Documents/transformationlayer/src/main/java/com/example/transformation/cartridge/MappingLoader.java
package com.example.transformation.cartridge;
import java.io.InputStream;
import java.nio.charset.StandardCharsets;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import org.springframework.core.io.Resource;
import org.springframework.core.io.ResourceLoader;
import org.springframework.stereotype.Component;
import org.yaml.snakeyaml.LoaderOptions;
import org.yaml.snakeyaml.Yaml;
import org.yaml.snakeyaml.constructor.Constructor;
@Component
public class MappingLoader {
private final ResourceLoader resourceLoader;
private final Yaml yaml;
private final ConcurrentMap cache = new ConcurrentHashMap<>();
public MappingLoader(ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
LoaderOptions options = new LoaderOptions();
options.setAllowDuplicateKeys(false);
this.yaml = new Yaml(new Constructor(MappingDefinition.class, options));
}
public MappingDefinition load(String mappingResourcePath) {
return cache.computeIfAbsent(mappingResourcePath, this::readYaml);
}
private MappingDefinition readYaml(String mappingResourcePath) {
Resource resource = resourceLoader.getResource(mappingResourcePath);
if (!resource.exists()) {
throw new CartridgeException("Mapping YAML not found: " + mappingResourcePath);
}
try (InputStream is = resource.getInputStream()) {
MappingDefinition def = yaml.loadAs(new String(is.readAllBytes(), StandardCharsets.UTF_8), MappingDefinition.class);
if (def == null) {
throw new CartridgeException("Empty mapping YAML: " + mappingResourcePath);
}
return def;
} catch (Exception e) {
throw new CartridgeException("Failed to read mapping YAML: " + mappingResourcePath, e);
}
}
}