반응형

자바8의 스트림 API 특징

선언형: 더 간결하고 가독성이 좋아진다.
조립할수있음: 유연성이 좋아진다.
병렬화: 성능이 좋아진다.

스트림이란 '데이터 처리 연산을 지원하도록 소스에서 추출된 연속된 요소'로 정의할 수 있다.
[딱 한번만 탐색할 수 있다]

스트림은 단 한번만 소비할 수 있다.

스트림 연산

java.util.stream.Stream 인터페이스는 많은 연산을 정의

filter, map, limit는 서로 연결되어 파이프라인을 형성한다. - 중간연산
collect로 파이프라인을 실행한 다음에 닫는다. - 최종연산

중간연산

filter나 sorted 같은 중간 연산은 다른 스트림을 반환
중간 연산의 중요한 특징은 단말 연산을 스트림 파이프라인에 실행하기 전까지는 아무 연산도 수행하지 않는다는 것이다.
즉 lazy하다는 것이다

최종연산

보통 최종 연산에 의해 List, Integer, void 등 스트림 이외의 결과가 반환.
forEach, count, collect처럼 스트림 파이프라인을 처리해서 스트림이 아닌 결과를 반환하는 연산을 최종 연산

반응형
반응형

LocalDate

현재시간

LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));

스트링 date 파싱하기

String date parse

LocalDate endDate = LocalDate.parse(endTime, DateTimeFormatter.ofPattern("yyyyMMdd"));

 

 

LocalDateTime

현재시간 알기

LocalDateTime.now().format(DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

스트링 date 파싱하기

String date parse

LocalDateTime endDateTime = LocalDateTime.parse(endTime, DateTimeFormatter.ofPattern("yyyyMMddHHmmss"));

날짜 데이터로 반환

LocalDate targetDate = LocalDate.of(2019,11,12);
 //결과 : 2019-11-12

 

mybatis-typehandlers-jsr310 아티팩트는 데이터베이스 컬럼의 날짜/시간 타입을 

Java 8부터 추가된 LocalDateLocalTimeLocalDateTimeYearMonth 클래스로의 자동 맵핑을 지원한다. 

 

MyBatis 3.4 버전부터 자동 지원되며 이전 버전은 별도의 typeHandler 등록이 필요하다.

3.4 이후부터는 별도로 등록없이 사용가능하다.

type 핸들러에 localDate 관련된 클래스 있는지 확인해보면 된다.

 

 

반응형
반응형

스프링에서 okhttp3 를 사용중 로그에 다음과 같은 오류 메시지가 떴다.

 

okhttp3 사용 중에 간헐적으로 unexpected end of stream on [주소] 가 나왔다.

 

request 를 요청받는 서버 설정마다 다른 것 같다.

해당 원인은

요청받는 서버의 keep_alive timeout 이 okhttp 로 요청하는 클라이언트 서버보다 timeout 시간이 낫기 떄문이다.

다시 말하면 클라이언트 서버가 timeout 이 받아주는 서버 timout 보다 길다.

 

나의 경우는 Okhttp3 로 요청하고 나서 프로세스가 조금 긴 경우에 다음과 같은 현상이 나왔다.

 

클라이언트가 서버에서 데이터를 받고나서 서버의 keep_alive timeout 시간이 지났음에도 클라이언트에서는 프로세스가 끝나지 않기 때문에 발생한다.

이런 경우 간단하게 해결 방법은 okhttp request 객체에 

addHeader("Connection","close") 

만 추가해주면 됐다.

 

Request request = new Request.Builder()
                .url(requestUrl)
                .addHeader("Connection", "close")
                .get()
                .build();

 

 

 

나의 경우 서버에서 데이터를 받아와서 처리하는데 많은 로직이 있어 처리시간이 짧지 않았고, 프로세스를 처리 하는 도중 요청받는 서버의 keep_alive timeout 시간이 지나 다음 오류를 뱉었다.

데이터를 받아 온 후라 다음 오류가 나도 상관없었지만, 오류를 모니터링 할때 보기 안좋기 때문에 해결해 주었다.

 

예를 들어 keep_alive timeout 이 긴 서버의 경우는 해당 오류가 나지 않을 것이다.

 

참고문헌

github.com/square/okhttp/issues/2738

 

반응형
반응형

Gson 라이브러리 및 Jackson 사용하여 Json String 을 만들때 Date 클래스 및 LocalDateTime 항목이 있는 class 으로 json string 으로 변환하게 되면 변환된 값이 yyyy-MM-dd'T'HH:mm:ss 이러한 포맷으로 변환이 되지 않는다.

 

이런 경우 모두 커스텀을 해줘야 한다.

 

Jackson 

Date with Jackson

public class ItemDate {

  private Integer id; 
  private String name; 
  private String createBy; 
  @JsonFormat(shape= JsonFormat.Shape.STRING, pattern="yyyy-MM-dd'T'HH:mm:ss.SSSZ", timezone="Asia/Seoul") 
  private Date createAt; 
    
}

 

이렇게 하면 "2019-05-15T11:23:10.108+0900" 와 같은 문자열로 변환이된다.

LocalDateTime with Jackson

public class ItemLocalDateTime { 
  private Integer id; 
  private String name; 
  private String createBy; 
  JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS") 
  @JsonDeserialize(using = LocalDateTimeDeserializer.class) 
  @JsonSerialize(using = LocalDateTimeSerializer.class) 
  private LocalDateTime createAt; 
}

이렇게 하면 JSON에서는 "2019-05-15T11:24:46.223" 와 같은 문자열로 변환이된다.

참고로, TimeZone을 생략했는데, Z를 붙이면 Unsupported field: OffsetSeconds 라는 예외가 발생한다.

지역 시간은 시간대 필드를 가지고 있지 않기 때문이다. 이것은 ZoneZonedDateTime에 대응하기 때문이다.

 

Gson

Gson의 경우 어노테이션이 아니라 GsonBuilder 로 타입을 지정해야한다.

registerTypeAdapter 을 통해 커스텀이 가능하므로 class 를 따로 만들어준다.

Date with Gson

class GsonDateConverter implements JsonSerializer<Date>, JsonDeserializer<Date> { 
private static final String FORMAT = "yyyy-MM-dd'T'HH:mm:ss.SSSZ"; 
	@Override 
    public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext context) { 
    	SimpleDateFormat simpleDateFormat = new SimpleDateFormat(FORMAT); return src == null ? null : new JsonPrimitive(simpleDateFormat.format(src)); 
  	} 

	@Override 
    public Date deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException { 
    	SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSSZ"); 
        try { return json == null ? null : simpleDateFormat.parse(json.getAsString()); } 
        catch (ParseException e) { throw new JsonParseException(e); } 
    } 
}

