Whiteship's Note


Petclinic을 통해 스프링 3.0 주요기능 살펴보기

Spring/etc : 2009.02.26 10:09


Spring Framework 3.0 M2 released

스프링 3.0 m2가 배포됐습니다. 유겐 휄러가 주요 기능을 잘 정리해 뒀네요. 아쉬운건 아직도 레퍼런스 업데이트가 되지 않았다는 겁니다. 어쩔 수 없이 아쉬운 사람이 우물 판다고 예제 코드를 뒤적거릴 수밖에 없더군요.

EL 지원에 관한 예제는 petclinic-servlet.xml에서 볼 수 있습니다.

    <bean id="visits" class="org.springframework.samples.petclinic.web.VisitsAtomView"/>

    <bean id="vets" class="org.springframework.web.servlet.view.xml.MarshallingView">
        <property name="contentType" value="application/vnd.springsource.samples.petclinic+xml"/>
        <property name="marshaller" ref="marshaller"/>
    </bean>

    <bean class="org.springframework.web.servlet.view.ContentNegotiatingViewResolver">
        <property name="mediaTypes">
            <map>
                <entry key="xml" value="#{vets.contentType}"/>
                <entry key="atom" value="#{visits.contentType}"/>
            </map>
        </property>
        <property name="order" value="0"/>
    </bean>

이렇게 visits와 vets 빈의 contentType 속성에 들어있는 값을 #{vets.contentType}, #{visits.contentType} 이런식으로 참조하여 다른 빈에 주입할수 있습니다. visits에는 contentType속성이 없는 것 같지만 이 속성은 AbstractView에 있는 속성이고 VisitsAtomView 클래스가 그것을 상속했기 때문에 기본값을 가지게 될 겁니다.

다음으로 RestTemplate 기능은 컨트롤러에서 볼 수 있습니다.

@Controller
@RequestMapping("/owners/{ownerId}/pets/new")
@SessionAttributes("pet")
public class AddPetForm {
...
    @RequestMapping(method = RequestMethod.GET)
    public String setupForm(@PathVariable("ownerId") int ownerId, Model model) {
        Owner owner = this.clinic.loadOwner(ownerId);
        Pet pet = new Pet();
        owner.addPet(pet);
        model.addAttribute("pet", pet);
        return "pets/form";
    }
...
}

이런 식으로 URL의 일부를 매서드 매개변수로 바인딩 해줍니다. 바인딩 할 필요가 없는 경우 와일드 카드를 이용할 수도 있습니다.

@Controller
@RequestMapping("/owners/*/pets/{petId}/visits/new")
@SessionAttributes("visit")
public class AddVisitForm {
...
    @RequestMapping(method = RequestMethod.GET)
    public String setupForm(@PathVariable("petId") int petId, Model model) {
        Pet pet = this.clinic.loadPet(petId);
        Visit visit = new Visit();
        pet.addVisit(visit);
        model.addAttribute("visit", visit);
        return "pets/visitForm";
    }
...
}

이런 식으로 말이죠. 간단해 보이네요~

다음으로는 AtomView 지원 기능도 살펴봤습니다. 3.0에 추가된 AbstractAtomFeedView 이 클래스를 상속해서 구현하면 되더군요.

public class VisitsAtomView extends AbstractAtomFeedView {
...
    @Override
    protected void buildFeedMetadata(Map<String, Object> model, Feed feed, HttpServletRequest request) {
...
    }

    @Override
    protected List<Entry> buildFeedEntries(Map<String, Object> model,
            HttpServletRequest request, HttpServletResponse response) throws Exception {
...
    }
   
}

구현은 이런 식이며 피드 메타 데이터는 Feed 객체에 Id와 Title을 그리고 Updated에 날짜를 채워줍니다. 코드에 에러가 있어서 Feed 라는 클래스를 자세히 보지 못해서 아쉽네요. 그 아래 매서드에서는 Entry List를 만들어서 반환해줍니다. Entry도 역시 자세히 보고 싶은데 못봤습니다. id, title, updated, summary 정보를 설정하는 코드로 대충 어떤 속성이 있는지 예상할 순 있었습니다.

마지막으로 OXM 기능을 살펴봤는데 끝내주더군요. @_@

    <bean id="vets" class="org.springframework.web.servlet.view.xml.MarshallingView">
        <property name="contentType" value="application/vnd.springsource.samples.petclinic+xml"/>
        <property name="marshaller" ref="marshaller"/>
    </bean>

    <oxm:jaxb2-marshaller id="marshaller">
        <oxm:class-to-be-bound name="org.springframework.samples.petclinic.Vets"/>
    </oxm:jaxb2-marshaller>

보시다시피 MarshallingView는 스프링 3.0에서 제공하는 클래스고 Vets는 도메인 클래스입니다. OXM을 지원하는 스키마까지 제공하는군요. 저렇게만 설정하면 객체를 알아서 XML로 변환해주느냐???... 아니요 한가지 더 필요합니다.

 @XmlRootElement
public class Vets {

    private List<Vet> vets;

    @XmlElement
    public List<Vet> getVetList() {
        if (vets == null) {
            vets = new ArrayList<Vet>();
        }
        return vets;
    }

}

캬... 뭐 대충 눈으로 보면 뭐하는건지 알 수 있게해주는 직관적인 코드입니다.

자 이정도면 대충 주요 기능은 거의다 조금 살펴본 것 같습니다. 예제 코드좀 돌려보면 좋겠는데 그건 나중에 해봐야겠습니다. 지금은 번역 땜시..-_-;;



신고
top


