## 설치 ("--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 (Streamstream = 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"));