반응형

한글로만 이루어진 문자열인지 검사하는 함수입니다.

  public static boolean isOnlyHangul(String mberNm) {
    if (mberNm == null) {
      return false;
    }
    return mberNm.matches("^[ㄱ-하-ㅣ가-힣]+$");
  }

 

반응형
반응형

로컬에서 결제취소하는데 아래 처럼 절대경로 설정오류가 발생합니다.

INIpayTX : 로그초기화오류 - inipayhome 절대경로 설정
INIpayTX : Check Field - INIPAYHOME
Exception in thread "main" java.lang.NullPointerException

한참을 삽질하다 이니시스 라이브러리 디컴파일까지 해서 알아낸 사실은 정말 절대 경로가 아니여서 였습니다. ㅜㅜ.

 

inipay.SetField("inipayhome""/test/inipay");

이렇게 설정을 했는데 보통은 윈도우에서도 절대경로라고 같이 사용할 수있는데,

실제로 코드는 java.io.File의 isAbsolute() 메서드를 사용해서 비교하고 있어서 절대경로가 아니라고 예외를 발생시키고 있습니다. ㅜㅜ

 

c:\\ 이런식으로 prefix를 해주면 저 오류는 발생하지 않네요.

 

 

반응형

'java' 카테고리의 다른 글

java - Integer 비교  (0) 2019.05.12
STS4 에 jsp editor 가 없네요.  (0) 2019.05.11
java log4j.properties samlple  (0) 2019.05.03
java 정규식 한글 검사  (0) 2019.05.03
spring boot mysql connector java version property  (0) 2019.04.09
spring send mail  (0) 2019.03.23
spring security headers frameOptions 선택적 disable  (0) 2019.03.19
spring boot favicon.ico  (0) 2019.03.09
반응형

spring boot 2 를 사용합니다.

 

그런데 db가 mysql 5 버전입니다. 

 

그래서 jdbc도 5.x 을 사용하고 싶습니다.

 

그래서 아래처럼 설정했지만 의미 없습니다.

    <dependency>
      <groupId>mysql</groupId>
      <artifactId>mysql-connector-java</artifactId>
      <version>5.1.47</version>
      <scope>runtime</scope>
    </dependency>

네, spring-boot-dependencies 에 의해서 강제로 8.0.13으로 managed 됩니다.

 

