From 43be2fff9809bc9a45ea9aabe978a91dc9b9d97a Mon Sep 17 00:00:00 2001 From: forge-bot Date: Sat, 18 Jul 2026 10:09:46 +0000 Subject: [PATCH 1/7] forge: open work branch for ACM-FW-001-attempt-1-run-93ee9ccb3cf3 --- .forge/ACM-FW-001-attempt-1-run-93ee9ccb3cf3.md | 3 +++ 1 file changed, 3 insertions(+) create mode 100644 .forge/ACM-FW-001-attempt-1-run-93ee9ccb3cf3.md diff --git a/.forge/ACM-FW-001-attempt-1-run-93ee9ccb3cf3.md b/.forge/ACM-FW-001-attempt-1-run-93ee9ccb3cf3.md new file mode 100644 index 0000000..26e7979 --- /dev/null +++ b/.forge/ACM-FW-001-attempt-1-run-93ee9ccb3cf3.md @@ -0,0 +1,3 @@ +# ACM-FW-001-attempt-1-run-93ee9ccb3cf3 + +Forge 이슈 작업 브랜치 `forge/ACM-FW-001-attempt-1-run-93ee9ccb3cf3`. From a31101b71a7f58728a4c32706ca44a32c7b3d1f0 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Sat, 18 Jul 2026 10:10:41 +0000 Subject: [PATCH 2/7] =?UTF-8?q?[framework]=20TxCore=20=E2=86=92=20common-f?= =?UTF-8?q?ramework=20(ACM-FW-001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../acquirecore/framework/TxContext.java | 58 +++++++++++++------ 1 file changed, 40 insertions(+), 18 deletions(-) diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxContext.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxContext.java index 1a56634..710de64 100644 --- a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxContext.java +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxContext.java @@ -1,31 +1,53 @@ package com.klaro.acquirecore.framework; -import java.util.HashMap; import java.util.Map; +import java.util.HashMap; /** - * 레거시 TxCore {@code TXBUF} (고정 슬롯 키/값 버퍼) 의 Spring 대체 자리표시자. - * - *

