Whiteship's Note

'2008/05'에 해당되는 글 27건

  1. 2008.05.29 Continuous Integration의 장점 1 (2)
  2. 2008.05.25 5.7. Diagnosing problems
  3. 2008.05.24 5.6. Considerations when using external libraries
  4. 2008.05.24 5.5. Importing and Exporting packages
  5. 2008.05.19 Terracotta (2)
  6. 2008.05.19 Maven을 사용하세요. 코딩이 편해집니다. (11)
  7. 2008.05.19 5.4. Spring XML authoring support
  8. 2008.05.17 유겐 휄러(Juergen Hoeller) 횽아한테 스프링 한 수 배우다.
  9. 2008.05.14 5.3. Required Spring Framework and Spring Dynamic Modules Bundles
  10. 2008.05.14 5.2. Extender configuration options
  11. 2008.05.14 Spring 2.5 @Controller 사용시 BindingResult 주의 할 것.
  12. 2008.05.14 Impala 프레임워크 "너무 빠르자나!!"
  13. 2008.05.13 Maven의 archetype:create DEPRECATED 되다.
  14. 2008.05.13 Maven으로 Spring DM Project 만들기(Eclipse에서 import 가능)
  15. 2008.05.12 자바의 레퍼런스 클래스 사용 가이드라인
  16. 2008.05.11 OSIV 사용시 주의 할 것
  17. 2008.05.09 Logging/StaticLog
  18. 2008.05.08 5.1. Bundle format and Manifest headers
  19. 2008.05.08 DWR이 제대로 자바스크립트 만들었는지 확인하기
  20. 2008.05.07 Whiteship's Spring Security 총 정리 (3)
  21. 2008.05.07 Security Namespace Configuration PART 2
  22. 2008.05.07 Security Namespace Configuration PART 1 (8)
  23. 2008.05.06 은근슬쩍 배포된 Spring 2.5.4 (2)
  24. 2008.05.05 Chapter 5. Packaging and Deploying Spring-based OSGi applications
  25. 2008.05.04 스프링 OSGi 번들 저장소
  26. 2008.05.03 하악하악 테스트 커버리지 (2)
  27. 2008.05.03 Spring Security 2.0.1 Released (2)

Continuous Integration의 장점 1



황당한 사태가 벌어져도 당황할 필요 없다.

불과 몇 초전에 발생했던 황당한 사태 입니다.

사용자 삽입 이미지
오늘 하루도 정말 열심히 일했고 할 만큼 했다는 생각이 들어서 최종적으로 커밋을 깔끔하게 집에 갈 생각이었습니다. 그런데 왠 걸.. FAILED 메시지가 구글톡에 전해졌습니다. 아 놔~~ 집에 가고 싶은데.. ㅠ.ㅠ

대나무(Bamboo) 숲으로 들어가서 고장난 빌드 로그를 살펴봤습니다. 이런!! 테스트가 죄다(는 아니지만 상당히 많이) 깨졌군요.

사용자 삽입 이미지

헐.. 60개가 넘는 테스트가 한 방에... 그것도 바로 전까지 모두 Success 였는데.. 흠~ 예전 같으면 갑자기 어깨가 굳고 눈동자가 커지면서 조급해졌을텐데, 이젠 좀 CI랑 친해졌는지 더이상 두려워지지 않고 차근 차근 로그를 보고 깨진 테스트를 로컬(이클립스와 콘솔에서 메이븐으로)에서 실행해 봅니다.

그리고 문제의 원인을 발견합니다. 문제의 원인은 분명 이전에 커밋한 코드와 방금 커밋하기 전 코드 사이에서 생겨난 겁니다. 왜냐면 바로 전 단계의 빌드는 Success였기 때문이죠.

문제의 원인은 간단했습니다. 스프링 XML 설정파일에서 빈 이름을 잘못 설정했기 때문입니다. 그래서 모든 통합테스트들이 깨졌고, 단위테스트들은 살아남았던 겁니다. 이걸 계기로 통합테스트가 총 몇 갠지도 덤으로 알 수 있게 됐네요. ㅋㅋ

어쨋거나 중요한 건 CI가 주는 장점 한 가지. 바로 버그 발생 원인 탐사 범위를 좁혀준다는 거... 매우 귿입니다.

이젠 그만 집으로 ㄱㄱㄱ
신고

'Continuous Integration > In Action' 카테고리의 다른 글

Continuous Integration의 장점 1  (2) 2008.05.29
top


5.7. Diagnosing problems

Spring DM/Chapter 5 : 2008.05.25 22:40


여러분이 사용하기로 선택한 OSGi 플랫폼 구현체는 현제 OSGi 환경 상태에 대한 정보를 잘 제공해 주어야 한다. 예를 들어, -console 아규먼트를 Equinox 시작시 추가하면 커맨드 라인 콘솔을 제공한다. 이것을 통해 어떤 번들들이 설치되어 있고 그들의 상태와, 해당 번들에 의해 공개된 패키지와 서비스, 번들 Resolve 실패 원인, 번들 라이프사이클 다루기 등을 할 수 있다.

게다가, 스프링 자체와 스프링 DM 번들은 문제의 원인을 조사할때 사용할 수 있는 확장 가능한 로깅 체계가 마련되어 있다. Simple Logging Facade for Java(slf4j) slf4j.jar 와 slf4j-log4j13.jar 번들을 설치하길 권장한다. 그렇게만 하면, 번들 클래스패스 루트에 log4j.properties 파일을 생성하여 사용할 수 있다.

스프링 DM 모듈은 commons-logging API를 내부적으로 사용하고 있는데, 이것은 로깅 구현체가 완전히 끼워맞출 수 있는pluggble 형태로 개발되었다는 것을 의미한다.
신고
top


5.6. Considerations when using external libraries

Spring DM/Chapter 5 : 2008.05.24 23:08


대부분의 엔터프라이즈 애플리케이션 라이브러리들은 context class loader를 통해서 타입과 리소스를 로딩할 수 있다고 가정한다. 보통 개발자가 이 것을 직접 다루지는 않지만, 애플리케이션 서버, 컨테이너 또는 멀티 쓰레드로 동작하는 애플리케이션들은 context class loader를 사용하고 있다.

OSGi R4에서는 context class loader를 통해 가용한 타입이나 리소스 집합에 대해 정의되어 있지 않다. 이것은 OSGi 플랫폼이 쓰레드 context class loader 값을 보장하지 못한다는 것이고 다시 말하자면, ccl을 관리하지 않는다는 것이다.

따라서 클래스 로딩을 하거나 동적으로 새로운 클래스를 생성하는 코드가 OSGi 환경에서는 제대로 동작하지 않을 수 있다.

스프링 DM은 해당 번들의 application context를 생성하는 도중에 번들의 클래스패스에서 가용한 모든 타입과 리소스들을 ccl을 통해 접근할 수 있도록 보장한다. 또한 스프링 DM은 외부 서비스를 호출할 때와 공개한 서비스에 대한 요청에 서비스를 할 때 CCL을 통해서 접근 가능한 클래스와 리소스를 설정할 수 있다. 방법은 5.5에서 설명했다.

OSGi R5에서는 제 3의 라이브러리에 의해 추가된 암묵적인 클래스 패스와 생성된 클래스를 지원하기 위한 스펙을 정하고 있다. 그전까지는 DynamicImport-Package manifest 헤더를 사용하거나,  Equinox의 버디buddy 매커니즘을 사용할 수 있을 것이다.
신고
top


5.5. Importing and Exporting packages

Spring DM/Chapter 5 : 2008.05.24 00:19


Import-Package와 Export-Package manifest 헤더에 대한 자세한 내용은 OSGi 스펙을 참조하도록.

번들은 자신이 의존성을 가지는 모든 외부 패키지들을 Import-Package를 사용해서 설정한다.

만약 다른 번들들이 사용할 필요가 있는 타입을 제공해야 한다면, Export-Package를 사용하여 외부 번들에서 사용할 수 있는 모든 패키지를 설정한다.
신고
top


Terracotta

Good Tools : 2008.05.19 23:44


우와 이런게 있었네..

Configuration Management

이 글을 보다가..

Using Terracotta for Configuration Management

이 글을 보다가 결국

Terracotta 홈

이리로 들어가서 데모 동영상도 보고..

스프링 싱글톤 빈 클러스터링 하기라는 글도 보고.. 클러스터링 환경에서 하이버 사용할 때 발생하는 어쩌구 저쩌구도 있고.. 메이븐 플러긴까지.. 재밌네. 귿이얌. 예비군 다녀와서 살펴봐야지. 2박 3일.. 아흑..OTL

신고
top


Maven을 사용하세요. 코딩이 편해집니다.

Build/Maven : 2008.05.19 23:14


mvn eclipse:eclipse -DdownloadSources=true -DdownloadJavadocs=true

eclipse:eclipse는 단순하게 메이븐 프로젝트를 이클립스 프로젝트로 변화해주는 것 뿐만이 아니라 이런 사랑스럽기까지한 일도 해줍니다.

대부분 자바 프로젝트에서 수 많은 라이브러리들을 사용하고 계실 줄로 압니다. 그 중에서 오픈소스도 여럿 되고 그러다 보면 소스 코드도 볼 수 있는데, 이클립스에서 "Alt + 왼쪽 클릭"으로 링크를 타고 들어가듯이 소스를 네비게이션 하는 하루 일과 중에서도 꽤 큰 비중을 차지할 것입니다.

