Compare commits
No commits in common. "forge/ACM-FW-001-attempt-1-run-93ee9ccb3cf3" and "main" have entirely different histories.
forge/ACM-
...
main
7 changed files with 18 additions and 342 deletions
|
|
@ -1,3 +0,0 @@
|
|||
# ACM-FW-001-attempt-1-run-93ee9ccb3cf3
|
||||
|
||||
Forge 이슈 작업 브랜치 `forge/ACM-FW-001-attempt-1-run-93ee9ccb3cf3`.
|
||||
|
|
@ -1,30 +0,0 @@
|
|||
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; }
|
||||
}
|
||||
|
|
@ -1,53 +1,31 @@
|
|||
package com.klaro.acquirecore.framework;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Transaction context DTO holding state for a single transaction lifecycle.
|
||||
* Migrated from txcore.h tx_context_t struct.
|
||||
* 레거시 TxCore {@code TXBUF} (고정 슬롯 키/값 버퍼) 의 Spring 대체 자리표시자.
|
||||
*
|
||||
* <p>실 전환에서는 요청/응답 DTO(record) 가 정형 필드를 담고, 이 컨텍스트는
|
||||
* 서비스 체인({@code tx_call}) 간 전달되는 느슨한 키/값 상태만 보관한다.
|
||||
*/
|
||||
public class TxContext {
|
||||
public final class TxContext {
|
||||
|
||||
/** Transaction state enumeration. Migrated from txcore.h tx_state_t. */
|
||||
public enum TxState {
|
||||
IDLE, PENDING, ACTIVE, COMMITTED, ROLLED_BACK, FAILED
|
||||
private final Map<String, Object> slots = new HashMap<>();
|
||||
|
||||
/** TXBUF 필드 설정 (레거시 {@code Fchg} 상당). */
|
||||
public TxContext put(String key, Object value) {
|
||||
slots.put(key, value);
|
||||
return this;
|
||||
}
|
||||
|
||||
private String txId;
|
||||
private String serviceName;
|
||||
private TxState state;
|
||||
private long startTime;
|
||||
private long endTime;
|
||||
private Map<String, Object> attributes;
|
||||
private String errorMessage;
|
||||
|
||||
public TxContext() {
|
||||
this.attributes = new HashMap<>();
|
||||
this.state = TxState.IDLE;
|
||||
/** TXBUF 필드 조회 (레거시 {@code Fget} 상당). */
|
||||
public Object get(String key) {
|
||||
return slots.get(key);
|
||||
}
|
||||
|
||||
public TxContext(String txId, String serviceName) {
|
||||
this();
|
||||
this.txId = txId;
|
||||
this.serviceName = serviceName;
|
||||
/** 적재된 슬롯 개수. */
|
||||
public int size() {
|
||||
return slots.size();
|
||||
}
|
||||
|
||||
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<String, Object> getAttributes() { return attributes; }
|
||||
public void setAttributes(Map<String, Object> 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; }
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,57 +0,0 @@
|
|||
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<Object> action) {
|
||||
return template.execute(serviceName, action);
|
||||
}
|
||||
|
||||
public TxResult executeTransaction(String serviceName, TxConfig config, Supplier<Object> 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);
|
||||
}
|
||||
}
|
||||
|
|
@ -1,47 +0,0 @@
|
|||
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; }
|
||||
}
|
||||
|
|
@ -1,69 +0,0 @@
|
|||
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<String, TxServiceHandler> services = new ConcurrentHashMap<>();
|
||||
private final Map<String, TxConfig> 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<String, TxServiceHandler> action) {
|
||||
services.forEach(action);
|
||||
}
|
||||
|
||||
public int getServiceCount() {
|
||||
return services.size();
|
||||
}
|
||||
}
|
||||
|
|
@ -1,96 +0,0 @@
|
|||
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<Object> 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<Object> 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; }
|
||||
}
|
||||
Reference in a new issue