728x90

https://community.letsencrypt.org/t/android-7-0-cant-establish-ssl-connection/152050

 

Android 7.0 can't establish ssl connection

My domain is: starline.ru I ran this command: android 7.0 can't connect It produced this output: Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x76a8eb8a00: Failure in SSL library, usually a protocol error error:10000410:SSL

community.letsencrypt.org

 

 

My domain is: starline.ru 4

I ran this command: android 7.0 can't connect

It produced this output:

 
Caused by: javax.net.ssl.SSLProtocolException: SSL handshake terminated: ssl=0x76a8eb8a00: Failure in SSL library, usually a protocol error error:10000410:SSL routines:OPENSSL_internal:SSLV3_ALERT_HANDSHAKE_FAILURE (external/boringssl/src/ssl/s3_pkt.c:641 0x76a8e2b0a0:0x00000001) error:1000009a:SSL routines:OPENSSL_internal:HANDSHAKE_FAILURE_ON_CLIENT_HELLO (external/boringssl/src/ssl/s3_clnt.c:800 0x76d2e2c253:0x00000000)
at com.android.org.conscrypt.NativeCrypto.SSL_do_handshake(Native Method)
at com.android.org.conscrypt.OpenSSLSocketImpl.startHandshake(OpenSSLSocketImpl.java:364)

SSLV3 - wrapper string, actualy sslv3 not use

My web server is (include version):

 
nginx version: nginx/1.14.1
built with OpenSSL 1.1.0f  25 May 2017 (running with OpenSSL 1.1.0l  10 Sep 2019)
TLS SNI support enabled

The operating system my web server runs on is (include version): Debian GNU/Linux 9.13 (stretch)