이럴땐 propertis를 이용해 아래와 같이 설정해주면 됩니다.

  <properties>
    <mysql.version>5.1.47</mysql.version>
  </properties>`

 

반응형
반응형

spring을 이용해 메일 발송하는 샘플 코드 입니다.


  <dependency>
    <groupId>javax.mail</groupId>
    <artifactId>mail</artifactId>
    <version>1.4.7</version>
  </dependency>
  <dependency>
    <groupId>org.springframework</groupId>  
    <artifactId>spring-context-support</artifactId>
    <version>5.1.5.RELEASE</version>  
  </dependency>


 
import java.io.UnsupportedEncodingException;
import java.util.Properties;
 
import javax.mail.MessagingException;
import javax.mail.internet.InternetAddress;
import javax.mail.internet.MimeMessage;
 
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.mail.MailException;
import org.springframework.mail.javamail.JavaMailSenderImpl;
import org.springframework.mail.javamail.MimeMessageHelper;
 
public class MailUtil {
  private static Logger logger = LoggerFactory.getLogger(MailUtil.class);
 
  public static void sendMail(String subject, String address, String personal, String htmlBody, PrettyLog prettyLog)
      throws MessagingException, UnsupportedEncodingException {
    prettyLog.start("메일발송");
    prettyLog.append("SUBJECT", subject);
    prettyLog.append("ADDRESS", address);
    prettyLog.append("PERSONAL", personal);
    prettyLog.append("BODY", htmlBody.substring(0, Math.min(10, htmlBody.length())));
    JavaMailSenderImpl mailSender = new JavaMailSenderImpl();
    mailSender.setHost(CoreConstants.MAIL_HOST);
    mailSender.setPassword(CoreConstants.MAIL_PW);
    mailSender.setPort(CoreConstants.MAIL_PORT);
    mailSender.setUsername(CoreConstants.MAIL_ID);
    if (mailSender.getPort() == 587) {
      Properties javaMailProperties = new Properties();
      javaMailProperties.setProperty("mail.smtp.starttls.enable""true");
      mailSender.setJavaMailProperties(javaMailProperties);
    }
 
    MimeMessage msg = mailSender.createMimeMessage();
    MimeMessageHelper helper = new MimeMessageHelper(msg, false"UTF-8");
    helper.setFrom(new InternetAddress("보내는사람메일주소", "보내는사람이름"));
    helper.setTo(new InternetAddress(address, personal));
    helper.setSubject(subject);
    helper.setText(htmlBody, true);
    // helper.addAttachment("파일명", new File("파일 경로"));
    try {
      mailSender.send(msg);
    } catch (MailException ex) {
      prettyLog.append("ERROR""메일발송 실패");
      logger.error("메일발송실패", ex);
    }
    prettyLog.stop();
  }
}
 


반응형
반응형

Spring Boot 환경에서 Spring security를 사용하고 있습니다.


원래 ...headers().frameOptions().sameOrigin() 을 기본으로 전체 적용해서 사용하고 있었습니다.


그런데 외부에 iframe에 들어가야하는 페이지가 하나 추가적으로 필요하게 되었습니다.


그냥 url 1개만 X-Frame-Options 헤더를 제거하면 되는데 이게 생각처럼 쉽지가 않았습니다.


아무리 구글링해도 명쾌한 해답이 없었습니다.


무수한 삽질 끝에 regexMatcher 를 이용해 URL 기반으로 Spring security 설정을 분리할 수 있었습니다.


/link/iframe 이라는 URL 만 X-Frame-Options 헤더를 제거하는 코드 샘플입니다.

@EnableWebSecurity
@Configuration
public class DefaultHttpSecurityConfig extends WebSecurityConfigurerAdapter {
  ...
  @Override
  protected void configure(HttpSecurity http) throws Exception {
    http.antMatcher("/link/iframe")//
        // ---------------
        .authorizeRequests()//
        //
        .antMatchers("/link/iframe").permitAll()//   
        .and().headers().frameOptions().disable();
 
    http.regexMatcher("^((?!" + "/WEB-INF/jsp/link/iframe.jsp" + ").)*$")
        // ---------------
        .authorizeRequests()//
        //
        .antMatchers(//
            ...
        ).permitAll()//
 
        .anyRequest().authenticated()//
        .and().headers().frameOptions().sameOrigin()//
        ...
    // ---------------
    ;
 
    ...
   
  }
 
  ...
}


특이사항으로 regexMatcher는 url이 아닌 view 실제 경로로 비교를 합니다.


org.springframework.security.web.util.matcher.RegexRequestMatcher 클래서의 matches 메서드에서 확인할 수 있습니다.


당연히 URL 비교로 생각하고 이 부분에서 삽질을 많이 했네요. ( 도대체 왜 view파일로 비교하는거지 ㅡㅡ? )


반응형
반응형

Spring boot 에서 사용자가 정의한 favicon을 사용하기위한 방법입니다.


application.properties

spring.mvc.favicon.enabled=false


반응형
반응형

java.lang.IllegalStateException: No Java compiler available for configuration options compilerClassName: [null] and compiler: [null]


오류 발생시에 아래 라이브러리 추가.


        <!-- Add this -->
        <dependency>
            <groupId>org.eclipse.jdt.core.compiler</groupId>
            <artifactId>ecj</artifactId>
            <version>4.6.1</version>
            <scope>provided</scope>
        </dependency>


* Spring-boot 2.1.3.RELEASE 에서 발생

반응형
반응형

mybatis spring boot starter 를 이용하고 있습니다.

    <dependency>
      <groupId>org.mybatis.spring.boot</groupId>
      <artifactId>mybatis-spring-boot-starter</artifactId>
      <version>1.3.2</version>
    </dependency>


보통은 application.properites 에서 아래와 같이 설정해서 사용하고 있습니다.

mybatis.config-location=classpath:config/mybatis-config.xml


config/mybatis-config.xml

<?xml version="1.0" encoding="UTF-8" ?>
<!DOCTYPE configuration
  PUBLIC "-//mybatis.org//DTD Config 3.0//EN"
  "http://mybatis.org/dtd/mybatis-3-config.dtd">
<configuration>
  <!-- <properties resource="config/jdbc.properties" /> -->
  <settings>
    <setting name="cacheEnabled" value="true" />
    <setting name="useGeneratedKeys" value="true" />
    <setting name="defaultExecutorType" value="REUSE" />
    <setting name="defaultStatementTimeout" value="3000" />
  </settings>
  <typeAliases>
    <package name="goni9071.plugin.member.entity" />
    <package name="goni9071.core.entity" />
  </typeAliases>
  <mappers>
    <mapper resource="core/sql/a.xml" />
    <mapper resource="core/sql/b.xml" />
    <mapper resource="core/sql/c.xml" />
    <mapper resource="plugin/member/sql/d.xml" />
    <mapper resource="plugin/member/sql/e.xml" />
    <mapper resource="plugin/member/sql/f.xml" />
  </mappers>
 
</configuration>


개발을 하다보면 프로젝트를 여러개로 나누어서 해야하는 경우도 있습니다.

그리고 쿼리들도 서로 의존성 없이 나누어져서 사용해야할 수도 있습니다.

multiple datasource 이야기는 아닙니다.


one datasource 에서 독릭접으로 프로젝트를 구성하고 mybatis도 따로 구성하고 싶은 이야기입니다.

하지만 one datasource 에서 Connection pool을 별도로 사용하고 싶지는 않습니다.


One Datasource + One Connection Pool + Multiple Configuration 이 정도 인것 같습니다.


아무리 찾아봐도 mybatis-config.xml 을 여러개로 나누어서 사용하는 방법은 없는 것 같았습니다.


하지만 mybatis-config.xml 을 Java Config로 변경하고 Java Config를 여러개로 나누는 것은 가능했습니다.



application.properties

mybatis.configuration.cache-enabled=true
mybatis.configuration.use-generated-keys=true
mybatis.configuration.default-executor-type=reuse
mybatis.configuration.default-statement-timeout=3000



Core 프로젝트

 
import java.io.InputStream;
 
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.io.Resources;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class CorerMybatisConfig {
  private static Logger logger = LoggerFactory.getLogger(CorerMybatisConfig.class);
 
  @Bean
  ConfigurationCustomizer coreMybatisConfigurationCustomizer() {
    return new ConfigurationCustomizer() {
      @Override
      public void customize(org.apache.ibatis.session.Configuration configuration) {
        try {
          configuration.getTypeAliasRegistry().registerAliases("goni9071.core.entity");
          String[] mapperResources = { "core/sql/a.xml""core/sql/b.xml""core/sql/c.xml" };
          for (String resource : mapperResources) {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            ErrorContext.instance().resource(resource);
            mapperParser.parse();
          }
        } catch (Exception e) {
          logger.error("Mybatis Loading Error", e);
        }
      }
 
    };
  }
}


Plugin Member 프로젝트

 
import java.io.InputStream;
 
import org.apache.ibatis.builder.xml.XMLMapperBuilder;
import org.apache.ibatis.executor.ErrorContext;
import org.apache.ibatis.io.Resources;
import org.mybatis.spring.boot.autoconfigure.ConfigurationCustomizer;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
 
@Configuration
public class PluginMemberMybatisConfig {
  private static Logger logger = LoggerFactory.getLogger(CorerMybatisConfig.class);
 
  @Bean
  ConfigurationCustomizer coreMybatisConfigurationCustomizer() {
    return new ConfigurationCustomizer() {
      @Override
      public void customize(org.apache.ibatis.session.Configuration configuration) {
        try {
          configuration.getTypeAliasRegistry().registerAliases("goni9071.plugin.member.entity");
          String[] mapperResources = { "plugin/member/sql/d.xml""plugin/member/sql/e.xml""plugin/member/sql/f.xml" };
          for (String resource : mapperResources) {
            InputStream inputStream = Resources.getResourceAsStream(resource);
            XMLMapperBuilder mapperParser = new XMLMapperBuilder(inputStream, configuration, resource, configuration.getSqlFragments());
            ErrorContext.instance().resource(resource);
            mapperParser.parse();
          }
        } catch (Exception e) {
          logger.error("Mybatis Loading Error", e);
        }
      }
 
    };
  }
}



주의할점은 아래 코드에서 보이는 것 처럼 ConfigurationCustomizer 가 실행되려면 application.properites 에 mybatis.configuration 가 하나 이상있어야 합니다. 그리고 mybatis.config-location 가 있어도 안됩니다.


org.mybatis.spring.boot.autoconfigure.MybatisAutoConfiguration

...
  @Bean
  @ConditionalOnMissingBean
  public SqlSessionFactory sqlSessionFactory(DataSource dataSource) throws Exception {
    SqlSessionFactoryBean factory = new SqlSessionFactoryBean();
    factory.setDataSource(dataSource);
    factory.setVfs(SpringBootVFS.class);
    if (StringUtils.hasText(this.properties.getConfigLocation())) {
      factory.setConfigLocation(this.resourceLoader.getResource(this.properties.getConfigLocation()));
    }
    Configuration configuration = this.properties.getConfiguration();
    if (configuration == null && !StringUtils.hasText(this.properties.getConfigLocation())) {
      configuration = new Configuration();
    }
    if (configuration != null && !CollectionUtils.isEmpty(this.configurationCustomizers)) {
      for (ConfigurationCustomizer customizer : this.configurationCustomizers) {
        customizer.customize(configuration);
      }
    }
...


반응형
반응형

쓸때마다 헤메는 HttpServletRequest  method 정리입니다.

 

요청 URL : http://localhost:8090/jam/abc?type=spider

 

 

 

반응형
반응형
 
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.List;
import java.util.zip.ZipEntry;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;
 
import com.google.common.collect.Lists;
 
public class ZipUtil {
  public static void zip(String zipFileName, List<File> files) throws IOException {
    byte[] buf = new byte[1024];
 
    ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFileName));
    try {
 
      for (int i = 0; i < files.size(); i++) {
        FileInputStream in = new FileInputStream(files.get(i));
        try {
          String fileName = files.get(i).getAbsolutePath();
 
          ZipEntry ze = new ZipEntry(fileName);
          out.putNextEntry(ze);
 
          int len;
          while ((len = in.read(buf)) > 0) {
            out.write(buf, 0, len);
          }
          out.closeEntry();
        } finally {
          in.close();
        }
      }
    } finally {
      if (out != null) {
        out.close();
      }
    }
  }
 
  public static List<String> unzip(String zipFilePath, String destDir) throws IOException {
    File dir = new File(destDir);
    if (!dir.exists()) {
      dir.mkdirs();
    }
    byte[] buffer = new byte[1024];
    FileInputStream fis = null;
    ZipInputStream zis = null;
    ZipEntry ze = null;
    List<String> fileList = Lists.newArrayList();
    try {
      fis = new FileInputStream(zipFilePath);
      zis = new ZipInputStream(fis);
      ze = zis.getNextEntry();
      while (ze != null) {
        String fileName = ze.getName();
        File newFile = new File(destDir + File.separator + fileName);
        fileList.add(fileName);
        if (ze.isDirectory()) {
          newFile.mkdirs();
        } else {
          FileOutputStream fos = new FileOutputStream(newFile);
          int len;
          while ((len = zis.read(buffer)) > 0) {
            fos.write(buffer, 0, len);
          }
          fos.close();
        }
        zis.closeEntry();
        ze = zis.getNextEntry();
      }
      zis.closeEntry();
      zis.close();
    } finally {
      if (fis != null) {
        fis.close();
      }
    }
    return fileList;
  }
}
 


반응형

+ Recent posts