[Servlet 3.0 @MultipartConfig]파일 업로드가 좀 편해졌으려나…

먼저, Servlet 3.0으로 파일업로드를 해보자.

일단, 간단한 파일 업로드 폼을 만들었다.

다음은 @WebServlet@MultipartConfig를 추가해서 업로드 파일을 저장할 장소와 파일 크기, 요청의 총 크기 등을 설정할 수 있다.

여기서 핵심은, Part라는 API다. MultiPart 타입의 요청에서 한 부분을 Part라고 참조할 수 있으니, 해당 요청에 파일만 업로드 했다는 가정하에, 한 파일당 하나의 Part가 생신다고고 볼 수 있겠다. 이 Part에 접근하는 방법은 두개.

  • 전체다 가져오기: req.getParts()
  • 한개만 가져오기: req.getPart(String name)

일단 괜찮아 보인다. 용량 설정과 디렉토리 설정을 할 수 있다는 것이 좋다. 이렇게 하면, 요청마다 업로드와 관련 된 설정을 유동적으로 가져가기 좋아보인다. 대신 여러번 설정해야 하면 귀찮기도 하겠지만, 커스텀 애노테이션을 만들거나, 자바 클래스 상속으로 해결할 수 있을지도 모르니깐.. (근데 이건 안해봐서 좀.. ㅋ)

시험삼아 chmox.dmg파일을 업로드해보고, Request 정보를 출력해봤다.

Name:
file
Header:
content-type
application/octet-stream
content-disposition
form-data; name=”file”; filename=”Chmox-0.3.dmg”
Size:
256905

파일 이름을 알아내려면 content-diposition 헤더 정보를 읽어서 그 중에서도 filename=”부터 “까지를 잘라내야하는데, 참 귀찮은 일이다. 그보다는 파일을 선택했을 때 뷰에서 해당 파일 이름을 input type=”text”에 자동으로 채워주고, 서버에서는 그 값을 받아서 사용하는게 더 좋겠다.

이번에는 이 기능을 스프링 3.1을 사용해서 해봤다.

http://static.springsource.org/spring/docs/3.1.x/spring-framework-reference/html/mvc.html#mvc-multipart-resolver-standard

레퍼런스를 참고했다. 일단 레퍼런스에 글로만 설명된대로 web.xml을 손봐야한다.

이렇게 하면 이제 해당 DispatcherServlet으로 들어오는 요청이 Servlet 3.0 파일 업로드 기능을 사용할 수 있게 된다.

아 그리고 빈을 하나 추가해줘야 한다. 아파치 커먼스 파일업로드를 쓸때도 하나 등록했었으니깐.. 이건 뭐 쌤쌤.

이전에는 요거 대신 아래 녀석을 등록했었다.

차이가 있다면, 파일 업로드 용량을 스프링 빈으로 설정하던걸, web.xml로 옮겨갔다는 것… 난 이전이 더 편한것 같다.

이런 고민이 생긴다.

“특정 요청마다 다른 기준으로 용량을 제한해야 한다면?”

스프링으로는 어떻게 하면 좋을까? multipartResolver는 DispatcherServlet당 하나밖에 못쓰니까.. 뭔가 URL 별로, 다른 MultipartResolver를 사용할 수 있는 기능을 가진 MultipartResolver를 만들면 될까나~?

[Servlet 3.0 @WebServlet]스프링 DispatcherServlet을 서블릿 3.0 @WebServlet으로 올려볼까?

서블릿 3.0에 web.xml없이 애노테이션만 추가하면 서블릿으로 자동 등록되는 기능이 생겼다.

http://www.servletworld.com/servlet-tutorials/servlet3/webservlet-annotation-example.html

자세한 내용은 여기 있고..

우선, pom.xml에 의존성을 추가해야한다.

이렇게 두개가 필요하고, 이런류의 의존성은 보통 provided 스코프로 사용하게 좋은 습관이다. 왜그런지는 생략;

이렇게 간단하게 자바 코드만 추가하고, web.xml을 전혀 만들지 않아도, 브라우저에서 /test를 요청하면 Hello를 볼 수 있다.

난 사실 처음에 스프링 3.1에서 서블릿 3.0을 지원한다길래 이런 모습을 상상했었다.

서블릿 3.0부터는 web.xml을 사용할 필요가 없고, DisaptcherServlet도 HttpServlet을 (매우 깊게) 상속해서 만든 클래스니까 이런식으로 등록할 수 있지 않을까? 싶었다.

해보니까 잘 된다.

그래도 저렇게 쓰진 않겠지;;; ㅋ

[Java Tool] jstat

http://download.oracle.com/javase/6/docs/technotes/tools/share/jstat.html

JVM 퍼포먼스 통계치를 보여준다. GC 튜닝할 때 사용하는 주요 툴.

jstat 제너럴옵션 | 출력옵션 vmid 인터벌 횟수