실 전환에서는 요청/응답 DTO(record) 가 정형 필드를 담고, 이 컨텍스트는 - * 서비스 체인({@code tx_call}) 간 전달되는 느슨한 키/값 상태만 보관한다. + * Transaction context DTO holding state for a single transaction lifecycle. + * Migrated from txcore.h tx_context_t struct. */ -public final class TxContext { +public class TxContext { - private final Map slots = new HashMap<>(); - - /** TXBUF 필드 설정 (레거시 {@code Fchg} 상당). */ - public TxContext put(String key, Object value) { - slots.put(key, value); - return this; + /** Transaction state enumeration. Migrated from txcore.h tx_state_t. */ + public enum TxState { + IDLE, PENDING, ACTIVE, COMMITTED, ROLLED_BACK, FAILED } - /** TXBUF 필드 조회 (레거시 {@code Fget} 상당). */ - public Object get(String key) { - return slots.get(key); + private String txId; + private String serviceName; + private TxState state; + private long startTime; + private long endTime; + private Map attributes; + private String errorMessage; + + public TxContext() { + this.attributes = new HashMap<>(); + this.state = TxState.IDLE; } - /** 적재된 슬롯 개수. */ - public int size() { - return slots.size(); + public TxContext(String txId, String serviceName) { + this(); + this.txId = txId; + this.serviceName = serviceName; } + + public String getTxId() { return txId; } + public void setTxId(String txId) { this.txId = txId; } + public String getServiceName() { return serviceName; } + public void setServiceName(String serviceName) { this.serviceName = serviceName; } + public TxState getState() { return state; } + public void setState(TxState state) { this.state = state; } + public long getStartTime() { return startTime; } + public void setStartTime(long startTime) { this.startTime = startTime; } + public long getEndTime() { return endTime; } + public void setEndTime(long endTime) { this.endTime = endTime; } + public Map getAttributes() { return attributes; } + public void setAttributes(Map attributes) { this.attributes = attributes; } + public Object getAttribute(String key) { return attributes.get(key); } + public void setAttribute(String key, Object value) { attributes.put(key, value); } + public String getErrorMessage() { return errorMessage; } + public void setErrorMessage(String errorMessage) { this.errorMessage = errorMessage; } + public boolean isActive() { return state == TxState.ACTIVE || state == TxState.PENDING; } } From a9be4bc4ef2556ed520758022bcb0340e3415866 Mon Sep 17 00:00:00 2001 From: forge-bot Date: Sat, 18 Jul 2026 10:10:42 +0000 Subject: [PATCH 3/7] =?UTF-8?q?[framework]=20TxCore=20=E2=86=92=20common-f?= =?UTF-8?q?ramework=20(ACM-FW-001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../klaro/acquirecore/framework/TxConfig.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxConfig.java diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxConfig.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxConfig.java new file mode 100644 index 0000000..95220fd --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxConfig.java @@ -0,0 +1,30 @@ +package com.klaro.acquirecore.framework; + +/** + * Transaction configuration DTO. Migrated from txcore.h tx_config_t. + */ +public class TxConfig { + private int timeoutSeconds = 30; + private int maxRetries = 3; + private boolean autoCommit = false; + private String isolationLevel = "READ_COMMITTED"; + private boolean rollbackOnException = true; + + public TxConfig() {} + + public TxConfig(int timeoutSeconds, int maxRetries) { + this.timeoutSeconds = timeoutSeconds; + this.maxRetries = maxRetries; + } + + public int getTimeoutSeconds() { return timeoutSeconds; } + public void setTimeoutSeconds(int timeoutSeconds) { this.timeoutSeconds = timeoutSeconds; } + public int getMaxRetries() { return maxRetries; } + public void setMaxRetries(int maxRetries) { this.maxRetries = maxRetries; } + public boolean isAutoCommit() { return autoCommit; } + public void setAutoCommit(boolean autoCommit) { this.autoCommit = autoCommit; } + public String getIsolationLevel() { return isolationLevel; } + public void setIsolationLevel(String isolationLevel) { this.isolationLevel = isolationLevel; } + public boolean isRollbackOnException() { return rollbackOnException; } + public void setRollbackOnException(boolean rollbackOnException) { this.rollbackOnException = rollbackOnException; } +} From f10a82ec162ce7d66c1b3406f9b793edf00368bd Mon Sep 17 00:00:00 2001 From: forge-bot Date: Sat, 18 Jul 2026 10:10:43 +0000 Subject: [PATCH 4/7] =?UTF-8?q?[framework]=20TxCore=20=E2=86=92=20common-f?= =?UTF-8?q?ramework=20(ACM-FW-001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../klaro/acquirecore/framework/TxResult.java | 47 +++++++++++++++++++ 1 file changed, 47 insertions(+) create mode 100644 boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxResult.java diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxResult.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxResult.java new file mode 100644 index 0000000..7d86491 --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxResult.java @@ -0,0 +1,47 @@ +package com.klaro.acquirecore.framework; + +/** + * Transaction result DTO. Migrated from txcore.h tx_result_t. + */ +public class TxResult { + private boolean success; + private String txId; + private String message; + private Object data; + private long durationMs; + + public TxResult() {} + + public TxResult(boolean success, String txId) { + this.success = success; + this.txId = txId; + } + + public TxResult(boolean success, String txId, String message) { + this(success, txId); + this.message = message; + } + + public static TxResult ok(String txId) { + return new TxResult(true, txId, "Transaction committed successfully"); + } + + public static TxResult ok(String txId, String message) { + return new TxResult(true, txId, message); + } + + public static TxResult fail(String txId, String message) { + return new TxResult(false, txId, message); + } + + public boolean isSuccess() { return success; } + public void setSuccess(boolean success) { this.success = success; } + public String getTxId() { return txId; } + public void setTxId(String txId) { this.txId = txId; } + public String getMessage() { return message; } + public void setMessage(String message) { this.message = message; } + public Object getData() { return data; } + public void setData(Object data) { this.data = data; } + public long getDurationMs() { return durationMs; } + public void setDurationMs(long durationMs) { this.durationMs = durationMs; } +} From 63f85f0f818aeaca557a3a95dc54c7b125ac058d Mon Sep 17 00:00:00 2001 From: forge-bot Date: Sat, 18 Jul 2026 10:10:44 +0000 Subject: [PATCH 5/7] =?UTF-8?q?[framework]=20TxCore=20=E2=86=92=20common-f?= =?UTF-8?q?ramework=20(ACM-FW-001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../framework/TxServiceRegistry.java | 69 +++++++++++++++++++ 1 file changed, 69 insertions(+) create mode 100644 boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxServiceRegistry.java diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxServiceRegistry.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxServiceRegistry.java new file mode 100644 index 0000000..4488127 --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxServiceRegistry.java @@ -0,0 +1,69 @@ +package com.klaro.acquirecore.framework; + +import org.springframework.stereotype.Service; +import jakarta.annotation.PostConstruct; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; +import java.util.function.BiConsumer; + +/** + * Service registry for transaction services. Migrated from txcore.c tx_service_registry. + * Replaces TX_SERVICE/registry pattern with @Service dispatch. + */ +@Service +public class TxServiceRegistry { + + /** Functional interface for transaction service handlers. */ + @FunctionalInterface + public interface TxServiceHandler { + TxResult execute(TxContext context); + } + + private final Map services = new ConcurrentHashMap<>(); + private final Map serviceConfigs = new ConcurrentHashMap<>(); + + @PostConstruct + public void initialize() {} + + public void registerService(String serviceName, TxServiceHandler handler) { + services.put(serviceName, handler); + } + + public void registerService(String serviceName, TxServiceHandler handler, TxConfig config) { + services.put(serviceName, handler); + serviceConfigs.put(serviceName, config); + } + + public void unregisterService(String serviceName) { + services.remove(serviceName); + serviceConfigs.remove(serviceName); + } + + public TxServiceHandler getService(String serviceName) { + return services.get(serviceName); + } + + public boolean hasService(String serviceName) { + return services.containsKey(serviceName); + } + + public TxConfig getConfig(String serviceName) { + return serviceConfigs.getOrDefault(serviceName, new TxConfig()); + } + + public TxResult execute(String serviceName, TxContext context) { + TxServiceHandler handler = services.get(serviceName); + if (handler == null) { + return TxResult.fail(context.getTxId(), "Service not found: " + serviceName); + } + return handler.execute(context); + } + + public void forEachService(BiConsumer action) { + services.forEach(action); + } + + public int getServiceCount() { + return services.size(); + } +} From 2ead4f104a28047e3c3b612361426f38d77c323e Mon Sep 17 00:00:00 2001 From: forge-bot Date: Sat, 18 Jul 2026 10:10:45 +0000 Subject: [PATCH 6/7] =?UTF-8?q?[framework]=20TxCore=20=E2=86=92=20common-f?= =?UTF-8?q?ramework=20(ACM-FW-001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../acquirecore/framework/TxTemplate.java | 96 +++++++++++++++++++ 1 file changed, 96 insertions(+) create mode 100644 boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxTemplate.java diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxTemplate.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxTemplate.java new file mode 100644 index 0000000..1dc3d8d --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxTemplate.java @@ -0,0 +1,96 @@ +package com.klaro.acquirecore.framework; + +import org.springframework.stereotype.Component; +import org.springframework.transaction.annotation.Transactional; +import org.springframework.transaction.annotation.Isolation; +import org.springframework.transaction.annotation.Propagation; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.UUID; +import java.util.function.Supplier; + +/** + * Transaction template providing @Transactional equivalent functionality. + * Migrated from txcore.c tx_begin/tx_commit/tx_rollback functions. + */ +@Component +public class TxTemplate { + + /** Exception thrown when transaction execution fails. */ + public static class TxExecutionException extends RuntimeException { + private final String txId; + private final TxContext.TxState state = TxContext.TxState.FAILED; + + public TxExecutionException(String message) { super(message); this.txId = null; } + public TxExecutionException(String message, Throwable cause) { super(message, cause); this.txId = null; } + public TxExecutionException(String txId, String message) { super(message); this.txId = txId; } + public TxExecutionException(String txId, String message, Throwable cause) { super(message, cause); this.txId = txId; } + public String getTxId() { return txId; } + public TxContext.TxState getState() { return state; } + } + + @Autowired + private TxServiceRegistry registry; + + @Transactional(propagation = Propagation.REQUIRED, rollbackFor = Exception.class) + public TxResult execute(String serviceName, Supplier action) { + return execute(serviceName, new TxConfig(), action); + } + + @Transactional(propagation = Propagation.REQUIRED, isolation = Isolation.READ_COMMITTED, rollbackFor = Exception.class) + public TxResult execute(String serviceName, TxConfig config, Supplier action) { + String txId = UUID.randomUUID().toString(); + TxContext context = new TxContext(txId, serviceName); + context.setStartTime(System.currentTimeMillis()); + context.setState(TxContext.TxState.ACTIVE); + try { + Object result = action.get(); + context.setEndTime(System.currentTimeMillis()); + context.setState(TxContext.TxState.COMMITTED); + TxResult txResult = TxResult.ok(txId); + txResult.setData(result); + txResult.setDurationMs(context.getEndTime() - context.getStartTime()); + return txResult; + } catch (Exception e) { + context.setEndTime(System.currentTimeMillis()); + context.setState(TxContext.TxState.ROLLED_BACK); + context.setErrorMessage(e.getMessage()); + TxResult txResult = TxResult.fail(txId, e.getMessage()); + txResult.setDurationMs(context.getEndTime() - context.getStartTime()); + throw new TxExecutionException(txId, "Transaction failed: " + e.getMessage(), e); + } + } + + public TxContext begin(String serviceName) { + String txId = UUID.randomUUID().toString(); + TxContext context = new TxContext(txId, serviceName); + context.setStartTime(System.currentTimeMillis()); + context.setState(TxContext.TxState.PENDING); + return context; + } + + public TxContext begin(String serviceName, TxConfig config) { + TxContext context = begin(serviceName); + context.setAttribute("config", config); + return context; + } + + @Transactional(propagation = Propagation.REQUIRES_NEW) + public TxResult commit(TxContext context) { + context.setEndTime(System.currentTimeMillis()); + context.setState(TxContext.TxState.COMMITTED); + return TxResult.ok(context.getTxId()); + } + + public TxResult rollback(TxContext context, String reason) { + context.setEndTime(System.currentTimeMillis()); + context.setState(TxContext.TxState.ROLLED_BACK); + context.setErrorMessage(reason); + return TxResult.fail(context.getTxId(), reason); + } + + public TxResult executeViaRegistry(String serviceName, TxContext context) { + return registry.execute(serviceName, context); + } + + public TxServiceRegistry getRegistry() { return registry; } +} From e27ae734ce86af48ecb674f8f41d6580794f125c Mon Sep 17 00:00:00 2001 From: forge-bot Date: Sat, 18 Jul 2026 10:10:47 +0000 Subject: [PATCH 7/7] =?UTF-8?q?[framework]=20TxCore=20=E2=86=92=20common-f?= =?UTF-8?q?ramework=20(ACM-FW-001)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../klaro/acquirecore/framework/TxCore.java | 57 +++++++++++++++++++ 1 file changed, 57 insertions(+) create mode 100644 boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxCore.java diff --git a/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxCore.java b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxCore.java new file mode 100644 index 0000000..9ef868d --- /dev/null +++ b/boot/common-framework/src/main/java/com/klaro/acquirecore/framework/TxCore.java @@ -0,0 +1,57 @@ +package com.klaro.acquirecore.framework; + +import org.springframework.stereotype.Component; +import org.springframework.beans.factory.annotation.Autowired; +import java.util.function.Supplier; + +/** + * TxCore main entry point. Migrated from txcore.h/txcore.c. + * Provides centralized transaction management facade. + */ +@Component +public class TxCore { + + private final TxServiceRegistry registry; + private final TxTemplate template; + + @Autowired + public TxCore(TxServiceRegistry registry, TxTemplate template) { + this.registry = registry; + this.template = template; + } + + public TxServiceRegistry getRegistry() { return registry; } + public TxTemplate getTemplate() { return template; } + + public TxResult executeTransaction(String serviceName, Supplier action) { + return template.execute(serviceName, action); + } + + public TxResult executeTransaction(String serviceName, TxConfig config, Supplier action) { + return template.execute(serviceName, config, action); + } + + public void registerService(String serviceName, TxServiceRegistry.TxServiceHandler handler) { + registry.registerService(serviceName, handler); + } + + public void registerService(String serviceName, TxServiceRegistry.TxServiceHandler handler, TxConfig config) { + registry.registerService(serviceName, handler, config); + } + + public boolean hasService(String serviceName) { + return registry.hasService(serviceName); + } + + public TxContext beginTransaction(String serviceName) { + return template.begin(serviceName); + } + + public TxResult commitTransaction(TxContext context) { + return template.commit(context); + } + + public TxResult rollbackTransaction(TxContext context, String reason) { + return template.rollback(context, reason); + } +}