반응형


RestTemplate 설정 예제


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
    HttpComponentsClientHttpRequestFactory factory = new HttpComponentsClientHttpRequestFactory();
    // 읽기타임아웃 - 5초동안 연결은 되었지만 처리 속도가 느려 응답 없으면 타임아웃
    factory.setReadTimeout(5000); // milliseconds
    // 연결타임아웃 - 5초동안 연결이 되지 않으면 타임아웃(잘못된 주소 연결 혹은 서비스 다운)
    factory.setConnectTimeout(5000);// milliseconds
 
    // 커넥션 풀 설정
    HttpClient httpClient = HttpClientBuilder.create()//
        .setMaxConnTotal(50)// IP:PORT 관계없이 전체 커넥션 수
        .setMaxConnPerRoute(50)// IP:PORT 마다 커넥션 수
        .build();
    factory.setHttpClient(httpClient);
    // 커넥션 풀 설정
 
    RestTemplate restTemplate = new RestTemplate(factory);
 
    HttpClientContext context = HttpClientContext.create();
    restTemplate.setRequestFactory(new HttpComponentsClientHttpRequestFactory() {
      @Override
      protected HttpContext createHttpContext(HttpMethod httpMethod, URI uri) {
        if (context.getAttribute(HttpClientContext.COOKIE_STORE) == null) {
          context.setAttribute(HttpClientContext.COOKIE_STORE, new BasicCookieStore());
          Builder builder = RequestConfig.custom()
              // .setCookieSpec(CookieSpecs.IGNORE_COOKIES)
              // .setAuthenticationEnabled(false)
              .setRedirectsEnabled(false);
          context.setRequestConfig(builder.build());
        }
        return context;
      }
    });



http client

1
2
3
4
    <dependency>
      <groupId>org.apache.httpcomponents</groupId>
      <artifactId>httpclient</artifactId>
    </dependency>


import

1
2
3
4
5
6
7
8
9
10
import org.apache.http.client.HttpClient;
import org.apache.http.client.config.RequestConfig;
import org.apache.http.client.config.RequestConfig.Builder;
import org.apache.http.client.protocol.HttpClientContext;
import org.apache.http.impl.client.BasicCookieStore;
import org.apache.http.impl.client.HttpClientBuilder;
import org.apache.http.protocol.HttpContext;
import org.springframework.http.HttpMethod;
import org.springframework.http.client.HttpComponentsClientHttpRequestFactory;
import org.springframework.web.client.RestTemplate;


반응형
반응형

JSON 형식으로 서버에 로그를 남기고 있습니다. 

그런데 객체를 통으로 JSON으로 변환하다 보니 원하지 않거나 혹은 데이터를 변형해서 남기고 싶을 때가 있습니다.

여러가지 방법이 있지만 자유도 측면에서 custom filter 를 애용합니다.

 

아래 예제코드를 참조하시면 입맛에 맛게 마음대로 JSON 변환을 제어할 수 있습니다.

 

예제 코드

 

* FormatUtil.getSecurity 는 Masking 처리해주는 함수 입니다.

 

결과

 

 

반응형

'java' 카테고리의 다른 글

maven cache clear. 메이븐 캐시 클리어.  (0) 2018.07.31
spring boot 2.0 default connection pool  (2) 2018.07.25
fasterxml json parsing uppsercase  (0) 2018.07.24
spring resttemplate httpclient connection pool  (0) 2018.07.24
spring boot multi db and jndi in mybatis  (0) 2018.07.13
JNDI TEST  (0) 2018.07.09
fasterxml json camelcase  (2) 2018.06.18
spring jdbc sample  (0) 2018.06.04
반응형

이 포스트는 Spring boot + Mybatis 를 전제하고 있습니다.


스프링부트를 이용하면서 다른 종류의 DB를 동시에 사용해야하는 경우가 종종 있습니다.


그런데 이번엔 JNDI 환경까지 겹치면서 1번 DB는 Tomcat (jdbc pool) Datasource 를 사용하고 2번 DB는 JNDI를 이용해야 하는 이런 상황이 되었습니다.


여기에 멈추지 않고 Local 개발환경에서 JNDI 를 사용하기는 또 부담스럽고 귀찮으니 Tomcat embedded JNDI 도 사용해봤습니다.


