2011-03-08 23 views
1

TomcatにデプロイするRESTサービスをテストします。 JUnitでこれを行うには、かなりうまく動作するTomcat 6を埋め込みました。Embedded Tomcat 6でSpringへのアクセス

私はDAOや他のBeanにアクセスする必要があるため、Springコンテキストにアクセスする必要があります。

どのようにこれを達成することができますか?私はRootContextを試しましたが、何も見つかりませんでした。敬具

、 エリック

編集:私は知っているは解決策を発見したが、しかしその悪いハック:今

import java.io.IOException; 
import java.util.Enumeration; 
import java.util.Map; 
import java.util.Properties; 
import java.util.concurrent.ConcurrentHashMap; 

import javax.servlet.ServletContext; 
import javax.servlet.ServletContextEvent; 
import javax.servlet.ServletContextListener; 

import org.apache.commons.logging.Log; 
import org.apache.commons.logging.LogFactory; 
import org.springframework.beans.BeanUtils; 
import org.springframework.beans.factory.DisposableBean; 
import org.springframework.beans.factory.access.BeanFactoryLocator; 
import org.springframework.beans.factory.access.BeanFactoryReference; 
import org.springframework.context.ApplicationContext; 
import org.springframework.context.ApplicationContextException; 
import org.springframework.context.access.ContextSingletonBeanFactoryLocator; 
import org.springframework.core.io.ClassPathResource; 
import org.springframework.core.io.support.PropertiesLoaderUtils; 
import org.springframework.util.ClassUtils; 
import org.springframework.util.ObjectUtils; 
import org.springframework.web.context.ConfigurableWebApplicationContext; 
import org.springframework.web.context.ContextLoader; 
import org.springframework.web.context.WebApplicationContext; 

public class ContextLoaderListener extends ContextLoader implements ServletContextListener { 

    private ContextLoader contextLoader; 

    /** 
    * Config param for the root WebApplicationContext implementation class to 
    * use: "<code>contextClass</code>" 
    */ 
    public static final String CONTEXT_CLASS_PARAM = "contextClass"; 

    /** 
    * Name of servlet context parameter (i.e., "<code>contextConfigLocation</code>") 
    * that can specify the config location for the root context, falling back 
    * to the implementation's default otherwise. 
    * @see org.springframework.web.context.support.XmlWebApplicationContext#DEFAULT_CONFIG_LOCATION 
    */ 
    public static final String CONFIG_LOCATION_PARAM = "contextConfigLocation"; 

    /** 
    * Optional servlet context parameter (i.e., "<code>locatorFactorySelector</code>") 
    * used only when obtaining a parent context using the default implementation 
    * of {@link #loadParentContext(ServletContext servletContext)}. 
    * Specifies the 'selector' used in the 
    * {@link ContextSingletonBeanFactoryLocator#getInstance(String selector)} 
    * method call, which is used to obtain the BeanFactoryLocator instance from 
    * which the parent context is obtained. 
    * <p>The default is <code>classpath*:beanRefContext.xml</code>, 
    * matching the default applied for the 
    * {@link ContextSingletonBeanFactoryLocator#getInstance()} method. 
    * Supplying the "parentContextKey" parameter is sufficient in this case. 
    */ 
    public static final String LOCATOR_FACTORY_SELECTOR_PARAM = "locatorFactorySelector"; 

    /** 
    * Optional servlet context parameter (i.e., "<code>parentContextKey</code>") 
    * used only when obtaining a parent context using the default implementation 
    * of {@link #loadParentContext(ServletContext servletContext)}. 
    * Specifies the 'factoryKey' used in the 
    * {@link BeanFactoryLocator#useBeanFactory(String factoryKey)} method call, 
    * obtaining the parent application context from the BeanFactoryLocator instance. 
    * <p>Supplying this "parentContextKey" parameter is sufficient when relying 
    * on the default <code>classpath*:beanRefContext.xml</code> selector for 
    * candidate factory references. 
    */ 
    public static final String LOCATOR_FACTORY_KEY_PARAM = "parentContextKey"; 

