feat: DJErp 어댑터 URL 매핑을 properties 외부화 및 동적 등록으로 변경

- DJErpAdapterMappingRegistrar 신규 추가: ContextRefreshedEvent 시점에
  dj.erp.adapter.paths(쉼표 구분)를 읽어 callApi 메서드를 동적으로 등록
  OAuth2의 FrameworkEndpointHandlerMapping과 충돌하지 않도록 exact type 비교로 선택
- DJErpApiAdapterController: 하드코딩된 @RequestMapping 제거 (Registrar에 위임)
- DJErpApiAdapterService: @Primary 추가 (Root/Servlet Context 중복 스캔 시 충돌 방지)
- applicationContext-jpa.xml: Root Context에서 @Controller/@RestController 제외 필터 추가
- authserver-servlet.xml: context:property-placeholder 및 DJErpAdapterMappingRegistrar 빈 선언 추가
- env.D/T/P.properties: dj.erp.adapter.paths 속성 추가

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
curry772
2026-04-30 16:54:23 +09:00
parent efcc7fe208
commit 840136b8ca
8 changed files with 154 additions and 34 deletions
@@ -20,7 +20,12 @@
http://www.springframework.org/schema/util http://www.springframework.org/schema/util/spring-util.xsd
">
<context:component-scan base-package="com.eactive.eai" />
<context:component-scan base-package="com.eactive.eai">
<context:exclude-filter type="annotation"
expression="org.springframework.stereotype.Controller"/>
<context:exclude-filter type="annotation"
expression="org.springframework.web.bind.annotation.RestController"/>
</context:component-scan>
<bean id="entityManagerFactory" primary="true"
class="org.springframework.orm.jpa.LocalContainerEntityManagerFactoryBean">
@@ -17,6 +17,10 @@
http://www.springframework.org/schema/mvc
http://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:property-placeholder
location="/WEB-INF/properties/*.${eai.systemmode}.properties"
ignore-unresolvable="true" />
<context:component-scan
base-package="com.eactive.eai.authserver" />
<context:component-scan
@@ -31,4 +35,8 @@
<mvc:interceptors>
<bean class="com.eactive.eai.adapter.interceptor.HttpResponseLoggingInterceptor"/>
</mvc:interceptors>
<bean class="com.eactive.eai.adapter.controller.DJErpAdapterMappingRegistrar">
<property name="paths" value="${dj.erp.adapter.paths}" />
</bean>
</beans>
@@ -20,3 +20,6 @@ eai.systemmode=D
eai.systemtype=API
eai.tableowner=AGWADM
eai.server.extractor=[0,2],[0,2],[-2]
# DJErp API Adapter request mapping paths (comma-separated)
dj.erp.adapter.paths=/dj/{path:^(?!oauth).*$},/dj/{path:^(?!oauth).*$}/**
@@ -12,3 +12,6 @@ hibernate.dialect=org.hibernate.dialect.Oracle12cDialect
eai.systemtype=API
eai.server.extractor=[0,2],[0,2],[-2]
# DJErp API Adapter request mapping paths (comma-separated)
dj.erp.adapter.paths=/dj/{path:^(?!oauth).*$},/dj/{path:^(?!oauth).*$}/**
@@ -18,3 +18,6 @@ eai.systemmode=D
eai.systemtype=API
eai.tableowner=dapigw
eai.server.extractor=[0,2],[0,2],[-2]
# DJErp API Adapter request mapping paths (comma-separated)
dj.erp.adapter.paths=/dj/{path:^(?!oauth).*$},/dj/{path:^(?!oauth).*$}/**
@@ -0,0 +1,91 @@
package com.eactive.eai.adapter.controller;
import java.lang.reflect.Method;
import java.util.Map;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationListener;
import org.springframework.context.event.ContextRefreshedEvent;
import org.springframework.web.servlet.mvc.method.RequestMappingInfo;
import org.springframework.web.servlet.mvc.method.annotation.RequestMappingHandlerMapping;
import com.eactive.eai.common.util.Logger;
/**
* properties 파일의 dj.erp.adapter.paths 값을 읽어
* DJErpApiAdapterController 의 callApi 메서드를 동적으로 URL에 등록한다.
*
* 경로는 쉼표로 구분하여 여러 개를 지정할 수 있다.
* 예) /dj/{path:^(?!oauth).*$},/dj/{path:^(?!oauth).*$}/**
*/
public class DJErpAdapterMappingRegistrar implements ApplicationListener<ContextRefreshedEvent> {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
@Autowired
private DJErpApiAdapterController djErpApiAdapterController;
private String paths;
private boolean registered = false;
public void setPaths(String paths) {
this.paths = paths;
}
@Override
public void onApplicationEvent(ContextRefreshedEvent event) {
if (registered) {
return;
}
if (StringUtils.isBlank(paths)) {
logger.warn("DJErpAdapterMappingRegistrar] dj.erp.adapter.paths 가 설정되지 않았습니다.");
return;
}
// OAuth2의 FrameworkEndpointHandlerMapping 을 제외하고 MVC 기본 매핑만 선택
Map<String, RequestMappingHandlerMapping> candidates =
event.getApplicationContext().getBeansOfType(RequestMappingHandlerMapping.class);
RequestMappingHandlerMapping requestMappingHandlerMapping = candidates.values().stream()
.filter(m -> m.getClass() == RequestMappingHandlerMapping.class)
.findFirst()
.orElse(null);
if (requestMappingHandlerMapping == null) {
logger.warn("DJErpAdapterMappingRegistrar] RequestMappingHandlerMapping 을 찾을 수 없습니다.");
return;
}
try {
Method callApi = DJErpApiAdapterController.class.getDeclaredMethod(
"callApi", HttpServletRequest.class, HttpServletResponse.class);
RequestMappingInfo.BuilderConfiguration options = new RequestMappingInfo.BuilderConfiguration();
options.setPatternParser(requestMappingHandlerMapping.getPatternParser());
for (String path : paths.split(",")) {
path = path.trim();
if (path.isEmpty()) {
continue;
}
RequestMappingInfo mappingInfo = RequestMappingInfo
.paths(path)
.options(options)
.build();
requestMappingHandlerMapping.registerMapping(mappingInfo, djErpApiAdapterController, callApi);
logger.warn("DJErpAdapterMappingRegistrar] 등록 완료: " + path);
}
registered = true;
} catch (NoSuchMethodException e) {
logger.error("DJErpAdapterMappingRegistrar] callApi 메서드를 찾을 수 없습니다.", e);
}
}
}
@@ -16,7 +16,6 @@ import org.springframework.http.HttpStatus;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.util.AntPathMatcher;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import com.eactive.eai.adapter.AdapterGroupVO;
@@ -57,7 +56,6 @@ public class DJErpApiAdapterController implements HttpAdapterServiceKey {
* @See com.eactive.eai.authserver.config.AuthorizationServerConfig.configure(AuthorizationServerEndpointsConfigurer
* endpoints)
*/
@RequestMapping(value = { "/dj/{path:^(?!oauth).*$}", "/dj/{path:^(?!oauth).*$}/**" })
public ResponseEntity<String> callApi(HttpServletRequest servletRequest, HttpServletResponse servletResponse)
throws Exception {
// /ONLWeb/api/v1/public/getUserInfo.svc
@@ -1,5 +1,40 @@
package com.eactive.eai.adapter.service;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.net.URLDecoder;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Base64;
import java.util.Collections;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Properties;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.mina.common.ByteBuffer;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;
import org.springframework.context.annotation.Primary;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.security.oauth2.provider.ClientDetails;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import com.eactive.eai.adapter.AdapterGroupVO;
import com.eactive.eai.adapter.AdapterPropManager;
import com.eactive.eai.adapter.AdapterVO;
@@ -8,6 +43,9 @@ import com.eactive.eai.adapter.http.HttpMemoryLogger;
import com.eactive.eai.adapter.http.HttpMethodType;
import com.eactive.eai.adapter.http.dynamic.HttpAdapterServiceSupport;
import com.eactive.eai.adapter.http.dynamic.filter.JwtAuthFilter;
//for encrypt/decrypt jwhong
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.eactive.eai.common.context.ElinkTransactionContext;
import com.eactive.eai.common.exception.ExceptionUtil;
import com.eactive.eai.common.message.MessageType;
import com.eactive.eai.common.util.CommonLib;
@@ -19,41 +57,12 @@ import com.eactive.eai.message.StandardMessageUtil;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.node.ObjectNode;
import org.apache.commons.lang3.StringUtils;
import org.apache.commons.lang3.time.StopWatch;
import org.apache.mina.common.ByteBuffer;
import org.json.simple.JSONObject;
import org.json.simple.JSONValue;
import org.json.simple.parser.JSONParser;
import org.springframework.http.HttpHeaders;
import org.springframework.http.HttpMethod;
import org.springframework.http.MediaType;
import org.springframework.stereotype.Service;
import org.springframework.util.AntPathMatcher;
import javax.servlet.ServletInputStream;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.URLDecoder;
import java.util.*;
//for encrypt/decrypt jwhong
import com.eactive.eai.authserver.service.OAuth2Manager;
import com.kjbank.encrypt.exchange.crypto.AES256Cipher;
import com.kjbank.encrypt.exchange.crypto.AESCipher;
import com.kjbank.encrypt.exchange.crypto.AES256GCMCipher;
import java.nio.charset.StandardCharsets;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.ObjectOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import org.springframework.security.oauth2.provider.ClientDetails;
import com.kjbank.encrypt.exchange.crypto.AESCipher;
import com.nimbusds.jwt.SignedJWT;
import com.eactive.eai.common.context.ElinkTransactionContext;
@Primary
@Service
public class DJErpApiAdapterService extends HttpAdapterServiceSupport {
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);