그런데 그렇게 하다보면 좀 답답하시죠? 소스 코드는 안 나타나고 벽에 부딪힌거 마냥 .class 파일이 나타나서 길을 가로막습니다. 그래도 뭐 방법은 있습니다. attach 머시기 메뉴를 사용해서 소스 코드가 있는 위치를 알려주면 됩니다. 귀찮죠.

그래서 위의 명령어가 멋진겁니다. 한 번 사용하시면 해당 프로젝트가 참조하는 라이브러리의 소스 파일과 javaDoc을 다운로드 해옵니다. 멋지죠. 저 명령어 한 번 사용하고 이클립스에서 F5키로 프로젝트 다시 로딩 해주고(이게 꼭 필요한 과정인지는 모르겠습니다. 전 습관처럼 콘솔창에서 프로젝트가지고 장난치면 이클립스에서 매번 F5키를 눌러줍니다.) 소스 네비게이션을 하다보면 감탄하게 될 겁니다.

아예 플러그인 기본값으로 파라미터 설정해서 mvn eclipse:eclipse만 실행해도 소스코드랑 JavaDoc을 받아오게 설정할 수 도 있습니다. 참조 
신고
top


5.4. Spring XML authoring support

Spring DM/Chapter 5 : 2008.05.19 22:44


스프링 2.0은 보다 쉬운 XML 설정과 확장 가능한 XML을 도입했다. 이 중 후자는 커스텀 스키마를 만들어 스프링 XML 인프라가 자동으로 해당 설정을 읽을 수 있는 것이 가능해졌다. 그냥 그것들을 클래스패스에 두기만 하면 된다.(이 부분은 토비 사부님이 1회 KSUG에서 발표하셨던 내용) 스프링 DM은 이 기본 지식을 활용해서 OSGi 환경에서 부가적인 코드나 menifest 선언 필요 없이 커스텀 스키마를 사용할 수 있도록 했다.

Spring DM은 OSGi 공간에 배포되는 모든 번들(스프링 DM 번들이든 아니든 상관없이)들을 조사하여 커스텀 스프링 네임스페이스 선언을 가지고 있는지 스캔한다.(META-INF/spring.handlers 와 META-INF/spring.schemas 번들 영역을 확인한다.) 만약에 해당하는 선언을 찾으면, 스프링 DM은 해당 스키마를 만들고 해당 네임스페이스는 OSGi 서비스를 통해서 자동으로 스프링 번들에 의해 사용이 가능해진다

=> 즉, 커스텀 스키마를 사용하는 번들을 배포할 때 필요한 건, 네임스페이스 파서와 스키마를 제공하는 라이브러리를 OSGi 플랫폼에 배포하기만 하면 된다는 것이다.

번들의 클래스 패스 내부에 있는 커스컴 스키마는 OSGi 공간에서 다른 번들들에 의해 사용될 수 있다. 하지만, 번들 내부 라이브리의 네임스페이스는 다른 번들들에의해 공유되지 않는다. 다른 번들들이 볼 수 없다.

=> 번들로 배포된 네임스페이스만 공개 되고, 내장된 라이브러리의 네임스페이스는 공개하지 않음.

스프링 DM을 사용하면, 커스컴 네임스페이스 기능을 어떠한 부가작없도 필요없이 투명하게 지원한다. 내장된 네임스페이스 제공자(Embedded namespace provider)들은 우선권을 가지지만 공유하지는 않는다. 이와 반대로 번들로 배포된 제공자(providers deployed as bundle)들은 다른 번들들에서 참조할 수 있다.
신고
top


유겐 휄러(Juergen Hoeller) 횽아한테 스프링 한 수 배우다.



updated 2008-05-17 유겐의 마무리 멘트와 3.0에 추가될 기능 언급 추가. 캬캬캬 끝까지 친절한 유겐 횽.

스프링 지라에 올린 이슈(개선 사항으로..)

We are using GenericController with Spring 2.5 @Controller APIs. And we are using @SessionAttribute like this.
@SessionAttributes(value="model")
This session attribute name "model" are used by many diffent controllers and many JSPs.

The problem is that diffent session attribute stored with same name(here is "model").
ex, When open pop-up page from parent page, the pop-up page's session attribute will override the parent page's session attribute with same name "model". So, after close the pop-up and if we click save button on parent page, we can meet ClassCastException. ^^;;

We resolve this by modify org.springframework.web.bind.annotation.support.HandlerMethodInvoker class

updateModelAttributes method's

from
this.sessionAttributeStore.storeAttribute(webRequest, attrName, attrValue);

to
this.sessionAttributeStore.storeAttribute(webRequest, attrValue.getClass().getSimpleName() + attrName, attrValue);

and resolveModelAttribute method's

from
Object sessionAttr = this.sessionAttributeStore.retrieveAttribute(webRequest, attrName);

to
Object sessionAttr = this.sessionAttributeStore.retrieveAttribute(webRequest, paramType.getSimpleName() + attrName);

This modification don't need any change or modification existing Controllers and JSPs. They now use there own session with same name.

How about apply this modification?
And could you remove final keywords from HandlerMethodInvoker to extend that class?

Now we just copy and paste original source and modify this above code, and AnnotationMethodHandlerAdapter too. AnnotationMethodHandlerAdapter class using inner class thar extends HandlerMethodInvoker(ServletHandlerMethodInvoker). So we change the HandlerMethodInvoker import statement in AnnotationMethodHandlerAdapter to our modified version. kk.. Please think about it.

유겐 횽아 1차 답변 및 대안

We actually envisioned such use cases and designed the SessionAttributeStore strategy accordingly... You should be able to implement your custom attribute prefix strategy through decorating the SessionAttributeStore, configuring your custom decorator through the "sessionAttributeStore" property on AnnotationMethodHandlerAdapter.

Juergen

2차 질문

I saw SessionAttributeStore class but in this case that class cann't help us.

Bacause, the session attribute's (internal) name sould be change according to the command object's class's name(or each controllers).
Yes, we could do this by add @SessionAttribute(name="foobar") on every controllers. but, we made and use custom tags that expects only one session attribute name "model". So we set @SessionAttribute(name="model") this on GenericController and we don't set any @SessinoAttribute on sub-clsss of GenericController.(If you want to see this class code, I can show you.)

With SessionAttributeStrore extending, we can only set a static prefix(this is not what i want.). Even if we can modify storing name by overriding storeAttribute(WebRequest request, String attributeName, Object attributeValue) method with attribute.getClass().getSimpleName()(ex. "Foomodel"), but how can we pick up that session object by that name(ex. "Foomodel") by overring retrieveAttribute(WebRequest request, String attributeName) method. We can't. There is not enough information about runtime command object type or name.

