프로그래밍/JAVA2013. 2. 1. 11:40

포함된 메소드


  • Date getNextYear(Date date) - 1년 후의 날을 구한다.
  • Date getPreviousYear(Date date) - 1년 전의 날을 구한다.
  • Date getNextMonth(Date date) - 한달 후의 날을 구한다.
  • Date getPreviousMonth(Date date) - 한달 전의 날을 구한다.
  • Date getNextWeek(Date date) - 7일 후의 날을 구한다.
  • Date getPreviousWeek(Date date) - 7일 전의 날을 구한다.
  • Date getNextDate(Date date) - 다음 날을 구한다.
  • Date getPreviousDate(Date date) - 하루 전 날을 구한다.
  • Date getFirstDateOfWeek(Date date) - 해당 주의 첫번째 날을 구한다.
  • Date getLastDateOfWeek(Date date) - 해당 주의 마지막 날을 구한다.
  • Date getFirstDateOfMonth(Date date) - 해당 연도 달의 첫번째 날을 구한다.
  • Date getFirstDateOfMonth(int year, int month) - 해당 연도 달의 첫번째 날을 구한다.
  • Date getLastDateOfMonth(Date date) - 해당 연도 달의 마지막 날을 구한다.
  • Date getLastDateOfMonth(int year, int month) - 해당 연도 달의 마지막 날을 구한다.
  • Date minimized(Date date) - 시,분,초를 모두 최소치로 초기화한다.
  • Date maximize(Date date) - 시,분,초를 모두 최대치로 초기화한다.

소스코드
import java.util.Calendar;
import java.util.Date;

import org.apache.commons.lang.time.DateUtils;

public class DateUtilsEx {

	public static Date getDate(int year, int month, boolean truncate) {
    	Calendar cal = Calendar.getInstance();
    	cal.set(Calendar.YEAR, year);
    	cal.set(Calendar.MONTH, month);
    	
    	if( truncate ) {
    		cal = DateUtils.truncate(cal, Calendar.MONTH);
    	}
    	
    	return cal.getTime();
    }

    public static Date getDate(int year, int month) {
    	return getDate(year, month, false); 
    }
    
    /**
	 * 1년 후의 날을 구한다.
	 */
	public static Date getNextYear(Date date) {
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.add(Calendar.YEAR, 1);
    	
    	return cal.getTime();
    }

	/**
	 * 1년 전의 날을 구한다.
	 */
    public static Date getPreviousYear(Date date) {
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.add(Calendar.YEAR, -1);
    	
    	return cal.getTime();
    }
    
    /**
	 * 한달 후의 날을 구한다.
	 */
    public static Date getNextMonth(Date date) {
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.add(Calendar.MONTH, 1);
    	
    	return cal.getTime();
    }
    
    /**
	 * 한달 전의 날을 구한다.
	 */
	public static Date getPreviousMonth(Date date) {
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.add(Calendar.MONTH, -1);
    	
    	return cal.getTime();
    }
	
	/**
	 * 7일 후의 날을 구한다.
	 */
	public static Date getNextWeek(Date date) {
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.add(Calendar.DATE, 7);
    	
    	return cal.getTime();
    }
    
	/**
	 * 7일전의 날을 구한다.
	 */
	public static Date getPreviousWeek(Date date) {
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.add(Calendar.DATE, -7);
    	
    	return cal.getTime();
    }
	
	/**
	 * 다음 날을 구한다.
	 */
	public static Date getNextDate(Date date) {
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.add(Calendar.DATE, 1);
    	
    	return cal.getTime();
    }
    
	/**
	 * 하루 전 날을 구한다.
	 */
	public static Date getPreviousDate(Date date) {
    	Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.add(Calendar.DATE, -1);
    	
    	return cal.getTime();
    }
	
	/**
	 * 해당 주의 첫번째 날을 구한다.
	 */
	public static Date getFirstDateOfWeek(Date date) {
		Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
    	cal.add(Calendar.DATE, (dayOfWeek-1)*-1);
    	
    	return cal.getTime();
	}
	
	/**
	 * 해당 주의 마지막 날을 구한다.
	 */
	public static Date getLastDateOfWeek(Date date) {
		Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	int dayOfWeek = cal.get(Calendar.DAY_OF_WEEK);
    	cal.add(Calendar.DATE, 7-dayOfWeek);
    	
    	return cal.getTime();
	}
	
	/**
	 * 해당 연도 달의 첫번째 날을 구한다.
	 */
	public static Date getFirstDateOfMonth(Date date) {
		Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.getActualMinimum(Calendar.DAY_OF_MONTH));
		return cal.getTime();
	}
	
	/**
	 * 해당 연도 달의 첫번째 날을 구한다.
	 */
	public static Date getFirstDateOfMonth(int year, int month) {
		Calendar cal = Calendar.getInstance();
    	
		cal.set(Calendar.YEAR, year);
    	cal.set(Calendar.MONTH, month-1);
    	cal.set(Calendar.DATE, cal.getActualMinimum(Calendar.DAY_OF_MONTH));
		
		return minimized(cal.getTime());
	}
	
	/**
	 * 해당 연도 달의 마지막 날을 구한다.
	 */
	public static Date getLastDateOfMonth(Date date) {
		Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.set(cal.get(Calendar.YEAR), cal.get(Calendar.MONTH), cal.getActualMaximum(Calendar.DAY_OF_MONTH));
		return cal.getTime();
	}
	
	/**
	 * 해당 연도 달의 마지막 날을 구한다.
	 */
	public static Date getLastDateOfMonth(int year, int month) {
		Calendar cal = Calendar.getInstance();
    	
		cal.set(Calendar.YEAR, year);
    	cal.set(Calendar.MONTH, month-1);
    	cal.set(Calendar.DATE, cal.getActualMaximum(Calendar.DAY_OF_MONTH));
		
		return maximize(cal.getTime());
	}
	
	/**
	 * 시,분,초를 모두 최소치로 초기화한다.
	 */
	public static Date minimized(Date date) {
		return DateUtils.truncate(date, Calendar.DATE);
	}
	
	/**
	 * 시,분,초를 모두 최대치로 초기화한다.
	 */
	public static Date maximize(Date date) {
		Calendar cal = Calendar.getInstance();
    	cal.setTime(date);
    	
    	cal.set(Calendar.HOUR_OF_DAY, 23);
    	cal.set(Calendar.MINUTE, 59);
    	cal.set(Calendar.SECOND, 59);
		
		return cal.getTime();
	}
	
}


Posted by devop

댓글을 달아 주세요

  1. 짱이에용

    2019.10.21 15:44 신고 [ ADDR : EDIT/ DEL : REPLY ]

프로그래밍/JAVA2013. 1. 30. 13:57

oval는 java object를 어노테이션 기반으로 손쉽게 검증할 수 있도록 하는 validation framework입니다.


OVal is a pragmatic and extensible validation framework for any kind of Java objects (not only JavaBeans). Constraints can be declared with annotations (@NotNull, @MaxLength), POJOs or XML. Custom constraints can be expressed as custom Java classes or by using scripting languages such as JavaScript, Groovy, BeanShell, OGNL or MVEL. Besides field/property validation OVal implements Programming by Contract features by utilizing AspectJ based aspects. This for example allows runtime validation of method arguments.