보너스로 @Profile 을 이용해서 마음대로 각 DB에서 설정에 따라 자유롭게 Datasource 를 골라 쓸 수 있게 해보았습니다.



 
import javax.naming.NamingException;
import javax.sql.DataSource;
 
import org.apache.ibatis.session.SqlSessionFactory;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Primary;
import org.springframework.context.annotation.Profile;
import org.springframework.jdbc.datasource.lookup.JndiDataSourceLookup;
 
@Configuration
@MapperScan(value = Constants.PACKAGE_BASE + ".dao", sqlSessionFactoryRef = "coreSqlSessionFactory")
public class CoreRepositoryConfig {
 
  @Bean
  @ConfigurationProperties(prefix = "spring.datasource.primary")
  public JndiPropertyHolder getJndiPropertyHolder() {
    return new JndiPropertyHolder();
  }
 
  @Primary
  @Bean("coreDatasource")
  @Profile("jndi")
  public DataSource jndiDatasource() throws IllegalArgumentException, NamingException {
    String jndiName = getJndiPropertyHolder().getJndiName();
    return (DataSource) new JndiDataSourceLookup().getDataSource(jndiName);
  }
 
  @Primary
  @Bean("coreDatasource")
  @Profile("!jndi")
  @ConfigurationProperties(prefix = "primary.spring.datasource")
  public org.apache.tomcat.jdbc.pool.DataSource coreDatasource() {
    return (org.apache.tomcat.jdbc.pool.DataSource) DataSourceBuilder.create()
        .type(org.apache.tomcat.jdbc.pool.DataSource.class).build();
  }
 
  @Bean(name = "coreSqlSessionFactory")
  @Primary
  public SqlSessionFactory coreSqlSessionFactory(@Qualifier("coreDatasource") DataSource coreDatasource,
      ApplicationContext applicationContext) throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(coreDatasource);
    sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:mybatis/config.xml"));
    return sqlSessionFactoryBean.getObject();
  }
 
  @Bean(name = "coreSqlSessionTemplate")
  @Primary
  public SqlSessionTemplate coreSqlSessionTemplate(SqlSessionFactory coreSqlSessionFactory) throws Exception {
    return new SqlSessionTemplate(coreSqlSessionFactory);
  }
 
  class JndiPropertyHolder {
    private String jndiName;
 
    public String getJndiName() {
      return jndiName;
    }
 
    public void setJndiName(String jndiName) {
      this.jndiName = jndiName;
    }
  }
}


<CoreRepositoryConfig.java>


1번 DB의 Java Config 파일이라고 보시면 되겠습니다.


import org.apache.ibatis.session.SqlSessionFactory;
import org.apache.tomcat.jdbc.pool.DataSource;
import org.mybatis.spring.SqlSessionFactoryBean;
import org.mybatis.spring.SqlSessionTemplate;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.boot.autoconfigure.jdbc.DataSourceBuilder;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.ApplicationContext;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
@Configuration
@MapperScan(value = Constants.PACKAGE_BASE + ".secondary.dao", sqlSessionFactoryRef = "secondarySqlSessionFactory")
public class SecondaryRepositoryConfig {
  @Bean("secondaryDatasource")
  @ConfigurationProperties(prefix = "secondary.spring.datasource")
  public DataSource secondaryDatasource() {
    return (DataSource) DataSourceBuilder.create().type(DataSource.class).build();
  }
  @Bean(name = "secondarySqlSessionFactory")
  public SqlSessionFactory secondarySqlSessionFactory(@Qualifier("secondaryDatasource") DataSource secondaryDatasource,
      ApplicationContext applicationContext) throws Exception {
    SqlSessionFactoryBean sqlSessionFactoryBean = new SqlSessionFactoryBean();
    sqlSessionFactoryBean.setDataSource(secondaryDatasource);
    sqlSessionFactoryBean.setConfigLocation(applicationContext.getResource("classpath:secondary/mybatis-config.xml"));
    return sqlSessionFactoryBean.getObject();
  }
  @Bean(name = "secondarySqlSessionTemplate")
  public SqlSessionTemplate secondarySqlSessionTemplate(SqlSessionFactory secondarySqlSessionFactory) throws Exception {
    return new SqlSessionTemplate(secondarySqlSessionFactory);
  }
}