메이븐 Maven 2.0.10 배포!

Build/Maven : 2009.02.19 11:16


http://maven.apache.org/release-notes.html
자세한 내용은 위 링크에서 참조하세요~

버그 30개 가량 고쳤고 10개 가량의 개선 사항이 있었군요. 기존 빌드에 영향을 줄 수 있는 것 두 가지를 요약해 뒀는데..

한 가지는 settings.xml에서 미러 저장소 설정할 때 문서에 나와있는던 내용과 다르게 맨 마지막에 대응하는 저장소를 사용했었는데 그걸 다시 수정해서 맨 처음에 대응하는 저장소를 사용하도록 고쳤다는 내용이고,

다른 하나는 pom 모델에서 hashmap을 사용하여 의존성 순서 판단 하던것을 linkedHashMap을 사용하도록 변경했다는 내용입니다.

@_@ 둘 다 잘 모르는거네요.
신고
top


SWF 9장 시스템 설정



9.1. 도입

이번 장에서는 웹 플로우 시스템을 웹 환경에서 사용하기 적당하도록 설정하는 방법을 살펴본다.

9.2. webflow-config.xsd

웹 플로우는 스프링 스키마를 제공하여 여러분이 시스템을 설정할 수 있도록 한다. 이 스키마를 사용하려면 그것을 기반시설 계층의 빈 파일 중 하나에 추가하라.

<beans xmlns="http://www.springframework.org/schema/beans"
       xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
       xmlns:webflow="http://www.springframework.org/schema/webflow-config"
       xsi:schemaLocation="
           http://www.springframework.org/schema/beans
           http://www.springframework.org/schema/beans/spring-beans-2.5.xsd
           http://www.springframework.org/schema/webflow-config
           http://www.springframework.org/schema/webflow-config/spring-webflow-config-2.0.xsd">       

    <!-- Setup Web Flow here -->
   
</beans>

9.3. 기본 시스템 설정

다음 절에서 웹 플로우 시스템을 여러분 애플리케이션에 설정할 때 필요한 최소한의 설정을 살펴보겠다

9.3.1. FlowRegistry

플로우를 FlowRegistry에 등록하라.

<webflow:flow-registry id="flowRegistry">
    <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>

9.3.2. FlowExecutor

플로우를 실행하는 핵심 서비스 FlowExecutor를 배포하라.

<webflow:flow-executor id="flowExecutor" />

스프링 MVC와 스프링 Faces 부분에서 웹 플로우 시스템을 어떻게 MVC와 JSF에 통합하는지 살펴보라.

9.4. flow-registry 옵션

이번 절은 flow-registry 설정 옵션을 살펴본다

9.4.1. 플로우 위치 명시하기

location 엘리먼트를 사용하여 등록할 플로우 정의 경로를 명시한다. 기본으로 base-path가 정의되어 있지 않다면 플로우는 자신의 파일 이름에서 확장자를 뺀 식별자를 부여받는다.

<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
        
9.4.2. 커스텀 플로우 식별자 부여하기

id를 명시하여 커스텀 등록 식별자를 플로우에 부여한다.

<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" id="bookHotel" />
           
9.4.3. 플로우 meta-attributes 부여하기

flow-definition-attributes 엘리먼틀르 사용하여 등록한 플로우에 커스텀 meta-attributes를 부여한다.

<webflow:flow-location path="/WEB-INF/flows/booking/booking.xml">
    <flow-definition-attributes>
        <attribute name="caption" value="Books a hotel" />
    </flow-definition-attributes>
</webflow:flow-location>
           

9.4.4. 위치 패턴으로 플로우 등록하기

flow-location-pattern 엘리먼트를 사용하여 명시한 리소스 위치 패턴에 대응하는 플로우를 등록한다.

<webflow:flow-location-pattern value="/WEB-INF/flows/**/*-flow.xml" />
           
9.4.5. 플로우 위치 기반 경로

base-path 엘리먼트를 사용하여 애플리케이션의 모든 플로우에 대한 기본 위치를 정의한다. 모든 플로우 위치는 기본 경로를 기준으로 상대 경로가 된다. 기본 경로는 '/WEB-INF'같은 리소스 경로 또는 'classpath:org/springframework/webflow/samples' 같은 클래스패스 위치가 될 수 있다.

<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF">
    <webflow:flow-location path="/hotels/booking/booking.xml" />
</webflow:flow-registry>
           
With a base path defined, the algorithm that assigns flow identifiers changes slightly. Flows will now be assigned registry identifiers equal to the the path segment between their base path and file name. For example, if a flow definition is located at '/WEB-INF/hotels/booking/booking-flow.xml' and the base path is '/WEB-INF' the remaining path to this flow is 'hotels/booking' which becomes the flow id.

기본 경로를 정의함으로서 플로우 식별자를 부여하는 알고리즘이 약간 바뀐다. 플로우는 이제 기본 경로와 파일이름 사아의 값을 등록 식별자로 부여받는다. 예를 들어, 플로우가 정의되어 있는 위치가 '/WEB-INF/hotels/booking/booking-flow.xml' 이렇고 기본 경로가 '/WEB-INF' 라면 남는 부분인  'hotels/booking'가 플로우의 id가 된다.

[팁]    디렉토리 당 플로우 정의
각각의 플로우 정의를 별도의 디렉토리로 묶는 것이 좋은 습관이라는 것을 기억해두자. 이렇게 하면 모듈화를 증진시키고 독립적인 리소스를 프로우 정의와 함께 묶을 수 있다. 또한 규약을 이용할 때 두 개의 플로우가 같은 식별자를 갖는 것을 방지할 수 있다.