maven을 통한 설치

<dependency>
	<groupId>net.sf.oval</groupId>
	<artifactId>oval</artifactId>
	<version>1.81</version>
</dependency>

또는 아래 링크에서 라이브러리 및 문서를 다운로드할 수 있습니다.


http://oval.sourceforge.net/


OVal를 이용해 java object validation을 수행하기 위한 첫 번째 단계는 아래 예제 처럼 class field에 어노테이션을 사용해 제약조건을 설정하는 것입니다. 제약조건은 class field외에도 getter 메소드에 사용할 수 있습니다.)


OVal는 아래 예제에서 사용된 제약조건외에도 pre-built 된 다양한 제약조건을 제공합니다.

(사용가능한 제약조건 어노테이션은 net.sf.oval.constraint package에서 확인할 수 있습니다.)

public class Person {

	@NotNull
	@NotEmpty
	@Length(max=10)
	private String name;
	
	@NotNull
	@Digits
	private Integer age;
	
	@NotNull
	@NotEmpty
	@Length(max=10)
	private String city;

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	public String getCity() {
		return city;
	}

	public void setCity(String city) {
		this.city = city;
	}
}

제약조건들의 AND 또는 OR와 같은 logical한 제약조건 검사를 실행하기 위해 expression language를 사용할 수도 있습니다.


 @Assert(expr = "_value ==_this.deliveryAddress || _value == _this.invoiceAddress", lang = "jexl")


  • _value : contains the value to validate (field value or getter return value)
  • _this : is a reference to the validated object


위 예제에서 age의 경우 항상 0보다 커야 하므로 @Assert를 다음과 같이 활용할 수 있습니다.


@Assert(expr="_value>0", lang="jexl")
private Integer age;


사용가능한 expression language는 


  • bsh or beanshell for BeanShell
  • groovy for Groovy
  • jexl for JEXL
  • js or javascript for JavaScript (via Mozilla Rhino)
  • mvel for MVEL
  • ognl for OGNL
  • ruby or jruby for Ruby (via JRuby)

위와 같으며, 해당 language의 라이브러리가 필요합니다.
필자의 경우 apache commons의 jexl을 주로 사용합니다.

<dependency>
    	<groupId>org.apache.commons</groupId>
	<artifactId>commons-jexl</artifactId>
	<version>2.1.1</version>
</dependency>

자 이제 제약조건을 설정한 object를 실제 validation 하는 방법은 다음과 같습니다.

Validator validator = new Validator();

Person p = new Person();

// collect the constraint violations
List<ConstraintViolation> violations = validator.validate(p);

if( violations.size() > 0 ) {
	for(ConstraintViolation violation : violations) {
		System.out.println(violation.getMessage());
	}
}

object에 제약조건에 위반되는 field값이 있다면 validator.validate 메소드의 실행 결과 리스트 사이즈는 0보다 큽니다. 


위 코드에서 Person object는 name, age, city filed에 대한 값이 null 이기 때문에 위 예제에서 설정한 제약조건을 위반하였고, 다음과 같은 메세지를 확인할 수 있습니다.


  • Person.name cannot be null
  • Person.age cannot be null
  • Person.age does not satisfy condition: _value>0
  • Person.city cannot be null

OVal는 위에 설명한 기능외에도 다양한 방식을 통해 object validation을 수행할 수 있고, Spring 등과 같은 프레임워크에 통합도 가능합니다. 보다 자세한 정보는 http://oval.sourceforge.net/ 에서 확인할 수 있습니다.

Posted by devop

댓글을 달아 주세요

프로그래밍/JAVA2011. 2. 14. 15:23
1. Jersey
Jersey는 JAX-RS, 즉 RESTful 웹 서비스용 자바 API(JSR-311)의 오픈 소스 구현체 입니다. JAX-RS는 자바 어노테이션 API로 REST 아키텍처 스타일을 따르는 자바 기반 RESTful 웹 서비스를 손쉽게 작성하도록 지원합니다.



2. Jersey 사용하기
Jersey를 이용해 RESTful 웹 서비스를 구축하기 위해 다음과 같은 파일을 위 사이트에서 다운로드 합니다. 
(이 글을 쓰는 시점에서 jersey의 다운로드 버전은 1.5 입니다.)


  • asm-3.1.jar 
  • jersey-core-1.5.jar
  • jersey-server-1.5.jar
  • jersey-json-1.5.jar
  • jersey-spring-1.5.jar

jersey-json-1.5.jar와 jersey-spring-1.5.jar는 필수 요소는 아니지만 가각, JSON 객체의 JAXB 지원과 Jersey와 Spring의 통합을 위해 필요한 파일 입니다.

그 외 Spring3와 필요한 파일을 다운받아 다음 그림과 같이 라이브러리에 추가합니다.


위 그림에서는 Jersey와 Spring외에 log4j, iBatis, Apache-Commons 파일이 추가되어 있습니다.