    /** 
    * Name of the class path resource (relative to the ContextLoader class) 
    * that defines ContextLoader's default strategy names. 
    */ 
    private static final String DEFAULT_STRATEGIES_PATH = "ContextLoader.properties"; 


    private static final Properties defaultStrategies; 

    static { 
     // Load default strategy implementations from properties file. 
     // This is currently strictly internal and not meant to be customized 
     // by application developers. 
     try { 
      ClassPathResource resource = new ClassPathResource(DEFAULT_STRATEGIES_PATH, ContextLoader.class); 
      defaultStrategies = PropertiesLoaderUtils.loadProperties(resource); 
     } 
     catch (IOException ex) { 
      throw new IllegalStateException("Could not load 'ContextLoader.properties': " + ex.getMessage()); 
     } 
    } 


    /** 
    * Map from (thread context) ClassLoader to corresponding 'current' WebApplicationContext. 
    */ 
    private static final Map<ClassLoader, WebApplicationContext> currentContextPerThread = 
      new ConcurrentHashMap<ClassLoader, WebApplicationContext>(1); 

    /** 
    * The 'current' WebApplicationContext, if the ContextLoader class is 
    * deployed in the web app ClassLoader itself. 
    */ 
    private static volatile WebApplicationContext currentContext; 

    /** 
    * The root WebApplicationContext instance that this loader manages. 
    */ 
    private WebApplicationContext context; 

    /** 
    * Holds BeanFactoryReference when loading parent factory via 
    * ContextSingletonBeanFactoryLocator. 
    */ 
    private BeanFactoryReference parentContextRef; 


    /** 
    * Initialize Spring's web application context for the given servlet context, 
    * according to the "{@link #CONTEXT_CLASS_PARAM contextClass}" and 
    * "{@link #CONFIG_LOCATION_PARAM contextConfigLocation}" context-params. 
    * @param servletContext current servlet context 
    * @return the new WebApplicationContext 
    * @see #CONTEXT_CLASS_PARAM 
    * @see #CONFIG_LOCATION_PARAM 
    */ 
    public WebApplicationContext initWebApplicationContext(ServletContext servletContext) { 
     if (servletContext.getAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE) != null) { 
      throw new IllegalStateException(
        "Cannot initialize context because there is already a root application context present - " + 
        "check whether you have multiple ContextLoader* definitions in your web.xml!"); 
     } 

     Log logger = LogFactory.getLog(ContextLoader.class); 
     servletContext.log("Initializing Spring root WebApplicationContext"); 
     if (logger.isInfoEnabled()) { 
      logger.info("Root WebApplicationContext: initialization started"); 
     } 
     long startTime = System.currentTimeMillis(); 

     try { 
      // Determine parent for root web application context, if any. 
      ApplicationContext parent = loadParentContext(servletContext); 

      // Store context in local instance variable, to guarantee that 
      // it is available on ServletContext shutdown. 
      this.context = createWebApplicationContext(servletContext, parent); 
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, this.context); 

      ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 
      if (ccl == ContextLoader.class.getClassLoader()) { 
       currentContext = this.context; 
      } 
      else if (ccl != null) { 
       currentContextPerThread.put(ccl, this.context); 
      } 

      if (logger.isDebugEnabled()) { 
       logger.debug("Published root WebApplicationContext as ServletContext attribute with name [" + 
         WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE + "]"); 
      } 
      if (logger.isInfoEnabled()) { 
       long elapsedTime = System.currentTimeMillis() - startTime; 
       logger.info("Root WebApplicationContext: initialization completed in " + elapsedTime + " ms"); 
      } 