만약 기본 경로가 명시되어 있지 않거나 플로우 정의가 기본 경로에 있다면 파일이름(에서 확장자를 뺸)것을 사용하여 id를 대입한다. 예를 들어, 만약 플로우 정의 파일이 'booking.xml'라면 플로우 식별자는 간단하게 'booking'이 된다.

위치 패턴은 등록 기본 경로와 함께 사용하면 매우 막강하다. 플로우 식별자가 '*-flow'가 되지 않고 디렉토리 경로 기반이 된다. 예를 들어..

<webflow:flow-registry id="flowRegistry" base-path="/WEB-INF">
    <webflow:flow-location-pattern value="/**/*-flow.xml" />
</webflow:flow-registry>
           
위 예제에서 WEB-INF 밑에 /user/login, /user/registration, /hotels/booking, /flights/booking에 위치한 플로우들이 있다고 했을 때 플로우 id는 각각 user/login, user/registration, hotels/booking,  flights/booking가 된다.

9.4.6. FlowRegistry 계층구조 설정하기

Use the parent attribute to link two flow registries together in a hierarchy. When the child registry is queried, if it cannot find the requested flow it will delegate to its parent.

parent 속성을 사용하여 두 플로우 등록을 하나의 계층구조로 묶을 수 있다. 요청받은 플로우를 하위 레지스트리에서 찾지 못하면 parent로 위임한다.

<!-- my-system-config.xml -->
<webflow:flow-registry id="flowRegistry" parent="sharedFlowRegistry">
    <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>

<!-- shared-config.xml -->
<webflow:flow-registry id="sharedFlowRegistry">
    <!-- Global flows shared by several applications -->
</webflow:flow-registry>
           
9.4.7. 커스텀 FlowBuilder 서비스 설정하기

flow-builder-services 속성을 사용하여 flow-registry에서 플로우를 만들 때 사용할 서비스와 설정을 커스터마이징할 수 있다. 만약 flow-builder-services 태그가 명시되어 있지 않다면 기본 서비스 구현체를 사용한다. 태그가 정의되어 있다면 커스터마이징 하고 싶은 서비스를 참조하면 된다.

<webflow:flow-registry id="flowRegistry" flow-builder-services="flowBuilderServices">
    <webflow:flow-location path="/WEB-INF/flows/booking/booking.xml" />
</webflow:flow-registry>

<webflow:flow-builder-services id="flowBuilderServices" />
           

설정 가능한 서비스는 conversion-service, expression-parser, view-factory-creator다. 이 서비스들은 여러분이 정의한 커스텀 빈을 첨조하도록 설정되었다. 예를 들어..

<webflow:flow-builder-services id="flowBuilderServices"
    conversion-service="conversionService"
    expression-parser="expressionParser"
    view-factory-creator="viewFactoryCreator" />

<bean id="conversionService" class="..." />
<bean id="expressionParser" class="..." />
<bean id="viewFactoryCreator" class="..." />
           

9.4.7.1. conversion-service

conversion-service 속성을 사용하여 웹 플로우 시스템이 사용할 ConversionService를 커스터마이징할 수 있다. Converter는 플로우 실행도중 어떤 타입을 다른 타입으로 변경할 필요가 있을 때 사용한다. 기본 ConversionService는 숫자, 클래스, Enum같은 기본 객체 타입에 대한 컨버터를 등록한다.

9.4.7.2. expression-parser

expression-parser 속성을 사용하여 웹 플로우 시스템이 사용할 ExpressionParser를 커스터마이징할 수 있다. 기본 ExpressionParser는 클래스패스에서 사용할 수 있다면 Unified EL을 사용하고 그렇지 않다면 OGNL을 사용한다.

9.4.7.3. view-factory-creator

view-factory-creator 속성을 사용하여 웹 플로우 시스템이 사용할 ViewFactoryCreator를 커스터미이징 할 수 있다. 기본 ViewFactoryCreator는 JSP, Velocity, Freemarker를 랜더링 할 수 있는 스프링 MVC ViewFactory를 생성한다.

설정 가능한 설정은 development다. 이 설정은 플로우 생성 과정 중에 적용할 전역 설정 속성이다.

9.4.7.4. development

플로우를 개발 모드로 변경하려면 이것을 true로 설정하라. 개발 모드는 메시지 번들 같은 독립적인 플로우 리소스 변경을 포함하여 플로우 정의 변경 핫-릴로딩을 변경한다.

9.5. flow-executor 옵션

이번 장에서는 flow-executor 설정 옵션을 살펴본다.

9.5.1. 플로우 실행 리스너 부착하기

flow-execution-listeners 엘리먼틀르 사용하여 플로우 실행 생명주기 리스너를 등록한다.

<flow-execution-listeners>
    <listener ref="securityListener"/>
    <listener ref="persistenceListener"/>
</flow-execution-listeners>
           
특정 플로우만 관찰할 리스너를 설정할 수도 있다.

<listener ref="securityListener" criteria="securedFlow1,securedFlow2"/>
           

9.5.2. FlowExecution 영속성 튜닝하기

flow-execution-repository 엘리먼트를 사용하여 플로우 실행 영속성 설정을 튜닝한다.

<flow-execution-repository max-executions="5" max-execution-snapshots="30" />
           
9.5.2.1. max-executions

max-executions 속성을 설정하여 사용자 세션당 생성할 수 있는 플로우 실행 수 상한선을 설정한다.

9.5.2.2. max-execution-snapshots