Previously, the RSA private key was generated. And everything worked.
Now a private key has been generated as EC and application (with okhttp3 https://square.github.io/okhttp 12 or direct use ssl) on Android 7.0 - cannot connect (application using android webclient backend is working).

 Solved by rnz in post #10
 
Once again I regenerated the certificate with the RSA, since ApplePay also had problems.

 

 

You're almost certainly running into this issue:

W…

The only way to fix this is by NOT using an EC certificate. Right now you can work around by switching to P-256, but as the future E1 intermediate is P-384, that's not a long term solution.

 


요약

Android Side 에서 SSL Connection Handshake 가 날 때에 인증에 EC 인증서를 사용하면 안된다는 이야기이다. 즉, 안드로이 측면에서 고치려면 RSA 를 강제하고 인증서를 넘기면 된다.

728x90

Improve replication friendliness of the MySQL schema

 

Status quo

The table BATCH_JOB_EXECUTION_PARAMS currently has no primary key.

For a single node MySQL deployment, this does not cause any issue. For a setup of multiple nodes with replication, this is however not ideal as it causes performance problems with row-based replication and leads to ambiguity when calculating check sums over tables to validate the integrity of the replication.

To prevent such issues, MySQL 8 offers the system variable sql_require_primary_key. If set to ON, it is currently not possible to deploy the Spring Batch schema.

Suggestion

As job parameters are handled by Spring Batch internally as maps, it already works with the assumption that job parameter keys are unique for each job execution. This allows to declare a primary key on BATCH_JOB_EXECUTION_PARAMS consisting of JOB_EXECUTION_ID and KEY_NAME.

It would be nice if this (or any other) primary key were defined on BATCH_JOB_EXECUTION_PARAMS in the MySQL schema.

The tables BATCH_JOB_SEQ, BATCH_JOB_EXECUTION_SEQ, BATCH_STEP_EXECUTION_SEQ also do not have a primary key. As they contain only a single row, respectively, this is not a problem in practice. But if the column UNIQUE_KEY were to be declared as primary key, this would allow a seamless deployment to a MySQL instance with sql_require_primary_key=ON.

 

 


Quote - schema-mysql.sql 

BATCH_JOB_EXECUTION_PARAMS

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS  (
	JOB_EXECUTION_ID BIGINT NOT NULL ,
	TYPE_CD VARCHAR(6) NOT NULL ,
	KEY_NAME VARCHAR(100) NOT NULL ,
	STRING_VAL VARCHAR(250) ,
	DATE_VAL DATETIME(6) DEFAULT NULL ,
	LONG_VAL BIGINT ,
	DOUBLE_VAL DOUBLE PRECISION ,
	IDENTIFYING CHAR(1) NOT NULL ,
    PRIMARY KEY (JOB_EXECUTION_ID, KEY_NAME)
	constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
	references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;

 

Other meta tables

CREATE TABLE BATCH_STEP_EXECUTION_SEQ (
	ID BIGINT NOT NULL,
	UNIQUE_KEY CHAR(1) NOT NULL,
    PRIMARY KEY(UNIQUE_KEY),
	constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;

INSERT INTO BATCH_STEP_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_STEP_EXECUTION_SEQ);

CREATE TABLE BATCH_JOB_EXECUTION_SEQ (
	ID BIGINT NOT NULL,
	UNIQUE_KEY CHAR(1) NOT NULL,
    PRIMARY KEY(UNIQUE_KEY),
	constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;

INSERT INTO BATCH_JOB_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_EXECUTION_SEQ);

CREATE TABLE BATCH_JOB_SEQ (
	ID BIGINT NOT NULL,
	UNIQUE_KEY CHAR(1) NOT NULL,
    PRIMARY KEY(UNIQUE_KEY),
	constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;

INSERT INTO BATCH_JOB_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_SEQ);

 

[important]

Do not create generated key in PARAMS table like below;

https://docs.spring.io/spring-batch/reference/schema-appendix.html

 

Meta-Data Schema :: Spring Batch

The Spring Batch Metadata tables closely match the domain objects that represent them in Java. For example, JobInstance, JobExecution, JobParameters, and StepExecution map to BATCH_JOB_INSTANCE, BATCH_JOB_EXECUTION, BATCH_JOB_EXECUTION_PARAMS, and BATCH_ST

docs.spring.io

 

728x90
Spring Batch 에서는 meta table이라고 해서 Batch 의 작업을 도와주는 테이블이 있는데...

 

 

 

 

여하튼 서론은 막론하고 MySQL 8.0 버전을 올리면서 이후부터는 System Variable 로 sql_require_primary_key 가 true 가 걸려있게 되는 경우 application.yml 에서 설정을 해도 작동을 하지 않는다 이말이다

 

전통적으로는 spring.batch.jdbc.

initialize-schema: always

 

이렇게 때려박아 주고 있는데, 여하튼 이거랑 별도로 환경변수에서 저렇게 잡아놓으면 데이터베이스가 생성하고 싶어도 생성할 수가 없다.

 

 

그렇다면 Spring Batch 공식문서로 가보자


The BATCH_JOB_EXECUTION_PARAMS Table

The BATCH_JOB_EXECUTION_PARAMS table holds all information relevant to the JobParameters object. It contains 0 or more key/value pairs passed to a Job and serves as a record of the parameters with which a job was run. For each parameter that contributes to the generation of a job’s identity, the IDENTIFYING flag is set to true. Note that the table has been denormalized. Rather than creating a separate table for each type, there is one table with a column indicating the type, as the following listing shows:

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS  (
	JOB_EXECUTION_ID BIGINT NOT NULL ,
	PARAMETER_NAME VARCHAR(100) NOT NULL ,
	PARAMETER_TYPE VARCHAR(100) NOT NULL ,
	PARAMETER_VALUE VARCHAR(2500) ,
	IDENTIFYING CHAR(1) NOT NULL ,
	constraint JOB_EXEC_PARAMS_FK foreign key (JOB_EXECUTION_ID)
	references BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
);
 

The following list describes each column:

  • JOB_EXECUTION_ID: Foreign key from the BATCH_JOB_EXECUTION table that indicates the job execution to which the parameter entry belongs. Note that multiple rows (that is, key/value pairs) may exist for each execution.
  • PARAMETER_NAME: The parameter name.
  • PARAMETER_TYPE: The fully qualified name of the type of the parameter.
  • PARAMETER_VALUE: Parameter value
  • IDENTIFYING: Flag indicating whether the parameter contributed to the identity of the related JobInstance.

Note that there is no primary key for this table. This is because the framework has no use for one and, thus, does not require it. If need be, you can add a primary key with a database generated key without causing any issues to the framework itself.


 

간단하게 요약하자면 원래 이 테이블에는 Primary Key 따위는 없는 것인데, 네놈이 꼴린다면 generated Key 를 생성해서 그 키를 Primary Key로 쓸 수 있다 이말씀이다.

 

참고로 MySQL에서 SEQUENCE 를 생성해서 사용하려고 해도 PRIMARY KEY 때문에 작동하지 않을 것이므로 

 

여하튼 데이터베이스를 알게된 이래로 generated key 라는 놈은 처음 들어봣는데

 

 

내용을 잘 살펴보아하자니 프로세스가 진행될 때마다 증가하면서 자동으로 생성되는 놈이라하니 꼭 AI 를 박아놓은 친구처럼 생겼다.

 

그래서 만들어보았다 generated key

CREATE TABLE BATCH_JOB_EXECUTION_PARAMS (
    PARAM_ID BIGINT AUTO_INCREMENT PRIMARY KEY,
    JOB_EXECUTION_ID BIGINT NOT NULL,
    TYPE_CD VARCHAR(6) NOT NULL,
    KEY_NAME VARCHAR(100) NOT NULL,
    STRING_VAL VARCHAR(250),
    DATE_VAL DATETIME(6) DEFAULT NULL,
    LONG_VAL BIGINT,
    DOUBLE_VAL DOUBLE PRECISION,
    IDENTIFYING CHAR(1) NOT NULL,
    CONSTRAINT JOB_EXEC_PARAMS_FK FOREIGN KEY (JOB_EXECUTION_ID)
        REFERENCES BATCH_JOB_EXECUTION(JOB_EXECUTION_ID)
) ENGINE=InnoDB;

INSERT INTO BATCH_STEP_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_STEP_EXECUTION_SEQ);
CREATE TABLE BATCH_STEP_EXECUTION_SEQ (
                                        SEQ_ID  BIGINT  AUTO_INCREMENT  PRIMARY KEY ,
                                          ID BIGINT NOT NULL,
                                          UNIQUE_KEY CHAR(1) NOT NULL,
                                          constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;


CREATE TABLE BATCH_JOB_EXECUTION_SEQ (
                                         SEQ_ID  BIGINT  AUTO_INCREMENT  PRIMARY KEY ,
                                         ID BIGINT NOT NULL,
                                         UNIQUE_KEY CHAR(1) NOT NULL,
                                         constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;

INSERT INTO BATCH_JOB_EXECUTION_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_EXECUTION_SEQ);

CREATE TABLE BATCH_JOB_SEQ (
                               SEQ_ID  BIGINT  AUTO_INCREMENT  PRIMARY KEY ,
                               ID BIGINT NOT NULL,
                               UNIQUE_KEY CHAR(1) NOT NULL,
                               constraint UNIQUE_KEY_UN unique (UNIQUE_KEY)
) ENGINE=InnoDB;

INSERT INTO BATCH_JOB_SEQ (ID, UNIQUE_KEY) select * from (select 0 as ID, '0' as UNIQUE_KEY) as tmp where not exists(select * from BATCH_JOB_SEQ);

 

이러면 쌈뽕하게 작동한다

728x90

첫번째 미션으로 유저 객체를 생성할 때 Builder 패턴을 이용해야 했다.

프로그래머스 스터디 뿐만 아니라 이전에 참고했던 여러 서적들(책 ‘처음 배우는 스프링 부트2’와 ‘스프링 부트와 AWS로 혼자 구현하는 웹서비스’)에서도 빌더 패턴을 사용해서 구현한다. 무엇보다도 찾아봤던 블로그, 책마다 빌더 패턴을 구현하는 모습이 서로 조금씩 달라서 빌더패턴을 코드로 이렇게 작성하는게 맞는건지 너무 헷갈렸다.

왜 빌더 패턴을 사용해야 하는 걸까? 빌더 패턴과 생성자의 차이는 무엇이고 어떤 상황에서 사용해야 하는 걸까? 코드로는 어떻게 구현할까? 이에 대해 한번 정리해 봐야겠다.


먼저 생성자란,

인스턴스가 생성될 때 호출되는 인스턴스 초기화 메소드이다. 연산자 new가 인스턴스를 생성하는 것이지 생성자가 인스턴스를 생성하는 것은 아니다.

User Sara = new User();

 

빌더패턴이란

생성자처럼 인스턴스를 초기화하고 new 대신 build() 메소드를 이용해 객체를 생성하는 방법이다. 기존 생성자의 문제점을 보완해서 객체 생성을 더 깔끔하고 유연하게 할 수 있다.

 

빌더 패턴은 왜 / 언제 사용해야할까?

일반 생성자 이용시, 인자 수가 늘어나면 코드 작성이 어려워지고 읽기 어려운 코드가 되고 만다

NutritionFacts cocaCola = new NutritionFacts(240, 8, 100, 3, 35, 27);

호출 코드만 보고서는 저 많은 인자가 도대체 무슨 값인지 알 수가 없고 특히 자료형이 같은 인자들이 많을 경우 컴파일 단계에서 걸러지지 않기 때문에 런타임시 문제 발생하기가 딱 좋다 - 실수하기 좋은 구조.

대안책으로 setter메소드를 이용한 자바빈 패턴이 있다.

// cocaCola을 영양 성분 정보를 setter메소드를 통해 초기화하는 자바빈 패턴
NutritionFacts cocaCola = new NutritionFacts();
cocaCola.setServingSize(240);
cocaCola.setServings(8);
cocaCola.setCalories(100);
cocaCola.setSodium(35);
cocaCola.setCarbohdydrate(27);

가독성은 좋아졌지만, 1회의 함수 호출로 객체 생성을 끝낼 수 없고 setter메서드로 인해 Immutable한 클래스를 만들 수가 없다. Setter 메소드 사용은 지양해야하며 변경 가능성은 최소화해야한다.

따라서 가독성도 좋고 immutable 객체로도 만들 수 있는 Builder 패턴을 사용하게 된다.

NutritionFacts cocaCola = new NutritionFacts.Builder(240, 8)
                                            .calories(100)
                                            .sodium(35)
                                            .carbohydrate(27)
                                            .build();

각 인자가 어떤 값을 의미하는지 알기가 쉽고 setter메소드가 없으므로 변경 불가능한 객체도 만들 수 있다. 한번에 객체를 생성하므로 객체 일관성이 깨지지 않으며, build() 함수로 잘못된 값이 입력되었는지 검증하게 할 수도 있다.

Builder패턴은

  • 생성자의 인자수가 많을 때 사용하며
  • 불변성과 네임드 파라미터(Named Parameter, 매개변수 이름을 통해서 인자 값을 전달)를 가져갈 수 있어
  • 안전하고 가독성이 좋다.

 

빌더패턴을 코드로 작성해보자

public class NutritionFacts {
    private final int servingSize;
    private final int servings;
    private final int calories;
    private final int fat;
    private final int sodium;
    private final int carbohydrate;

    public static class Builder {
        // Required parameters(필수 인자)
        private final int servingSize;
        private final int servings;

        // Optional parameters - initialized to default values(선택적 인자는 기본값으로 초기화)
        private int calories      = 0;
        private int fat           = 0;
        private int carbohydrate  = 0;
        private int sodium        = 0;

        public Builder() {
        }

        public Builder(int servingSize, int servings) {
            this.servingSize = servingSize;
            this.servings    = servings;
        }

        public Builder calories(int val) {
            calories = val;
            return this;    // 이렇게 하면 . 으로 체인을 이어갈 수 있다.
        }
        public Builder fat(int val) {
            fat = val;
            return this;
        }
        public Builder carbohydrate(int val) {
            carbohydrate = val;
            return this;
        }
        public Builder sodium(int val) {
            sodium = val;
            return this;
        }
        public NutritionFacts build() {
            return new NutritionFacts(this);
        }
    }

    private NutritionFacts(Builder builder) {
        servingSize  = builder.servingSize;
        servings     = builder.servings;
        calories     = builder.calories;
        fat          = builder.fat;
        sodium       = builder.sodium;
        carbohydrate = builder.carbohydrate;
    }
}

위와 같이 하면 다음처럼 객체를 생성해 사용하면 된다.

NutritionFacts cocaCola = new NutritionFacts
    .Builder(240, 8)    // 필수값 입력
    .calories(100)
    .sodium(35)
    .carbohydrate(27)
    .build();           // build() 가 객체를 생성해 돌려준다.

이렇게 정리해보니 궁금함이 생겼다. 생성자의 단점을 보완해서 나온게 빌더패턴이라면 생성자말고 빌더패턴만 사용하는게 좋지 않나? 그런데 왜 생성자와 빌더패턴을 섞어서 사용할까? => 스터디 리더분께 물어보니 명쾌하게 의견을 주셨다. 정답은 없다고. 생성자의 매개변수가 많아지면 네임드 파라미터의 빌더 패턴 사용을 고려해보는 것처럼 그 상황과 용도에 맞게 더 fit한 생성자를 이용하는게 더 좋다고. 결국 프로그래밍에 있어서 각 방법의 장단점을 확실하게 인지하고 상황에 맞는 방법을 유연하게 적용할 수 있는 능력이 정말 중요한 것 같다.

references

Effective Java 2/E
자바의 정석
https://johngrib.github.io/wiki/builder-pattern/
https://softwareengineering.stackexchange.com/questions/380397/why-do-we-need-a-builder-class-when-implementing-a-builder-pattern

 

Why do we need a Builder class when implementing a Builder pattern?

I have seen many implementations of the Builder pattern (mainly in Java). All of them have an entity class (let's say a Person class), and a builder class PersonBuilder. The builder "stacks" a vari...

softwareengineering.stackexchange.com

 

 

https://yjna2316.github.io/study/2020/11/06/%EC%83%9D%EC%84%B1%EC%9E%90%EC%99%80-%EB%B9%8C%EB%8D%94%ED%8C%A8%ED%84%B4/

 

 

+ Recent posts