      return this.context; 
     } 
     catch (RuntimeException ex) { 
      logger.error("Context initialization failed", ex); 
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, ex); 
      throw ex; 
     } 
     catch (Error err) { 
      logger.error("Context initialization failed", err); 
      servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE, err); 
      throw err; 
     } 
    } 

    /** 
    * Instantiate the root WebApplicationContext for this loader, either the 
    * default context class or a custom context class if specified. 
    * <p>This implementation expects custom contexts to implement the 
    * {@link ConfigurableWebApplicationContext} interface. 
    * Can be overridden in subclasses. 
    * <p>In addition, {@link #customizeContext} gets called prior to refreshing the 
    * context, allowing subclasses to perform custom modifications to the context. 
    * @param sc current servlet context 
    * @param parent the parent ApplicationContext to use, or <code>null</code> if none 
    * @return the root WebApplicationContext 
    * @see ConfigurableWebApplicationContext 
    */ 
    protected WebApplicationContext createWebApplicationContext(ServletContext sc, ApplicationContext parent) { 
     Class<?> contextClass = determineContextClass(sc); 
     if (!ConfigurableWebApplicationContext.class.isAssignableFrom(contextClass)) { 
      throw new ApplicationContextException("Custom context class [" + contextClass.getName() + 
        "] is not of type [" + ConfigurableWebApplicationContext.class.getName() + "]"); 
     } 
     ConfigurableWebApplicationContext wac = 
       (ConfigurableWebApplicationContext) BeanUtils.instantiateClass(contextClass); 

     // Assign the best possible id value. 
     if (sc.getMajorVersion() == 2 && sc.getMinorVersion() < 5) { 
      // Servlet <= 2.4: resort to name specified in web.xml, if any. 
      String servletContextName = sc.getServletContextName(); 
      wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + 
        ObjectUtils.getDisplayString(servletContextName)); 
     } 
     else { 
      // Servlet 2.5's getContextPath available! 
      try { 
       String contextPath = (String) ServletContext.class.getMethod("getContextPath").invoke(sc); 
       wac.setId(ConfigurableWebApplicationContext.APPLICATION_CONTEXT_ID_PREFIX + 
         ObjectUtils.getDisplayString(contextPath)); 
      } 
      catch (Exception ex) { 
       throw new IllegalStateException("Failed to invoke Servlet 2.5 getContextPath method", ex); 
      } 
     } 

     wac.setParent(parent); 
     wac.setServletContext(sc); 
     wac.setConfigLocation(sc.getInitParameter(CONFIG_LOCATION_PARAM)); 
     customizeContext(sc, wac); 
     wac.refresh(); 
     return wac; 
    } 

    /** 
    * Return the WebApplicationContext implementation class to use, either the 
    * default XmlWebApplicationContext or a custom context class if specified. 
    * @param servletContext current servlet context 
    * @return the WebApplicationContext implementation class to use 
    * @see #CONTEXT_CLASS_PARAM 
    * @see org.springframework.web.context.support.XmlWebApplicationContext 
    */ 
    protected Class<?> determineContextClass(ServletContext servletContext) { 
     String contextClassName = servletContext.getInitParameter(CONTEXT_CLASS_PARAM); 
     if (contextClassName != null) { 
      try { 
       return ClassUtils.forName(contextClassName, ClassUtils.getDefaultClassLoader()); 
      } 
      catch (ClassNotFoundException ex) { 
       throw new ApplicationContextException(
         "Failed to load custom context class [" + contextClassName + "]", ex); 
      } 
     } 
     else { 
      contextClassName = defaultStrategies.getProperty(WebApplicationContext.class.getName()); 
      try { 
       return ClassUtils.forName(contextClassName, ContextLoader.class.getClassLoader()); 
      } 
      catch (ClassNotFoundException ex) { 
       throw new ApplicationContextException(
         "Failed to load default context class [" + contextClassName + "]", ex); 
      } 
     } 
    } 

    /** 
    * Customize the {@link ConfigurableWebApplicationContext} created by this 
    * ContextLoader after config locations have been supplied to the context 
    * but before the context is <em>refreshed</em>. 
    * <p>The default implementation is empty but can be overridden in subclasses 
    * to customize the application context. 
    * @param servletContext the current servlet context 
    * @param applicationContext the newly created application context 
    * @see #createWebApplicationContext(ServletContext, ApplicationContext) 
    */ 
    protected void customizeContext(
      ServletContext servletContext, ConfigurableWebApplicationContext applicationContext) { 
    } 

    /** 
    * Template method with default implementation (which may be overridden by a 
    * subclass), to load or obtain an ApplicationContext instance which will be 
    * used as the parent context of the root WebApplicationContext. If the 
    * return value from the method is null, no parent context is set. 
    * <p>The main reason to load a parent context here is to allow multiple root 
    * web application contexts to all be children of a shared EAR context, or 
    * alternately to also share the same parent context that is visible to 
    * EJBs. For pure web applications, there is usually no need to worry about 
    * having a parent context to the root web application context. 
    * <p>The default implementation uses 
    * {@link org.springframework.context.access.ContextSingletonBeanFactoryLocator}, 
    * configured via {@link #LOCATOR_FACTORY_SELECTOR_PARAM} and 
    * {@link #LOCATOR_FACTORY_KEY_PARAM}, to load a parent context 
    * which will be shared by all other users of ContextsingletonBeanFactoryLocator 
    * which also use the same configuration parameters. 
    * @param servletContext current servlet context 
    * @return the parent application context, or <code>null</code> if none 
    * @see org.springframework.context.access.ContextSingletonBeanFactoryLocator 
    */ 
    protected ApplicationContext loadParentContext(ServletContext servletContext) { 
     ApplicationContext parentContext = null; 
     String locatorFactorySelector = servletContext.getInitParameter(LOCATOR_FACTORY_SELECTOR_PARAM); 
     String parentContextKey = servletContext.getInitParameter(LOCATOR_FACTORY_KEY_PARAM); 

     if (parentContextKey != null) { 
      // locatorFactorySelector may be null, indicating the default "classpath*:beanRefContext.xml" 
      BeanFactoryLocator locator = ContextSingletonBeanFactoryLocator.getInstance(locatorFactorySelector); 
      Log logger = LogFactory.getLog(ContextLoader.class); 
      if (logger.isDebugEnabled()) { 
       logger.debug("Getting parent context definition: using parent context key of '" + 
         parentContextKey + "' with BeanFactoryLocator"); 
      } 
      this.parentContextRef = locator.useBeanFactory(parentContextKey); 
      parentContext = (ApplicationContext) this.parentContextRef.getFactory(); 
     } 

     return parentContext; 
    } 

    /** 
    * Close Spring's web application context for the given servlet context. If 
    * the default {@link #loadParentContext(ServletContext)} implementation, 
    * which uses ContextSingletonBeanFactoryLocator, has loaded any shared 
    * parent context, release one reference to that shared parent context. 
    * <p>If overriding {@link #loadParentContext(ServletContext)}, you may have 
    * to override this method as well. 
    * @param servletContext the ServletContext that the WebApplicationContext runs in 
    */ 
    public void closeWebApplicationContext(ServletContext servletContext) { 
     servletContext.log("Closing Spring root WebApplicationContext"); 
     try { 
      if (this.context instanceof ConfigurableWebApplicationContext) { 
       ((ConfigurableWebApplicationContext) this.context).close(); 
      } 
     } 
     finally { 
      ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 
      if (ccl == ContextLoader.class.getClassLoader()) { 
       currentContext = null; 
      } 
      else if (ccl != null) { 
       currentContextPerThread.remove(ccl); 
      } 
      servletContext.removeAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE); 
      if (this.parentContextRef != null) { 
       this.parentContextRef.release(); 
      } 
     } 
    } 


    /** 
    * Obtain the Spring root web application context for the current thread 
    * (i.e. for the current thread's context ClassLoader, which needs to be 
    * the web application's ClassLoader). 
    * @return the current root web application context, or <code>null</code> 
    * if none found 
    * @see org.springframework.web.context.support.SpringBeanAutowiringSupport 
    */ 
    public static WebApplicationContext getCurrentWebApplicationContext() { 
     ClassLoader ccl = Thread.currentThread().getContextClassLoader(); 
     if (ccl != null) { 
      WebApplicationContext ccpt = currentContextPerThread.get(ccl); 
      if (ccpt != null) { 
       return ccpt; 
      } 
     } 
     return currentContext; 
    } 


    /** 
    * Initialize the root web application context. 
    */ 
    public void contextInitialized(ServletContextEvent event) { 
     this.contextLoader = createContextLoader(); 
     if (this.contextLoader == null) { 
      this.contextLoader = this; 
     } 
     this.contextLoader.initWebApplicationContext(event.getServletContext()); 
    } 

    /** 
    * Create the ContextLoader to use. Can be overridden in subclasses. 
    * @return the new ContextLoader 
    * @deprecated in favor of simply subclassing ContextLoaderListener itself 
    * (which extends ContextLoader, as of Spring 3.0) 
    */ 
    @Deprecated 
    protected ContextLoader createContextLoader() { 
     return null; 
    } 

    /** 
    * Return the ContextLoader used by this listener. 
    * @return the current ContextLoader 
    * @deprecated in favor of simply subclassing ContextLoaderListener itself 
    * (which extends ContextLoader, as of Spring 3.0) 
    */ 
    @Deprecated 
    public ContextLoader getContextLoader() { 
     return this.contextLoader; 
    } 


    /** 
    * Close the root web application context. 
    */ 
    public void contextDestroyed(ServletContextEvent event) { 
     if (this.contextLoader != null) { 
      this.contextLoader.closeWebApplicationContext(event.getServletContext()); 
     } 
     cleanupAttributes(event.getServletContext()); 
    } 

    /** 
    * Find all ServletContext attributes which implement {@link DisposableBean} 
    * and destroy them, removing all affected ServletContext attributes eventually. 
    * @param sc the ServletContext to check 
    */ 
    static void cleanupAttributes(ServletContext sc) { 
     Enumeration attrNames = sc.getAttributeNames(); 
     while (attrNames.hasMoreElements()) { 
      String attrName = (String) attrNames.nextElement(); 
      if (attrName.startsWith("org.springframework.")) { 
       Object attrValue = sc.getAttribute(attrName); 
       if (attrValue instanceof DisposableBean) { 
        try { 
         ((DisposableBean) attrValue).destroy(); 
        } 
        catch (Throwable ex) { 
         System.out.println("Couldn't invoke destroy method of attribute with name '" + attrName + "'"); 
        } 
       } 
      } 
     } 
    } 

    public WebApplicationContext getContext() { 
     return context; 
    } 

} 