max-execution-snapshots 속성을 설정하여 플로우 실행당 가질 수 있는 히스토리 스냅샷 수 상한선을 정할 수 있다. 스냅샷을 못찍게 하려면 이 값을 0으로 설정하라. 무한대로 스냅샷을 찍으려면 이 값을 -1로 설정하라.
신고

'Spring Web Flow > Chapter 9' 카테고리의 다른 글

SWF 9장 시스템 설정  (0) 2009.02.18
top


Whiteship's Over The Rainbow





요즘은 Andras Spielt를 연습하고 있는데 역시나 틀리지 않고 치는건 넘 어려워서 쉬운 걸로 하나 올려봅니다. ㅎㅎ 잼나요. 피아노~
신고
top


10분 완성 스프링 인티그레이션 (스크린캐스팅)





Spring Integration In 10 Minutes를 스크린캐스팅으로 찍어봤습니다. 굉장히 쉽게 접근할 수 있네요.
신고
top


SWF 8장 플로우 상속



8.1. 개요

플로우 상속은 한 플로우가 다른 플로우 설정을 상속할 수 있게한다. 상속은 플로우와 스테이트 수준에서 모두 발생할 수 있다. 가장 흔한 유즈케이스는 상위 플로우로 전역적인 트랜지션과 예외 핸들러를 정의하고 하위 플로우로 그 설정을 상속받는 것이다.

상위 플로우를 찾으려면 다른 플로우들처럼 flow-registry에 추가해야 된다.

8.2. 플로우 상속은 자바 상속과 비슷한가?

상위에 정의한 요소를 하위에서 접근할 수 있다는 측면에서는 자바 상속과 플로우 상속이 비슷하다. 하지만 주요 차이점이 있다.

하위 플로우는 상위 플로우의 요소를 재정의 할 수 없다. 상위와 하위 플로우에 있는 같은 요소는 병합(merge)된다. 상위 플로우에만 있는 요소는 하위 플로우에 추가된다.

하위 플로우는 여러 상위 플로우를 상속받을 수 있다. 자바 상속은 단일 클래스로 제한된다.

8.3. 플로우 상속 타입

8.3.1. 플로우 수준 상속

플로우 수준 상속은 flow 엘리먼트의 parent 속성으로 정의한다. 이 속성은 콤마로 구분한 상속받을 플로우 식별자 목록을 명시한다. 하위 프로우는 목록에 명시된 순서대로 각각의 상위 플로우를 상속 받는다. 첫 번째 상속으로 상위 플로우에 있는 요소와 내용을 추가하고 나면 그것을 다시 하위 플로우로 간주하고 그 다음 상위 프로우를 상속 받는다. 그런식으로 계속 이어진다.

<flow parent="common-transitions, common-states">

8.3.2. 스테이트 수준 상속

스테이트 수준 상속은 플로우 수준 상속과 비슷하다. 유일한 차이점은 플로우 전체가 아니라 오직 해당 스테이트 하나만 상위로 부터 상속 받는다.

플로우 상속과 달리 오직 하나의 상위만 허용한다. 또한 상속받을 플로우 스테이트의 식별자가 반드시 정의되어 있어야 한다. 플로우와 스테이트 식별자는 #로 구분한다.

상위와 하위 스테이트는 반드시 같은 타입이어야 한다. 예를 들어 view-state는 ent-state를 상속받을 수 없다. 오직 view-state만 상속받을 수 있다.

<view-state id="child-state" parent="parent-flow#parent-view-state">
           
8.4. 추상 플로우

종종 상위 플로우는 직접 호출하지 않도록 설계한다. 그런 플로우를 실행하지 못하도록 abstract로 설정할 수 있다. 만약 추상 플로우를 실행하려고 하면 FlowBuilderException가 발생한다.

<flow abstract="true">

8.5. 상속 알고리즘

하위 플로우가 상위 플로우를 상속할 때 발생하는 기본적인 일은 상위와 하위 플로우를 병합하여 새로운 플로우를 만드는 것이다. 웹 플로우 정의 언어에는 각각의 엘리먼트에 대해 어떻게 병합할 것인가에 대한 규칙이 있다.