<SecondaryRepositoryConfig.java>


SecondaryRepositoryConfig.java 는 2번 DB의 Java Config 입니다.


spring.profiles.active=local,jndi
 
spring.datasource.primary.jndi-name=MYJNDI
 
secondary.spring.datasource.driverClassName=oracle.jdbc.driver.OracleDriver
secondary.spring.datasource.url=jdbc:oracle:thin:@IP:PORT:DB
secondary.spring.datasource.username=아뒤
secondary.spring.datasource.password=패쓰워드
secondary.spring.datasource.max-active=1
secondary.spring.datasource.max-idle=1
secondary.spring.datasource.min-idle=1
secondary.spring.datasource.max-wait=-1
secondary.spring.datasource.initial-size=1
secondary.spring.datasource.test-on-borrow=true
secondary.spring.datasource.test-while-idle=true
secondary.spring.datasource.validation-query=select 'SECONDARY' from dual
secondary.spring.datasource.time-between-eviction-runs-millis=300000


<application.properties>


이 녀석은 Spring Boot 의 설정파일인 application.properties 입니다.



CoreRepositoryConfig.java 에서만 JNDI 혹은 Tomcat Datasouce를 쓸건지 선택할 수 있도록 되어있는데 이부분은 application.properties 의 설정중에


spring.profiles.active 에 jndi가 포함되어 있느냐 없느냐를 @Profile("jndi") or @Profile("!jndi") 로 구분해서 선택적으로 Datasource를 제공하고 있습니다.


JNDI 의 이름은 @ConfigurationProperties(prefix = "spring.datasource.primary") 를 이용해  


application.properties 의 spring.datasource.primary.jndi-name=MYJNDI 에서 획득하고 있습니다.


만약 1번 DB에서 JNDI 를 사용하지 않으려면 spring.profiles.active에서 jndi 를 지우고 2번 DB 설정처럼 primary.spring.datasource 설정들을 지정하면 됩니다.


그리고 2번 DB의 사용은 @ConfigurationProperties(prefix = "secondary.spring.datasource") 을 이용해 application.properties 에서 db설정 정보를 가져와 


Tomcat datasource를 사용하고 있습니다.


마지막으로 Local 환경에서도 JNDI 로 테스트하기 위해 Embedded Tomcat JNDI를 이용해 보겠습니다.



 
import javax.sql.DataSource;
 
import org.apache.catalina.Context;
import org.apache.catalina.startup.Tomcat;
import org.apache.tomcat.util.descriptor.web.ContextResource;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainer;
import org.springframework.boot.context.embedded.tomcat.TomcatEmbeddedServletContainerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.Profile;
 
@Configuration
@Profile({ "local""dev" })
public class TomcatEmbdded {
  @Bean
  public TomcatEmbeddedServletContainerFactory tomcatFactory() {
    return new TomcatEmbeddedServletContainerFactory() {
 
      @Override
      protected TomcatEmbeddedServletContainer getTomcatEmbeddedServletContainer(Tomcat tomcat) {
        tomcat.enableNaming();
        return super.getTomcatEmbeddedServletContainer(tomcat);
      }
 
      @Override
      protected void postProcessContext(Context context) {
        ContextResource resource = new ContextResource();
        resource.setName("JNDI이름");
        resource.setType(DataSource.class.getName());
        resource.setProperty("driverClassName""oracle.jdbc.driver.OracleDriver");
        resource.setProperty("url""jdbc:oracle:thin:@IP:PORT:DB");
        resource.setProperty("username""아뒤");
        resource.setProperty("password""패쓔워드");
        resource.setProperty("factory""org.apache.tomcat.jdbc.pool.DataSourceFactory");
        resource.setProperty("type""javax.sql.DataSource");
 
        context.getNamingResources().addResource(resource);
      }
    };
  }
}


<TomcatEmbdded.java - 오타가 ..>


@Profile({ "local", "dev" }) 를 통해서 spring.profiles.active 에 local 이나 dev 가 포함되어 있다면 Embedded Tomcat JNDI 를 사용할 수 있게 됩니다.