다음으로 web.xml을 다음과 같이 편집합니다.


  sample
  Spring-Jersey sample project
  
  
  	contextConfigLocation
  	/WEB-INF/sample-context.xml
  
  
  	org.springframework.web.context.ContextLoaderListener
  
  
  	Jersey Spring Servlet
  	com.sun.jersey.spi.spring.container.servlet.SpringServlet
  	
  		com.sun.jersey.config.property.packages
  		org.itams.sample.rest;
  	
  
  
  	Jersey Spring Servlet
  	/rest/*
  
  
  	encodingFilter
  	org.springframework.web.filter.CharacterEncodingFilter
  	
  		encoding
		UTF-8
  	
  
  
  
    index.html
  

org.itams.sample.rest 패키지 이하의 클래스를 Resource를 사용할 것이며, applicationContext로 sample-context.xml를 사용합니다.

다음은 sample-context.xml의 내용입니다.




	
	
	
	
        
            
                classpath:jdbc.properties
            
        
    
    
    
		
        
        
        
        
    
    
    
    
    
     
    	
    	
    
	
	 
    	
    
    
    
    
    
		
    
    
    
    
    
	
		
	
    
    
    
    
	
		
			
		
	

Spring + iBatis를 DAO 구현은 다음과 같습니다.
package org.itams.sample.rest.hello.dao;

import org.itams.sample.rest.hello.Hello;

public interface HelloDAO {

	public Hello getHello();
	
}
package org.itams.sample.rest.hello.dao;

import org.itams.sample.rest.hello.Hello;
import org.springframework.orm.ibatis.support.SqlMapClientDaoSupport;

public class HelloIBatisDAO extends SqlMapClientDaoSupport implements HelloDAO {

	@Override
	public Hello getHello() {
		return (Hello)getSqlMapClientTemplate().queryForObject("selectHello");
	}

}
JAXB를 통해 XML 또는 JSON객체로 마샬링/언마샬링을 지원하는 POJO객체 Hello는 다음과 같습니다.
package org.itams.sample.rest.hello;

import javax.xml.bind.annotation.XmlRootElement;

@XmlRootElement(name="Hello")
public class Hello {

	private String message;

	public String getMessage() {
		return message;
	}

	public void setMessage(String message) {
		this.message = message;
	}
	
}

마지막으로  RESTful 웹 서비스를 URL을 통해 노출하는 인터페이스 클래스는 다음과 같습니다.
package org.itams.sample.rest.hello.service;

import org.itams.sample.rest.hello.Hello;

public interface HelloService {

	public Hello getHello(String accept);
	
}
package org.itams.sample.rest.hello.service;

import javax.ws.rs.GET;
import javax.ws.rs.HeaderParam;
import javax.ws.rs.Path;
import javax.ws.rs.Produces;
import javax.ws.rs.core.MediaType;

import org.apache.log4j.Logger;
import org.itams.sample.rest.hello.Hello;

@Path("/hello")
public class HelloServiceImpl implements HelloService {

	private Logger logger = Logger.getLogger("org.itams.sample.service");

	private HelloServiceDelegate delegate;
	
	public HelloServiceImpl(HelloServiceDelegate delegate) {
		this.delegate = delegate;
	}

	@Override
	@GET
	@Produces({MediaType.APPLICATION_XML, MediaType.APPLICATION_JSON})
	public Hello getHello(@HeaderParam("Accept") String accept) {
		logger.info("Method: GET, Accept: "+accept);
		return delegate.getHello();
	}
	
}
package org.itams.sample.rest.hello.service;

import org.itams.sample.rest.hello.Hello;
import org.itams.sample.rest.hello.dao.HelloDAO;

public class HelloServiceDelegate {

	private HelloDAO dao;
	
	public HelloServiceDelegate(HelloDAO dao) {
		this.dao = dao;
	}
	
	public Hello getHello() {
		return dao.getHello();
	}
	
}
RESTClient를 통해 RESTful 서비스의 정상 구동을 확인합니다.


서버로부터 리턴받을 데이터의 mime-type을 Accept로 명시하여 사용할 수 있습니다.
Accept가 생략될 경우 데이터의 기본 mime-type은 application/xml로 처리됩니다.
Posted by devop

댓글을 달아 주세요

프로그래밍/JAVA2010. 4. 30. 10:42
Javadoc은 java 소스파일 내의 선언 및 주석을 해석해, 클래스, 인터페이스, 생성자 , 메소드, 및 필드에 도착해 기술한 HTML 페이지 세트를 생성하는 툴입니다. Javadoc의 주석은 /** 로 시작하여 */로 끝나야 하며 각 라인은 하나 이상의 *로 시작해야 하는 규칙을 가지고 있습니다. 또한 주석내에 HTML 태그를 포함할 수 있습니다.

JAutoDoc은 Javadoc을 이클립스에서 편리하게 사용하기 위한 플러그인 입니다.


이클립스에서 Help > Install New Softwere를 선택하고 다음과 같은 주소를 입력하면 JAutoDoc을 설치할 수 있습니다.

 http://jautodoc.sourceforge.net/update/


설치가 완료되면 이클립스를 재시작합니다. 

소스파일에 Javadoc주석을 생성하기 위해 다음과 같이 오른쪽 마우스를 클릭해 JAutoDoc > Add Javadoc을 선택합니다.


다음과 같이 소스파일에 Javadoc주석이 생성된 것을 확인할 수 있습니다.