LocalDateTime with Gson

public class GsonLocalDateTimeAdapter implements JsonSerializer<LocalDateTime>, JsonDeserializer<LocalDateTime> {
    @Override public JsonElement serialize(LocalDateTime localDateTime, Type srcType, JsonSerializationContext context) {
        return new JsonPrimitive(DateTimeFormatter.ISO_LOCAL_DATE_TIME.format(localDateTime));
    }

    @Override public LocalDateTime deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
        return LocalDateTime.parse(json.getAsString(), DateTimeFormatter.ISO_LOCAL_DATE_TIME);
    }
}

사용

Gson gson = new GsonBuilder().registerTypeAdapter(LocalDateTime.class, new GsonLocalDateTimeAdapter())
			.registerTypeAdapter(LocalDate.class, new GsonLocalDateAdapter()).create();

다음처럼  변환하고자 하는 데이터타입 클래스를 어댑터와 같이 registerTypeAdpater 메소드에 추가하면 된다.

 

반응형
반응형

java 에서 파싱을 해주는 라이브러리가 있는데 대표적인 라이브러리고 gson 과 jackson 있다.

gson 을 사용시 기본적으로 new Gson().toJson() 및 fromJson() 으로 기본적인 파싱이 되지만, 종종 커스텀이 필요한 경우가 있다,

이런 경우 Desrializer 및 Serializer 기능이 필요하다.

1. Custom Serialization

Serialization 은 toJson 할 경우 필요하다. 자바 객체를 Json 으로 변환 시 필요하다.

예시

Serialization 생성

public class BooleanSerializer implements JsonSerializer<Boolean> {

    public JsonElement serialize(Boolean aBoolean, Type type,
        JsonSerializationContext jsonSerializationContext) 
    {
        if(aBoolean){
           return new JsonPrimitive(1);
        }
        return new JsonPrimitive(0);
    }
}

사용

    public static void main(String[] args) throws Exception 
    {
        Employee emp = new Employee(1, "Lokesh", "Gupta", "howtodoinjava@gmail.com", true);

        Gson gson = new GsonBuilder()
                .registerTypeAdapter(Boolean.class, new BooleanSerializer())
                .setPrettyPrinting()
                .create();

        String json = gson.toJson(emp);

        System.out.println(json);
    }