maven dependency

    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-configuration-processor</artifactId>
      <optional>true</optional>
    </dependency>


@ConfigurationProperties 를 사용하기 위해 필요합니다.


org.apache.tomcat.jdbc.pool.DataSourceFactory 를 사용하기 위해선 tomcat-jdbc 가 필요한데 

저같은 경우 mybatis-spring-boot-starter (mybatis-spring-boot-starter > spring-boot-starter-jdbc > tomcat-jdbc ) 에 포함된 걸 이용하고 있습니다.

반응형
반응형
import javax.naming.NamingException;
 
import org.apache.commons.dbcp.BasicDataSource;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mock.jndi.SimpleNamingContextBuilder;
 
public class JndiDatasourceCreator {
  private static final Logger LOGGER = LoggerFactory.getLogger(JndiDatasourceCreator.class);
 
  private static final String username = "userName";
  private static final String password = "password";
  private static final String jndiName = "DB_Name";
  private static final String driverClassName = "Driver Name";
  private static final String url = "jdbc://localhost:port/dbName";
  public static BasicDataSource dataSource;
 
  public static void create() throws Exception {
    try {
      final SimpleNamingContextBuilder builder = new SimpleNamingContextBuilder();
      dataSource = new BasicDataSource();
      dataSource.setUsername(username);
      dataSource.setPassword(password);
      dataSource.setDriverClassName(driverClassName);
      dataSource.setUrl(url);
      builder.bind("java:comp/env/jdbc/" + jndiName, dataSource);
      builder.activate();
    } catch (NamingException ex) {
      LOGGER.info(ex.getMessage());
    }
  }
}



보통 개발환경에서는 JNDI 를 사용하지 않는데 JNDI를 테스할 경우에는 위의 코드를 이용해서 JNDI 환경에서 잘 동작하는지 테스트 할 수 있다.


    <dependency>
      <groupId>commons-dbcp</groupId>
      <artifactId>commons-dbcp</artifactId>
      <version>1.4</version>
    </dependency>
    <dependency>
      <groupId>org.springframework.boot</groupId>
      <artifactId>spring-boot-starter-test</artifactId>
      <scope>test</scope>
    </dependency>


<필요한 라이브러리>

반응형
반응형

fasterxml jackson 라이브러리를 이용해 json 을 String <----> Object 변환 시 SnakeCase <----> CamelCase 를 적용하는 방법입니다.


버전 2.7 부터 PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES 대신 PropertyNamingStrategy.SNAKE_CASE 를 사용해야 합니다.


import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.PropertyNamingStrategy;
 
public class Test {
  public static void main(String[] args) {
    ObjectMapper mapper = new ObjectMapper();
    // @deprecated Since 2.7 use {@link #SNAKE_CASE} instead;
    // mapper.setPropertyNamingStrategy(PropertyNamingStrategy.CAMEL_CASE_TO_LOWER_CASE_WITH_UNDERSCORES);
    mapper.setPropertyNamingStrategy(PropertyNamingStrategy.SNAKE_CASE);
  }
}



반응형
반응형

Spring jdbc repository sample 코드 입니다.


import java.util.List;
 
import xxx.Category;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.jdbc.core.BeanPropertyRowMapper;
import org.springframework.jdbc.core.JdbcTemplate;
import org.springframework.stereotype.Repository;
 
@Repository
public class CategoryRepository {
 
    @Autowired
    private JdbcTemplate jdbcTemplate;
 
    StringBuffer query;
 
    public List<Category> selectCategorys() {
        query = new StringBuffer();
        query.append("SELECT id, name ");
        query.append("  FROM category ");
        query.append(" ORDER BY id");
        return jdbcTemplate.query(query.toString(), new Object[] {},
                new BeanPropertyRowMapper<Category>(Category.class));
    }
 
    public int selectCategoryTotalCount() {
        return jdbcTemplate.queryForObject("SELECT count(*) AS total_count FROM category", Integer.class);
    }
 
    public Category selectCategory(int id) {
        return (Category) jdbcTemplate.queryForObject("SELECT id, name FROM category WHERE id = ?"new Object[] { id },
                new BeanPropertyRowMapper<>(Category.class));
    }
 