Javadoc의 주요 주석태그는 다음과 같습니다.

  • @author : 클래스나 인터페이스의 제작자 표시
  • @version : 버전정보
  • @return : 메소드가 void형이 아닌경우 return value type을 기술
  • @exception : 메소드가 발생 시킬수 있는 예외를 기술
  • @throws : @exception Tag와 동일
  • @deprecated : 다음버젼에서 폐기된 메소드를 알림
  • @param : 매개변수에 대한 설명(@param 변수 설명의 형태)
  • @serial : 기본적으로 직렬화 할 수 있는 클래스의 멤버를 설명
  • @see class_name#member  : 클래스 , 인터페이스, 메소드, 생성자 혹은 URL에 대한 전후참조표시
  • @since : Tag를 가진 객체가 언제 추가 되었는지 명시
  • {@link class_name#member label} : 메소드나 필드의 상호 참조에 대한 링크를 표시

* @see의 경우 Javadoc의 See Also: 문장과 링크가 만들어지나, @link의 경우 주석내에 링크를 생성합니다.

Javadoc의 주석 작성법은 다음과 같습니다.

  • method 상단에 /** .. */ 주석을 추가하면, Javadoc의 대상이된다.
  • comment 는 반드시 /** 으로 시작해야한다. /* 은 Javadoc의 대상이 아니다.
  • method 설명은 html 형식으로 출력된다. 즉, 줄바꿈을 하려면 <br/> 태그를 사용해야한다.
  • @param, @return, @throws 는 method 에서 필수속성이다.
  • @throws는 사용자가 처리해야할 예외이다. 따라서 이 예외가 언제 발생하는지에 대해 반드시 설명이 필요하다.
  • @return 에서 반환타입에 대해서 기술할 필요가 없다.
  • interface가 존재한다면, 구현체에서는 주석을 생성할 필요가 없다.
    interface에서 정의된 method의 의도대로 구현을 한 것이므로, interface에서의 주석만으로 충분하다.
    단, interface 에 정의되어 있지 않은 구현체의 method 는 주석을 필요로 한다.
  • 주석 작성시, 다른 클래스를 참조할 경우에는 {@link } 를 사용한다.
    이 것은 Javadoc에서 하이퍼링크를 생성한다.
  • 주석 작성시, code를 기술할 경우엔, <pre> 태그를 사용한다.

보다 자세한 내용은 http://java.sun.com/j2se/javadoc/index.jsp 에서 확인할 수 있습니다.

이클립스에서 프로젝트 > Export > Java > Javadoc을 통해 Javadoc를 생성할 수 있습니다.
한글처리를 위헤 VM option으로 다음을 입력합니다.

 -locale ko_KR -encoding UTF-8 -charset UTF-8 -docencoding UTF-8
Posted by devop

댓글을 달아 주세요

  1. 오.. 좋은기능 ㄳㄳ

    2010.05.03 15:59 신고 [ ADDR : EDIT/ DEL : REPLY ]

프로그래밍/JAVA2010. 4. 22. 11:03
이클립스에는 JUnit이 기본적으로 포함되어 있기 때문에 쉽게 테스트 케이스를 작성하고, 테스트를 해볼 수 있습니다.

아래 그림과 같이 Java 프로젝트를 생성하고 Number라는 클래스를 작성합니다.


Number클래스는 JUnit을 통해 테스트를 실행하게 될 클래스이며, test는 테스트 클래스들이 들어갈 패키지 입니다.(패키지명은 자유롭게 지정할 수 있습니다.)

Number 클래스의 코드는 다음과 같습니다.
public class Number {

	private int value;
	
	public Number() {
		this(0);
	}
	
	public Number(int value) {
		this.value = value;
	}
	
	public int add(int rhs) {
		return value += rhs;
	}
	
	public int minus(int rhs) {
		return value -= rhs;
	}
	
	public int multiply(int rhs) {
		return value *= rhs;
	}
	
	public int divide(int rhs) {
		return value /= rhs;
	}
	
	public int getValue() {
		return value;
	}
	
}
테스트 클래스를 작성하기 위해 프로젝트에 JUnit을 추가해야 합니다.
프로젝트의 Properties를 열고 Java Build Path에서 Add Library 버튼을 클릭해 JUnit을 프로젝트에 추가할 수 있습니다.



선택할 수 있는 JUnit 은 버전3과 버전4가 있는데 본 예제에서는 4를 선택하도록 하겠습니다.


아래 그림과 같이 JUnit 라이브러리가 프로젝트에 추가된 것을 확인할 수 있습니다.


Number 클래스에서 오른쪽 마우스를 클릭해 New - JUnit Test Case를 클릭합니다.

아래 그림과 같이 Package를 Number.test로 지정하고 Next를 클릭합니다.


아래 그림과 같이 테스트를 수행할 Number 클래스를 체크하고 Finish 버튼을 클릭하면 테스트 클래스가 생성됩니다.


테스트 코드는 다음과 같습니다.
package Number.test;

import static org.junit.Assert.*;

import org.junit.Test;

import Number.Number;

public class NumberTest {

	@Test
	public void testNumber() {
		Number num = new Number();
		assertEquals(0, num.getValue());
	}

	@Test
	public void testNumberInt() {
		Number num = new Number(10);
		assertEquals(10, num.getValue());
	}

	@Test
	public void testAdd() {
		Number num = new Number(10);
		assertEquals(20, num.add(10));
	}

	@Test
	public void testMinus() {
		Number num = new Number(10);
		assertEquals(5, num.minus(5));
	}

	@Test
	public void testMultiply() {
		Number num = new Number(5);
		assertEquals(25, num.multiply(5));
	}

	@Test
	public void testDivide() {
		Number num = new Number(20);
		assertEquals(2, num.divide(10));
	}

	@Test
	public void testGetValue() {
		Number num = new Number(20);
		assertEquals(20, num.getValue());
	}

}
테스트를 실행하기 위해 NumberTest 클래스에서 오른쪽 마우스를 클릭해 Run as - JUnit Test를 클립합니다.

테스트가 실행되고 아래 그림과 같이 모든 테스트가 성공했음을 확인할 수 있습니다.


만약 테스트가 실패하면 아래 그림과 같이 실패한 테스트와 메세지를 확인할 수 잇습니다.


Posted by devop
TAG java, JUnit, TDD

댓글을 달아 주세요

프로그래밍/JAVA2010. 4. 21. 15:56
JDK 5.0 릴리즈에는 어노테이션(annotation)이라 불리는 메타데이터 기능이 도입되었다. 어노테이션은 코드 조각의 작성자 명이나 컴파일러가 특정 오류를 억제하도록 지시하는 것과 같이 프로그램의 일부가 아닌 프로그램에 관한 데이터를 제공해 준다. 어노테이션은 코드가 어떻게 수행되는 것에는 아무런 영향을 주지 않는다.


1. 어노테이션의 기초
어노테이션은 @표시 뒤에 어노테이션 이름을 붙이며 클래스, 필드, 메소드 등과 같은 프로그램의 선언부에 적용할 수 있다. 
어노테이션은 가장 처음으로 그리고 종종(관례상) 그 줄에 나타나며 임의의 인수를 포함할 수 있다.

어노테이션은 세 가지 기본 범주로 나눌수 있다.

  • Marker 어노테이션은 변수가 없다. 이 어노테이션은 이름으로 구분되며 추가 데이터 없이 나타난다. 예를 들어, @MarkerAnnotation은 marker 어노테이션이다. 데이터가 없으며 단지 어노테이션 이름만 있을 뿐이다. 
  • Single-value 어노테이션은 marker와 비슷하지만 데이터를 제공한다. 싱글 비트 데이트를 제공하기 때문에 간단한 신택스를 사용할 수 있다. (단, 어노테이션 유형이 이 문법을 수용해야 함): @SingleValueAnnotation("my data")이는 @표시만 제외하고는 일반적인 자바 메소드 호출과 비슷하다. 
  • Full 어노테이션은 다중 데이터 멤버를 갖고 있다. 결과적으로 전체 신택스를 사용해야 한다. (그리고 어노테이션은 일반 자바 메소드와 더 이상 비슷하지 않다): @FullAnnotation(var1="data value 1", var2="data value 2", var3="data value 3")

JDK 5.0에서는 3가지 빌트인 어노테이션이 제공되며 직접 작성할 수 있는 커스텀 어노테이션 또한 지원된다.


2. Override 어노테이션
Override는 상위 클래스에서 선언된 한 메소드를 오버라이드 할 것임을 컴파일러에게 알려준다. 따라서 Override는 메소드에 대해서만 사용되어야 한다. (클래스, 패키지 선언, 기타 구조체는 안된다.) Override로 표시된 한 메소드가 상위 클래스에 있는 메소드를 오버라이드하는 데 실패할 경우 컴파일러는 에러를 발생시킨다.
public class OverrideTester {

  public OverrideTester() { }

  @Override
  public String toString() {
    return super.toString() + " [Override Tester Implementation]";
  }

  @Override
  public int hashCode() {
    return toString().hashCode();
  }

}
위 예제에서 @Override어노테이션은 두 개의 메소드, toString()과 hashCode()가 OverrideTester 클래스의 수퍼클래스 (java.lang.Object)에서 메소드의 버전을 오버라이드 한다는 것을 나타내고 있다. 
언뜻 보기에는 사소한 것 같지만 매우 좋은 기능이다. 이들 메소드를 오버라이딩 하지 않고는 클래스를 컴파일 할 수 없다.


3. Deprecated 어노테이션
Deprecated가 표시된 메소드가 더 이상 사용되지 말아야 함을 가리킨다. 컴파일러는 프로그램이 비추천(deprecated) 메소드나 클래스 혹은 변수를 사용할 때마다 경고를 발생시킨다.


4. SuppressWarnings 어노테이션
SuppressWarnings 어노테이션은 컴파일러가 다르게 발생될 특정 경고를 억제하도록 해준다.
제네릭를 생각해보자. 제네릭은 모든 유형의 새로운 type-safe 작동을 만든다. 특히 자바 컬렉션의 경우 더욱 그렇다. 하지만 generics 때문에 컴파일러는 컬렉션이 type-safety 없이 사용될 때 경고를 던진다.
@SuppressWarnings("unchecked")
public void nonGenericsMethod() {
  List wordList = new ArrayList();    // no typing information on the List
  wordList.add("foo");                // causes error on list addition
}
위 예제는 제네릭이 나오기 전에 작성된 레거시 코드에 대한 경고를 억제해준다.

'프로그래밍 > JAVA' 카테고리의 다른 글

OVal - Object Validation Framework for java  (0) 2013.01.30
Quartz cronExpression 테스트  (0) 2012.12.17
Jersey와 Spring으로 RESTful 웹 서비스 구축하기  (0) 2011.02.14
Javadoc  (1) 2010.04.30
이클립스에서 JUnit 사용하기  (0) 2010.04.22
Java 어노테이션(Annotation)  (0) 2010.04.21
Java Decompiler jad  (2) 2010.03.17
Apache Xml Security을 이용한 XML 전자서명  (1) 2010.03.17
MSSQL with JDBC  (0) 2010.02.03
JavaMail 첨부파일 읽기  (0) 2010.01.28
JavaMail with IMAP  (0) 2010.01.27
Posted by devop

댓글을 달아 주세요

프로그래밍/JAVA2010. 3. 17. 17:46
C와 같은 언어는 소스파일을 컴파일하면 해당 시스템에 적합한 바이너리 코드를 생성합니다.
바이너리 코드를 역컴파일 하는 것이 불가능 한것은 아니나, 이것은 지루하고 복잡한 작업입니다.
흔히들 이와같은 작업을 역공학(리버스 엔지니어링)이라 하며, 소프트웨어 보안과 라이센스 정책에 크게 위협이 될수 있는 분야이기도 합니다.

Java의 경우 C와 같은 언어와 달리 바이트 형태의 class파일을 생성하는데 'Write Once, Run Anywhere"라는 Java의 패러다임에서 알수 있듯이, JavaVM이 존재하는 모든 시스템에서 실행될수 있는 시스템 독립적인 코드입니다.
따라서, 바이트 코드와 같은 경우는 바이너리 코드와 달리 손쉽게 역컴파일이 가능합니다.

jad는 바로 Java의 class파일을 디컴파일 해주는 프로그램입니다.
즉, java소스 파일은 없고 class파일만 존재하고 있을때, jad를 이용해 class파일을 java파일로 변환할 수 있습니다.


jad를 Eclipse IDE에 통합하여 편리하게 사용할수 있게 도와주는 plugin이 있습니다.
(Eclipse 3.4에 jadclipse3.3 설치가 가능합니다.)



1. 설치하기

아래 그림과 같이 다운로드 받은 jad파일을 Eclipse디렉토리에 복사합니다.


JadClipse파일(net.sf.jadclipse_3.3.0.jar)을 Eclipse plugins디렉토리에 복사합니다.


Eclipse를 재식하고 Window > Preference > Java > JadClipse에서 아래 그림과 같이 Path to decompiler을 입력합니다.


한글이 깨지는 것은 방지하기 위해 아래 그림과 같이 마지막 항목을 체크합니다.




2. class파일 디컴파일

디컴파일하고자 하는 class파일을 더블클릭하면, 아래 그림과 같이 디컴파일된 java파일이 나타납니다.




3. 난독처리

Java 프로그램은 컴파일된 코드만 배포한다고 하여도(바이트 코드) 이로부터 소스코드를 쉽게 만들어 낼 수 있으므로 소프트웨어 보호라는 측면에서 많은 문제점을 가지고 있습니다. 이와 같은 위험으로부터 소스코드를 보호하기 위해 소스코드를 난독화 할수 있습니다. 난독화는 는프로그램 코드를 변환하는 방법의 일종으로, 프로그램에 사용된 변수명을 아무 의미없는 이름으로 변환하는 등 코드를 지저분하게 하여 사람이 읽기 어렵게 만들어주는 기술입니다.

* Java 역컴파일 방지 툴: http://proguard.sourceforge.net/

'프로그래밍 > JAVA' 카테고리의 다른 글

Quartz cronExpression 테스트  (0) 2012.12.17
Jersey와 Spring으로 RESTful 웹 서비스 구축하기  (0) 2011.02.14
Javadoc  (1) 2010.04.30
이클립스에서 JUnit 사용하기  (0) 2010.04.22
Java 어노테이션(Annotation)  (0) 2010.04.21
Java Decompiler jad  (2) 2010.03.17
Apache Xml Security을 이용한 XML 전자서명  (1) 2010.03.17
MSSQL with JDBC  (0) 2010.02.03
JavaMail 첨부파일 읽기  (0) 2010.01.28
JavaMail with IMAP  (0) 2010.01.27
JavaMail을 이용하며 메일전송  (0) 2010.01.15
Posted by devop

댓글을 달아 주세요

  1. 이클립스 버전이 3.2라 안타깝네요.
    하지만 도움이 되었습니다.
    감사합니다~~

    2010.03.24 09:59 [ ADDR : EDIT/ DEL : REPLY ]
  2. ranma

    도움되었습니다. 감사합니다.

    2010.07.22 16:02 [ ADDR : EDIT/ DEL : REPLY ]

프로그래밍/JAVA2010. 3. 17. 15:28
다음과 같은 XML 파일을 Apache Xml Security를 이용하여 Eveloped-Signature를 생성하는 과정은 다음과 같다.


1. 원본 XML의 내용
<?xml version="1.0" encoding="UTF-8"?>
<Book>
	<Title>XML 전자서명</Title>
	<Author>이연복</Author>
	<Price>10000</Price>
	<Publisher>SNUT</Publisher>
</Book>

2. 전자서명 코드
package org.sopt.dev;

import java.security.PrivateKey;
import java.security.cert.X509Certificate;

import javax.xml.XMLConstants;

import org.apache.xml.security.exceptions.XMLSecurityException;
import org.apache.xml.security.signature.XMLSignature;
import org.apache.xml.security.transforms.Transforms;
import org.w3c.dom.Document;
import org.w3c.dom.Element;
import org.w3c.dom.NodeList;

public class XMLSigner {

	private Document document = null;
	
	private PrivateKey privatekey = null;
	
	private X509Certificate x509Cert = null;
	
	public XMLSigner(Document document, PrivateKey privatekey, X509Certificate x509Cert) {
		this.document = document;
		this.privatekey = privatekey;
		this.x509Cert = x509Cert;
	}
	
	public Document sign() throws XMLSecurityException {
		XMLSignature sig = new XMLSignature(document, 
				XMLConstants.XMLNS_ATTRIBUTE_NS_URI,
				"http://www.w3.org/2000/09/xmldsig#rsa-sha1",
				"http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
		
		NodeList nodeList = document.getElementsByTagName("Book");
		Element bookElement = (Element)nodeList.item(0);
		nodeList = document.getElementsByTagName("Title");
		bookElement.insertBefore(sig.getElement(), (Element)nodeList.item(0));
		
		Transforms transforms = new Transforms(document);
		transforms.addTransform("http://www.w3.org/2000/09/xmldsig#enveloped-signature");
		transforms.addTransform("http://www.w3.org/TR/2001/REC-xml-c14n-20010315");
		sig.addDocument("", transforms, "http://www.w3.org/2000/09/xmldsig#sha1");
		
		sig.addKeyInfo(x509Cert);
		sig.sign(privatekey); 
		
		return document;
	}
	
}

3. 전자서명 결과
<?xml version="1.0" encoding="UTF-8"?>
<Book>
<ds:Signature xmlns:ds="http://www.w3.org/2000/09/xmldsig#">
<ds:SignedInfo>
<ds:CanonicalizationMethod Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:CanonicalizationMethod>
<ds:SignatureMethod Algorithm="http://www.w3.org/2000/09/xmldsig#rsa-sha1"></ds:SignatureMethod>
<ds:Reference URI="">
<ds:Transforms>
<ds:Transform Algorithm="http://www.w3.org/2000/09/xmldsig#enveloped-signature"></ds:Transform>
<ds:Transform Algorithm="http://www.w3.org/TR/2001/REC-xml-c14n-20010315"></ds:Transform>
</ds:Transforms>
<ds:DigestMethod Algorithm="http://www.w3.org/2000/09/xmldsig#sha1"></ds:DigestMethod>
<ds:DigestValue>eJGWkV4mDEzNaQebWXTmAGFP20Q=</ds:DigestValue>
</ds:Reference>
</ds:SignedInfo>
<ds:SignatureValue>
vURLd5SJnYi85UDMGutu9Czmh4SuxR56iC7Wv5znKlev0ubUCqhvZrN+9mp3H10IaW1ZJxaGu0V1
/Rg576FwVR1GVr6d3at2SLkbUZDN2DdtNPYIvBGlmRNVmdSDtDB6QhG1lhv3WmStI6Huo4lk75Ig
yyDz5PJy8OlH+R5nk+k=
</ds:SignatureValue>
<ds:KeyInfo>
<ds:X509Data>
<ds:X509Certificate>
MIIFSTCCBDGgAwIBAgIECkDV5DANBgkqhkiG9w0BAQUFADBKMQswCQYDVQQGEwJrcjEQMA4GA1UE
CgwHeWVzc2lnbjEVMBMGA1UECwwMQWNjcmVkaXRlZENBMRIwEAYDVQQDDAl5ZXNzaWduQ0EwHhcN
MDkxMTE3MTUwMDAwWhcNMTAxMTE4MTQ1OTU5WjCBqTELMAkGA1UEBhMCa3IxEDAOBgNVBAoMB3ll
c3NpZ24xEzARBgNVBAsMCnhVc2U0RXNlcm8xDDAKBgNVBAsMA0tNQjEcMBoGA1UECwwTU0FNSlVO
RyBEQVRFU0VSVklDRTFHMEUGA1UEAww+7IK87KCV642w7J207YOA7ISc67mE7IqkKChTQU1KVU5H
IERBVEVTRVJWSUNFKTAwMDQ2ODg3MDA0MDY2NDMwgZ8wDQYJKoZIhvcNAQEBBQADgY0AMIGJAoGB
AMHbMPCKVTKMVkZe4s1tW/wTDzqijdU7Oe2rGlfOuKNcdAjDxkLj77IOgLou/DIwJ0imN6xWcfd1
1stHDfl3Yi6Ehz91gaQCXENXhyhsOFP7Gie06emxstSE3tn725+U6A2w1s7Ty311k0l7U4Bf1LNJ
Db8Imp5XAaPWg+zk2U7LAgMBAAGjggJZMIICVTCBjwYDVR0jBIGHMIGEgBRK+70zLYux0YyUa//g
QjZfHJHLCKFopGYwZDELMAkGA1UEBhMCS1IxDTALBgNVBAoMBEtJU0ExLjAsBgNVBAsMJUtvcmVh
IENlcnRpZmljYXRpb24gQXV0aG9yaXR5IENlbnRyYWwxFjAUBgNVBAMMDUtJU0EgUm9vdENBIDGC
AidgMB0GA1UdDgQWBBTH8Zj4a7Qm0qa80rc6wvATAZ694jAOBgNVHQ8BAf8EBAMCBsAwegYDVR0g
AQH/BHAwbjBsBgoqgxqMmkUBAQYIMF4wLgYIKwYBBQUHAgIwIh4gx3QAIMd4yZ3BHLKUACCs9cd4
x3jJncEcACDHhbLIsuQwLAYIKwYBBQUHAgEWIGh0dHA6Ly93d3cueWVzc2lnbi5vci5rci9jcHMu
aHRtMGgGA1UdEQRhMF+gXQYJKoMajJpECgEBoFAwTgwZ7IK87KCV642w7J207YOA7ISc67mE7Iqk
KDAxMC8GCiqDGoyaRAoBAQEwITAHBgUrDgMCGqAWBBSlWn1wqt/tN4hKoAVB0RQKp7gj/jByBgNV
HR8EazBpMGegZaBjhmFsZGFwOi8vZHMueWVzc2lnbi5vci5rcjozODkvb3U9ZHAzcDM3OTgzLG91
PUFjY3JlZGl0ZWRDQSxvPXllc3NpZ24sYz1rcj9jZXJ0aWZpY2F0ZVJldm9jYXRpb25MaXN0MDgG
CCsGAQUFBwEBBCwwKjAoBggrBgEFBQcwAYYcaHR0cDovL29jc3AueWVzc2lnbi5vcmc6NDYxMjAN
BgkqhkiG9w0BAQUFAAOCAQEAB9Im0fBWHNmU8ROrlGnsAPINOvBpOu1IG7CSfxgsrSqW4DVnjuT+
+ShEccR0+IpHuBDp1a/IYzWMln69Fh7Uso+Ac60btXoPKJh8+HtlkQfaVgfdAk2zihpuB05Gg1p2
qJIk6wQgkAgreDRYMCKz00je7DD2v4tNfBmslJ4CkMXO3IVHdyudoHChDoLjuYBWDKu0JIlUiO0m
ydvyKT48SrVgNxVCA52BOsTc5XDT0f/lVRIDQ90KYA6UA1l8oZ5P6GEIwWNDnH97NGblobyhoFfT
s5ERFjKsZjygZxAdQroBWH8TNFZvLxMoOionBy/A3SGyMkmrWcr+rb/u1wC5YA==
</ds:X509Certificate>
</ds:X509Data>
</ds:KeyInfo>
</ds:Signature>

	<Title>XML 전자서명</Title>
	<Author>이연복</Author>
	<Price>10000</Price>
	<Publisher>SNUT</Publisher>
</Book>
위 코드는 입력된 XML파일의 모든 노드셋을 대상으로 c14n 정규화 알고리즘을 적용한 후 sha1 알고리즘으로 digest value를 생성한다. 이후 CanonicalizationMethod와 SignatureMethod에 명시된 알고리즘에 따라 SignedInfo를 정규화하고, 서명을 수행한다.

 KeyInfo 노드는 서명을 제3 자가 서명을 검증할수 있도록 검증키(공개키)를 포함한다.

XMLSigner의 privatekey와 x509Cert는 각각 공인인증서의 개인키(signPri.key)와 공개키(signCert.der)이다.

'프로그래밍 > JAVA' 카테고리의 다른 글

Jersey와 Spring으로 RESTful 웹 서비스 구축하기  (0) 2011.02.14
Javadoc  (1) 2010.04.30
이클립스에서 JUnit 사용하기  (0) 2010.04.22
Java 어노테이션(Annotation)  (0) 2010.04.21
Java Decompiler jad  (2) 2010.03.17
Apache Xml Security을 이용한 XML 전자서명  (1) 2010.03.17
MSSQL with JDBC  (0) 2010.02.03
JavaMail 첨부파일 읽기  (0) 2010.01.28
JavaMail with IMAP  (0) 2010.01.27
JavaMail을 이용하며 메일전송  (0) 2010.01.15
RSA 암호화  (0) 2010.01.15
Posted by devop

댓글을 달아 주세요

  1. 잘 보고 갑니다.

    2012.04.18 17:25 신고 [ ADDR : EDIT/ DEL : REPLY ]

프로그래밍/JAVA2010. 1. 15. 10:19
1. 개요
RSA는 공개키 암호시스템의 하나로, 암호화 뿐만 아니라 전자서명이 가능한 최초의 알고리즘으로 알려져 있다. RSA가 갖는 전자서명 기능은 인증을 요구하는전자 상거래 등에 RSA의 광범위한 활용을 가능하게 하였다.

RSA 암호체계의 안정성은 큰 숫자를 소인수분해하는 것이 어렵다는 것에 기반을 두고 있다. 그러므로 큰 수의 소인수분해를 획기적으로 빠르게 할 수 있는 알고리즘이 발견된다면 이 암호 체계는 가치가 떨어질 것이다. 1993년 피터 쇼어는 쇼어 알고리즘을 발표하여, 양자 컴퓨터를 이용하여 임의의 정수를 다항 시간 안에 소인수분해하는 방법을 발표하였다. 따라서 양자 컴퓨터가 본격적으로 실용화되면 RSA 알고리즘은 무용지물이 될 것이다. 그러나 양자 컴퓨터가 이 정도 수준으로 실용화되려면 아직 여러 해가 더 필요할 것으로 보인다.

RSA는 두 개의 키를 사용한다. 여기서 키란 메시지를 열고 잠그는 상수(constant)를 의미한다. 이 중 공개키(public key)는 모두에게 알려져 있으며, 메시지를 암호화(encrypt)하는데 쓰인다. 이렇게 암호화된 메시지는 개인키(private key)를 가진 자만이 복호화(decrypt)하여 열어볼 수 있다. 다시 말하면, 누구나 어떤 메시지를 암호화할 수 있지만, 그것을 해독하여 열람할 수 있는 사람은 개인키를 지닌 단 한 사람 뿐인 것이다. RSA는 소인수분해의 난해함에 기반하여, 공개키만을 가지고는 개인키를 쉽게 짐작할 수 없도록 디자인되어 있다.


2. 키의 생성
A와 B가 보안이 보장되어 있지 않은 환경에서 서로 비밀 메시지를 주고 받고 싶다고 가정하자. B가 A에게 메시지를 전달하기 위해서는 A의 공개키가 필요하다. A는 아래와 같은 방법을 통해 그만의 공개키와 개인키를 제작한다.

  1. p \,와 q \, 라고 하는 두 개의 서로 다른 (p \ne q소수를 고른다.
  2. 두 수를 곱하여 N = p q \, 을 찾는다.
  3. \varphi(N) = (p-1)(q-1) \, 를 구한다.
  4. \varphi(N) 보단 작고, \varphi(N)와 서로 소인 정수 e를 찾는다.
  5. 유클리드 호제법을 이용하여 d e \equiv 1 \pmod{\varphi(N)} 을 만족시키는 를 구한다.

A의 공개키는 위에서 구한 두 개의 숫자로 이루어진 <N, e>이고, 개인키는 d이다. A는 <N, e>만을 B에게 공개하고, B는 이 공개키를 사용하여 자신의 메시지를 암호화하게 된다. 여기서 p와 q의 보안은 매우 중요하다. 이를 가지고 d와 e의 계산이 가능하기 때문이다. 그리하여 공개키와 개인키가 생성이 된 후에는 이 두 숫자를 지워버리는 것이 안전하다.


3. 암호화
B가 M이란 메시지를 A에게 보내고 싶다고 하자. 일단 B는 이 M를 N보다 작은 숫자로 변환한다. (이 변환법(padding scheme)은 A에게도 미리 알려져 있어야 한다. 이를테면, 메시지를 토막내어 하나의 메시지가 일정 수의 비트를 넘지 않게 하는 방법이 있다. 하지만 실제로는 이중보안을 위해 더욱 복잡한 변환법이 사용된다.) 그리고 B는 A의 공개키 <N, e>를 획득하고, 다음과 같이 c를 계산한다.

  c = m^e \mod{N}

그리고 이 c를 A에게 보낸다.


4. 복호화
A는 암호화된 메시지 c를 B에게서 건네받았고, N과 d를 알고 있다. 다음 식을 통해 m을 찾는다.

m = c^d \mod{N}

위에서 설명하였듯 m을 가지고 A는 M을 찾아낼 수 있는 방법을 알고 있다.


다음 소스코드는 공인인증서의 공개키를 이용해 RSA암호화를 수행하는 클래스이다.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.Key;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class RSAEncoder {

	public static int RSA_ENCODING_KEY_SIZE = 64;
	
	private byte[] toEncode;
	
	private Key publicKey;
	
	public RSAEncoder(byte[] toEncode, Key publicKey) {
		if( toEncode == null || publicKey == null )
			throw new IllegalArgumentException();
		this.toEncode = toEncode;
		this.publicKey = publicKey;
	}
	
	public byte[] encode() throws NoSuchAlgorithmException, NoSuchProviderException, 
			NoSuchPaddingException, InvalidKeyException, 
			IllegalBlockSizeException, BadPaddingException {
		Cipher c = Cipher.getInstance("RSA", "BC");
		c.init(Cipher.ENCRYPT_MODE, publicKey);
		int length = toEncode.length;
		if( length > RSA_ENCODING_KEY_SIZE ) {
			ByteArrayInputStream bis = null;
			ByteArrayOutputStream bos = null;
			try {
				bis = new ByteArrayInputStream(toEncode);
				bos = new ByteArrayOutputStream();
				int count = 0;
				byte[] buf = new byte[RSA_ENCODING_KEY_SIZE];
				while( (count=bis.read(buf)) != -1 )
					bos.write(c.doFinal(buf, 0, count));
				return bos.toByteArray();
			} catch(Exception e) {
			} finally {
				if( bis != null ) try { bis.close(); } catch(Exception ignore) {/**/}
				if( bos != null ) try { bos.close(); } catch(Exception ignore) {/**/}
			}
		}
		return c.doFinal(toEncode);
	}
	
	public byte[] getToEncode() {
		return toEncode;
	}
	
	public void setToEncode(byte[] toEncode) {
		this.toEncode = toEncode;
	}
	
	public Key getPublicKey() {
		return publicKey;
	}
	
	public void setPublicKey(Key publicKey) {
		this.publicKey = publicKey;
	}
}
공인인증서의 공개키는 1024bit 즉, 128바이트로 이루어져 있다. 위 코드에서는 BouncyCastleProvider를 사용하였는데, RSA암호화시 입력데이터의 길이가 키의 길이보다 크다면 예외가 발생한다. 따라서 키의 크기보다 같거나 작게 입력데이트를 나누어 암호화를 수행한다.

