보통 자바 서블릿 환경에서 웹 페이지를 만들 때에는 JSP를 가장 많이 씁니다만, 벨로시티도 꽤 괜찮은 도구로 알려져 있습니다. 벨로시티를 쓰게 되면 안 만들 수가 없는 게 툴입니다. 벨로시티는 언어 자체가 워낙에 간결하기 때문에 배우고 쓰기 쉽지만 그만큼 지원 기능이 약한 것도 사실입니다. 물론 이렇게 저렇게 기술을 부리면 웬만큼 되긴 하지만 그러다보면 코드가 지저분해지고 '간결함'이라는 장점도 사라지게 마련입니다 그래서 자바로 툴 클래스를 만들어서 기능을 보강하곤 합니다.

스프링 프레임워크와 벨로시티를 함께 쓸 경우, 때때로 스프링의 IoC 기능을 쓰는 툴을 만들고 싶을 때가 있습니다. 예를 들어서 스프링의 DAO 기능을 이용해서 회원관리를 할 경우에 벨로시티 툴을 이용해서 DAO로부터 회원 정보를 가져다가 뷰에 뿌리고 싶죠. 스프링은 벨로시티를 지원하긴 합니다. 하지만 문제는 툴을 만드는 작업은 스프링에서 관여하지 않고 벨로시티 엔진으로 그냥 넘겨버린다는 겁니다. 따라서, 벨로시티의 툴박스 XML 파일을 써서 툴을 인식시키면 툴에는 IoC가 적용되지 않아 관련 프로퍼티가 null이 되므로 툴을 쓸 때 오류를 일으킵니다.

스프링 웹 MVC에서 벨로시티 툴박스를 쓸 경우, 뷰 클래스로 VelocityToolboxView를 씁니다. 여기에 보면 createVelocityContext() 메소드가 있는데 여기서 툴박스를 구성하게 됩니다. 이 소스를 보면, 툴박스 파일이 지정이 되어 있을 경우에, ToolboxManager.getInstance() 메소드를 통해서 툴 개체들을 만들고 이를 맵으로 가져옵니다. 하지만 실제로 벨로시티 툴로 동작을 하기 위해서는 이 맵을 벨로시티 컨텍스트에 setToolBox(), 메소드로 등록해야 합니다. 바로 이 직전에 끼어들어서 맵 개체에 있는 툴을 바꿔치기하는 겁니다.

    protected Context createVelocityContext(
            Map model, HttpServletRequest request,
            HttpServletResponse response)
            throws Exception {
            ...
            ToolboxManager toolboxManager
                    = ServletToolboxManager.getInstance(
                            getServletContext(),
                            getToolboxConfigLocation());
            Map toolboxContext
                    = toolboxManager.getToolbox(velocityContext);
            (상속받은 클래스에서 메소드를 재정의해서 이 지점에 끼어듭니다)
            velocityContext.setToolbox(toolboxContext);
            ...
    }

스프링의 애플리케이션 컨텍스트는 getBeanNamesForType(Class) 메소드를 제공합니다. 이 메소드는 파라미터로 지정된 클래스와 그 서브클래스에 대한 빈 정의가 있는지를 찾아서 그 이름을 돌려줍니다. 돌려주는 값은 문자열 배열인데 같은 클래스, 그리고 서브클래스에 대한 빈 정의가 하나보다 많을 수 있기 때문입니다. 하지만 우리는 전제조건을 한 툴에 대해서 한 가지 정의만 쓰도록 하고, 무조건 배열 첫번째 원소의 빈 이름으로 빈을 생성할 것입니다. 그런데 여기서 한 가지 조심할 문제가 있습니다. 뷰 클래스는 getApplicationContext() 메소드를 제공하기 때문에 스프링의 애플리케이션 컨텍스트에 접근할 수 있지만, 이 컨텍스트는 action-servlet.xml에 있는 빈 정의만을 가지고 있습니다. 따라서 다른 파일에 빈 정의가 있으면 이를 찾지 못하고 오류를 일으킵니다. 이 문제 때문에 상당히 머리가 아팠는데, 그래서 아주 지저분한 방법으로 문제를 구현했다가 오늘에서야 이 사실을 알았습니다. 으으...

애플리케이션 컨텍스트는 getParent()를 통해서 상위 애플리케이션 컨텍스트에 접근할 수 있도록 해 주므로, getParent()를 통해 계속 따라 올라가면 최상위 단계까지 갈 수 있습니다. getParent()null을 돌려주면 그 애플리케이션 컨텍스트가 최상위입니다. 이건 getRootApplicationContext() 메소드로 따로 구현했다고 칩시다. 그러면 다음과 같이 createVelocityContext() 메소드를 재정의합니다.

    protected Context createVelocityContext(
            Map model, HttpServletRequest request,
            HttpServletResponse response)
            throws Exception {
            ...
            ToolboxManager toolboxManager
                    = ServletToolboxManager.getInstance(
                            getServletContext(),
                            getToolboxConfigLocation());
            Map toolboxContext
                    = toolboxManager.getToolbox(velocityContext);


            // Search tools which are defined as beans
            // in the Spring Framework context.
            ApplicationContext ctx = getRootApplicationContext();
            Set keySet = toolboxContext.keySet();
            
            for (Object key : keySet) {
                Object tool = toolboxContext.get(key);
                
                String[] beanName
                        = ctx.getBeanNamesForType(tool.getClass());

                
                if (!ArrayUtils.isEmpty(beanName)) {
                    // Use first element only
                    // no matter how much elements are in beanName.
                    Object toolBean = ctx.getBean(beanName[0]);
                    toolboxContext.put(key, toolBean);                    
                }
            }
            
            velocityContext.setToolbox(toolboxContext);

            ...
    }


방법은 간단합니다.
툴을 가지고 있는 맵 개체의 키 세트를 뽑아낸 다음에, 이것으로 루프를 돌려가면서 키에 대한 툴 인스턴스를 얻습니다. 이 툴의 클래스가 스프링 빈으로 정의된 것인지 찾아본 다음에, 빈으로 정의가 되어 있으면 빈 개체를 스프링을 통해서 만들고 원래 맵에 있던 녀석과 바꿔치기하는 것입니다. 그리고 나면 시치미 뚝 떼고 벨로시티 컨텍스트에 맵을 등록해 줍니다. 그러면 스프링 빈이 아닌 툴은 벨로시티가 만든 그대로이고, 스프링 빈인 툴은 스프링이 만들고 IoC를 적용한 인스턴스가 되어 벨로시티에서도 제대로 작동합니다. 이렇게 만들어진 뷰를 벨로시티 뷰 리졸버의 viewClass 프로퍼티에 지정하면 스프링 빈으로 정의된 벨로시티 툴을 문제 없이 쓸 수 있습니다. 주의할 것인 스프링 빈인 벨로시티 툴을 쓰려면 반드시 스프링 컨텍스트 파일에 빈 정의를 하고 벨로시티 툴박스 파일에도 이 클래스를 지정해야 합니다.
Posted by MP4/13

BLOG main image
Drive with your sense. by MP4/13
Add to Technorati Favorites

카테고리

분류 전체보기 (1202)
광속질주 (205)
취생몽사 (160)
주지육림 (3)
독서삼매 (10)
음담패설 (28)
전광석화 (146)
우매상자 (66)
포장후면 (20)
악마사전 (6)
팔도유람 (20)
혹세무민 (508)
일상포착 (30)
Total : 3,916,122
Today : 611 Yesterday : 624