제너럴옵션

  • help: 잘 나옴.
  • version: 이건 잘 안됨.
  • options: 출력 옵션 목록 보기.

출력 옵션
class: 클래스로더 통계

  • Loaded: 로딩한 클래스 수
  • Bytes: 로깅한 클래스 용량(Kbytes)
  • Unloaded: 언로딩한 클래스 수
  • Bytes: 언로딩한 클래스 수
  • Times: 로딩/언로딩 수행하는데 걸린 시간(총계일듯)

compiler: 핫스팟 JIT 컴파일러 통계

gc: 힙 영역 GC 통계

gccapacity: 전체 메모리 영역 사이즈 통계

  • NGCMN: 뉴 영역 최소 크기
  • NGCMX: 뉴 영역 최대 크기
  • NGC: 현재 뉴 영역 크기
  • S0C: 현재 서바이버 0영역 크기
  • S1C: 현재 서바이버 1영역 크기
  • EC: 현재 에덴 영역 크기
  • OGCMN: 올드 영역 최소 크기
  • OGCMX: 올드 영역 최대 크기
  • OGC: 현재 올드 영역 크기
  • PGCMN: 펌 영역 최소 크기
  • PGCMX: 펌 영역 최대 크기
  • PGC: 현재 펌 영역 크기
  • YGC: 영 GC 발생 수
  • FGC: 풀 GC 발생 수

gcutil: GC 통계

  • S0: 서바이버 0영역 현재 용량 비율(현재 먹고 있는거/전체 * 100 %)
  • S1: 서바이버 1영역 현재 용량 비율
  • E: 현재 에덴 영역 용량 비율
  • O: 현재 올드 영역 용량 비율
  • P: 현재 펌 영역 용량 비율
  • YGC: 영 영역 GC 발생 수
  • YGCT: 영 영역 GC 수행 시간(누적)
  • FGC: 풀 GC 발생 수
  • FGCT: 풀 GC 수행 시간(누적)
  • GCT: 전체 GC 수행 시간(누적)

gccause: gcutil과 비슷한데 GC cause를 보여준다.

gcnew 뉴 영역 GC 통계

gcnewcapacity: 뉴 영역 사이즈 통계

gcold
gcoldcapacity
gcpermcapacity
printcompilation: 핫스팟 컴파일 메서드 통계


[Java Tool] jps

http://download.oracle.com/javase/1.5.0/docs/tooldocs/share/jps.html

자바 프로세스 확인할 수 있는 툴. JVM홈/bin 디렉토리에 들어있는 명령어기 때문에 보통 PATH로 지정되어 있을테니 그냥 사용하면 된다.

톰캣을 띄워놓고 jps를 입력해봤다.

옵션
-q: 프로세스 이름을 생략한다. (비추)

-m: 메인 메서드로 전달한 인자값 보여준다.

-l: 메인 클래스 또는 애플리케이션의 JAR 파일 전체 경로를 보여준다.

-v, -V, -J는 생략


자바에서 Generic 타입 객체 만들기 (T 타입 객체 만들기)

퀴즈.. 자바 Generic에서 타입으로 넘긴 T의 객체를 만들 수 있을까…정답은 “만들 수 있다.” 인데 전 오답을 말했네요. 크헉;; 예전에 포스팅도 하고 코딩까지 해놨으면서 이런 실수를!!

무슨 문제냐면..

[java]

public class Generic<T> {

protected T objectT;

public Generic() {

//TODO T 타입 객체를 objectT에 할당하기

}

}

[/java]

[java]
public class Book {}
[/java]

[java]
public class BookGeneric extends Generic<Book>{}
[/java]

이때 다음 테스트가 돌아가야 됩니다.

[java]
public class GenericTest {

@Test public void typeInfer(){

BookGeneric bookGeneric = new BookGeneric();

assertNotNull(bookGeneric.objectT);

}
}

[/java]

충분히 만들 수 있습니다. 요렇게 하면 됩니다. 저도 어떤 외국 블로거에서 봤던 코드인데 회사 플젝에서도 써먹고 봄싹 플젝에서도 써먹고 있습니다. 물론 전 그냥 타입만 필요해서 Class<T> classT 추론하는데까지만 쓰고 객체를 만들 일은 없어서 newInstance() 까지 호출한적은 거의 없지만요..

[java]
public class Generic<T> {

protected Class<T> classT;

protected T objectT;

@SuppressWarnings("unchecked")

public Generic() {

ParameterizedType genericSuperclass = (ParameterizedType) getClass().getGenericSuperclass();

Type type = genericSuperclass.getActualTypeArguments()[0];

if (type instanceof ParameterizedType) {

this.classT = (Class) ((ParameterizedType) type).getRawType();

} else {

this.classT = (Class) type;

}

try {

this.objectT = classT.newInstance();

} catch (InstantiationException e) {

throw new RuntimeException(e);

} catch (IllegalAccessException e) {

throw new RuntimeException(e);

}

}

}
[/java]