RSA암호화된 데이터를 공인인증서의 개인키를 이용해 복호화하는 클래스는 다음과 같다.
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.NoSuchProviderException;
import java.security.PrivateKey;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.NoSuchPaddingException;

public class RSADecoder {

	public static int RSA_DECODING_KEY_SIZE = 128;
	
	private byte[] toDecode;
	
	private PrivateKey privatekey;
	
	public RSADecoder(byte[] toDecode, PrivateKey privatekey) {
		if( toDecode == null || privatekey == null )
			throw new IllegalArgumentException();
		this.toDecode = toDecode;
		this.privatekey = privatekey;
	}
	
	public byte[] decode() throws NoSuchAlgorithmException, NoSuchProviderException, 
			NoSuchPaddingException, InvalidKeyException, 
			IllegalBlockSizeException, BadPaddingException {
		Cipher c = Cipher.getInstance("RSA", "BC");
		c.init(Cipher.DECRYPT_MODE, privatekey);
		int length = toDecode.length;
		if( length > RSA_DECODING_KEY_SIZE ) {
			ByteArrayInputStream bis = null;
			ByteArrayOutputStream bos = null;
			try {
				bis = new ByteArrayInputStream(toDecode);
				bos = new ByteArrayOutputStream();
				int count = 0;
				byte[] buf = new byte[RSA_DECODING_KEY_SIZE];
				while( (count=bis.read(buf)) != -1 )
					bos.write(c.doFinal(buf, 0, count));
				return bos.toByteArray();
			} catch(Exception e) {
			} finally {
				if( bis != null ) try { bis.close(); } catch(Exception ignore) {/**/}
				if( bos != null ) try { bos.close(); } catch(Exception ignore) {/**/}
			}
		}
		return c.doFinal(toDecode);
	}

