## 설치 ("--enable-web-dashboard","false")
$ claude mcp add-json "serena" '{"command":"uvx","args":["--from","git+https://github.com/oraios/serena","serena-mcp-server","--enable-web-dashboard","false"]}'
## 삭제
$ claude mcp remove serena
## 설치 ("--enable-web-dashboard","false")
$ claude mcp add-json "serena" '{"command":"uvx","args":["--from","git+https://github.com/oraios/serena","serena-mcp-server","--enable-web-dashboard","false"]}'
## 삭제
$ claude mcp remove serena
@Validated 기능 사용 시 Spring의 AOP Proxy 동과 관련되어 AService 클래스내에 @Autowired BService bService가 null 해결 방법
@Validated
@Service
class AService {
@Autowired
private BService bService;
private Test getTest() {
// bService null인 상황일때
}
}
@ActiveProfiles({ Application.ACTIVE_PROFILE_TEST_CASE })
@SpringBootTest
@ExtendWith(SpringExtension.class)
@TestMethodOrder(MethodOrderer.Alphanumeric.class)
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
@Slf4j
class TestCase {
@MockBean
private BService bService;
@Autowired
private AService aService;
@Test
void test() {
...
BService bService = new BService();
Mockito.when(bService.get1("1")).thenReturn(new Test());
...
ReflectionTestUtils.invokeMethod(AopTestUtils.getTargetObject(aService), AService.class, , "getTest");
}
}Class에 선언되지 않은 필드 정보가 넘어옴(columns 필드 선언되지 않음)
org.jboss.resteasy.spi.ReaderException: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "columns"
...
Caused by: com.fasterxml.jackson.databind.exc.UnrecognizedPropertyException: Unrecognized field "columns"
무시하려면
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
@JsonIgnoreProperties(ignoreUnknown = true)
public class ..... implementation ('org.springframework.boot:spring-boot-starter-jdbc') {
exclude group: 'com.zaxxer', module: 'HikariCP'
}
implementation group: 'org.apache.commons', name: 'commons-dbcp2', version: '2.9.0'
spring:
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3380/testdb
username: root
password: test
dbcp2:
initial-size: 5
max-total: 10
test-while-idle: true
time-between-eviction-runs-millis: 1000
num-tests-per-eviction-run: 1
// https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/aes.js
// https://cdnjs.cloudflare.com/ajax/libs/crypto-js/3.1.2/rollups/pbkdf2.js
var passphrase = "key...변경";
var iv = CryptoJS.lib.WordArray.random(128/8);
var key = CryptoJS.enc.Hex.parse(CryptoJS.SHA1(passphrase).toString().substring(0,32));
console.log("key: ", key)
var ct = CryptoJS.AES.encrypt(str, key, { iv: iv });
var enc = iv.concat(ct.ciphertext).toString();
console.log("enc: ", enc)
## enc: b3ff8dd004bb643f0aba857baccb0d45e1565d2f4a1d727c9a268580d3a2031b
console.log("Result: " + CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Hex.parse(enc.substring(32))
}, CryptoJS.enc.Hex.parse(CryptoJS.SHA1(passphrase).toString().substring(0,32)),
{
iv: CryptoJS.enc.Hex.parse(enc.substring(0,32)),
}).toString(CryptoJS.enc.Utf8));
## Result: abcd
enc = "B8160A9EDCA2CFECE3E6444BFDE09B780D8DD2D873B93080E7F5A6F4B5644217";
console.log("JAVA 에서 암호화한 문자열 복호화 Result: " + CryptoJS.AES.decrypt({
ciphertext: CryptoJS.enc.Hex.parse(enc.substring(32))
}, CryptoJS.enc.Hex.parse(CryptoJS.SHA1(passphrase).toString().substring(0,32)),
{
iv: CryptoJS.enc.Hex.parse(enc.substring(0,32)),
}).toString(CryptoJS.enc.Utf8));
## JAVA 에서 암호화한 문자열 복호화 Result: abcd
// com.google.guava:guava:29.0-jre
import com.google.common.base.Charsets;
import com.google.common.hash.Hashing;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import javax.crypto.Cipher;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import javax.xml.bind.DatatypeConverter;
import java.security.AlgorithmParameters;
import java.util.Arrays;
public class AesCryptUtil {
private static Logger logger = LoggerFactory.getLogger(AesCryptUtil.class);
public static String encryptAES(String data, String secretKey) {
try {
byte[] secretKeys = Arrays.copyOfRange(Hashing.sha1().hashString(secretKey, Charsets.UTF_8).asBytes(), 0, 16);
SecretKey secret = new SecretKeySpec(secretKeys, "AES");
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.ENCRYPT_MODE, secret);
AlgorithmParameters params = cipher.getParameters();
byte[] iv = params.getParameterSpec(IvParameterSpec.class).getIV();
byte[] cipherText = cipher.doFinal(data.getBytes(Charsets.UTF_8));
return DatatypeConverter.printHexBinary(iv) + DatatypeConverter.printHexBinary(cipherText);
} catch (Exception e) {
logger.error("===== exception", e);
throw new RuntimeException(e);
}
}
public static String decryptAES(String data, String secretKey) {
try {
byte[] secretKeys = Arrays.copyOfRange(Hashing.sha1().hashString(secretKey, Charsets.UTF_8).asBytes(), 0, 16);
String hexedIv = data.substring(0, 32);
String hexedCipherText = data.substring(32);
byte[] iv = DatatypeConverter.parseHexBinary(hexedIv);
byte[] cipherText = DatatypeConverter.parseHexBinary(hexedCipherText);
Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(secretKeys, "AES"), new IvParameterSpec(iv));
return new String(cipher.doFinal(cipherText), Charsets.UTF_8);
}catch (Exception e) {
logger.error("===== exception", e);
throw new RuntimeException(e);
}
}
public static void main(String[] args) throws Exception {
String key = "key...변경";
String abcd = AesCryptUtil.encryptAES("abcd", key);
System.out.println("enc: " + abcd);
System.out.println("dec: " + AesCryptUtil.decryptAES(abcd, key));
System.out.println("javascript 암호화한 문자열 복호화 dec: " + AesCryptUtil.decryptAES("b3ff8dd004bb643f0aba857baccb0d45e1565d2f4a1d727c9a268580d3a2031b", key));
}
}
[참고] https://stackoverflow.com/questions/37368710/decrypt-aes-cbc-pkcs5padding-with-cryptojs
Decrypt AES/CBC/PKCS5Padding with CryptoJS
I generate 128bit AES/CBC/PKCS5Padding key using Java javax.crypto API. Here is the algorithm that I use: public static String encryptAES(String data, String secretKey) { try { byte[]
stackoverflow.com
import org.apache.commons.codec.DecoderException;
import org.apache.commons.codec.binary.Hex;
public class Test {
public static void main(String[] args) {
String hexString = "123 abc [0xea][0xb0][0x80][0xeb][0x82][0x98] [0xeb][0x8b][0xa4][0xeb][0x9d][0xbc]";
do {
hexString = hexToString(hexString); // output : 123 abc 가나 다라
} while (hexString.indexOf("[0x") > -1);
System.out.println("en : " + hexString);
}
private static String hexToString(String s) {
try {
int idx = s.indexOf("[0x");
if (idx > -1) {
String t = s.substring(idx, idx + 18);
String c = new String(Hex.decodeHex(t.replaceAll("\\[0x", "").replaceAll("\\]", "").toCharArray()));
s = s.replace(t, c);
}
} catch (DecoderException e) {
throw new RuntimeException(e);
}
return s;
}
} ## HttpClient 4.x
SSLContextBuilder builder = new SSLContextBuilder();
builder.loadTrustMaterial(null, (certificate, authType) -> true);
SSLConnectionSocketFactory sslsf = new SSLConnectionSocketFactory(
builder.build(),
NoopHostnameVerifier.INSTANCE);
httpClientBuilder.setSSLSocketFactory(sslsf);


javax.net.ssl.SSLHandshakeException: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.ssl.Alerts.getSSLException(Alerts.java:192)
at sun.security.ssl.SSLSocketImpl.fatal(SSLSocketImpl.java:1946)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:316)
at sun.security.ssl.Handshaker.fatalSE(Handshaker.java:310)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1639)
at sun.security.ssl.ClientHandshaker.processMessage(ClientHandshaker.java:223)
at sun.security.ssl.Handshaker.processLoop(Handshaker.java:1037)
at sun.security.ssl.Handshaker.process_record(Handshaker.java:965)
at sun.security.ssl.SSLSocketImpl.readRecord(SSLSocketImpl.java:1064)
at sun.security.ssl.SSLSocketImpl.performInitialHandshake(SSLSocketImpl.java:1367)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1395)
at sun.security.ssl.SSLSocketImpl.startHandshake(SSLSocketImpl.java:1379)
at sun.net.www.protocol.https.HttpsClient.afterConnect(HttpsClient.java:559)
at sun.net.www.protocol.https.AbstractDelegateHttpsURLConnection.connect(AbstractDelegateHttpsURLConnection.java:185)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream0(HttpURLConnection.java:1564)
at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1492)
at java.net.HttpURLConnection.getResponseCode(HttpURLConnection.java:480)
at sun.net.www.protocol.https.HttpsURLConnectionImpl.getResponseCode(HttpsURLConnectionImpl.java:347)
at UrlConTest.main(UrlConTest.java:15)
Caused by: sun.security.validator.ValidatorException: PKIX path building failed: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:397)
at sun.security.validator.PKIXValidator.engineValidate(PKIXValidator.java:302)
at sun.security.validator.Validator.validate(Validator.java:262)
at sun.security.ssl.X509TrustManagerImpl.validate(X509TrustManagerImpl.java:324)
at sun.security.ssl.X509TrustManagerImpl.checkTrusted(X509TrustManagerImpl.java:229)
at sun.security.ssl.X509TrustManagerImpl.checkServerTrusted(X509TrustManagerImpl.java:124)
at sun.security.ssl.ClientHandshaker.serverCertificate(ClientHandshaker.java:1621)
... 14 more
Caused by: sun.security.provider.certpath.SunCertPathBuilderException: unable to find valid certification path to requested target
at sun.security.provider.certpath.SunCertPathBuilder.build(SunCertPathBuilder.java:141)
at sun.security.provider.certpath.SunCertPathBuilder.engineBuild(SunCertPathBuilder.java:126)
at java.security.cert.CertPathBuilder.build(CertPathBuilder.java:280)
at sun.security.validator.PKIXValidator.doBuild(PKIXValidator.java:392)
... 20 more
import java.io.IOException;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.Paths;
import java.util.stream.Stream;
public class TextTest {
public static void main(String[] args) {
Path path1 = Paths.get("test.log");
Path path2 = Paths.get("test.log-2");
try (Stream stream = Files.lines(path1)) {
Files.write(path2, (Iterable)stream.filter(s->s.trim().startsWith("{")).filter(s->s.trim().endsWith("}"))::iterator);
} catch (IOException e) {
e.printStackTrace();
}
}
}
json-lib, com.fasterxml.jackson 둘의 차이점을 비교해본다.
별다른 옵션 없는 심플한 상황이다.
json-lib의 경우 Object형의 NULL 변환에 문제가 있어보인다.
[JsonTest .java]
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.io.IOException;
import org.junit.Test;
import com.fasterxml.jackson.databind.ObjectMapper;
import net.sf.json.JSONObject;
import net.sf.json.JSONSerializer;
public class JsonTest {
@Test
public void test_jsonlib_BeanCase() {
try {
TestBean inBean = new TestBean();
inBean.setA("a Value");
inBean.setB("");
inBean.setC(null);
//inBean.setD();
inBean.setInt11(1);
//inBean.setInt12();
inBean.setInt21(1);
inBean.setInt21(null);
inBean.setBool11(true);
//inBean.setBool12();
inBean.setBool21(true);
inBean.setBool21(null);
JSONObject jsonObj = JSONObject.fromObject( JSONSerializer.toJSON(inBean) );
String jsonStr = jsonObj.toString();
System.out.println("jsonStr: " + jsonStr);
jsonObj = (JSONObject) JSONObject.fromObject(jsonStr);
TestBean outBean = (TestBean) JSONObject.toBean(jsonObj, TestBean.class);
System.out.println(outBean);
assertEquals(inBean.getA(), outBean.getA());
assertEquals(inBean.getB(), outBean.getB());
//assertEquals(inBean.getC(), outBean.getC()); // 문제 소지
//assertEquals(inBean.getD(), outBean.getD()); // 문제 소지
assertEquals(outBean.getC(), "");
assertEquals(outBean.getD(), "");
assertEquals(inBean.getInt11(), outBean.getInt11());
assertEquals(inBean.getInt12(), outBean.getInt12());
//assertEquals(inBean.getInt21(), outBean.getInt21()); // 문제 소지(아예 변환 안됨)
//assertEquals(inBean.getInt22(), outBean.getInt22()); // 문제 소지(아예 변환 안됨)
assertEquals(outBean.getInt21(), new Integer(0));
assertEquals(outBean.getInt22(), new Integer(0));
assertEquals(inBean.isBool11(), outBean.isBool11());
assertEquals(inBean.isBool12(), outBean.isBool12());
//assertEquals(inBean.getBool21(), outBean.getBool21()); // 문제 소지(아예 변환 안됨)
//assertEquals(inBean.getBool22(), outBean.getBool22()); // 문제 소지(아예 변환 안됨)
assertEquals(outBean.getBool21(), new Boolean(false));
assertEquals(outBean.getBool22(), new Boolean(false));
} catch (Exception e) {
System.err.println("-- e: " + e);
}
}
@Test
public void test_jackson_BeanCase() {
try {
ObjectMapper jacksonMapper = new ObjectMapper();
TestBean inBean = new TestBean();
inBean.setA("a Value");
inBean.setB("");
inBean.setC(null);
//inBean.setD();
inBean.setInt11(1);
//inBean.setInt12();
inBean.setInt21(1);
inBean.setInt21(null);
inBean.setBool11(true);
//inBean.setBool12();
inBean.setBool21(true);
inBean.setBool21(null);
System.out.println(inBean);
String jsonStr = jacksonMapper.writeValueAsString(inBean);
System.out.println("jsonStr: " + jsonStr);
TestBean outBean = jacksonMapper.readValue(jsonStr, TestBean.class);
assertEquals(inBean.getA(), outBean.getA());
assertEquals(inBean.getB(), outBean.getB());
assertEquals(inBean.getC(), outBean.getC());
assertEquals(inBean.getD(), outBean.getD());
assertEquals(outBean.getC(), null);
assertEquals(outBean.getD(), null);
assertEquals(inBean.getInt11(), outBean.getInt11());
assertEquals(inBean.getInt12(), outBean.getInt12());
assertEquals(inBean.getInt21(), outBean.getInt21());
assertEquals(inBean.getInt22(), outBean.getInt22());
assertEquals(inBean.isBool11(), outBean.isBool11());
assertEquals(inBean.isBool12(), outBean.isBool12());
assertEquals(inBean.getBool21(), outBean.getBool21());
assertEquals(inBean.getBool22(), outBean.getBool22());
} catch (IOException e) {
System.err.println("-- e: " + e);
}
}
}
[TestBean.java]
import lombok.Getter;
import lombok.Setter;
@Setter @Getter
public class TestBean {
String a;
String b;
String c;
String d;
int int11;
int int12;
Integer int21;
Integer int22;
boolean bool11;
boolean bool12;
Boolean bool21;
Boolean bool22;
}
[build.gradle]
sourceCompatibility='1.7'
dependencies {
//compile group: 'commons-collections', name: 'commons-collections', version: '3.2'
//compile 'org.apache.maven.plugins:maven-war-plugin:+'
compile 'net.sf.json-lib:json-lib:2.4:jdk15@jar'
compile 'com.fasterxml.jackson.dataformat:jackson-dataformat-xml:2.6.4'
compile 'commons-lang:commons-lang:2.6'
compile 'commons-logging:commons-logging:1.2'
compile 'commons-collections:commons-collections:3.2.2'
compile 'commons-beanutils:commons-beanutils:1.9.2'
compile 'net.sf.ezmorph:ezmorph:1.0.6'
testCompile 'junit:junit:4.11'
testCompile 'org.hamcrest:hamcrest-all:1.3'
}
import org.apache.commons.lang.math.NumberUtils;
import org.apache.commons.lang.StringUtils;
// 자연수 + 공백
assertFalse(StringUtils.isNumeric(null));
assertTrue (StringUtils.isNumeric(""));
assertFalse(StringUtils.isNumeric(" "));
assertTrue (StringUtils.isNumeric("0"));
assertTrue (StringUtils.isNumeric("123"));
assertFalse(StringUtils.isNumeric("12 3"));
assertFalse(StringUtils.isNumeric("ab2c"));
assertFalse(StringUtils.isNumeric("12-3"));
assertFalse(StringUtils.isNumeric("12.3"));
assertFalse(StringUtils.isNumeric("-123"));
assertFalse(StringUtils.isNumeric("+123"));
// 자연수
assertFalse(NumberUtils.isDigits(null));
assertFalse(NumberUtils.isDigits(""));
assertFalse(NumberUtils.isDigits(" "));
assertTrue (NumberUtils.isDigits("0"));
assertTrue (NumberUtils.isDigits("123"));
assertFalse(NumberUtils.isDigits("12 3"));
assertFalse(NumberUtils.isDigits("ab2c"));
assertFalse(NumberUtils.isDigits("12-3"));
assertFalse(NumberUtils.isDigits("12.3"));
assertFalse(NumberUtils.isDigits("-123"));
assertFalse(NumberUtils.isDigits("+123"));
// 정수 + 실수 (+ 앞에 붙으면 안됨)
assertFalse(NumberUtils.isNumber(null));
assertFalse(NumberUtils.isNumber(""));
assertFalse(NumberUtils.isNumber(" "));
assertTrue (NumberUtils.isNumber("0"));
assertTrue (NumberUtils.isNumber("123"));
assertFalse(NumberUtils.isNumber("12 3"));
assertFalse(NumberUtils.isNumber("ab2c"));
assertFalse(NumberUtils.isNumber("12-3"));
assertTrue (NumberUtils.isNumber("12.3"));
assertTrue (NumberUtils.isNumber("-123"));
assertFalse(NumberUtils.isNumber("+123"));