결과물

{
  "id": 1,
  "firstName": "Lokesh",
  "lastName": "Gupta",
  "email": "howtodoinjava@gmail.com",
  "active": 1
}

다음과 같이 boolean 이 1또는 0 으로 Json 변환시 사용하려면 다음과 같이 사용하면 된다.

반대로 1또는 0을 true 또는 false 로 변환하려면 JsonDeserializer 을 반대로 implements 를 하면 된다.

이 떄 registerTypeAdapter 메소드는 빌더 타입이라 추가로 클래스를 붙이고 싶은 경우가 있으면 추가하면 된다.

날짜를 gson으로 Serialize 하기

또 사용해야 하는 이유는 날짜 Date 클래스를 gson 으로 Json String 을 만들시

Aug 31, 2020 10:26:17 처럼 날짜가 표시된다. 하지만 2020-08-31 10:26:17 이렇게 변환되기를 바랄것이다.

JsonSerializer<Date> ser = new JsonSerializer<Date>() {
    @Override
    public JsonElement serialize(Date src, Type typeOfSrc, JsonSerializationContext
                context) {
        return src == null ? null : new JsonPrimitive(src.getTime());
    }
};

JsonDeserializer<Date> deser = new JsonDeserializer<Date>() {
    @Override
    public Date deserialize(JsonElement json, Type typeOfT,
                JsonDeserializationContext context) throws JsonParseException {
        return json == null ? null : new Date(json.getAsLong());
    }
};

Gson gson = new GsonBuilder()
                .registerTypeAdapter(Date.class, ser)
                .registerTypeAdapter(Date.class, deser)
                .create();

다음과 같이 익명클래스 로 객체 선언 후 사용 가능 하다.

 

2. Custom Deserialization

반대로 json String 을 객체로 변환시 아래와 같이 사용하면 된다.

json 문자열을 객체로 파싱할때 빈 문자열을 null 로 치환

public class EmptyToNullStringDeserializerGson implements JsonDeserializer<String> {

	@Override
	public String deserialize(JsonElement json, Type typeOfT, JsonDeserializationContext context) throws JsonParseException {
		String nullCheckString = json.getAsJsonPrimitive().getAsString();
		return "".equals(nullCheckString) ? null : nullCheckString;  
	}

}

사용법

GsonBuilder gb = new GsonBuilder();
gb.registerTypeAdapter(String.class, new EmptyToNullStringDeserializerGson());
Gson gson = gb.create();

클래스 response =  gson.fromJson(jsonStr, 클래스명.class);

 

 

참고문헌

https://riptutorial.com/android/example/15339/custom-json-deserializer-using-gson
https://futurestud.io/tutorials/gson-advanced-custom-deserialization-basics
https://howtodoinjava.com/gson/custom-serialization-deserialization/

반응형
반응형

1. gzip 압축하기

public byte[] compress(String value) throws Exception {

    ByteArrayOutputStream byteArrayOutputStream = new ByteArrayOutputStream();
    GZIPOutputStream gzipOutStream =new GZIPOutputStream(
             new BufferedOutputStream(byteArrayOutputStream));
    gzipOutStream.write(value.getBytes());
    gzipOutStream.finish();
    gzipOutStream.close();

    return byteArrayOutputStream.toByteArray();
}
public static byte[] compress(final String str) throws IOException {
    if ((str == null) || (str.length() == 0)) {
      return null;
    }
    ByteArrayOutputStream obj = new ByteArrayOutputStream();
    GZIPOutputStream gzip = new GZIPOutputStream(obj);
    gzip.write(str.getBytes("UTF-8"));
    gzip.flush();
    gzip.close();
    return obj.toByteArray();
  }

 

2. gzip 압축 풀기