	public byte[] getToDecode() {
		return toDecode;
	}

	public void setToDecode(byte[] toDecode) {
		this.toDecode = toDecode;
	}

	public PrivateKey getPrivateKey() {
		return privatekey;
	}

	public void setPrivateKey(PrivateKey privatekey) {
		this.privatekey = privatekey;
	}	
}

'프로그래밍 > JAVA' 카테고리의 다른 글

Apache Xml Security을 이용한 XML 전자서명  (1) 2010.03.17
MSSQL with JDBC  (0) 2010.02.03
JavaMail 첨부파일 읽기  (0) 2010.01.28
JavaMail with IMAP  (0) 2010.01.27
JavaMail을 이용하며 메일전송  (0) 2010.01.15
RSA 암호화  (0) 2010.01.15
Java 쓰레드  (0) 2009.12.04
스트러츠 properties 한글 편집  (0) 2009.12.03
Java XML Parser JDOM  (2) 2009.09.03
JRE Detection  (0) 2009.08.11
자바 웹 스타트(Java Web Start)  (0) 2009.08.07
Posted by devop

댓글을 달아 주세요

프로그래밍/JAVA2009. 12. 4. 11:15
쓰레드(Thread)란, 프로그램의 실행 흐름으로서, 일반적으로 프로그램이 시작되며 main 메소드가 호출되고 하나의 메인 쓰레드를 지니게 됩니다. 프로그램에 따로 쓰레드를 생성하여 관리하는 코드가 없다면 프로그램은 시작부터 끝가지 단 하나의 쓰레드를 지니게되며 이런 프로그램을 싱글쓰레드 프로그램이라 합니다.