엘리먼트에는 두 종류가 있다. 병합 가능한 것(mergeable)과 병합이 가능하지 않은 것(non-mergeable이 있다. 병합가능한 엘리먼트는 만약 엘리먼트가 같다면 병합을 시도한다. 병합이 가능하지 않은 엘리먼트는 항상 최종 플로우에 직접 포함된다. 병합 과정 중에 수정하지 않는다.

노트
상위 플로우에있는 외부 리소스 경로는 절대 경로여야 한다. 상대 경로는 두 플로우를 병합할 때 상위 플로우와 하위 플로우가 위치한 디렉토리가 다르면 깨질 수 있다. 일반 병합하면, 상위 플로우에 있던 모든 상대 경로는 하위 플로우 기준으로 바뀐다.

8.5.1. 병합 가능한 엘리먼트

만약 같은 타입의 엘리먼트고 입력한 속성이 같다면 상위 엘리먼트의 내용을 하위 엘리먼트로 병합한다. 병합 알고리즘은 계속해서 병합하는 상위와 하위의 서브 엘리먼트를 각각 병합한다. 그렇지 않으면 상위 플로우의 엘리먼트를 하위 플로우에 새로운 엘리먼트로 추가한다.

대부분의 경우 상위 프로우릐 엘리먼트가 하위 플로우 엘리먼트에 추가된다. 이 규칙에 예외로는 시작할 때 추가 될 액션 엘리먼트(evaluate, render, set)가 있다. 상위 액션의 결과를 하위 액션 결과로 사용하게 한다.

병합이 가능한 엘리먼트는 다음과 같다.
  • action-state: id
  • attribute: name
  • decision-state: id
  • end-state: id
  • flow: 항상 병합
  • if: test
  • on-end: 항상 병합
  • on-entry: 항상 병합
  • on-exit: 항상 병합
  • on-render: 항상 병합
  • on-start: 항상 병합
  • input: name
  • output: name
  • secured: attributes
  • subflow-state: id
  • transition: on and on-exception
  • view-state: id
8.5.2. 병합 할 수 없는 엘리먼트

병합할 수 없는 엘리먼트는 다음과 같다
  • bean-import
  • evaluate
  • exception-handler
  • persistence-context
  • render
  • set
  • var

신고

'Spring Web Flow > Chapter 8' 카테고리의 다른 글

SWF 8장 플로우 상속  (0) 2009.02.16
top


SWF 7장 플로우 보안하기



7.1 개요

보안은 모든 애플리케이션에서 중요한 개념이다. 최종 사용자는 URL을 추측하여 사이트의 임의 영역에 접근해서는 안 된다. 중요한 부분은 반드시 권한을 가지고 있는 요청만 접근할 수 있어야 한다. 스프링 시큐리티는 검증된 보안 플랫폼으로 애플리케이션에 여러 수준으로 통합할 수 있다. 이번 장에서는 플로우 실행 보안을 집중적으로 살펴본다.

7.2. 플로우를 어떻게 보안할까?

플로우 실행 보안은 세 단계로 한다.
  • 스프링 시큐리티를 인증 권한 규칙으로 설정한다.
  • 플로우 정의에 보안 규칙을 정의하는 secured 엘리먼트를 추가한다.
  • SecurityFlowExecutionListener를 추가하여 보안 규칙을 처리한다.
이 모든 단계를 완료해야 하며 그렇지 않으면 보안 규칙은 적용되지 않는다.

7.3. secured 엘리먼트

secured 엘리먼트는 완전히 들어오기 전에 권한 확인을 하도록 고안되었다. 보안하고 있는 플로우 실행 스테이지 마다 두 번 이상 발생하면 안 된다.

플로우 실행에서 세 단계를 보안할 수 있다. flow, state, transition이다. 각각에서 secured 엘리먼트의 의미는 모두 동일하다. secured 엘리먼트는 자신이 보안하는 엘리먼트 안에 위치한다. 예를 들어 state를 보안하려면 해당 state 내부에 secured 엘리먼트를 추가하면 된다.

<view-state id="secured-view">
    <secured attributes="ROLE_USER" />
    ...
</view-state>
       
7.3.1. 보안 속성

attributes 속성은 콤바로 구분한 스프링 시큐리비 권한 속성 목록이다. 보통 특정 보안 롤(role)을 명시한다. 스프링 시큐리티 접근 결정 매니저(access decision manager)에 의해 이 속성에 입력한 값과 사용자가 가지고 있는 값을 비교한다.

<secured attributes="ROLE_USER" />
           
기본적으로, 롤 기반 접근 결정 관리자를 사용하여 사용자가 접근할 수 있는지 확인한다. 만약 애플리케이션이 권한 룰을 사용하지 않는다면 이 부분을 오버라이딩할 필요가 있다.

7.3.2. 매칭 타입

가용한 매칭 타입에는 두 가지가 있다. any와 all이 있다. Any는 필요한 보안 속성 중에 사용자가 하나라도 가지고 있다면 접근을 허용한다. All은 사용자가 명시되어 있는 보안 속성을 모두 가지고 있는 경우에 접근을 허용한다.

<secured attributes="ROLE_USER, ROLE_ANONYMOUS" match="any" />
   
이 속성은 필수가 아니다. 정의하지 않으면 기본 값은 any다.

The match attribute will only be respected if the default access decision manager is used.

7.4. SecurityFlowExecutionListener

보안 규칙을 폴로우 내부에 정의하는 것 만으로는 플로우 실행을 보호하지 못한다. SecurityFlowExecutionListener가 웹 플로우 설정에 정의되어 있어야 플로우 실행기(executor)에 적용된다.

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">
    <webflow:flow-execution-listeners>
        <webflow:listener ref="securityFlowExecutionListener" />
    </webflow:flow-execution-listeners>
</webflow:flow-executor>

<bean id="securityFlowExecutionListener"
      class="org.springframework.webflow.security.SecurityFlowExecutionListener" />

애플리케이션의 특정 부분에서 만약 접근이 거부되면 AccessDeniedException를 던진다. 이 예외는 나중에 스프링 시큐리티가 처리하고 사용자에게 권한을 요청하는데 사용된다. 이 예외는 예외 스택을 무제한으로 올라갈 수 있다는 것이 중요한데 이렇게 하지 않으면 최종 사용자는 권한을 요청할 수 없다.

7.4.1. 커스텀 접근 결정 관리자

만약 애플리케이션이 롤 기반이 아닌 권한을 사용중이라면 커스텀 AccessDecisionManager를 설정할 필요가 있다. 기본 결정 관리자를 오버라이드 하고 시큐리티 리스너의 accessDecisionManager 속성에 설정할 수 있다. 스프링 시큐리티 레퍼런스 문서를 참조하여 결정 관리자에 대해 자세히 살펴보기 바란다.

<bean id="securityFlowExecutionListener"
      class="org.springframework.webflow.security.SecurityFlowExecutionListener">
    <property name="accessDecisionManager" ref="myCustomAccessDecisionManager" />
</bean>

7.5. 스프링 시큐리티 설정하기

스프링 시큐리티는 일관된 설정 옵션을 제공한다. 모든 애플리케이션과 환경이 자신만의 보안 요구사항을 가지고 있기 떄문에 스프링 시큐리티 레퍼런스 문서에서 가용한 옵션을 익히는것이 좋다.

 7.5.1. 스프링 설정

스프링 설정은 (보호하는 URL과 로그인/로그아웃 방법 같은) http 관련 설정과 authentication-provider를 설정한다. 예제 애플리케이션에 로컬 인증 공급자를 설정했다.

<security:http auto-config="true">
    <security:form-login login-page="/spring/login"
                         login-processing-url="/spring/loginProcess"
                         default-target-url="/spring/main"
                         authentication-failure-url="/spring/login?login_error=1" /> 
    <security:logout logout-url="/spring/logout" logout-success-url="/spring/logout-success" />
</security:http>

<security:authentication-provider>
    <security:password-encoder hash="md5" />
    <security:user-service>
        <security:user name="keith" password="417c7382b16c395bc25b5da1398cf076"
                       authorities="ROLE_USER,ROLE_SUPERVISOR" />
        <security:user name="erwin" password="12430911a8af075c6f41c6976af22b09"
                       authorities="ROLE_USER,ROLE_SUPERVISOR" />
        <security:user name="jeremy" password="57c6cbff0d421449be820763f03139eb"
                       authorities="ROLE_USER" />
        <security:user name="scott" password="942f2339bf50796de535a384f0d1af3e"
                       authorities="ROLE_USER" />
    </security:user-service>
</security:authentication-provider>
           
7.5.2. web.xml 설정

web.xml 파일에서 filter는 모든 요청을 가로채도록 정의했다. 이 필터는 로그인/로그아웃 요청을 다루고 적절하게 처리할 것이다. 또한 AccesDeniedExceptions 예외를 잡아서 사용자를 로그인 페이지로 리다이렉트 시킨다.



 
신고

'Spring Web Flow > Chapter 7' 카테고리의 다른 글

SWF 7장 플로우 보안하기  (0) 2009.02.12
top


SWF 6장 플로우가 관리하는 영속성



6.1. 개요

대부분의 애플리케이션은 여러 방법으로 데이터에 접근한다. 여러 사용자가 공유하는 데이터를 여럿이 수정한다. 따라서 트랜잭션 데이터 접근 속성이 필요하다. 관계형 데이터 집합을 도메인 객체로 변형하여 애플리케이션 처리를 도와준다. 웹 플로우는 "플로우가 관리하는 영속성"(flow managed persistence)을 제공하여 플로우가 객체 영속성 문맥을 만들고, 커밋하고, 닫을 수 있도록 한다. 웹 플로우는 하이버네이트와 JPA 객체 영속화 기술과 연동한다.

플로우-관리 영속성과 별도로 PesistenceContext 관리를 애플리케이션의 서비스 계층에서 완전히 캡슐화하는 패턴이 있다. 이런 경우 웹 계층은 영속화에 관여하지 않는다. 그 대신 서비스 계층으로 념주겨거나 반환받은 detached object를 가지고 동작한다. 이번 장은 플로우-관리 영속성에 초점을 맞추고 이 기능을 언제 어떻게 사용하는지 살펴보겠다.

6.2. FlowScoped PersistenceContext

이 패턴은 플로우가 시작할 때 flowScope 안에 PersistenceContext를 생성한다. 이 컨텍스트를 사용하여 플로우 실행 코드에서 데이터 접근을 하고 마지막에 영속화 요소에 대한 변경을 커밋한다. 이 패턴은 변경사항 커밋을 오직 플로우 실행 마지막에만 하기 때문에 중간 단계 편집 독립성을 제공한다. 이 패턴은 보통 낙천적인 롹킹으로 여러 사용자의 동시 수정 무결성을 보장한다. 플로우 과정을 일정 시간을 넘어서까지 저장하거나 재시작 하려면 플로우 상태에 대한 영속성 저장소를 사용해야 한다. 만약 저장과 재시작 기능이 필요 없다면 플로우 상태를 표준 HTTP 세션-기반으로 저장해도 충분하다. 그런 경우 커밋 이전에 세션 만료나 종가 발생하면 변경 사항들을 잃어버리게 된다.

 

FlowScoped PersistenceContext 패턴을 사용하려면 해당 플로우를 persistence-context로 설정하라.

 

<?xml version="1.0" encoding="UTF-8"?>

<flow xmlns="http://www.springframework.org/schema/webflow"

      xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"

      xsi:schemaLocation="http://www.springframework.org/schema/webflow

                          http://www.springframework.org/schema/webflow/spring-webflow-2.0.xsd">

 

    <persistence-context />

 

</flow>

 

그런 다음 적당한 FlowExecutionListener를 설정하여 이 패턴을 플로우에 적용하라. 만약 하이버네이트를 사용하고 있다면 HibernateFlowExecutionListener를 등록하고, JPA를 사용하고 있다면 JpaFlowExecutionListener를 등록하라.

 

<webflow:flow-executor id="flowExecutor" flow-registry="flowRegistry">

    <webflow:flow-execution-listeners>

        <webflow:listener ref="jpaFlowExecutionListener" />

    </webflow:flow-execution-listeners>

</webflow:flow-executor>

 

<bean id="jpaFlowExecutionListener"

      class="org.springframework.webflow.persistence.JpaFlowExecutionListener">

    <constructor-arg ref="entityManagerFactory" />

    <constructor-arg ref="transactionManager" />

</bean>

 

종료 지점에서 커밋 하려면 end-state commit 속성에 명시하라.

 

<end-state id="bookingConfirmed" commit="true" />

 

이렇게. 플로우를 시작하면 리스너는 새로운 EntiryManager flowScope에 할당한다. EntityManager persistenceContext 변수를 사용하여 여러분 플로우 내에서 어디서든 참조할 수 있다. 게다가 스프링을 사용하는 모든 데이터 접근은 자동으로 이 EntityManager를 사용하게 될 것이다. 그런 데이터 접근 작업은 중간 단계 편집을 독립적으로 관리하기 위해 항상 트랜잭션이 없거나  읽기-전용 트랜잭션으로 수행한다

신고

'Spring Web Flow > Chapter 6' 카테고리의 다른 글

SWF 6장 플로우가 관리하는 영속성  (0) 2009.02.11
top


스프링 트랜잭션 주의할 것

Spring/etc : 2009.02.07 20:46


Transaction strategies: Understanding transaction pitfalls

@Transactional(readOnly = true, propagation=Propagation.REQUIRED)
public long insertTrade(TradeData trade) throws Exception {
   // 어떤 코드
}

위 코드에 있는 insertTrade를 실행행하면 어떤 결과가 발생할까요?

A. read-only connection 예외가 발생한다.
B. 데이터를 추가하고 커밋한다.
C. read-only가 true라서 아무것도 하지 않는다.

당연히 A일 것 같은데... // 어떤 코드 부분이 JDBC 코드일 경우이고 JPA나 하이버네이트 같은 ORM 코드면 propagation 설정 REQUIRED가 모든 것을 재정의해서 새로운 트랜잭션을 시작하고 read-only 플래그가 없는 것 처럼 동작하게 된다네요... @_@

지금은 넘 졸려서 낼 자세히 읽어봐야겠습니다.

따라서 읽기 전용 매서드의 경우 다음과 같이 SUPPORTS 프로퍼게이션 모드를 사용하는게 타당하다고 합니다. 왜냐면 보통 다음과 같이 읽기 전용 매서드를 설정하는데..

@Transactional(readOnly = true)
public TradeData getTrade(long tradeId) throws Exception {
   return em.find(TradeData.class, tradeId);
}

이 때 기본 프로퍼게이션이 REQUIRED 모드기 때문에 매번 새로운 트랜잭션을 만들어 사용하고 사용하는 DB에 따라서는 불필요한 읽기 롹까지 사용해서 데드락을 발생시킬 수도 있다고 하기 때문입니다. 따라서..

@Transactional(readOnly = true, propagation=Propagation.SUPPORTS)
public TradeData getTrade(long tradeId) throws Exception {
   return em.find(TradeData.class, tradeId);
}

이렇게 SUPPORTS로 변경하여 기존 트랜잭션이 있으면 사용하고 없으면 사용하지 않도록 하거나..

public TradeData getTrade(long tradeId) throws Exception {
   return em.find(TradeData.class, tradeId);
}

요렇게 읽기 전용인 경우에는 아예 @Transactional 애노테이션을 아예 사용하지 않는게 나아보입니다. 굳이 원자화 할 것도 없고~ Isolation level만 적당선에서 타협한다면 롹을 걸 일도 없고~


REQUIRES_NEW  사용시 주의 할 것.

@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception {...}

@Transactional(propagation=Propagation.REQUIRES_NEW)
public void updateAcct(TradeData trade) throws Exception {...}

이런 매서드와 트랜잭션 설정이 있을 때

@Transactional(propagation=Propagation.REQUIRES_NEW)
public long insertTrade(TradeData trade) throws Exception {
   em.persist(trade);
   updateAcct(trade);
   //exception occurs here! Trade rolled back but account update is not!
   ...
}

이런 식으로 코딩하면... updateAcct() 이후에 에러가 발생하면 updateAcct는 쿼리가 날아가는데 나머지 내용은 롤백 되는 현상이 발생할 수 있죠. 왜냐면 매번 새로운 트랜잭션을 만들기 때문에 insertTrade를 실행하는 트랜잭션과 updateAcct를 실행하는 트랜잭션이 별개기 때문입니다.

뭐~ 이건 쉽네요. 걍 REQUIRED로 쓰거나 MANDATORY를 쓰면 됩니다.

롤백과 관련해서는 checked exception에 대비해야 한다.

@Transactional(propagation=Propagation.REQUIRED)
public TradeData placeTrade(TradeData trade) throws Exception {
   try {
      insertTrade(trade);
      updateAcct(trade);
      return trade;
   } catch (Exception up) {
      //log the error
      throw up;
   }
}

이런 코드가 과연 안전할까? 아니요. 왜냐면 저 코드에서 만약 updateAcct() 매서드 처리 도중에  checked exception이 발생하면 insertTrade() 매서드는 그대로 실행하고 예외도 그냥 던지고 말기 때문에 데이터가 불안전한 상태가 될 것입니다. 따라서 checked exception에 대비해서..

@Transactional(propagation=Propagation.REQUIRED, rollbackFor=Exception.class)
public TradeData placeTrade(TradeData trade) throws Exception {
   try {
      insertTrade(trade);
      updateAcct(trade);
      return trade;
   } catch (Exception up) {
      //log the error
      throw up;
   }
}

이런 식으로 하는게 좋겠습니다.



신고
top


빈 속성에 값 설정 했는지 확인하기

Spring/etc : 2009.02.04 21:22


null 체크가 될 수도 있겠고 어떤 값이든 주입 됐는지 확인하는 것일 수도 있겠습니다. 예전에 대충 정리해둔 적은 있는데 잊어버렸던 거라서 다시 정리해 둡니다.

<bean> 엘리먼트에 dependency-check라는 속성이 이 속성을 설정하지 않으면(기본값) 빈 속성에 어떤 값이나 빈이 주입 되었는지 확인하지 않습니다.

simple 값을 주면 기본 타입과 컬렉션 타입만 검사합니다.

object 값을 주면 simple이 검사하는 것을 제외한 레퍼런스 타입을 검사합니다.

all 값을 주면 모든 속성을 검사합니다.

요즘은 @Autowired로 주로 DI를 하기 때문에 점점 예전 빈 설정 내용들은 잊혀져 가는 것 같습니다. @Autowired는 기본 값이 필수로 되어있죠. 그래서 오히려 반대로 없어도 괜찮다고 설정 하려면 애노테이션에 required=false 속성을 추가해 줘야 합니다.

...
    private MovieFinder movieFinder;

    @Autowired(required=false)
    public void setMovieFinder(MovieFinder movieFinder) {
        this.movieFinder = movieFinder;
    }
...

이런 식으로..
신고
top


싱글톤, 비싱글톤 언제 써야 할까?

Spring/etc : 2009.02.04 21:10


참고:  Pro Spring 2.5 3장

요즘 이 책을 재벌 작업을 하고 있는데 공부한지 오래되서 잊었거나 미쳐 보지 못했던 내용들이 등장하면 피로가 조금 줄어드는 느낌을 받곤 합니다.

그 중 하나가 바로 이 글의 제목과 관련된 내용이었습니다. 스프링을 가지고 웹 애플리케이션을 몇 번 만들어 봤지만 singleton 스코프 빼곤 별로 써본 기억이 나질 않을 정도인데 새삼 생각해보게 하는 내용들이 있었습니다.

싱글톤이 적절한 경우
- 상태가 없는 공유 객체
- 읽기 전용 상태를 가지고 있는 공유 객체
- 상태를 공유하는 공유 객체
- 쓰기가 가능한 상태를 약간 가지고 있으면서 매우 빈번하게 사용하는 객체

비싱글톤이 적절한 경우
- 쓰기가 가능한 상태를 가진 객체
- private 상태를 가지고 있는 객체

여기서 주목해야 할 것은 같은 쓰기 가능한 상태를 가진 객체인데 어느것은 싱글톤 어느 것은 비싱글톤이 적절하다는 부분인데 동기화 비용과 객체 생성 비용을 가지고 나누더군요.

자세한 내용은 프로 스프링 2.5 번역서를 참고하세요~ ㅋㅋㅋ
신고
top


스프링 2.5 환경에서 하이버네이트 사용하기



참조 요약: Spring One 2008 Wokring With Hibernate in a Spring 2.5 Environment

스프링의 HibernateTemplate
(Hibernate 3.1 이전)

- 스프링이 관리하는 트랜잭션을 사용한다.
- 예외 번역 제공

public class HibernateClinic extends HibernateDaoSupport {
...
}

Native Hibernate DAO
(Hibernate 3.1+_

- 트랜잭션 훅(hook)을 제공한다.
- transactional session을 찾는 로직을 제공할 수 있게 한다. sessionFactory.getCurrentSession()
- 예외 번역 제공 @Repository PersistenceExceptionTranslation post processor
- 순수 하이버네이트 API만 사용할 수 있다.

단위 테스트 종류
- "Logical" 단위 테스트
- 통합 테스트

스프링으로 통합 테스트하기
- DI
- 애플리케이션 컨텍스트 로딩 줄이기
- 자동 롤백
- 하이버네이트 + JDBC 조합

AbstractTransactionalDataSourceSpringContextTests
- Ctrl + Shift + T ATDSSCT
- 애플리케이션 컨텍스트 로딩, 캐슁
- DI
- JdbcTemplate 제공

WAR에서 하이버네이트 VS OSGi 개발
- 일반 WAR 배포
  - SessionFactory가 클래스패스에 도메인 타입들을 찾는다.
  - 저장소(DAO, repositoru)들이 SessionFactory를 사용한다.
- OSGi 개발
  - 여러 도메인 번들로 쪼갠다.
  - SessionFactory는 도메인 타입에 접근해야 한다.
  - 저장소는 SessionFactory에 접근해야 한다.

OSGi 환경에서 하이버네이트
- "infrastructure" 번들
  - SessionFactory 서비스를 제공하고
  - 모든 도메인 타입을 가져온다.
- 여러 도메인 모델
  - 도메인 타입을 공개한다.
  - SessionFactory 서비스를 사용한다.
- Petclinic 예제를 제공한다.

=====

발표에서 다루는 내용이 너무 많아서 다소 산만했습니다. 하이버네이트만 집중적으로 다뤘으면 어땠을까 싶더군요. @Repository 얘기가 나오니까 컴포넌트 스캔 얘기로 새고.. 도메인 객체에 repository 객체 주입하는 얘기가 나오니까 @Configurable이랑 aspectj 얘기로 새고.. 테스트 얘기 나오니까 Test Context 쪽으로 또 얘기가 새버렸다가 나중엔 다시 하이버네이트랑 OSGi 얘기 조금 꺼내고 끝~~

멋진건 54분이라는 짧은 발표 시간 중에도 저렇게 많은 내용과 중간 중간 전부 데모까지 보여줬다는 겁니다.
신고
top







티스토리 툴바