We want to use only one static session attribute name (not prefix) that can be stored and can be retrived according to there runtime object type.(ex. FooController(extends GenericController) reference Foo command object in session by "model"(internally "Foomodel"). And BarController(extends GenericController) also can reference Bar command object in session by "model"(internally "Barmodel".)

So we just do that in here(above modified HandlerMethodInvoker class).
At least we want to extend HandlerMethodInvoker class and set to AnnotationMethodHandlerAdapter.

Is there another way to do this?

유겐 횽아 2차 답변 및 질문 그리고 대안

Good point - you can't include the param type name in the session attribute name that way.

However, I would argue that including the param type is a half-baked solution to begin with: That way you'll isolate per param type, which will work when different windows operate on model attributes of different type. However, what if multiple windows operate on the same type of attribute? What if the same controller gets opened multiple times, e.g. to concurrently edit business accounts of different customers in multiple windows?

The idea behing the SessionAttributeStore strategy is that it may use a custom conversation mechanism: either storing attributes in a managed conversation object instead of in the plain session, or prefixing attribute names with a conversation identifier and still storing them in the session directly. The latter is what I would recommend for your purposes: Determine a conversation identifier for each request (from the given WebRequest object, e.g. from the URL or from a parameter) and use it as a prefix for each attribute name. This will work for storage as well as for retrieval, and will even work for multiple windows that operate on the same type of attribute.

Juergen

감사의 말

Thanks. I missed that situation (multiple windows on same type) and misunderstanded SessionAttributeStore.
I'll try to use SessionAttributeStore. I really appreciate your recommendation. ^^

문제 해결 알려주기.

Good!! We resolve this issue with you advice.

We made an interceptor that can capture URI, and made a custom SesstionAttributeStore that make session attribute session attribute using captured URI by WebRequest. Finally we configure it to AnnotationMethodHandlerAdapter

We are so impressed to Spring Framework's extensibility and your kind advice. Thanks a lot : )

유겐 횽아 마무리 멘트

Juergen Hoeller resolved SPR-4818.
------------------------------
----

      Resolution: Won't Fix
   Fix Version/s:     (was: 2.5.5)

OK, good to hear that it works for you! I'm closing this issue then, since the existing SessionAttributeStore strategy seems to be capable of handling this use case.

In Spring 3.0, we intend to provide general conversation support out of the box, also isolating concurrently active browser windows per conversation. This will essentially be a more extensive variant of what you're achieving through your custom SessionAttributeStore - then to be available out of the box in the context of general conversation management.

Juergen

ps : 유겐 횽아 최고!!
참조: http://jira.springframework.org/browse/SPR-4818
신고
top


5.3. Required Spring Framework and Spring Dynamic Modules Bundles

Spring DM/Chapter 5 : 2008.05.14 23:42


스프링 Extender가 제대로 동작하려면 OSGi 플랫폼에 배포해야 할 몇몇 번들 아티팩트들이 있다.
- org.springframework.osgi.extender : extender 번들 자신
- org.springframework.osgi.core : 스프링 DM을 지원하는 핵심 구현체 변들
- org.springframework.osgi.io : 스프링 DM I/O 지원 라이브러리 번들

여기에 추가로 스프링 라이브러리가 필요한데, 스프링 2.5부터는 OSGi 플랫폼에 바로 배포가 가능한 형태로 배포하고 있다. 그 중에 다음을 필요로 한다.
- spring-core.jar
- spring-context.jar
- spring-beans.jar
- spring-aop.jar

추가로 다음의 라이브러리 번들을 필요로 한다. OSGi 플랫폼에 배포 가능한 형태의 JAR 파일들을 Spring DM With Dependencies 형태로 배포한 파일에 들어있다.
- aopalliance
- backport-util(JDK 1.4에 돌릴 때만 필요)
- cglib-nodep
- commons-logging API(SLF4J 강추)
- logging 구현체
신고
top


5.2. Extender configuration options

Spring DM/Chapter 5 : 2008.05.14 23:29


번들과 관련된 설정이외에, 스프링 DM은 extender의 기본 행위를 설정할 수도 있다. 이것은 Spring-DM을 관리하는 환경Managed Environment에 포함시키거나 여러 번들에 걸친 기능이 필요할 때 유용하다. 확장 가능한 설정을 위해, extender는 OSGi의 fragment를 사용하여 기본 값을 재정의 할 수 있다. extender는 META-INF/spring 밑에 있는 모든 XML 파일을 읽어들이며 application context(OsgiBundleXmlApplicationContext)를 생성한다. extender의 기본 설정을 재정의하려면, 아래 테이블을 보고 원하는 빈을 읽어와서 정의를 하고 spring-osgi-extender.jar의 Fragment로 붙이면 된다.

Fragment-Host: org.springframework.bundle.osgi.extender

표생략

5.2.1. Listening to Extender events

번들의 application context 시작이나 실패시 로그를 남기고 싶을 수 있다. 이런 경우, 스프링 DM이 제공하는
org.springframework.osgi.context.event 패키지를 정의하여 OSGi application context가 보낼 수 있는 이벤틀를
정의할 수 있다.

현재 지원하는 이벤트는 딱 두개
- OsgiBundleContextRefreshedEvent
- OsgiBundleContextFailedEvent

이 이벤트들을 받길 원하는 쪽에서는 OsgiBundleApplicationContextListener를 구현해야 한다. 그리고 해당 서비스를
OSGi 서비스로 공개하야 한다. 그럼 스프링 DM이 알아서 리스너들을 찾아서 이벤트를 그쪽으로 보내줄 것이다.

이런식의 이벤트 처리는 OSGi의 장점을 모두 누릴 수 있다. 즉 extender가 이벤트 배포자나 이벤트 리스너로 부터
완전히 분리되어 있으며, 등록/해지 과정도 간단하다.

스프링 DM 이벤트와 스프링 이벤트와는 조금 차이가 있는데, 스프링 이벤트는 이벤트가 발생한 applicaion context
내부의 빈으로 이벤트를 보내지만, OSGi 이벤트는 자신을 지켜보고 있는 다른 application context의 빈으로 보내진다.

신고
top


Spring 2.5 @Controller 사용시 BindingResult 주의 할 것.

모하니?/Coding : 2008.05.14 18:13


스프링 레퍼런스에 BindingResult에 대한 언급은 단 한 줄.
org.springframework.validation.Errors / org.springframework.validation.BindingResult  validation results for a preceding command/form object (the immediate preceding argument).

이게 끝입니다. 한 줄이라고 무시하면 안 됩니다. 진짜 중요한 한 줄입니다.

public String update(@ModelAttribute("model") Foo model, BindingResult result, Bar bar)

public String update(@ModelAttribute("model") Foo model, Bar bar, BindingResult result)

이 두 줄의 코드는 어쩌면 아무런 차이가 없을 수도 있지만 Validator를 만들어 보시면 그 차이를 알 수 있습니다. 무슨 차이인지는 비밀입니다. ㅋㅋ 이미 스프링 레퍼런스에 다 설명이 나와있어서 비밀이랄 것도 없지만 말이죠.ㅋ

힌트 1.
validator.validate(model, result);

힌트 2.
public void validate(Object obj, Errors errors) {
    ...
}


신고
top


Impala 프레임워크 "너무 빠르자나!!"

Spring DM/etc : 2008.05.14 00:12


"영양" 프레임워크가 서버 사이드에서 공식 배포를 알렸습니다.

http://code.google.com/p/impala/wiki/QuestionAndAnswers 맨 아래에 영양이라고 이름 지은 이유가 나옵니다. 영양의 자태가 곱고graceful, 엘리강트하며elegant, 기민하기agile 때문이라고 합니다. 이름 잘 짓네요.ㅋㅋ

벌써부터 스프링 DM 기반 OSGi 프레임워크가 나왔습니다. 모듈화(어떻게 잘 쪼갤 것인가)에 대한 고민과 그들의 대안을 볼 수 있는 좋은 기회라고 생각합니다.

흠.. 대충 main(공통), module, test, web, repository로 쪼갰던게, 각각에 대한 자세한 설명은 아직 못 읽었습니다. 12시가 넘으면 안약을 넣어도 졸리기 때문에 자야겠습니다.

기능 요약
http://code.google.com/p/impala/wiki/Features

시작 하기
http://code.google.com/p/impala/wiki/GettingStarted

신고
top


Maven의 archetype:create DEPRECATED 되다.

Build/Maven : 2008.05.13 21:14


이런.. 아랫 글은 쓰자마자 Deprecated 해야겠네요.

mvn archetype:create 어쩌구 저쩌구를 실행하고 콘솔에 뜨는 메시지를 잘 보면, generate로 Deprecated 됐다는 메시지를 볼 수 있습니다. 돌리다가 나중에 해봐야지 그러고 그냥 지나쳤다가, 토비 사부님께서 언급하셔서 실행해보고 놀랐습니다.

사용자 삽입 이미지

처음엔 저렇게 중간에 한 번 확인하는 기능이 추가됐구나.. 어째 좀 귀찮을 수도 있겠는데.. 하고 넘어가려다 다시 mvn archetype:generate만 쳐보라고 하셔서 쳐봤더니..

어머나..
사용자 삽입 이미지

아키타입을 선택하고 그룹 아이디, 아티팩트 아이디, 버전, 기본 패지키를 입력할 수 있게 되었습니다. 잘 보시면 32번에 스프링 DM 프로젝트도 있습니다.

에구구.. 아랫글은 쓰자마자 삶이 끝나네요. 하루살이도 아니고 한시간살이급 포스팅이었습니다.

저 목록들은 archetype-catalog.xml에서 읽어오는데, Q4E 플러그인을 사용해서 커스텀 아키타입을 설정하는 방법도 있습니다.
신고
top


Maven으로 Spring DM Project 만들기(Eclipse에서 import 가능)

Build/Maven : 2008.05.13 18:03


참조
http://www.springframework.org/node/361
http://www.springframework.org/node/360
http://opensource.atlassian.com/confluence/spring/display/DOC/HowTo+build+Spring-Osgi+using+Maven+2
http://static.springframework.org/osgi/docs/1.1.0-m2/reference/html/appendix-archetype.html

기본 명령어는 다음과 같습니다.

mvn archetype:create \
-DarchetypeGroupId=org.springframework.osgi \
-DarchetypeArtifactId=spring-osgi-bundle-archetype \
-DarchetypeVersion=${version}   \
-DgroupId=<your-project-groupId>  \
-DartifactId=<your-project-artifactId> \
-Dversion=<your-project-version>

${version} 에는 배포된 스프링 DM 프로젝트 버전을 설정해주면 됩니다. 최신 버전인 1.1.0-m2 라고 설정하시면 되겠죠.

your-group-gourpId, your-project-artifactId, your-project-version 이 값들은 뭔지 아시리라.. 생각하고 생략하겠습니다.

그럼 대충 이런걸 타이핑해야 하는데... 정말 끔찍한 일이죠.

mvn archetype:create -DarchetypeGroupId=org.springframework.osgi -DarchetypeArtifactId=spring-osgi-bundle-archetype -DarchetypeVersion=1.1.0-m2 -DgroupId=whiteship -DartifactId=springdmweb -Dversion=1.0

이걸 어떻게 오타없이 타이핑하죠? 그래서 전 그냥 텍스트 파일에 붙여넣은 다음 확장자를 bat 파일로 바꿔서 dos 커맨드 창에서 createSpringDMProject.bat를 실행시킵니다.

필요하신 분들은 다운 받으신 다음에 groupdId와 artifactId 등을 수정하신 다음 사용하시면 됩니다. 그렇게 생성된 프로젝트는 이클립스에서 바로 import 해서 사용할 수 있습니다.

사용자 삽입 이미지
기본으로 테스트 클래스들까지 만들어 줍니다. 멋져부러...
신고
top


자바의 레퍼런스 클래스 사용 가이드라인

Java : 2008.05.12 00:25


참조: http://www.ibm.com/developerworks/java/library/j-refs/

SoftReference, WeakReference, PhantomReference 클래스들이 Java2에 추가되기 전에는 오직 Strong Reference만 사용할 수 있었다.

Object obj = new Object();

여기서 obj는 힙에 저장되어 있는 개체를 참조한다. obj 레퍼런스가 존재하는한 가비지 컬렉터Garbage Collector는 저장소를 비우지 않을 것이다.

obj가 스코프를 벗어나거나 null이 될 때야 비로소 해당 객체를 참조하는 레퍼런스가 없다는 가정하에 gc의 대상이 된다. 하지만, gc의 대상이 된다고 해서 무조건 자원을 반납하는게 아니라는 것이다. 왜냐면, gc 알고리즘이 매우 다양하고 그 중에 몇몇 알고리즘은 나이 많고, 오래 산 객체들을 짧게 산 객체들 보다 덜 검사하는 경향이 있다.  따라서 gc에 의해 가용한 객체가 절대로 반환되지 않을 수도 있다. 만약에 gc가 객체 자원을 반납하는 것보다 먼저 프로그램이 종료를 하면 문제가 발생할 수 있다. 따라서, 기본적으로 gc의 대상이 된다고 해서 자원을 반드시 반납한다는 것을 절대로 보장할 수 없다.

이 정보는 레퍼런스 클래스를 분석할 때 중요하다. 비록 특정 문제에서 유용한 클래스들이기는 하지만, gc의 기본 특징 때문에 여러분이 생각하는 것 만큼 유용하지는 않을 것이다. Soft, Weak 그리고 Phantom 레퍼런스 객체들은 gc를 막지 않고 힙 객체를 참조하는 세 가지 방법을 제공한다.
  • Strongly reachable: strong reference로 참조할 수 있는 객체
  • Softly reachable: Strongly reachable 하진 않지만, soft reference로 참조할 수 있는 객체
  • Weakly reachable: 생략.
  • Phantomly reachable: 생략.
  • Clear: 객체의 레퍼런스 필드를 null로 설정한다. and declaring the object in the heap that the reference class referred to as finalizable.
SoftReference 클래스

메모리를 지각하는Memory-sensitive 캐시에 대한 레퍼런스로 사용하는 클래스이다. JVM이 out-of-memory를 발생하기 직전에 비워버리는 레퍼런스로 객체를 참조한다. 중요한 건 gc가 동작할 때, Softly reachable한 객체는 자원을 해제 할 수도 있고 그렇지 않을 수도 있다는 것이다. 객체 자원 해제는 gc 알고리즘과 gc 가 작업을 하는 시점에 가용한 메모리 양과 관계가 있다.

WeakReference 클래스

WeakReference 클래스는 기본적인 맵핑canonicalized mappings에 대한 레퍼런스로 사용하는 클래스이다. 오래 살 필요가 없고 다시 생성하는 비용이 비싸지 않는 객체에 대한 레퍼런스로 유용하다. 중요한 것은 gc 가 발동하면, Weakly Reachable한 객체의 자원을 반납한다는 것이다. 하지만, weakly reachable 한 객체를 찾고 자원을 반납하기 까지는 gc가 몇 번 발동해야 할 것이다.

PhantomReference 클래스

PhantomReference 클래스는 수집 되기 직전에 어떤 일을 수행하고 싶을 때 사용한다. ReferenceQueue와 함께 사용해야 한다.

...나머지 생략...
- 위의 세 가지 레퍼런스가 소멸 되는 과정
- 위의 레퍼런스 클래스의 get()과 ReferenceQueue의 poll()을 사용한 gc 테스트
- Weak 레퍼런스 만들려면 기본 Strong References는 null로 설정해야 함
- Soft, Weak, Phantom 레퍼런스를 다시 생성하는 방법.(gc가 발동할 수 있다는 가정 하게 코딩 해야 함.)



신고
top

Java : 2008.05.12 00:25 Trackback. : Comment.

OSIV 사용시 주의 할 것

모하니?/Coding : 2008.05.11 23:32


OSIV 기본 지식 - 참조 http://www.hibernate.org/43.html

먼저 OSIV는 Open Session In View 패턴의 약자로 보통 OSIV 필터나 인터셉터 중 하나를 사용합니다. 사용하는 이유는? 뷰 랜더링을 완료 할 때까지 세션을 유지하기 위함이다. 세션이 닫힌 상태에서 프록시로 읽어온 콜렉션이나 레퍼런스의 속성에 접근하면 LazyInitializationException이 발생하고, 이 해결책으로 뷰를 랜더링 하기 위한 세션을 새로 열수도 있겠지만, 이 방법은 그리 좋치 않다. 일단 이 작업이 이전 세션에 포함되어야 적당하지 개별적인 작업 단위로 보기는 뭐시기하기 때문이다. 요청이 오면 새로운 Session과 Transaction을 생성하고 응답을 클라이언트에 보내기 직전에 Transaction을 커밋하고 Session을 닫는게 가장 단순한 OSIV의 기능이 되겠다.

여기에 Conversation을 고려하고 서브 트랜잭션(서브 트랜잭션을 지원한다면, 두 개의 트랜잭션으로 나워서 읽고/쓰기 작업을 하는 트랜잭션과 뷰에 랜더링 하는 읽기 전용 트랜잭션으로 쪼개는 것이 좋다. 쓰기롹을 빨리 반환할 수 있으니까.)그리고 예외 처리까지 고려해서 인터셉터나 필터를 만들어야 한다.

하이버네이트의 update() 기본 지식 - 참조 http://whiteship.tistory.com/1616

Persistent Context에 이미 Persistent 상태로 로딩되어 있는 객체가 있을 때, 그와 같은 id를 가진 객체를 또 다시 Persistent Context에 붙이려는 시도가 있을 때 NonUnique뭐시기 에러가 발생합니다. 흔히 Detached 상태의 객체를 update() 메소드를 사용하여 Persistent Context에 Reattch를 시도할 때 이런 예외가 발생할 수 있는데, 그럴 때는 merge를 하여 기존의 Persistent Context에 있는 객체의 값을 새로운 객체 값으로 덮어쓸 수도 있지만 이 때 merge() 메소드로 넘겨준 객체의 상태가 Persistent 상태로 변하지 않고 그대로 유지 되되며, merge()가 반환하는 레퍼런스와 기존에 Persistent Context에 존재하는 레퍼런스 두 개가 동일한 데이터를 가리키게 됨으로 프로그래밍에 혼란을 줄 수 있다. 따라서 해당 객체를 evict()를 사용하여 Persistent Context에서 빼내고 update()의 인자로 넘겨준 객체를 Persistent 상태로 만드는 것이 적절한 해결책일 것이다.

자... 이제 OSIV 필터를 사용하고 있을 때 Validator에 다음과 같은 코드가 있습니다.

public class MemberValidator implements Validator {
...
  @Autowired
  MemberService memberService;

  ...
  Member memberCommand = (Member)command;
  Member existingMember = memberService.get(memberCommand.getEmail());
  if(existingMember != null)
    errors.reject("email", "duplicated", "해당 이메일은 이미 가입되어 있습니다.");
  ...
 
...
}

위의 코드는 일단 상당히 별로 입니다. memberSerivce.isExistingEmail(memberComman); 라는 메소드를 만들어서 그 반환값을 가지고 조건을 거는게 더 좋은 API로 생각됩니다. MemberService의 isExistingEmail() 에서 데이터베이스에 접근하는 코드는 memberDao를 사용해서 Member 객체를 가져오고 지지고 볶는게 훨씬 좋습니다. 그리고 사실 저런 경우 멤버 객체를 가져올 필요도 없고 레코드 갯수만 가져오면 되겠죠. 그런데 그냥.. 여기서는 그냥. 저렇게 코딩을 했다고 가정하겠습니다.

이런 상황에서 방금 말씀드린 코드의 책임 문제를 떠나 정말 중대한 문제가 있습니다.

그 문제가 뭔지는 알 갈쳐드립니다. 비밀이에요. (ㅋㅋ이미 문제의 원인과 해결책은 위의 기본 이론에 다 설명이 되어 있습니다.) 토비 사부님께 듣기로는 물개 선생님께서도 이와 같은 문제를 겪은 적이 있다는 얘기를 들었습니다. 이런 해프닝을 겪으면서 느낀 건 아무리 공부를 해도 역시.. 코딩을 해봐야... 알 수 있고.. 문제의 원인과 그 원인 해결책은 다시 공부를 해야 이해할 수 있다는 것입니다.

공부와 코딩을 떨어질래야 떨어질 수 없는 친구인거죠.
신고
top


Logging/StaticLog

Java : 2008.05.09 22:00


참조 : http://wiki.apache.org/jakarta-commons/Logging/StaticLog

로깅을 보통 두 가지 방법 중 하나를 사용할 것이다.

public class Foo {
  private Log log = LogFactory.getLog(Foo.class);
  ....
}

그리고

public class Foo {
  private static Log log = LogFactory.getLog(Foo.class);
  ....
}

static 키워드를 사용하는 것이 특정 상황에서는 이익이 될 수도 있지만, 그렇지 않은 경우 예상치 못한 문제를 발생시킬 수 있다. 여기서 각각이 어떤 경우에 유용한지 살펴본다.

Static 문제

static 사용의 기술적인 결과는 매우 명백하다. 해당 클래스 타입의 여러 객체들 사이에서 단일 Log 레퍼런스를 공유해서 사용한다. 분명히 메모리를 효율적으로 사용할 수 있다. 딱 하나의 레퍼런스(4 또는 8 바이트)만 있으면 몇 개의 객체를 생성하든 아무 상관없다. 그리고 CPU 효율도 좋다. 왜냐면 Log 인스턴스를 딱 한 번만 찾아서 가져오면Look up 되기 때문이다.

독립적인 애플리케이션 코드에서 static을 사용하는 괜찮은 생각이다.

하지만 자바 코드를 라이브러리 형태로 J2EE 서버같은 컨테이너에 배포해야 한다면 문제가 발생할 수 있다. 보통 컨테이너는  클래스 로더 계층 구조를 가지고 있다. 배포된 애플리케이션 마다 자바 ClassLoader 객체 하나씩과 컨테이너에 배포된 모든 애플리케이션들이 공유하는 클래스로더의 최상위 클래스로더Ancestry ClassLoader로 공유 클래스로더를 가지고 있다. 이때 애플리케이션 수준의(j2ee 컨테이너 또는 서블릿의 "webapp" 수준) 클래스 로더에서 Log 객체에 대한 static 레퍼런스를 참조하면 아무런 문제가 없다. 만약 여러 개의 애플리케이션이 해당 클래스를 배포하더라도 그들 각각의 복사체를 가지고 있게 될 뿐 애플리케이션 사이에서는 아무런 문제가 없다.

하지만 "private static Log log = ..."식의 코드를 사용하는 클래스가 여러 독립적인 애플리케이션들의 최상위 클래스로더에 배포될 수도 있다. 이 경우, log 멤버 변수는 딱 한 번만 초기화 된다. 왜냐면 클래스 로더는 딱 하나의 클래스 복사체만 가질 수 있으니까. 이 초기화는 해당 클래스의 객체를 만들려고 하거나 static 메소드를 호출할 때 한다. 클래스 초기화가 발생할 때 log 멤버는 어떤 값으로 설정 될까?
  • 각각의 "애플리케이션"에 상관 없이 "컨네이너" 레벨에 있는 계층 구조의 일부에 있는 Log 객체에 대한 레퍼런스
  • 현재 애플리케이션과 관계 있는 계층 구조의 일부에 있는 Log 객체에 대한 레퍼런스
  • log 메소드를 호출할 때마다 "현재 애플리케이션"이 무엇인지 판단하고 그에 해당하는 Log 객체로 위임하는 "프록시" 객체에 대한 레퍼런스
첫 번째 옵션은 로깅이 각각 애플리케이션 수준으로 설정되지 못한다는 것을 뜻한다. 로깅 설정이 뭐라고 설정되어 있건간에 모든 애플리케이션이 동일한 Log 레퍼런스를 사용하는 것이다. 그럼 모든 애플리케이션의 로그정보가 섞여서 출력될 것이다. 이게 가장 큰 문제다.

두 번째 옵션은 Log 객체가 자기를 맨 처음 호출한 애플리케이션에 설정된다는 것을 뜻한다. 컨테이너에 들어있는 다른 애플리케이션들의 로그 메시지는 모두 첫 번째 애플리케이션이 설정해둔 목적지로 보낼 것이다. 이것도 분명히 큰 문제다.

세번째 옵션은 각각의 애플리케이션 로깅 설정을 허용하고 각각의 필요한 로그 메시지들을 바르게 출력하고 필터링 한다. 하지만 성능 문제가 심각하다. 이것도 그다지 허용할 만한 방법이 아니다.

이 문서에서 "쓰레드 컨텍스트 클래스로더"나 다른 기술적인 문제는 언급하고 있지 않다. 문제는 그렇게 자세한 부분이 아니라 일반적인 개념에 있다. Log 객체를 여러 애플리케이션에서 공유할 때 애플리케이션 마다의 설정을 적용하는 것이 불가능하며 타당한 성능을 보장할 수 없다.

이 모든 문제의 진짜 핵심은 독립적이어야 하는 "애플리케이션"들에서 공유하는 데이터(클래스에 있는 static 멤버)가 있다는 것이다. 애플리케이션들 사이에 공유하는 클래스가 없다면 아무런 문제도 없다. 하지만 컨테이너 밴더들은 계속해서 공유 클래스를 쓰길 권하고 개발자들은 계속 그렇게 사용하고 있다.(이건 저자의 개인적인 생각)

그 해결책은 공유 클래스패스에 배포될 가능성이 있는 코드에서 Log 객체에 대한 "static" 레퍼런스를 사용하지 않는 것이다.

SLF4J 도 이런 문제를 가지고 있는가?

그렇다. SLF4J도 위에 나열한 것과 동일한 문제를 겪고 있으며 권장하는 방법은 동일하다. 공유 클래스패스에 배포될 코드에서는 log 객체에 대한 static 레퍼런스를 사용하지 말아라.

...중략...

java.util.logging도 이 문제를 가지고 있는가?

그렇다. java.util.logging API도 위에 나열한 것과 동일한 문제를 겪고 있으며 권장하는 방법은 동일하다. 공유 클래스패스에 배포될 코드에서는 log 객체에 대한 static 레퍼런스를 사용하지 말아라.

...중략...

Static logger 대안책

static을 떼어내라.

...중략...

그럼 static 메소드는 어떻게 하냐?

  public static void doStuff(...) {
    Log log = LogFactory.getLog("stuff");
    log.warn(...);
  }

...중략...

컨테이너가 지원하는 로깅

몇몇 컨테이너는 위의 문제를 해결하려고 노력했다. 특히 JBoss는 커스텀 log4j 필터를 제공해서 어쩌구 저쭈구 생략..

기존의 라이브러리 코드 수정하기

이 문서를 읽고나서 static Log를 static이 아닌 Log 객체를 사용하도록 고치려는 시도를 할 수 있는데, 이 때 주의할 것이 있다. 클래스의 직렬화 형식이 바뀐다는 것이다. 클래스의 serialVersionID를 꼭 바꿔주어라. 그래야 옛날 클래스 대신 새 클래스를 로딩할 것이다.

다음과 같이 스레드 세이프 싱글톤 객체를 사용하도록 할 수 있음.

  private transient Log log;
  private Log getLog() {
    if (log == null)
      log=LogFactory.getLog(Some.class);
    return log;
  }

  // old code
  // log.debug("foo");

  // new code
  getLog().debug("foo");

또다른 대안으로는 readResolve 메소드를 만들어서 역직렬화 시에 자동으로 호출하게 할 수도 있다.

  private transient Log log = LogFactory.getLog(Some.class);
  private Object readResolve() {
     log = LogFactory.getLog(Some.class);
     return this;
  }


====================================

아 어려워.
어쨋거나 static Log 쓰지 말라는 것..
JEE 컨테이너들의 클래스 로더 구조 땜시 희한한 문제가 발생할 수 있음.
신고
top

Java : 2008.05.09 22:00 Trackback. : Comment.

5.1. Bundle format and Manifest headers

Spring DM/Chapter 5 : 2008.05.08 20:05


각각의 애플리케이션 모듈은 OSGi 번들로 패키징해야 한다. 번들은 META-INF/MANIFEST.MF 파일을 가지고 있는 jar 파일이다. OSGi 서비스 플랫폼 핵심 스팩 3.2에서 그 자세한 내용을 살펴볼 수 있다. 몇몇 OSGi 구현체들은 풀어헤쳐진 jar 파일을 지원하지만 형식은 동일하다.

스프링 Extender는 번들은 "스프링이 가미된"Spring-Powered 번들을 인식하고 해당 번들에 연관된 애플리케이션 컨텍스트를 번들이 시작할 때 다음의 조건 중에 하나라도 만족하면 만들어 준다.
  • META-INF/spring 폴더에 하나 이상의 .xml 파일을 가지고 있을 경우.
  • META-INF/MENIFEST.MF 파일에 Spring-Context라는 헤더가 있을 경우.
보태자면, 만약 부가적으로 SpringExtender-Version 헤더를 manifest 파일에 추가하면, extender는 명시된 버전 조건을 충족시키는 번들만 인식할 것이다. 버전은 Bundle-Version에 명시되어 있다. SpringExtender-Version의 값은 OSGi 스펙을 따라야 한다.

Spring-Context 헤더가 빠져있는 경우 extender는 META-INF/spring 폴더 안에 있는 모든 파일을 유효한 스프링 설정 파일로 인식한다.

Application Context는 이런 파일들의 집합으로 구성된다. 권장하는 방법은 Applicaion Context 설정을 최소한 두 개의 파일로 나누는 것이다. 하나는 모듈이름-contet.xml 이고 다른 하나는 모듈이름-osgi-context.xml 로 말이다.
  • 모듈이름-context.xml - OSGi와 상관없는 일반적인 빈 설정들을 담는다. 최상위 엘리먼트를 bean으로 사용.
  • 모듈이름-osgi-context.xml - OSGi 서비스로 import/export 하는 빈들을 설정한다. 최상위 엘리먼트를 Spring OSGi 네임스페이스로 사용한다.
menifest 파일의 Spring-Context 헤더는 설정 파일 집합을 기술하기 위해 사용한다. 자원 경로는 상대 경로로 인식하고 여기에 하나라도 설정을 하면 META-INF/spring 폴더에 있는 파일들은 무시한다. 다음과 설정할 수 있다.

Spring-Context: config/account-data-context.xml, config/account-security-context.xml

가용한 설정 옵션
  • create-asynchronosly(false|true): 애플리케이션 컨텍스트를 비동기적으로 생성(이게 기본값)할지 동기적으로 생성할지 설정.
Spring-Context: config/account-data-context.xml;create-asynchrously:=false

  • wait-for-dependencies (true|false):  필수 서비스 의존성을 만족 할 때 까지 대기할지(이게 기본값) 말지 설정.
Spring-Context: config/osgi-*.xml;wait-for-dependencies:=false

  • timeout (300): 대기 시간 설정 기본값은 5분. 300초. 초 단위로 설정. wait-for-dependencies가 false면 이 값은 무시함.
Spring-Context: *;timeout:=60

  • publish-context (true|false): application context 객체 자체를 서비스 레지스트리에 등록할지(이게 기본값) 말지 설정.
Spring-Context: *;publish-context:=false



신고
top


DWR이 제대로 자바스크립트 만들었는지 확인하기

모하니?/Coding : 2008.05.08 15:56


기본 웹 애플리케이션 홈 URL/dwr

로 접속한다.

사용자 삽입 이미지

그럼 DWR이 만들어주는 자바스크립트 들이 보임.

사용자 삽입 이미지

자바스크립트 사용하려면 추가해야 하는 코드까지 나옴. 귿.. 복사해서 붙여넣으면 됨.
execute를 클릭해서 서버에 다녀오는지 확인한 다음 계속해서 개발하러 ㄱㄱㅆ..
신고
top


Whiteship's Spring Security 총 정리

Spring Security/etc : 2008.05.07 18:16


기본 개념

1.1. What is Acegi Security? : Acegi 정의와 개요.
2.2 Shared Components : 주요 구성 요소
2.3 Authentication (2) : 인증 절차를 도식화 했습니다.
Spring Acegi Tutorial : 가장 중요한 필터들에 대한 설명과 해당 필터들이 물고 있는 빈들을 보여줍니다.
3.2 Filters : 전체 필터들 간단 요약
4. Channel Security : https 사용에 대한 개요
Access Decision Manager : 접근 권한 관리를 하는 핵심 컴포넌트

웃긴 이야기

왜 이름이 Acegi 인가? : AbCdEfGhI, 스프링 서브 프로젝트가 되기 위한 노력.
Acegi 필터 등록할 때 발생할 수 있는 몹쓸 버그 : 필터 체인 사용하던 시절에 발생하던 웃긴 버그
Spring Security 설정 분류 및 커스터마이징 (5) : Acegi 관련 XML이 커져서 어떻게 나눌지 고민했던 시절

Spring Security(Acegi) 2.0 이전

Acegi로 웹 애플리케이션 보안하기 1
Acegi로 웹 애플리케이션 보안하기 2
Acegi로 웹 애플리케이션 보안하기 3
Acegi로 웹 애플리케이션 보안하기 4
Acegi로 웹 애플리케이션 보안하기 5 (2)
Acegi로 웹 애플리케이션 보안하기 6
Acegi로 웹 애플리케이션 보안하기 7

Spring Security 2.0 네임스페이스

Security Namespace Configuration PART 1 (1)
Security Namespace Configuration PART 2

많이 미흡하지만 이 정도면 Spring Security 학습에 어느 정도 도움이 되실 거라고 생각합니다.
Live with Passion!!! Live with Spring!!
신고
top


Security Namespace Configuration PART 2



2.3. Advanced Web Features

2.3.1. Adding HTTP/HTTPS Channel Security

애플리케이션에서 요청을 HTTP와 HTTPS로 아무거나 셋 중 하나의 채널로 받아들이도록 설정하려면 <intercept-irl> 의 requires-channel 속성을 사용한다.

  <http>
    <intercept-url pattern="/secure/**" access="ROLE_USER" requires-channel="https"/>
    <intercept-url pattern="/**" access="ROLE_USER" requires-channel="any"/>
    ...
  </http>

/secure/** 패턴의 HTTP를 사용해서 접근하면 HTTPS URL로 리다이렉트 된다. 가용한 옵션은 "http", "https", "any"(둘 중 아무거나)가 있음.

위 프로토콜을 기본 포트가 아닌 다른 포트에서 사용할 때는 포트 맵핑을 해준다.

  <http>
    ...
    <port-mappings>
      <port-mapping http="9080" https="9443"/>
    </port-mappings>
  </http>

채널에 대해서는 여기서 자세히

2.3.2. Concurrent Session Control


단일 사용자 계정을 한 명(세션)만 사용하도록 제한할 수 있다.
먼저 web.xml에 다음 리스너를 등록한다.

<listener>
  <listener-class>org.springframework.security.ui.session.HttpSessionEventPublisher</listener-class>
</listener>

그리고 application context에 다음을 추가한다.

  <http>
    ...
    <concurrent-session-control max-sessions="1" />
  </http>

이렇게 하면, 같은 계정으로 두 번 로그인 하면, 첫 번째 로그인한 유저가 invalid하게 된다. 두 번째 로그인하는 걸 막고 싶으면 다음과 같이 설정한다.

  <http>
    ...
    <concurrent-session-control max-sessions="1" exception-if-maximum-exceeded="true"/>
  </http>

mac-sessions 속성으로 한 계정당 가용한 세션 수를 정할 수 있나봅니다.

2.3.3. OpenID Login

  <http auto-config='true'>
    <intercept-url pattern="/**" access="ROLE_USER" />
    <openid-login />
  </http>

저렇게 설정하면, OpenID 로그인을 사용하게 된다. 그리고 자신의 OpenID 계정을 설정해야 한다. 그리고 사용자 정보를 in-memory <user-service>에 추가한다.(이건 어떻게 하는거지..?)

<user name="http://jimi.hendrix.myopenid.com/" password="notused" authorities="ROLE_USER" />

2.3.4. Adding in Your Own Filters

이전 버전의 Acegi에서는 커스텀 필터를 필터 체인 중에 끼워넣을 수 있었다. 또는 이미 존재하는 필터를 커스터마이징해서 사용하고 싶을 수 있다. 2.0 이후 부터는 필터 체인이 명시적으로 보이지 않는데 어떻게 해야할까?

네임스페이스를 사용할 때 필터의 순서는 항상 고정되어 있다. 각각의 필터들은 스프링의 Ordered 인터페이스를 구현했고 그에 따라서 초기화 과정에서 정렬된다. 각각 필터들은 다음과 같은 이름으로 맵핑되어 있다.

Alias

Filter Class

CHANNEL_FILTER

ChannelProcessingFilter

CONCURRENT_SESSION_FILTER

ConcurrentSessionFilter

SESSION_CONTEXT_INTEGRATION_FILTER

HttpSessionContextIntegrationFilter

LOGOUT_FILTER

LogoutFilter

X509_FILTER

X509PreAuthenticatedProcessigFilter

PRE_AUTH_FILTER

Subclass of AstractPreAuthenticatedProcessingFilter

CAS_PROCESSING_FILTER

CasProcessingFilter

AUTHENTICATION_PROCESSING_FILTER

AuthenticationProcessingFilter

BASIC_PROCESSING_FILTER

BasicProcessingFilter

SERVLET_API_SUPPORT_FILTER

classname

REMEMBER_ME_FILTER

RememberMeProcessingFilter

ANONYMOUS_FILTER

AnonymousProcessingFilter

EXCEPTION_TRANSLATION_FILTER

ExceptionTranslationFilter

NTLM_FILTER

NtlmProcessingFilter

FILTER_SECURITY_INTERCEPTOR

FilterSecurityInterceptor

SWITCH_USER_FILTER

SwitchUserProcessingFilter



커스텀 필터는 <custom-filter> 엘리먼트를 사용해서 체인에 있는 필터를 교체 할 수 있다.

  <beans:bean id="myFilter" class="com.mycompany.MySpecialAuthenticationFilter">
    <custom-filter position="AUTHENTICATION_PROCESSING_FILTER"/>
  </beans:bean>

필터 전 후에 끼워넣고 싶을 때는 after나 before 속성을 사용할 수 있으며 필터 이름 앞에 FIRST나 LAST를 붙일 수 있다.

2.3.5. Session Fixation 공격 방어

세션을 만들어서 사이트에 접속하려는 Session Fixation 공격이 있다. Spring Security는 사용자가 로그인 할 때 자동으로 새로운 세션을 만들어서 이 공격을 막는다. <http> 에 있는 session0-fixation-protection 속성에 다음과 같은 옵션을 사용하여 설정할 수 있다.
  • migrateSession - 새 세션을 만들고 기존의 세션 속성을 새 세션으로 복사한다. 이게 기본값이다.
  • none - 아무것도 하지 않음. 원래 세션을 유지한다.
  • newSession - 완전히 비어있는 clean 세션을 생성하고 기존 세션으로부터 아무것도 복사하지 않는다.
2.3.6. Setting a Custom AuthenticationEntryPoint

네임스페이스가 지원하는 인증 방식이 아닌 다른 방법을 사용하고 있어서 새로운 인증 필터와 Entry Point를 사용한다면 이 것들을 네임스페이스에 끼워넣고 싶을 것이다. AuthenticationEntryPoint 구현체를 <http>의 entry-point-ref 속성에 설정하면 된다.

2.4. Method Security

스프링 시큐리티 2.0 부터는 서비스 계층의 메소드에 JSR-250 시큐리티 애노테이션과 프레임워크의 @Secured 애노테이션을 사용할 수 있다. 빈 설정에 intercept-methods 엘리먼트를 추가하거나 AspectJ 스타일 포인트 컷으로 여러 개의 빈에 적용할 수 있다.

2.4.1. The <global-method-security> Element

이 엘리먼트를 등록해서 애노테이션 기반 보안이 가능하도록 설정한다.

<global-method-security secured-annotations="enabled" jsr250-annotations="true"/>


두 가지 타입의 애노테이션을 모두 지원하도록 설정했다.

2.4.1.1. Adding Security Pointcuts using protect-pointcut

<global-method-security>
    <protect-pointcut expression="execution(* com.mycompany.*Service.*(..))" access="ROLE_USER"/>
</global-method-security>

위 설정은 com.mycompany 패키지에 Service 라는 이름이 들어가는 빈들의 모든 메소드를 보안하도록 설정. ROLE_USER인 사용자만 해당 클래스의 메소드를 호출할 수 있다.

2.5. The Default AccessDecisionManager

여기는 기본적인 롤(ROLE) 기반의 접근 권한 관리 말고 좀 더 커스터마이징 하고자하는 분들을 위한 것임.

네임스페이스를 사용하면 기본 AccessDecisionManager 객체를 생성하고 등록해준다. 그리고 이 녀석을 사용해서 메소드 호출과 web URL 접근 가능 여부를 intercept-url와 protect-pointcut 설정을 바탕으로 결정한다.
기존 전략은 RoleVoter와 AuthenticatedVoter를 하나씩 가지고 있는 AffirmativeBased AccessDecisionManager를 사용한다.

2.5.1. Customizing the AccessDecisionManager

좀 더 복잡한 접근 제어 전략을 사용하려면 메소드와 웹 보안 둘에 대한 대체제를 설정하면 ㅕ된다.

메소드 보안을 용도로는 AccessDecisionmanager 빈을 가리키도록 gloval-security 엘리먼트의 acess-decision-manager-ref 속성을 사용한다.

  <global-method-security access-decision-manager-ref="myAccessDecisionManagerBean">
    ...
  </global-method-security>

웹 보안 접근 제어 대체제는 <http> 에 설정한다.

  <http access-decision-manager-ref="myAccessDecisionManagerBean">
    ...
  </http>

2.5.2. The Authentication Manager

스프링 시큐리티의 네임스페이스를 사용하면 인증 관리자 객체인 ProviderManager 타입의 객체도 생성해서 등록해준다. 이전 버전을 사용해봤다면 매우 친숙한 객체일 것이다.

추가적인 AuthenticationManager를 추가 등록하고 싶을 때 <custom-authentication0provider> 속성을 사용할 수 있다.

  <bean id="casAuthenticationProvider"
      class="org.springframework.security.providers.cas.CasAuthenticationProvider">
    <security:custom-authentication-provider />
    ...
  </bean>

그리고 이렇게 등록한 빈을 컨텍스트 안에 있는 다른 빈들이 AhtehnticationManager라고 참조하도록 별칭을 등록할 수 있다.

  <security:authentication-manager alias="authenticationManager"/>
  <bean id="casProcessingFilter" class="org.springframework.security.ui.cas.CasProcessingFilter">
     <security:custom-filter position="CAS_PROCESSING_FILTER"/>
     <property name="authenticationManager" ref="authenticationManager"/>
     ...
  </bean>




신고

'Spring Security > Chapter 2' 카테고리의 다른 글

Security Namespace Configuration PART 2  (0) 2008.05.07
Security Namespace Configuration PART 1  (8) 2008.05.07
top


Security Namespace Configuration PART 1



2.1. Introduction

- 스프링 네임스페이스
- 시큐리티 네임스페이스를 기본 네임스페이스로 사용하기

<beans:beans xmlns="http://www.springframework.org/schema/security"
   xmlns:beans="http://www.springframework.org/schema/beans">
    ...
</beans:beans>

2.1.1 Design of the Namespace

- 가장 흔하게 사용되는 형태를 잡아서 네임스페이스를 설계했다. (네임스페이스 만드는 방법은 KSUG 1회 세미나에서 토비형님께서 발표해주셨었음. 다시 듣고 싶음..) 다음과 같이 나눠져 있다.
  • Web/Http Security - 가장 복잡한 부분이다. 필터들을 등록하고 그와 연관된 서비스 빈들을 등록한다. 인증, URL 보안, 로그인 페이지 보여주기, 에러 페이지 보여주기 등 많은 일을 한다.
  • Business Object (Method) Security - 서비스 계층을 보안(보완이 아니라 보안!!)하기 위한 옵션
  • AuthenticationManager - 프레임워크 내 다른 부분들에서 필요한 인증 요청을 다룬다. 간단하게 인증 담당자.
  • AccessDecisionManager - 해당 리소스에 접근 권한이 있는지 결정하는 곳. 간단하게 권한 담당자. 자동으로 기본값이 세팅 되는데 물론 커스터마이징 수도 있다.
  • AuthenticationProviders - 인증 담당자가 사용할 인증 방법들. 여러 가지 방법이 있겠지만, 기억하기, OpenID, 로그인 페이지가 가장 흔할듯.
  • UserDetailService - 인증 담당자가 사용자 정보를 가져올 때 사용하는 빈.
2.2. Getting Started with Security Namespace Cofiguration

여러분들이 Spring Security를 애플리케이션에 빨리 적용 인증, 권한 기능을 사용하길 원하다는 가정하게 설명한다.

2.2.1. web.xml 설정하기

가장 먼저 할 일은 web.xml에 필터를 등록하는 것이다.

<filter>
  <filter-name>springSecurityFilterChain</filter-name>
  <filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
  <filter-name>springSecurityFilterChain</filter-name>
  <url-pattern>/*</url-pattern>
</filter-mapping>

특정 URL을 후킹해서 스프링 ApplicationContext에 등록되어 있는 머시기 빈이 그 요청 처리 앞에 끼어들도록 하는 것임. 위 설정은 /* 이렇게 모든 요청을 가로채도록 해놨음.

2.2.2. 최소 <http> 설정


웹 보안을 위해 필요한 최소 설정은 아래와 같다.

  <http auto-config='true'>
    <intercept-url pattern="/**" access="ROLE_USER" />
  </http>

모든 요청을 ROLE_USER 만 접근할 수 있다. 로그인 페이지에는 접근할 수가 있을까??? 없을 것 같은데.. 된다. 왜냐면 auto-config 때문에 설정된 <http-basic> 때문에.. 자세한건 아래에 있음.

<http> 엘리먼트 안에 <intercept-url> 엘리먼트 여러개를 설정할 수 있는데, 이 때 맨위에서 아래 순으로 가장 먼저 걸리는 녀석이 적용된다. 순서에 주의하자.

사용자 추가하기

  <authentication-provider>
    <user-service>
      <user name="jimi" password="jimispassword" authorities="ROLE_USER, ROLE_ADMIN" />
      <user name="bob" password="bobspassword" authorities="ROLE_USER" />
    </user-service>
  </authentication-provider>

아이디와 비번 권한 정보를 이렇게 직접 줄 수도 있다. 이 정보들을 프로퍼티 파일로 빼낼 수 도 있다. 자세한건 여기 참조. 그다지 안전해 보이지 않는 방법이다. 하지만 간단하게 적용할 수 있다는 것이 장점이다. 매우 작은 웹 애플리케이션에 적용하기엔 저 정도면 충분할 듯 하다. 여기에 정의한 사용자 정보들은 Authentication Manager가 인증 요청을 다룰 때 사용한다.

위에 설정을 10줄 밖에 안 했지만, 암묵적으로 몇개의 빈들이 설정된다. 로그인 처리라던지 "remember-me" 서비스 같은 것들이 자동으로 설정되었다. 이는 <http>에 auto-config 설정이 기본으로 되어있기 때문이다.

이전 버전에서 필터들의 순서가 이슈가 됐었는데 이제는 그런 걱정할 필요가 없다. 필터 등록하고 필터가 필요한 빈등록하는 일을 전부 <http> 엘리먼트가 책임진다. <http>는 옛날의 FilterChainProxy, <authentication-provider>는 옛날의 DaoAuthenticationProvider, <user-service>는 옛날의 InMemoryDaoImpl 라고 생각할 수 있다. 네임스페이스 처리 시스템이 ProviderManager는 자동으로 만들어주고 DaoAuthenticationProvider 얘를 거기에 자동으로 엮어서 등록한다.

이 내용은 Acegi를 아시는 분들만 이해하실 수 있습니다. 몰라도 별 지장은 없습니다.

2.2.2.1. What does auto-config Include?

auto-config 속성은 다음 코드를 압축시킨거다.

  <http>
    <intercept-url pattern="/**" access="ROLE_USER" />
    <form-login />
    <anonymous />
    <http-basic />
    <logout />
    <remember-me />
  </http>

2.2.2.2. Form and Basic Login Options

HTML 파일이나 JSP 파일을 만들지 않아도 Spring Security가 기본 로그인 화면을 만들어 준다. 물론 그걸 그냥 써도 되겠지만, 대부분 그러지는 않을 것이다. 다음과 같이 설정하여 별도의 로그인 폼을 지정할 수 있다.

  <http auto-config='true'>
    <intercept-url pattern="/login.jsp*" filters="none"/>
    <intercept-url pattern="/**" access="ROLE_USER" />
    <form-login login-page='/login.jsp'/>
  </http>

auto-config를 사용하는 상태에서 <form-login>도 설정하고 있는데, 이렇게 하면 기본 세팅값을 오버라이딩한다. /login.jsp 요청은 시큐리티 필터 처리를 하지 않도록 설정했다. 그래야 로그인 페이지에 접근할 수가 있다.

  <http auto-config='true'>
    <intercept-url pattern="/**" access="ROLE_USER" />
    <http-basic />
  </http>

<http-basic /> 이라는 기본 인증은 설정 위치에 상관없이 가장 우선시 된다. 따라서, 제한된 영역에 접근하려고 할 때 로그인창을 띄운다.

2.2.3. Using other Authentication Providers

사용자 인증 방법을 여러가지 사용할 수 있는데, 사용자 정보를 보통은 DB나 LDAP 서버에 저장해 둔다. LDAP 네임스페이스는 LDAP 챕터에 나와있으니 여기서 다루진 않는다. UserDetailSerivce에 대한 구현체가 있다면 다음과 같이 등록할 수 있다.

 <authentication-provider user-service-ref='myUserDetailsService'/>

DB에서 직접 사용자 정보를 끌어올 거라면 다음과 같이 설정할 수 있다.

  <authentication-provider>
    <jdbc-user-service data-source-ref="securityDataSource"/>
  </authentication-provider>

위 설정에서 securityDataSource는 그냥 DataSource 타입의 빈 이름이다. 이 때 해당 데이터소스와 연관을 맺는 데이터베이스에는 Spring Secutiry 표준 사용자 테이블 스키마를 따르고 있어야 하는데, 스키마가 그와 다를 경우에는 JdbcDaoImpl 빈을 만들고 그 빈을 user-service-ref에 설정해줄 수있다.

user-service-ref에는 UserDeteailsService 타입의 빈을 등록할 수 있는데, JdbcDaoImpl은 UserDetailsService를 구현한 클래스이기 때문에 등록할 수 있는것이다.

2.2.3.1. Adding a Password Encoder

<authentication-provider>
  <password-encoder hash="sha"/>
  <user-service>
    <user name="jimi" password="d7e6351eaa13189a5a3641bab846c8e8c69ba39f" authorities="ROLE_USER, ROLE_ADMIN" />
    <user name="bob" password="4e7421b1b8765d8f9406d87e7cc6aa784c4ab97f" authorities="ROLE_USER" />
  </user-service>
</authentication-provider>

위와 같이 <password-encoder has=sha"/>를 사용하여 해싱 알고리즘을 사용해서 암호화 할 수 있다. 사전어 공격을 막기 위해서 salt 값들을 사용할 수 있다.

<password-encoder hash="sha">
  <salt-source user-property="username"/>
</password-encoder>

너무 길어져서 Advanced와 Method Security는 Part 2에서 요약 하겠습니다.

신고

'Spring Security > Chapter 2' 카테고리의 다른 글

Security Namespace Configuration PART 2  (0) 2008.05.07
Security Namespace Configuration PART 1  (8) 2008.05.07
top


은근슬쩍 배포된 Spring 2.5.4



http://springframework.org/download

위에 가시면 이미 2.5.4가 나와있는 것을 볼 수 있습니다. 아직 공식 발표도 안 했고, Change Log도 업데이트되지 않았습니다. 몇일 전 Spring DM 이 업데이트 됐을 때 테스트를 하면서 알게 되었습니다. 메이븐으로 샘플 코드를 빌드하는 도중에 스프링 2.5.4를 받고 있길래.. @.@ '2.5.3 아니였나... 이게 모지..??' 이렇게 황당해 하면서 그냥 받았습니다.

배포 해놓고 Change Log 링크 수정하고 공지를 할 새도 없이 바쁜건지, 아니면 실수로 배포를 먼저 해버린건지 알 겨를은 없습니다. 어찌됐든 빠른 업데이트(불과 한 달)에 그저 놀라울 따름이니까요. 이러다 정말 올해 안해 3.0이 나오진 않을지.. ㅋㅋ

소스 코드를 다운받아서 열어본 "변경 사항"에는 다음과 같은 것들이 눈에 띕니다.

1. OSGi 관련한 변경 사항들.
2. AspectJ 1.6으로 의존성 버전 업
3. SimpleJdbcTestUtils 이런 것도 있었군요. 흠.. 언제 Utils를 한 번 다 훑어보긴 해야 하는데 말이죠.

ps : 제가 일하는 곳에서는 오늘 스프링 2.5.4와 스프링 시큐리티 2.0.1을 도입했습니다.  시큐리티~ 공부좀 해야겠네요.
신고
top


Chapter 5. Packaging and Deploying Spring-based OSGi applications

Spring DM/Chapter 5 : 2008.05.05 23:56


보통의 스프링 애플리케이션들은 단일 Application Context를 사용하거나, 서비스 계층, 데이터 계층 그리고 도메인 객체를 가지고 있는 부모 Context와 웹 계층 컴포넌트들을 가지고 있는 자식 Context로 나눠서 사용했을 것이다. Application Context는 여러 설정 파일들을 뭉쳐서 생성할 수 있었다.

애플리케이션을 OSGi에 배포할 때 자연스러운 방법은 애플리케이션을 OSGi 서비스 레지스트리를 사용하여 상호 작용하는 번들 집합체로 패키징 하는 것이다. 독립적인 하위 시스템은 독립적인 번들 또는 번들들의 집합체로 패키징해야 한다.(수직 파티셔닝) 하위 시스템은 단일 번들로 묶을 수도 있고, 레이어별로 번들을 구성할 수도 있다.(수평 파티셔닝) 예를 들어, 웹 애플리케이션은 간단하게 네 조각으로 나눌 수가 있다. 웹 번들, 서비스 계층 번들, 데이터 계층 번들, 도메인 모델 번들.
사용자 삽입 이미지

이 예제에서 데이터 계층 번들( 분홍색 Application bundles의 첫 번째 녹색 네모)은 데이터 계층 Application Context에 여러 내부 컴포넌트(bean들)들을 가지고 있으며 그 들 중에서 두 개의 빈을 공개적으로 외부에서 접근 가능하도록 그들을 OSGi 서비스 레지스트리에 등록했다.

서비스 계층 번들(Application bundles의 두 번째 녹색 네모)은 역시 여러 개의 내부 커포넌트를 가진 Application Context를 가지고 있고, 그 중 몇개가 데이터 계층 서비스들을 사용하려고 import 하고 있다. 서비스 계층 컴포넌트 중에 두 개를 외부에서 이용할 수 있도록 OSGi 서비스 레지스트리에 등록했다.(레지스트리 그림은 빠져있어서 2% 아쉽..아니면 서비스로 노출시킨 bean은 다른 색으로 그려주던지...)

웹 컴포넌트 번들(Application bundles의 세 번째 녹색 네모)은 Web application context에 여러 컴포넌트들을 가지고 있고 그 중 몇 개는 서비스를 OSGi 서비스 레지스트리로부터 참조해 온다.

도메인 모델 번들(Application bundles의 네 번째 녹색 네모)은 오직 도메인 모델 타입은 컴포넌트(걍 빈이라고 하지..)가 될 필요가 없기 떄문에 이 번들에는 Application context가 없다.

==============================

애플리케이션을 여러 번들로 쪼갤 때 가장 기본적으로 생각해 볼 수 있는 단서를 제공해 줍니다. 스프링 레퍼런스는 정말 잘 만드는 것 같습니다. 수평으로 쪼개기와 수직으로 쪼개기라.. 멋져부러...
신고
top


스프링 OSGi 번들 저장소

Spring DM/etc : 2008.05.04 22:54


스프링 DM과 Maven을 사용하시는 분들에겐 희소식입니다.
OSGi 플랫폼에 배포가 가능한 상태의 번들들을 모아둔 스프링 저장소 입니다.

http://www.springsource.com/repository/app/

위의 링크에서 필요한 번들을 검색하여 다운로드 할 수 있으며 Maven이나 Ivy를 사용하시는 분들은 <dependency> 엘리먼트를 사용하여 의존성을 추가할 수 있습니다.

다음은 hibernate annotation 번들을 검색한 결과 화면입니다.

사용자 삽입 이미지
자세한 설명은 여기를 참조하세요.


신고
top


하악하악 테스트 커버리지



사용자 삽입 이미지

Bamboo가 만들어준 클로버 테스트 커버리지 리포트 입니다. 너무 단순해 보여서 탈이지만, 바쁘기 땜시 딱 저것만 봐도 충분합니다.

0%가 나온 날은 그날 빌드가 안 돌아가서 그렇습니다. 41%로 시작했다가 쭉쭉 떨어져서 17%를 바닥 치고 다시 50%까지 성장하고 있습니다. 투자 할만해 보이시나요? ㅋㅋ

테스트 커버리지를 올리기로 결심한 것은 박재성님의 CI 강좌 이후 뒷풀이 때 박재성님과 나눈 대화 때문이었습니다. "테스트 커버리지 70% 이상을 유지하시는데 힘드시지 않은가요?" 라는 질문을 드렸고 그 뒤에 박재성님께서 매우 좋은 답변을 해주셔서 저에게 자극이 됐던 것 같습니다.

현재 프로젝트를 진행하며 토비형님과 함께 OSAF도 만들어가고 있습니다. 실제 프로젝트를 진행하며 스프링 2.5, 하이버 3.2 기반의 OSAF 프레임워크를 만들고 있습니다. 그래서인지 정말로 필요한 코드들만 프레임워크에 들어가고 있으며 잘 만들면 ROO나 RoR의 스캐폴딩이 부럽지 않은 기능과 센스를 갖춘 프레임워크가 나올 것으로 기대하고 있으며 또 그렇게 만들려고 노력하고 있습니다. 그 정도 되려면 스프링 프레임워크 정도의 테스트 커버리지는 기본으로 갖춰줘야겠지요.

사용자 삽입 이미지

Anyway, 성장이 좀 더뎌지긴 했지만 꾸준히 올라갈 것으로 예상됩니다. 어떻게 아냐구요? 아직도 OSAF 코드 중에 테스트를 만들어야 할 대상들이 남아있기 때문이죠. 그리고 제가 만들기로 마음먹었기 때문에 커버리지는 분명히 좀 더 올라갈 것입니다.
신고
top


Spring Security 2.0.1 Released

Spring Security/etc : 2008.05.03 09:54


참조 : http://www.springframework.org/node/648

빠르네요 빨라. 이번에는 버그 수정과 몇 가지 개선사항들이 있었습니다.
- Remember me 버그 수정
- JPA 붙인거에 전역 메소드 시큐리티 필터 버그 수정
- ...
신고
top







티스토리 툴바