요즘 대부분의 CPU들은 다중코어 아키텍처를 택하고 있기때문에, CPU의 능력을 최대한 활용하기 위해서는 싱글쓰레드로 프로그램보다는 다수의 쓰레드를 두어 병렬처리가 가능하도록 설계된 멀티쓰레드 프로그램이 좋습니다.

1. Thread 클래스
Java에서 쓰레드를 생성하기 위해서 Thread 클래스를 상속하여 이용하거나, Runnable 인터페이스를 구현하여 Thread 객체를 생성할수 있습니다. 2가지 방법 모두 쓰레드를 사용하는데 문제는 없으나 Thread 클래스를 직접 상속하여 run 메소드를 오버라이드 하는 방법보다는 Runnable 인터페이스를 이용하는 방법을 추천합니다.(Java에서는 다중 상속이 불가능 하기때문에 Thread를 상속하는 방법은 다른 클래스를 상속받을수 없음을 의미합니다.)
class MyThread extends Thread {  
	@Override
	public void run() {
		// TODO here
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		MyThread t = new MyThread();
		t.start();
		t.join();
	}
}
public class ThreadTest {
	public static void main(String[] args) {
		Thread t = new Thread(new Runnable() {
			@Override
			public void run() {
			        // TODO here	
			}
		});
		t.start();
		t.join();
	}
}

