init
This commit is contained in:
@@ -0,0 +1,57 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import com.eactive.eai.adapter.socket2.protocol.ByteMessage;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class ByteCharSequence implements CharSequence{
|
||||
private final byte[] data;
|
||||
private final int length;
|
||||
private final int offset;
|
||||
|
||||
public ByteCharSequence(byte[] data){
|
||||
this(data, 0, data.length);
|
||||
}
|
||||
public ByteCharSequence(byte[] data, int offset, int length){
|
||||
this.data = data;
|
||||
this.offset = offset;
|
||||
this.length = length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int length() {
|
||||
return this.length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public char charAt(int index) {
|
||||
return (char)(data[offset + index] & 0xff);
|
||||
}
|
||||
|
||||
@Override
|
||||
public CharSequence subSequence(int start, int end) {
|
||||
return new ByteCharSequence(data, offset + start, end - start);
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception{
|
||||
ByteMessage b = new ByteMessage();
|
||||
b.append((byte)0xD4);
|
||||
b.append((byte)0xD6);
|
||||
b.append((byte)0xC3);
|
||||
b.append((byte)0xF5);
|
||||
b.append((byte)0xF0);
|
||||
b.append((byte)0xF9);
|
||||
b.append((byte)0xF4);
|
||||
b.append((byte)0x40);
|
||||
b.append((byte)0x40);
|
||||
b.append("0000HDRISO12345678901234567890123456789012345678901234567".getBytes());
|
||||
|
||||
Matcher m = Pattern.compile("[0-9]{4}HDR").matcher(new ByteCharSequence(b.getBytes()));
|
||||
int index =0;
|
||||
if (m.find()){
|
||||
index = m.start();
|
||||
}
|
||||
System.out.println("index = " + index);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,57 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import java.nio.ByteBuffer;
|
||||
import java.nio.charset.CharacterCodingException;
|
||||
import java.nio.charset.Charset;
|
||||
import java.nio.charset.CharsetDecoder;
|
||||
|
||||
public class CharsetDecoderUtil {
|
||||
private Charset charset = null;
|
||||
private CharsetDecoder decoder = null;
|
||||
|
||||
public CharsetDecoderUtil(String encoding) {
|
||||
this.charset = Charset.forName(encoding);
|
||||
this.decoder = charset.newDecoder();
|
||||
}
|
||||
|
||||
public static String toKor(String s) {
|
||||
if (s != null) {
|
||||
return changeCode(s, "iso_8859-1", "KSC5601");
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
public static String changeCode(String s, String s1, String s2) {
|
||||
String convString = null;
|
||||
if (s != null) {
|
||||
try {
|
||||
convString = new String(s.getBytes(s1), s2);
|
||||
} catch (Exception exception) {
|
||||
//Log.errorlog(exception.toString());
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return convString;
|
||||
}
|
||||
|
||||
public String decode(ByteBuffer buffer) {
|
||||
String string = null;
|
||||
// buffer가 null일 경우 return한다.
|
||||
if (buffer == null) {
|
||||
return "";
|
||||
}
|
||||
try {
|
||||
// buffer를 decode한다.
|
||||
string = decoder.decode(buffer).toString();
|
||||
} catch (CharacterCodingException e) {
|
||||
string = "";
|
||||
}
|
||||
// decode한 값이 null일 경우 ""를 리턴 한다.
|
||||
if (string == null) {
|
||||
return "";
|
||||
} else {
|
||||
return string;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,70 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import java.util.Base64;
|
||||
|
||||
import javax.crypto.Cipher;
|
||||
import javax.crypto.spec.SecretKeySpec;
|
||||
|
||||
//TOUSE
|
||||
//kbank 어댑터에서 업무데이터의 암호화 필드에 대한 암복호화
|
||||
//OAuth2.0 의 ClientID를 통해 등록된 key를 조회하여 사용
|
||||
public class CryptoUtil {
|
||||
public static final String defaultCharset = "utf-8";
|
||||
private CryptoUtil() {
|
||||
|
||||
}
|
||||
|
||||
public static String encryptAES(String key, String text) {
|
||||
return encrypt(key, text, "AES");
|
||||
}
|
||||
|
||||
public static String decryptAES(String key, String base64EncodedText) {
|
||||
return decrypt(key, base64EncodedText, "AES");
|
||||
}
|
||||
|
||||
public static String encrypt(String key, String text, String algoritm) throws IllegalArgumentException {
|
||||
try {
|
||||
SecretKeySpec secureKey = new SecretKeySpec(key.getBytes(), algoritm);
|
||||
Cipher cipher = Cipher.getInstance(algoritm);
|
||||
cipher.init(Cipher.ENCRYPT_MODE, secureKey);
|
||||
byte[] encryptData = cipher.doFinal(text.getBytes(defaultCharset));
|
||||
return new String(Base64.getEncoder().encode(encryptData));
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static String decrypt(String key, String text, String algoritm) throws IllegalArgumentException {
|
||||
try {
|
||||
SecretKeySpec secureKey = new SecretKeySpec(key.getBytes(), algoritm);
|
||||
byte[] encryptedData = Base64.getDecoder().decode(text.getBytes(defaultCharset));
|
||||
Cipher cipher = Cipher.getInstance(algoritm);
|
||||
cipher.init(Cipher.DECRYPT_MODE, secureKey);
|
||||
byte[] plainText = cipher.doFinal(encryptedData);
|
||||
return new String(plainText);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException(e.toString());
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String key = "ONAuROzlqWB4VuBwO5C/FA==";
|
||||
String text = "Hello AES 한글";
|
||||
|
||||
System.out.println("Original Text: " + text);
|
||||
|
||||
long startEncryptTime = System.nanoTime();
|
||||
String enc = encryptAES(key, text);
|
||||
long endEncryptTime = System.nanoTime();
|
||||
long encryptDuration = endEncryptTime - startEncryptTime;
|
||||
System.out.println("Encrypted Text: " + enc);
|
||||
System.out.println("Encryption took: " + encryptDuration + " nanoseconds");
|
||||
|
||||
long startDecryptTime = System.nanoTime();
|
||||
String dec = decryptAES(key, enc);
|
||||
long endDecryptTime = System.nanoTime();
|
||||
long decryptDuration = endDecryptTime - startDecryptTime;
|
||||
System.out.println("Decrypted Text: " + dec);
|
||||
System.out.println("Decryption took: " + decryptDuration + " nanoseconds");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,120 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import com.eactive.eai.common.lifecycle.Lifecycle;
|
||||
import com.eactive.eai.common.lifecycle.LifecycleException;
|
||||
import com.eactive.eai.common.lifecycle.LifecycleListener;
|
||||
import com.eactive.eai.common.lifecycle.LifecycleSupport;
|
||||
import com.eactive.eai.common.server.EAIServerManager;
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Timer;
|
||||
|
||||
// Server Ping Test Health Check
|
||||
public class HealthCheckManager implements Lifecycle {
|
||||
private static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
|
||||
|
||||
private static HealthCheckManager manager = new HealthCheckManager();
|
||||
|
||||
private LifecycleSupport lifecycle = new LifecycleSupport(this);
|
||||
|
||||
private boolean started;
|
||||
|
||||
private HashMap<String, Integer> serverHealthMap;
|
||||
|
||||
private HealthCheckRunner runner;
|
||||
private Timer timer;
|
||||
|
||||
public static HealthCheckManager getInstance() {
|
||||
return manager;
|
||||
}
|
||||
|
||||
public void start() throws LifecycleException {
|
||||
if (started)
|
||||
throw new LifecycleException("RECEAICHC001");
|
||||
lifecycle.fireLifecycleEvent(STARTING_EVENT, this);
|
||||
this.serverHealthMap = new HashMap<String, Integer>();
|
||||
EAIServerManager mgr = EAIServerManager.getInstance();
|
||||
this.serverHealthMap.put(mgr.getLocalServerName(), 0);
|
||||
|
||||
long period = 1000 * 5;
|
||||
// try {
|
||||
// period = Long.parseLong(pmanager.getProperty(PROP_GROUP, PROP_PERIOD));
|
||||
// } catch(Exception e) {}
|
||||
|
||||
this.runner = new HealthCheckRunner();
|
||||
this.runner.setHealthCheckManager(this);
|
||||
|
||||
this.timer = new Timer("HeadthCheckerTimer");
|
||||
this.timer.schedule(this.runner, period, period);
|
||||
|
||||
started = true;
|
||||
lifecycle.fireLifecycleEvent(STARTED_EVENT, this);
|
||||
}
|
||||
|
||||
public void stop() throws LifecycleException {
|
||||
if (!started)
|
||||
throw new LifecycleException("RECEAICHC002");
|
||||
logger.warn("Stopping");
|
||||
lifecycle.fireLifecycleEvent(STOPING_EVENT, this);
|
||||
|
||||
|
||||
this.timer.cancel();
|
||||
|
||||
this.timer = null;
|
||||
this.runner = null;
|
||||
|
||||
if(this.serverHealthMap != null) {
|
||||
this.serverHealthMap.clear();
|
||||
this.serverHealthMap = null;
|
||||
}
|
||||
|
||||
started = false;
|
||||
lifecycle.fireLifecycleEvent(STOPPED_EVENT, null);
|
||||
logger.warn("Stopped");
|
||||
}
|
||||
|
||||
public void addLifecycleListener(LifecycleListener listener) {
|
||||
lifecycle.addLifecycleListener(listener);
|
||||
}
|
||||
|
||||
public LifecycleListener[] findLifecycleListeners() {
|
||||
return lifecycle.findLifecycleListeners();
|
||||
}
|
||||
|
||||
public void removeLifecycleListener(LifecycleListener listener) {
|
||||
lifecycle.removeLifecycleListener(listener);
|
||||
}
|
||||
|
||||
public boolean isStarted() {
|
||||
return this.started;
|
||||
}
|
||||
|
||||
public void addFailCount(String instId, int count) {
|
||||
if(!started) return;
|
||||
Integer failCount = this.serverHealthMap.get(instId);
|
||||
|
||||
if (failCount == null) {
|
||||
this.serverHealthMap.put(instId, count);
|
||||
} else {
|
||||
int newCount = 0;
|
||||
if (count > 0)
|
||||
newCount = failCount + count;
|
||||
this.serverHealthMap.put(instId, newCount);
|
||||
}
|
||||
logger.info("HealthCheckManager] Server Health : " + instId + " - " + this.serverHealthMap.get(instId));
|
||||
}
|
||||
|
||||
public boolean getServerStatus(String instId) {
|
||||
if(!started) return false;
|
||||
Integer failCount = this.serverHealthMap.get(instId);
|
||||
if (failCount == null) {
|
||||
return false;
|
||||
} else {
|
||||
if (failCount >= 2)
|
||||
return false;
|
||||
else
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,44 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import java.util.TimerTask;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.eactive.eai.common.server.EAIServerManager;
|
||||
import com.eactive.eai.common.server.EAIServerVO;
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
|
||||
// HealthCheckManager에 등록된 HealthCheck Beans를 일정 주기로 실행
|
||||
public class HealthCheckRunner extends TimerTask {
|
||||
static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
|
||||
private HealthCheckManager manager;
|
||||
|
||||
public HealthCheckRunner() {
|
||||
}
|
||||
|
||||
public void setHealthCheckManager(HealthCheckManager manager) {
|
||||
this.manager = manager;
|
||||
}
|
||||
|
||||
public void run() {
|
||||
EAIServerManager mgr = EAIServerManager.getInstance();
|
||||
EAIServerVO server = mgr.getEAIServer(mgr.getLocalServerName());
|
||||
if (server == null) {
|
||||
return;
|
||||
}
|
||||
String instId = server.getFailoverSvr();
|
||||
|
||||
if (StringUtils.isBlank(instId)) {
|
||||
logger.warn("HealthCheckRunner] FailOver Server instance not set - " + mgr.getLocalServerName());
|
||||
return;
|
||||
}
|
||||
|
||||
boolean isAlive = ServerPingUtil.pingHost(instId);
|
||||
|
||||
if (isAlive) {
|
||||
this.manager.addFailCount(instId, 0);
|
||||
} else {
|
||||
this.manager.addFailCount(instId, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
public class HexaConverter {
|
||||
public static final String bytesToHexa(byte[] abyte) {
|
||||
StringBuilder s = new StringBuilder();
|
||||
if (abyte == null)
|
||||
return s.toString();
|
||||
for (int i = 0; i < abyte.length; i++) {
|
||||
s.append( Integer.toHexString((abyte[i] & 0xf0) >> 4) )
|
||||
.append( Integer.toHexString(abyte[i] & 0xf));
|
||||
}
|
||||
return s.toString();
|
||||
}
|
||||
|
||||
public static final byte[] hexaToBytes(String hexa) {
|
||||
if((hexa.length() % 2) != 0) return null;
|
||||
byte[] bytes = new byte[hexa.length() / 2];
|
||||
|
||||
String hexaBytes = null;
|
||||
for(int i=0; i <bytes.length;i++) {
|
||||
hexaBytes = hexa.substring(i*2, i*2+2);
|
||||
int byteBit = Integer.parseInt(hexaBytes, 16);
|
||||
bytes[i] = (byte)byteBit;
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
// public static void main(String[] argv) {
|
||||
// String str = "TEST\nÇѱÛ";
|
||||
// String hexStr = HexaConverter.bytesToHexa(str.getBytes());
|
||||
// System.out.println(hexStr);
|
||||
// System.out.println(new String(HexaConverter.hexaToBytes(hexStr), Charset.forName("euc-kr") ) );
|
||||
// System.out.println(new String(HexaConverter.hexaToBytes(hexStr)) );
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,47 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import java.util.regex.Matcher;
|
||||
import java.util.regex.Pattern;
|
||||
|
||||
public class IpUtil {
|
||||
/**
|
||||
* matchIps에 ip 목록을 컴마(,)구분자로 나열 하고 requestIp와 맞는지 비교
|
||||
*
|
||||
* @param matchIps 컴마구분자 matchIp 목록 예제) 192.168.0.1,192.168.0.0/24,192.168.1.*
|
||||
* @param requestIp 원격IP
|
||||
* @return
|
||||
*/
|
||||
public static boolean isMatchIp(String matchIps, String requestIp) {
|
||||
String[] matchIpArr = org.springframework.util.StringUtils.tokenizeToStringArray(matchIps, ",");
|
||||
for (String match : matchIpArr) {
|
||||
if (match.indexOf('*') >= 0) {
|
||||
String matchRegexp = match.replace("\\*", "[0-9]+").trim();
|
||||
Matcher matcher = Pattern.compile(matchRegexp, Pattern.CASE_INSENSITIVE).matcher(requestIp);
|
||||
if (matcher.matches()) {
|
||||
return true;
|
||||
}
|
||||
} else if (match.indexOf('/') >= 0) { // CIDR 표기법 "192.168.0.0/24"
|
||||
org.apache.commons.net.util.SubnetUtils utils = new org.apache.commons.net.util.SubnetUtils(match);
|
||||
if (utils.getInfo().isInRange(requestIp)) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if (match.equals(requestIp)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String matchdIps = "192.168.0.1,192.168.0.0/24,192.168.1.*";
|
||||
String requestIp = "192.168.1.2";
|
||||
if (IpUtil.isMatchIp(matchdIps, requestIp)) {
|
||||
System.out.println(true);
|
||||
} else {
|
||||
System.out.println(false);
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import org.json.simple.JSONObject;
|
||||
import org.json.simple.JSONValue;
|
||||
|
||||
public class JsonBeatiUtil {
|
||||
public static String SimpleBeautiful(Object input) {
|
||||
JSONObject json = null;
|
||||
String data = "";
|
||||
if (input instanceof JSONObject) {
|
||||
json = (JSONObject) input;
|
||||
} else if (input instanceof String) {
|
||||
json = (JSONObject) JSONValue.parse((String) input);
|
||||
} else if (input instanceof byte[]) {
|
||||
String s = new String((byte[]) input);
|
||||
json = (JSONObject) JSONValue.parse(s);
|
||||
}
|
||||
data = json.toJSONString();
|
||||
int tabCount = 0;
|
||||
StringBuilder inputBuilder = new StringBuilder();
|
||||
char[] inputChar = data.toCharArray();
|
||||
for (int i = 0; i < inputChar.length; i++) {
|
||||
String charI = String.valueOf(inputChar[i]);
|
||||
if (charI.equals("}") || charI.equals("]")) {
|
||||
tabCount--;
|
||||
if (!String.valueOf(inputChar[i - 1]).equals("[") && !String.valueOf(inputChar[i - 1]).equals("{")) {
|
||||
inputBuilder.append(simpleNewLine(tabCount));
|
||||
}
|
||||
}
|
||||
inputBuilder.append(charI);
|
||||
if (charI.equals("{") || charI.equals("[")) {
|
||||
tabCount++;
|
||||
if (String.valueOf(inputChar[i + 1]).equals("]") || String.valueOf(inputChar[i + 1]).equals("}")) {
|
||||
continue;
|
||||
}
|
||||
inputBuilder.append(simpleNewLine(tabCount));
|
||||
}
|
||||
if (charI.equals(",")) {
|
||||
inputBuilder.append(simpleNewLine(tabCount));
|
||||
}
|
||||
}
|
||||
return inputBuilder.toString();
|
||||
}
|
||||
|
||||
private static String simpleNewLine(int tabCount) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
builder.append("\n");
|
||||
for (int j = 0; j < tabCount; j++) {
|
||||
builder.append(" ");
|
||||
}
|
||||
return builder.toString();
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
String data = "";
|
||||
data += "{ ";
|
||||
data += " \"EAI_UJSON2DEPTHEXT_REQ1\":{ ";
|
||||
data += " \"abc\":\"cba \", ";
|
||||
data += " \"grid1\":[ ";
|
||||
data += " { ";
|
||||
data += " \"long1\":1, ";
|
||||
data += " \"data2\":\" \", ";
|
||||
data += " \"grid2\":[ ";
|
||||
data += " { ";
|
||||
data += " \"data3\":\"12345\", ";
|
||||
data += " \"big4\":14.00000 ";
|
||||
data += " }, ";
|
||||
data += " { ";
|
||||
data += " \"data3\":\"13 \", ";
|
||||
data += " \"big4\":14.00001 ";
|
||||
data += " } ";
|
||||
data += " ] ";
|
||||
data += " }, ";
|
||||
data += " { ";
|
||||
data += " \"long1\":10000, ";
|
||||
data += " \"data2\":\" \", ";
|
||||
data += " \"grid2\":[ ";
|
||||
data += " { ";
|
||||
data += " \"data3\":\"23 \", ";
|
||||
data += " \"big4\":24.00000 ";
|
||||
data += " } ";
|
||||
data += " ] ";
|
||||
data += " }, ";
|
||||
data += " { ";
|
||||
data += " \"long1\":1000000000, ";
|
||||
data += " \"data2\":\" \", ";
|
||||
data += " \"grid2\":[ ";
|
||||
data += " { ";
|
||||
data += " \"data3\":\"33 \", ";
|
||||
data += " \"big4\":1 ";
|
||||
data += " }, ";
|
||||
data += " { ";
|
||||
data += " \"data3\":\" \", ";
|
||||
data += " \"big4\":1234567890.00034 ";
|
||||
data += " }, ";
|
||||
data += " { ";
|
||||
data += " \"data3\":\"35 \", ";
|
||||
data += " \"big4\":0.00036 ";
|
||||
data += " } ";
|
||||
data += " ] ";
|
||||
data += " } ";
|
||||
data += " ], ";
|
||||
data += " \"abcd\":\"123\", ";
|
||||
data += " \"grid3\":[ ";
|
||||
data += " { ";
|
||||
data += " \"long1\":1, ";
|
||||
data += " \"data2\":\" \", ";
|
||||
data += " \"grid4\":[ ";
|
||||
data += " { ";
|
||||
data += " \"data5\":\"00023\", ";
|
||||
data += " \"big6\":24 ";
|
||||
data += " } ";
|
||||
data += " ] ";
|
||||
data += " }, ";
|
||||
data += " { ";
|
||||
data += " \"long1\":1000000000, ";
|
||||
data += " \"data2\":\" \", ";
|
||||
data += " \"grid4\":[ ";
|
||||
data += " { ";
|
||||
data += " \"data5\":\"00033\", ";
|
||||
data += " \"big6\":0.0 ";
|
||||
data += " }, ";
|
||||
data += " { ";
|
||||
data += " \"data5\":\" \", ";
|
||||
data += " \"big6\":123456789012345 ";
|
||||
data += " }, ";
|
||||
data += " { ";
|
||||
data += " \"data5\":\"00035\", ";
|
||||
data += " \"big6\":900000003600011 ";
|
||||
data += " } ";
|
||||
data += " ] ";
|
||||
data += " } ";
|
||||
data += " ] ";
|
||||
data += " } ";
|
||||
data += "} ";
|
||||
data = "{\"EAI_UJSON2DEPTHEXT_REQ1\":{\"abc\":\"cba \",\"grid1\":[{\"long1\":1,\"data2\":\" \",\"grid2\":[{\"data3\":\"12345\",\"big4\":\"14.00000\"},{\"data3\":\"13 \",\"big4\":\"14.00001\"}]},{\"long1\":10000,\"data2\":\" \",\"grid2\":[{\"data3\":\"23 \",\"big4\":\"24.00000\"}]},{\"long1\":1000000000,\"data2\":\" \",\"grid2\":[{\"data3\":\"33 \",\"big4\":\"0.00000\"},{\"data3\":\" \",\"big4\":\"0.00034\"},{\"data3\":\"35 \",\"big4\":\"0.00036\"}]}],\"abcd\":\"123\",\"grid3\":[{\"long1\":1,\"data2\":\" \",\"grid4\":[{\"data5\":\"00023\",\"big6\":\"24\"}]},{\"long1\":1000000000,\"data2\":\" \",\"grid4\":[{\"data5\":\"00033\",\"big6\":\"0\"},{\"data5\":\" \",\"big6\":\"34\"},{\"data5\":\"00035\",\"big6\":\"900000003600011\"}]}]}}";
|
||||
|
||||
System.out.println(JsonBeatiUtil.SimpleBeautiful(data));
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,125 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
// TOUSE
|
||||
// kbbank 어댑터에서 업무데이터의 암호화 필드에 대한 암복호화시
|
||||
// 해당 필드에 대한 get/set 을 수행
|
||||
public class JsonPathUtil {
|
||||
private JsonPathUtil() {
|
||||
|
||||
}
|
||||
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
// 특정 경로의 값을 설정하는 함수
|
||||
public static String setValueAtPath(String jsonString, String path, String newValue, boolean isPretty) {
|
||||
try {
|
||||
// JSON 문자열을 JsonNode 객체로 파싱
|
||||
JsonNode rootNode = objectMapper.readTree(jsonString);
|
||||
|
||||
// 경로에 해당하는 부모 노드를 가져옴
|
||||
JsonNode parentNode = getParentNode(rootNode, getParentPath(path));
|
||||
|
||||
// 경로의 마지막 노드 이름 또는 배열 인덱스 추출
|
||||
String fieldNameOrIndex = getFieldName(path);
|
||||
|
||||
// 배열 또는 객체에서 값을 설정
|
||||
if (parentNode.isArray()) {
|
||||
((ArrayNode) parentNode).set(Integer.parseInt(fieldNameOrIndex), objectMapper.convertValue(newValue, JsonNode.class));
|
||||
} else if (parentNode.isObject()) {
|
||||
((ObjectNode) parentNode).put(fieldNameOrIndex, newValue);
|
||||
}
|
||||
|
||||
// 수정된 JSON 문자열 반환
|
||||
if(isPretty)
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode);
|
||||
else
|
||||
return objectMapper.writeValueAsString(rootNode);
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 특정 경로의 값을 가져오는 함수
|
||||
public static String getValueAtPath(String jsonString, String path) {
|
||||
try {
|
||||
// JSON 문자열을 JsonNode 객체로 파싱
|
||||
JsonNode rootNode = objectMapper.readTree(jsonString);
|
||||
|
||||
// 경로에 해당하는 노드의 값을 반환
|
||||
JsonNode resultNode = getNodeAtPath(rootNode, path);
|
||||
return resultNode != null ? resultNode.asText() : null;
|
||||
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
// 경로에서 부모 경로를 추출하는 유틸리티 함수
|
||||
private static String getParentPath(String path) {
|
||||
int lastSlashIndex = path.lastIndexOf('/');
|
||||
return lastSlashIndex == 0 ? "/" : path.substring(0, lastSlashIndex);
|
||||
}
|
||||
|
||||
// 경로에서 필드 이름 또는 배열 인덱스를 추출하는 유틸리티 함수
|
||||
private static String getFieldName(String path) {
|
||||
int lastSlashIndex = path.lastIndexOf('/');
|
||||
return path.substring(lastSlashIndex + 1);
|
||||
}
|
||||
|
||||
// 특정 경로에 해당하는 부모 노드를 가져오는 유틸리티 함수
|
||||
private static JsonNode getParentNode(JsonNode rootNode, String parentPath) {
|
||||
return getNodeAtPath(rootNode, parentPath);
|
||||
}
|
||||
|
||||
// 특정 경로에 해당하는 노드를 가져오는 유틸리티 함수
|
||||
private static JsonNode getNodeAtPath(JsonNode rootNode, String path) {
|
||||
String[] tokens = path.split("/");
|
||||
JsonNode currentNode = rootNode;
|
||||
|
||||
for (String token : tokens) {
|
||||
if (token.isEmpty()) continue;
|
||||
|
||||
if (token.matches("\\d+")) {
|
||||
// 배열 인덱스 처리
|
||||
currentNode = currentNode.get(Integer.parseInt(token));
|
||||
} else {
|
||||
// 객체 필드 처리
|
||||
currentNode = currentNode.get(token);
|
||||
}
|
||||
|
||||
if (currentNode == null) {
|
||||
return null; // 경로가 존재하지 않음
|
||||
}
|
||||
}
|
||||
return currentNode;
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
String jsonString = "{ \"person\": { \"name\": \"John\", \"age\": 30, \"groups\": [ { \"id\": 1, \"name\": \"Group A\" }, { \"id\": 2, \"name\": \"Group B\" }, { \"id\": 3, \"name\": \"Group C\" } ] } }";
|
||||
|
||||
System.out.println("Source JSON: \n" + jsonString);
|
||||
|
||||
// 경로를 사용하여 값 가져오기 (반복되는 그룹 포함)
|
||||
String getPath = "/person/groups/1/name";
|
||||
String secondGroupName = getValueAtPath(jsonString, getPath);
|
||||
System.out.println("getPath: " + getPath + " -> " + secondGroupName);
|
||||
|
||||
// 경로를 사용하여 값 설정하기 (반복되는 그룹 포함) 및 Pretty 출력
|
||||
String putPath = "/person/groups/2/name";
|
||||
String updatedJsonString = setValueAtPath(jsonString, putPath, "Group Z", true);
|
||||
System.out.println("putPath: " + putPath);
|
||||
System.out.println("Updated JSON (Pretty): \n" + updatedJsonString);
|
||||
|
||||
getPath = "/person/name";
|
||||
System.out.println("getPath: " + getPath + " -> " + getValueAtPath(jsonString, getPath));
|
||||
getPath = "/person/age";
|
||||
System.out.println("getPath: " + getPath + " -> " + getValueAtPath(jsonString, getPath));
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,19 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import java.util.Properties;
|
||||
|
||||
public class PropertiesUtil {
|
||||
/**
|
||||
* Copies a property from source to destination if the key exists in source.
|
||||
*
|
||||
* @param source The source properties.
|
||||
* @param destination The destination properties.
|
||||
* @param key The key to check and copy.
|
||||
*/
|
||||
public static void copyPropertyIfPresent(Properties source, Properties destination, Object key) {
|
||||
Object value = source.get(key);
|
||||
if (value != null) {
|
||||
destination.put(key, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import com.eactive.eai.common.server.EAIServerManager;
|
||||
import com.eactive.eai.common.server.EAIServerVO;
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.Socket;
|
||||
|
||||
public class ServerPingUtil {
|
||||
static Logger logger = Logger.getLogger(Logger.LOGGER_DEFAULT);
|
||||
|
||||
public static boolean pingHost(String host, int port, int timeout) {
|
||||
try (Socket socket = new Socket()) {
|
||||
socket.connect(new InetSocketAddress(host, port), timeout);
|
||||
return true;
|
||||
} catch (IOException e) {
|
||||
logger.warn(String.format("ServerPingUtil] pingHost (host= %s, port=%d) Failed.", host, port));
|
||||
return false; // Either timeout or unreachable or failed DNS lookup.
|
||||
}
|
||||
}
|
||||
|
||||
public static boolean pingHost(String instId) {
|
||||
boolean isAlive = true;
|
||||
EAIServerManager mgr = EAIServerManager.getInstance();
|
||||
EAIServerVO serverVo = mgr.getEAIServer(instId);
|
||||
|
||||
// if not found, server is invalid
|
||||
if (serverVo == null) {
|
||||
logger.warn("ServerPingUtil] invalid eLink instId = " + instId);
|
||||
return false;
|
||||
}
|
||||
|
||||
String address = serverVo.getAddress();
|
||||
int port = Integer.parseInt(serverVo.getPort());
|
||||
isAlive = pingHost(address, port, 3 * 1000);
|
||||
return isAlive;
|
||||
}
|
||||
|
||||
// public static void main(String[] argv) throws Exception {
|
||||
// String host = "localhost";
|
||||
// int port = 10310;
|
||||
// int timeout = 3;
|
||||
//
|
||||
// boolean isAlive = pingHost(host, port, timeout);
|
||||
// System.out.println(">> Server Ping Test ");
|
||||
// System.out.println(String.format("ServerPingUtil] pingHost (host= %s, port=%d) => %s",host, port, isAlive));
|
||||
//
|
||||
// HashMap<String, Integer> map = new HashMap<String, Integer>();
|
||||
//
|
||||
// Integer count = map.get("test");
|
||||
// System.out.println(count);
|
||||
//
|
||||
// map.put("test", 10);
|
||||
// count = map.get("test");
|
||||
// System.out.println(count);
|
||||
//
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,59 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import org.apache.commons.lang3.StringUtils;
|
||||
|
||||
import com.eactive.eai.adapter.socket2.protocol.ByteMessage;
|
||||
import com.eactive.eai.inbound.processor.ProcessVO;
|
||||
|
||||
public class SimHeaderUtil {
|
||||
public static int EAISVCCD_LEN = 30;// eaisvccd
|
||||
public static int BZWKSVCKEY_LEN = 40;// 업무추출키
|
||||
public static int INSTITUTIONCODE_LEN = 10;// 기관코드
|
||||
public static int TEMP_LEN = 20;// 임시
|
||||
|
||||
// TODO : check encoding or remove this class logic
|
||||
private static byte[] toByteArray(Object obj) {
|
||||
byte[] data = null;
|
||||
if (obj instanceof byte[]) {
|
||||
data = (byte[]) obj;
|
||||
} else if (obj instanceof String) {
|
||||
data = ((String) obj).getBytes();
|
||||
}
|
||||
return data;
|
||||
}
|
||||
|
||||
public static byte[] addSimHeader(String eaiSvcCd, String InstitutionCode, Object obj) {
|
||||
byte[] data = toByteArray(obj);
|
||||
|
||||
ByteMessage bm = new ByteMessage();
|
||||
bm.append(StringUtils.rightPad(eaiSvcCd, EAISVCCD_LEN));
|
||||
bm.append(StringUtils.rightPad("", BZWKSVCKEY_LEN));
|
||||
bm.append(StringUtils.rightPad(InstitutionCode, INSTITUTIONCODE_LEN));
|
||||
bm.append(StringUtils.rightPad("", TEMP_LEN));
|
||||
bm.append(data);
|
||||
return bm.getBytes();
|
||||
}
|
||||
|
||||
// 시뮬레이터에서 온 데이터중 SIM Header 를 vo에 셋팅
|
||||
public static ProcessVO getSimHeader(ProcessVO vo, byte[] obj) throws Exception {
|
||||
byte[] data = toByteArray(obj);
|
||||
ByteMessage bm = new ByteMessage(data);
|
||||
String eaiSvcCdKey = new String(bm.getBytes(0, EAISVCCD_LEN));
|
||||
String simKey = new String(bm.getBytes(EAISVCCD_LEN, BZWKSVCKEY_LEN));
|
||||
String instcdKey = new String(bm.getBytes(EAISVCCD_LEN + BZWKSVCKEY_LEN, INSTITUTIONCODE_LEN));
|
||||
String tempKey = new String(bm.getBytes(EAISVCCD_LEN + BZWKSVCKEY_LEN + INSTITUTIONCODE_LEN, TEMP_LEN));
|
||||
|
||||
vo.setSelectedKey(simKey.trim());
|
||||
vo.setInAdapterOsidInstiNo(instcdKey.trim());
|
||||
vo.setEaiSvcCd(eaiSvcCdKey.trim());
|
||||
|
||||
return vo;
|
||||
}
|
||||
|
||||
// 시뮬레이터에서 온 데이터중 SIM Header제거 하고 데이터만 추출
|
||||
public static byte[] getSimData(Object obj) {
|
||||
byte[] data = toByteArray(obj);
|
||||
ByteMessage bm = new ByteMessage((byte[]) data);
|
||||
return bm.getBytes(EAISVCCD_LEN + BZWKSVCKEY_LEN + INSTITUTIONCODE_LEN + TEMP_LEN);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,14 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import org.apache.commons.lang.StringUtils;
|
||||
|
||||
public class TestModeChecker {
|
||||
|
||||
private TestModeChecker() {
|
||||
|
||||
}
|
||||
|
||||
public static boolean isTestMode() {
|
||||
return StringUtils.equals(System.getProperty("use.test.trust", "n"), "y");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,27 @@
|
||||
package com.eactive.eai.util;
|
||||
|
||||
import java.net.MalformedURLException;
|
||||
import java.net.URL;
|
||||
|
||||
public class UrlUtil {
|
||||
public static String replaceIpAddress(String httpUrl, String newIp, String newPort) {
|
||||
try {
|
||||
URL url = new URL(httpUrl);
|
||||
String host = url.getHost();
|
||||
int port = url.getPort();
|
||||
String newUrl = "";
|
||||
if (port == -1) {
|
||||
newUrl = httpUrl.replace(host, newIp + ":" + newPort);
|
||||
} else {
|
||||
newUrl = httpUrl.replace(host + ":" + port, newIp + ":" + newPort);
|
||||
}
|
||||
return newUrl;
|
||||
} catch (MalformedURLException e) {
|
||||
return httpUrl;
|
||||
}
|
||||
}
|
||||
// public static void main(String[] args) throws Exception {
|
||||
// System.out.println(
|
||||
// replaceIpAddress("http://i-ada.postbank.go.kr:123/web/jmd/ONL/EAI/abcd", "192.168.0.1", "5555"));
|
||||
// }
|
||||
}
|
||||
@@ -0,0 +1,75 @@
|
||||
package com.eactive.eai.util.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
import com.eactive.eai.util.json.transformer.ValueTransformer;
|
||||
import com.fasterxml.jackson.core.JsonProcessingException;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.jayway.jsonpath.Configuration;
|
||||
import com.jayway.jsonpath.DocumentContext;
|
||||
import com.jayway.jsonpath.JsonPath;
|
||||
import com.jayway.jsonpath.spi.json.JacksonJsonNodeJsonProvider;
|
||||
import com.jayway.jsonpath.spi.mapper.JacksonMappingProvider;
|
||||
|
||||
public class JsonPathsTransform {
|
||||
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static <T, R> String modifyValuesAtPaths(String jsonString,
|
||||
Map<String, ValueTransformer<T, R>> pathTransformerMap, boolean isPretty) {
|
||||
String path = null;
|
||||
|
||||
// DocumentContext documentContext = JsonPath.parse(jsonString);
|
||||
|
||||
// 변경헤도 별차이가 없음.
|
||||
Configuration conf = Configuration.builder()
|
||||
.jsonProvider(new JacksonJsonNodeJsonProvider())
|
||||
.mappingProvider(new JacksonMappingProvider())
|
||||
.build();
|
||||
DocumentContext documentContext = JsonPath.using(conf).parse(jsonString);
|
||||
|
||||
for (Map.Entry<String, ValueTransformer<T, R>> entry : pathTransformerMap.entrySet()) {
|
||||
try {
|
||||
path = entry.getKey();
|
||||
ValueTransformer<T, R> transformer = entry.getValue();
|
||||
|
||||
// 현재 값 추출
|
||||
T currentValue = (T) documentContext.read(path, String.class);
|
||||
|
||||
// 변환기 통해 새 값 생성
|
||||
R newValue = transformer.transform(currentValue);
|
||||
// 새로운 값으로 설정
|
||||
documentContext.set(path, newValue);
|
||||
|
||||
if (logger.isDebug()) {
|
||||
// FIXME : kbank - 로그내용 수정
|
||||
String encLog = transformer.getClass().getName() + " : "
|
||||
+ String.format("path=%s, orgText[%s] plain[%s] newText[%s]", path, currentValue,
|
||||
transformer.getProperty("PLAIN_VALUE"), newValue);
|
||||
System.out.println(encLog);
|
||||
logger.debug(encLog);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
logger.error("SKIP ERROR jsonpath=" + path, e);
|
||||
}
|
||||
}
|
||||
return printJson(documentContext.jsonString(), isPretty);
|
||||
|
||||
}
|
||||
|
||||
// JSON을 pretty하게 출력하는 메서드
|
||||
private static String printJson(String jsonString, boolean isPretty) {
|
||||
try {
|
||||
Object jsonObject = objectMapper.readValue(jsonString, Object.class);
|
||||
if (isPretty) {
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(jsonObject);
|
||||
} else {
|
||||
return objectMapper.writeValueAsString(jsonObject);
|
||||
}
|
||||
} catch (JsonProcessingException e) {
|
||||
return jsonString;
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,108 @@
|
||||
package com.eactive.eai.util.json;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
import com.eactive.eai.common.util.Logger;
|
||||
import com.eactive.eai.util.json.transformer.ValueTransformer;
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import com.fasterxml.jackson.databind.node.ArrayNode;
|
||||
import com.fasterxml.jackson.databind.node.ObjectNode;
|
||||
|
||||
public class JsonSimplePathsTransform {
|
||||
static Logger logger = Logger.getLogger(Logger.LOGGER_ADAPTER);
|
||||
private static final ObjectMapper objectMapper = new ObjectMapper();
|
||||
|
||||
public static <T, R> String modifyValuesAtPaths(String jsonString,
|
||||
Map<String, ValueTransformer<T, R>> pathTransformerMap, boolean isPretty) {
|
||||
try {
|
||||
JsonNode rootNode = objectMapper.readTree(jsonString);
|
||||
|
||||
for (Map.Entry<String, ValueTransformer<T, R>> entry : pathTransformerMap.entrySet()) {
|
||||
String path = entry.getKey();
|
||||
ValueTransformer<T, R> transformer = entry.getValue();
|
||||
|
||||
JsonNode currentNode = getNodeAtPath(rootNode, path);
|
||||
if (currentNode != null && currentNode.isValueNode()) {
|
||||
T currentValue = extractValue(currentNode);
|
||||
R newValue = transformer.transform(currentValue);
|
||||
|
||||
if(logger.isDebug()) {
|
||||
// FIXME : kbank - 로그내용 수정
|
||||
String encLog = transformer.getClass().getName() +" : "+ String.format("path=%s, orgText[%s] plain[%s] newText[%s]", path, currentValue, transformer.getProperty("PLAIN_VALUE"), newValue);
|
||||
System.out.println(encLog);
|
||||
logger.debug(encLog);
|
||||
}
|
||||
setNodeValue(getParentNode(rootNode, getParentPath(path)), getFieldName(path), newValue);
|
||||
}
|
||||
}
|
||||
if(isPretty) {
|
||||
return objectMapper.writerWithDefaultPrettyPrinter().writeValueAsString(rootNode);
|
||||
}
|
||||
else {
|
||||
return objectMapper.writeValueAsString(rootNode);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
e.printStackTrace();
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static <T> T extractValue(JsonNode node) {
|
||||
if (node.isTextual()) {
|
||||
return (T) node.asText();
|
||||
} else if (node.isInt()) {
|
||||
return (T) (Integer) node.asInt();
|
||||
} else if (node.isDouble()) {
|
||||
return (T) (Double) node.asDouble();
|
||||
} else if (node.isBoolean()) {
|
||||
return (T) (Boolean) node.asBoolean();
|
||||
} else {
|
||||
return (T) node.asText();
|
||||
}
|
||||
}
|
||||
|
||||
private static <R> void setNodeValue(JsonNode parentNode, String fieldNameOrIndex, R newValue) {
|
||||
if (parentNode.isArray()) {
|
||||
((ArrayNode) parentNode).set(Integer.parseInt(fieldNameOrIndex),
|
||||
objectMapper.convertValue(newValue, JsonNode.class));
|
||||
} else if (parentNode.isObject()) {
|
||||
((ObjectNode) parentNode).set(fieldNameOrIndex, objectMapper.convertValue(newValue, JsonNode.class));
|
||||
}
|
||||
}
|
||||
|
||||
private static String getParentPath(String path) {
|
||||
int lastSlashIndex = path.lastIndexOf('/');
|
||||
return lastSlashIndex == 0 ? "/" : path.substring(0, lastSlashIndex);
|
||||
}
|
||||
|
||||
private static String getFieldName(String path) {
|
||||
int lastSlashIndex = path.lastIndexOf('/');
|
||||
return path.substring(lastSlashIndex + 1);
|
||||
}
|
||||
|
||||
private static JsonNode getParentNode(JsonNode rootNode, String parentPath) {
|
||||
return getNodeAtPath(rootNode, parentPath);
|
||||
}
|
||||
|
||||
private static JsonNode getNodeAtPath(JsonNode rootNode, String path) {
|
||||
String[] tokens = path.split("/");
|
||||
JsonNode currentNode = rootNode;
|
||||
|
||||
for (String token : tokens) {
|
||||
if (token.isEmpty())
|
||||
continue;
|
||||
|
||||
if (token.matches("\\d+")) {
|
||||
currentNode = currentNode.get(Integer.parseInt(token));
|
||||
} else {
|
||||
currentNode = currentNode.get(token);
|
||||
}
|
||||
|
||||
if (currentNode == null) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
return currentNode;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
package com.eactive.eai.util.json.transformer;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
|
||||
public class CustomValueTransformer implements ValueTransformer<String, String> {
|
||||
|
||||
private final Map<String, Object> properties = new HashMap<>();
|
||||
|
||||
@Override
|
||||
public void setProperty(String propertyName, Object value) {
|
||||
properties.put(propertyName, value);
|
||||
}
|
||||
@Override
|
||||
public String getProperty(String propertyName) {
|
||||
return (String) properties.get(propertyName);
|
||||
}
|
||||
|
||||
@Override
|
||||
public String transform(String value) {
|
||||
// 예: 변환할 때 "prefix"와 "suffix" 속성을 사용
|
||||
String prefix = (String) properties.getOrDefault("PREFIX", "");
|
||||
String suffix = (String) properties.getOrDefault("SUFFIX", "");
|
||||
properties.put("PLAIN_VALUE", value);
|
||||
return prefix + value + suffix;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,7 @@
|
||||
package com.eactive.eai.util.json.transformer;
|
||||
|
||||
public interface ValueTransformer<T, R> {
|
||||
void setProperty(String propertyName, Object value);
|
||||
String getProperty(String propertyName);
|
||||
R transform(T value) throws Exception;
|
||||
}
|
||||
Reference in New Issue
Block a user