public String decompress(byte[] value) throws Exception {

    ByteArrayOutputStream outStream = new ByteArrayOutputStream();

    GZIPInputStream gzipInStream = new GZIPInputStream(
                new BufferedInputStream(new ByteArrayInputStream(value)));

    int size = 0;
    byte[] buffer = new byte[1024];
    while ( (size = gzipInStream.read(buffer)) > 0 ) {
        outStream.write(buffer, 0, size);
    }
    outStream.flush();
    outStream.close();

    return new String(outStream.toByteArray());
}
public static String decompress(final byte[] compressed) throws IOException {
    final StringBuilder outStr = new StringBuilder();
    if ((compressed == null) || (compressed.length == 0)) {
      return "";
    }
    if (isCompressed(compressed)) {
      final GZIPInputStream gis = new GZIPInputStream(new ByteArrayInputStream(compressed));
      final BufferedReader bufferedReader = new BufferedReader(new InputStreamReader(gis, "UTF-8"));

      String line;
      while ((line = bufferedReader.readLine()) != null) {
        outStr.append(line);
      }
    } else {
      outStr.append(compressed);
    }
    return outStr.toString();
  }

  public static boolean isCompressed(final byte[] compressed) {
    return (compressed[0] == (byte) (GZIPInputStream.GZIP_MAGIC)) && (compressed[1] == (byte) (GZIPInputStream.GZIP_MAGIC >> 8));
  }
반응형
반응형

양방향 암호화 중 하나인 aes_encrypt 에 대해서 구현하겠습니다.

AES 함수는 데이터를 암호화와 복호화를 할 수 있는 양방향 암호화 모듈입니다.

 

mysql에 있는 기본 함수 aes_encrypt 로 암호화를 만드는 법과, 이에 따라 자바 코드에서도 암호화 및 복호화 할 수 있는 코드를 짜겠습니다.

mysql 에 있는 암호화모듈을 java 코드에서 구현해야 할 시점도 있습니다.

예를 들어 jpa 로 배치 insert 를 할 경우 db를 건건이 쿼리를 날리기도 번거롭고 말입니다.

 

그래서 mysql 에서 사용하는 aes_encrypt(f_data,f_key) 에 대한 자바 코드입니다.

 

select aes_encrypt("test", "key") 의 값은

이값은 byte 값이다. String 으로 바꾸기 위해 

select hex(aes_encrypt("test", "key")) 로 hex 도 추가합니다.

 

자바 코드는

public class CrypoUtil {


    /**
     *
     * @param key
     * @param text 암호화할 데이터
     */
    public static String encrypto(String key, String text) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, UnsupportedEncodingException, BadPaddingException, IllegalBlockSizeException {
        final Cipher encryptCipher = Cipher.getInstance("AES");
        encryptCipher.init(Cipher.ENCRYPT_MODE, generateMySQLAESKey(key, "UTF-8"));
        String result = new String(Hex.encodeHex(encryptCipher.doFinal(text.getBytes("UTF-8")))).toUpperCase();

        return result;
    }
    
    /**
     * @param key
     * @param text 암호화 풀 데이터
     */
    public static String decrypto(String key, String text) throws NoSuchPaddingException, NoSuchAlgorithmException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException, UnsupportedEncodingException, DecoderException {
        final Cipher decryptCipher = Cipher.getInstance("AES");
        decryptCipher.init(Cipher.DECRYPT_MODE, generateMySQLAESKey(key, "UTF-8"));
        String result = new String(decryptCipher.doFinal(Hex.decodeHex(text.toCharArray())));

        return result;
    }
    
    public static void main(String[] args) throws NoSuchPaddingException, BadPaddingException, NoSuchAlgorithmException, IllegalBlockSizeException, UnsupportedEncodingException, InvalidKeyException, DecoderException {
        String encrypTxt = encrypto("key", "test");
        System.out.println(encrypTxt);
        String decrypTxt = decrypto("key", encrypTxt);
        System.out.println(decrypTxt);
    }
}

여기서 주의 할 점은 encryptCipher.doFinal(text.getBytes("UTF-8")))) 이 값의 리턴값이 byte 라는 것입니다. 물론 mysql 에서도 aes_encrypt 도 byte 를 리턴해줘서 hex 로 변환해줘서 문자열로 변환해줘야 합니다,

반응형

'Java' 카테고리의 다른 글

[java] java 로 gzip 압축 및 풀기  (0) 2020.08.10
[자바] Intellij 에서 javadoc 문서 만들기  (0) 2020.06.02
junit 사용  (0) 2020.05.08
반응형

Locale 을 ko_KR

arguments 를 -encoding UTF-8 -charset UTF-8 -docencoding UTF-8  
로 설정해주면 된다.

다음과 같이 설정해주면 output directory 경로에 index.html 이 생성되고 파일 클릭하면 자바문서를 볼 수 있다.

반응형

'Java' 카테고리의 다른 글

[java] java 로 gzip 압축 및 풀기  (0) 2020.08.10
[Java] mysql aes_encrypt 를 java 코드로 만들기(AES 암호화)  (0) 2020.07.22
junit 사용  (0) 2020.05.08

+ Recent posts