    public int insertCategory(Category category) {
        return jdbcTemplate.update("INSERT INTO category (id, name) VALUES(?, ?)", category.getId(), category.getName());
    }
 
    public int updateCategory(Category category) {
        return jdbcTemplate.update("UPDATE category SET name=? WHERE id=?", category.getName(), category.getId());
    }
 
    public int deleteCategory(int id) {
        return jdbcTemplate.update("DELETE FROM category WHERE id = ?", id);
    }
 
}



반응형
반응형

스프링부트를 이용하면 기본으로 logback을 이용합니다.

 

기본은 CONSOLE APPENDER를 이용하게 되어있는데 실제 서비스를 적용할 때는 조금더 다양하게 로그를 남길 필요가 있습니다.

서비스 운영시 자주쓰는 로그 설정을 공유합니다.

 

아래 logback.xml 전체 내용에 주석을 보시면 어느정도 이해가 되실겁니다.

 

눈여겨 볼 만한 것들은 대충 이정도 인것 같습니다.

 

1. property를 이요한 변수 사용.

2. ThresholdFilter 와 LevelFilter의 차이점.

3. 시간 및 파일크기에 의한 파일 롤링.

4. 파일 롤링시 보관 개수 지정과 압축 여부 지정.

 

예제가 도움이 되시길 바랍니다.

 

logback.xml 전체 내용

 

 

추가로 처음부터 날짜 형식의 로그를 남기고 싶은경우 아래처럼 하면 됩니다.

  <timestamp key="byDate" datePattern="yyyy-MM-dd"/>
  <appender name="UniquePageViewLog" class="ch.qos.logback.core.FileAppender">
    <file>
      $LOG_DIR/page/uv.$byDate.log
    </file>
    <encoder>
      <pattern>%msg%n</pattern>
    </encoder>
  </appender>

 

반응형
반응형

spring boot embeded tomcat 이용시 accesslog 설정 방법입니다.

 

application.properties에 추가하면 됩니다.

 

server.tomcat.basedir=/app/goni9071/front
server.tomcat.accesslog.directory=logs
server.tomcat.accesslog.pattern=%{yyyy-MM-dd HH:mm:ss}t\t%s\t%r\t%{User-Agent}i\t%{Referer}i\t%a\t%b   
server.tomcat.accesslog.enabled=true   
 

 

반응형
반응형

spring boot 의 embed tomcat 을 사용하는 경우 포트 지정 방법입니다.

 

application.properties에 아래 설정을 추가하면 됩니다.

 

server.port=8090

 

아니면 시작 시 jvm 옵션으로 아래처럼 해도 됩니다.

 

-Dserver.port=8090

반응형
반응형

리눅스상에서 pe -ef를 통해 특정 프로세스를 찾을 수 있습니다.

그리고 | 파이프와 grep 을 통해 필터를 해서 필요한 프로세스만 확인을 합니다.


이 명령어를 자바에서 할 때 주의할 점이 있습니다.

아래 코드에 "Runtime.getRuntime()" 명령어 사용하는 라인을 보면 주석 처리 된 라인과 활성화 된 라인을 비교해 보시기 바랍니다.

주석처리 된 라인으로 실행하게 되면 아무 결과도 얻을 수 없습니다.


포인트는 배쉬 대신 쉘을 이용해서 명령어를 실행해야 파이프나 grep 이 정상적으로 동작한다는 겁니다.

아니면, 실제 저 명령어가 들어가 쉘을 하나 만들어놓고 그 쉘을 그냥 실행하는 방법도 있습니다.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
public class Test {
  public static void main(String[] args) {
    String line;
    try {
      // Process p = Runtime.getRuntime().exec("ps -ef | grep java | grep -v grep");
      Process p = Runtime.getRuntime().exec(new String[] {"sh""-c""ps -ef | grep java | grep -v grep"});
      BufferedReader input = new BufferedReader(new InputStreamReader(p.getInputStream()));
      while ((line = input.readLine()) != null) {
        System.out.println(line);
      }
    } catch (Exception err) {
      System.out.println(err);
    }
  }
}



반응형

+ Recent posts