2. 쓰레드 종료
쓰레드는 run메소드가 종료할때까지 유지되는데, 일반적으로 많은 워커쓰레드는 내부에 무한루프를 지니며, 외부의 입력조건에 따라 쓰레드를 종료시키는 구조를 지니고 있습니다. 자바에서 스레드를 종료할 때는 상태변수(state variable)를 사용해 run() 메소드를 탈출하는 방식을 사용하는데, 이 것을 "state based" singnaling이라 합니다. 기존에 제공되던 Thread.stop(), Thread.suspend(), Thread.resume()은 더 이상 사용할 수 없습니다.
class MyThread extends Thread {  
	
	private volatile boolean isRun = true;
	
	public void setRun(boolean isRun) {
		this.isRun = isRun;
	}

	@Override
	public void run() {
		while( isRun ) {
			// TODO here	
		}
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		MyThread t = new MyThread();
		// TODO here
		t.setRun(false);
	}
}
class MyThread extends Thread {  
	@Override
	public void run() {
		try {
			while( true ) {
				// TODO here
			}
		} catch (InterruptedException e) {
			Thread.currentThread().interrupt();
			return;
		}
	}
}

public class ThreadTest {
	public static void main(String[] args) {
		MyThread t = new MyThread();
		// TODO here
		t.interrupt();
	}
}
첫번째 방법은 직접 상태변수를 선언하고 그 것을 이용하는 것입니다. 자기 쓰레드의 종료 상태를 지정할 수 있는 상태변수(r/w enabled)를 변수로  정의하고, run() 메소드 내에서 주기적으로 종료 상태를 검사해서 메소드를 탈출(리턴)합니다. 상태변수는 volatile로 선언되거나 또는, 동기화된(synchronized) 방법으로 접근되어야 함에 주의해야합니다.

두번째 방법은 Thread.interrupt()를 이용하는 것입니다. VM 내부적으로 intrrupt status라는 내부변수를 사용하며, 위의 방법으로 다룰 수 없는 상태의 스레드를 종료하는 데 사용할 수 있습니다. 즉, wait(), wait(long), wait(long, int), 또는 join(), join(long), join(long, int), static sleep(long), static sleep(long, int) 등으로 블럭된(blocked state) 스레드의 경우에 해당 스레드 객체를 통해서 Thread.interrupt()를 호출하는 즉시, 블럭해제됨과 동시에 InterruptedException을 발생시킨후 interrupt status는 초기화(clear)됩니다.. 따라서 try 구문의 catch 블럭 내에서 적당한 종료 코드를 작성하여 쓰레드를 종료할 수 있으며, run() 메소드 외부에서 InterruptedException을 캐치한 경우에는, InterruptException을 rethrow 하거나, Thread.currentThread().interrupt()를 reassert함으로써 run()메소드까지 interrupt status가 전달되도록 해야 할수 있습니다. intrrupt status의 상태는 static Thread.interrupted()를 통해서 검사할 수 있습니다.

'프로그래밍 > JAVA' 카테고리의 다른 글

MSSQL with JDBC  (0) 2010.02.03
JavaMail 첨부파일 읽기  (0) 2010.01.28
JavaMail with IMAP  (0) 2010.01.27
JavaMail을 이용하며 메일전송  (0) 2010.01.15
RSA 암호화  (0) 2010.01.15
Java 쓰레드  (0) 2009.12.04
스트러츠 properties 한글 편집  (0) 2009.12.03
Java XML Parser JDOM  (2) 2009.09.03
JRE Detection  (0) 2009.08.11
자바 웹 스타트(Java Web Start)  (0) 2009.08.07
JFreeChart with SWT  (0) 2009.07.14
Posted by devop

댓글을 달아 주세요