は、のではなく、web.xmlにのContextLoaderListenerを追加スプリングContextLoaderListener。カスタムContextLoaderListenerは、Spring ContextLoaderListenerと全く同じ動作をしますが、別のgetContext()メソッドもあります。あなたがWebサーバーを実行している場合

ので、次やっSpringコンテキストにアクセスすることができます。

コンテキストrootContext = this.tomcat.getRootContext(); System.out.println( "context");

ContextLoaderListener ctxListener = (ContextLoaderListener) rootContext.getApplicationLifecycleListeners()[0]; 

    XmlWebApplicationContext ctx = (XmlWebApplicationContext)ctxListener .getContext(); 

答えて

0

確かに、Springは統合テストのためにframeworkを持っています。

カスタマイズしたい場合は、@RunWith(SpringJUnit4ClassRunner.class)、クラスレベルの注釈との統合テストのための独自の基本クラスを定義することができます(またはカスタム注釈取り扱いに入れたい場合はSpringJUnit4ClassRunnerの独自のサブクラスを作成する)と@ContextConfigurationクラス - テストに必要なコンテキスト設定を一覧表示するレベルアノテーションです。次に、アプリケーションコードとまったく同じ方法でBeanを注入するテストを記述します。

+0

お返事ありがとうございます。私はこれらの注釈についてよく知っており、かなり頻繁に使用しています。しかし、私が望むのは、自分のHttp RESTサービスが正しく動作するかどうかをテストすることです。これについては、Tomcat6を起動する必要があります。 – Erik

+0

@Erikは、Springコンテキストから抜け出すために必要なデータにアクセスできる追加のWebサービスを公開できますか? – dfichter

+0

はい、私はそれを行うことができました。私はその汚いハックだが、動作する解決策を見つけた。私は上にそれを掲示しています。 – Erik

関連する問題