문제

I am working on a project that uses openSymphony 2.1.2 and struts 2.3.8.

However on that code base I am trying to apply a piece code that was written for older versions of openSymphony and struts.

Here is that piece of code:

http://www.lischke-online.de/index.php/java/action-unit-testing-with-struts-2-0-part-2

(As a side note, I need that code in order to be able to simulate running the struts actions from my inherited project, in a system that is based on a different framework.)

I have a problem with the following paragraph:

final StrutsSpringObjectFactory ssf = new StrutsSpringObjectFactory("auto", "true", servletContext);
ObjectFactory.setObjectFactory(ssf);

On the second line, I'm getting the error: The method setObjectFactory(StrutsSpringObjectFactory) is undefined for the type ObjectFactory

However, when I look at the javadoc of ObjectFactory, I do not see an alternative method that would let me set a specific ObjectFactory (e.g. StrutsSpringObjectFactory), as the ObjectFactory that will be used when executing the Actions. So how do I set such a specific ObjectFactory with the new API?

Updated code:

static {
    sessionMap = new HashMap<String, Object>();

    // Create fake back end here.
    // ...

    // ===== Struts setup:
    // Create and use a file system resource loader otherwise Tiles will not find
    // our configuration file. The default resource loader is able to find struts.xml
    // if it is in the classpath, but not tiles.xml.
    final FileSystemResourceLoader loader = new FileSystemResourceLoader();

    final String[] config = new String[] { "WEB-INF/classes/struts.xml" };

    servletContext = new MockServletContext(loader);
    final XmlWebApplicationContext appContext = new XmlWebApplicationContext();

    // Link the servlet context and the Spring context.
    appContext.setServletContext(servletContext);
    appContext.setConfigLocations(config);
    appContext.refresh();
    servletContext.setAttribute(WebApplicationContext.ROOT_WEB_APPLICATION_CONTEXT_ATTRIBUTE,
        appContext);

    servletContext.addInitParameter(BasicTilesContainer.DEFINITIONS_CONFIG, "WEB-INF/tiles.xml");

    // Creating the tiles listener statically (not via class loader).
    final StrutsTilesListener tilesListener = new StrutsTilesListener();
    final ServletContextEvent event = new ServletContextEvent(servletContext);
    tilesListener.contextInitialized(event);

    // Use spring as the object factory for Struts
    //this code not working anymore with struts 2.3.2:
    //final StrutsSpringObjectFactory ssf = new StrutsSpringObjectFactory("auto", "true", servletContext);
    //ObjectFactory.setObjectFactory(ssf);

    //my change: (original note working with newer version of struts)
    //StrutsSpringObjectFactory.setObjectFactory(ssf);

    //(*) original location.

    // Dispatcher is the guy that actually handles all requests. Pass in
    // an empty. Map as the parameters but if you want to change stuff like
    // what config files to read, you need to specify them here
    // (see Dispatcher's source code)
    dispatcher = new Dispatcher(servletContext, new HashMap<String, String>()); 
    dispatcher.init();
    Dispatcher.setInstance(dispatcher);

    //(*) moved to here:
    //http://stackoverflow.com/questions/15821643/setting-strutsspringobjectfactory-as-my-current-objectfactory?noredirect=1#comment22519072_15821643
    final StrutsSpringObjectFactory ssf = new StrutsSpringObjectFactory("auto", "true", "false", servletContext, "false", dispatcher.getContainer());

}

Stack trace that I get on that code, when calling the test from within a webapp (a deployed war):

08:50:28,471 WARNING [javax.enterprise.resource.webcontainer.jsf.lifecycle] (http--127.0.0.1-8080-1) #{accountLookupBean.lookupAccounts()}: java.lang.ExceptionInInitial
izerError: javax.faces.FacesException: #{accountLookupBean.lookupAccounts()}: java.lang.ExceptionInInitializerError
        at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:118) [jsf-impl-2.1.5-jbossorg-1.jar:2.1.5-SNAPSHOT]
        at javax.faces.component.UICommand.broadcast(UICommand.java:315) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.0.Final]
        at javax.faces.component.UIData.broadcast(UIData.java:1093) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.0.Final]
        at javax.faces.component.UIData.broadcast(UIData.java:1093) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.0.Final]
        at javax.faces.component.UIData.broadcast(UIData.java:1093) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.0.Final]
        at javax.faces.component.UIViewRoot.broadcastEvents(UIViewRoot.java:794) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.0.Final]
        at javax.faces.component.UIViewRoot.processApplication(UIViewRoot.java:1259) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.0.Final]
        at com.sun.faces.lifecycle.InvokeApplicationPhase.execute(InvokeApplicationPhase.java:81) [jsf-impl-2.1.5-jbossorg-1.jar:2.1.5-SNAPSHOT]
        at com.sun.faces.lifecycle.Phase.doPhase(Phase.java:101) [jsf-impl-2.1.5-jbossorg-1.jar:2.1.5-SNAPSHOT]
        at com.sun.faces.lifecycle.LifecycleImpl.execute(LifecycleImpl.java:118) [jsf-impl-2.1.5-jbossorg-1.jar:2.1.5-SNAPSHOT]
        at javax.faces.webapp.FacesServlet.service(FacesServlet.java:593) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.0.Final]
        at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:329) [jbossweb-7.0.10.Final.jar:]
        at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:248) [jbossweb-7.0.10.Final.jar:]
        at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:275) [jbossweb-7.0.10.Final.jar:]
        at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:161) [jbossweb-7.0.10.Final.jar:]
        at org.jboss.as.web.security.SecurityContextAssociationValve.invoke(SecurityContextAssociationValve.java:154) [jboss-as-web-7.1.0.Final.jar:7.1.0.Final]
        at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:155) [jbossweb-7.0.10.Final.jar:]
        at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102) [jbossweb-7.0.10.Final.jar:]
        at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109) [jbossweb-7.0.10.Final.jar:]
        at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:368) [jbossweb-7.0.10.Final.jar:]
        at org.apache.coyote.http11.Http11Processor.process(Http11Processor.java:877) [jbossweb-7.0.10.Final.jar:]
        at org.apache.coyote.http11.Http11Protocol$Http11ConnectionHandler.process(Http11Protocol.java:671) [jbossweb-7.0.10.Final.jar:]
        at org.apache.tomcat.util.net.JIoEndpoint$Worker.run(JIoEndpoint.java:930) [jbossweb-7.0.10.Final.jar:]
        at java.lang.Thread.run(Thread.java:662) [rt.jar:1.6.0_29]
Caused by: javax.faces.el.EvaluationException: java.lang.ExceptionInInitializerError
        at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:102) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.
0.Final]
        at com.sun.faces.application.ActionListenerImpl.processAction(ActionListenerImpl.java:102) [jsf-impl-2.1.5-jbossorg-1.jar:2.1.5-SNAPSHOT]
        ... 23 more
Caused by: java.lang.ExceptionInInitializerError
        at com.myapp.services.AccountLookupServiceImpl.getRows(AccountLookupServiceImpl.java:38) [classes:]
        at com.myapp.beans.AccountLookupBean.lookupAccounts(AccountLookupBean.java:39) [classes:]
        at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) [rt.jar:1.6.0_29]
        at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39) [rt.jar:1.6.0_29]
        at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25) [rt.jar:1.6.0_29]
        at java.lang.reflect.Method.invoke(Method.java:597) [rt.jar:1.6.0_29]
        at org.apache.el.parser.AstValue.invoke(AstValue.java:262) [jbossweb-7.0.10.Final.jar:]
        at org.apache.el.MethodExpressionImpl.invoke(MethodExpressionImpl.java:278) [jbossweb-7.0.10.Final.jar:]
        at com.sun.faces.facelets.el.TagMethodExpression.invoke(TagMethodExpression.java:105) [jsf-impl-2.1.5-jbossorg-1.jar:2.1.5-SNAPSHOT]
        at javax.faces.component.MethodBindingMethodExpressionAdapter.invoke(MethodBindingMethodExpressionAdapter.java:88) [jboss-jsf-api_2.1_spec-2.0.0.Final.jar:2.0.0
.Final]
        ... 24 more
Caused by: org.springframework.beans.factory.BeanDefinitionStoreException: IOException parsing XML document from ServletContext resource [/WEB-INF/classes/struts.xml];
nested exception is java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/classes/struts.xml]
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:341) [spring-beans-3.2.1.RELEASE.jar:3.2.1.REL
EASE]
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:302) [spring-beans-3.2.1.RELEASE.jar:3.2.1.REL
EASE]
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:174) [spring-beans-3.2.1.RELEASE
.jar:3.2.1.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:209) [spring-beans-3.2.1.RELEASE
.jar:3.2.1.RELEASE]
        at org.springframework.beans.factory.support.AbstractBeanDefinitionReader.loadBeanDefinitions(AbstractBeanDefinitionReader.java:180) [spring-beans-3.2.1.RELEASE
.jar:3.2.1.RELEASE]
        at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:125) [spring-web-3.2.1.RELEASE.jar:3.2.1.R
ELEASE]
        at org.springframework.web.context.support.XmlWebApplicationContext.loadBeanDefinitions(XmlWebApplicationContext.java:94) [spring-web-3.2.1.RELEASE.jar:3.2.1.RE
LEASE]
        at org.springframework.context.support.AbstractRefreshableApplicationContext.refreshBeanFactory(AbstractRefreshableApplicationContext.java:130) [spring-context-
3.2.1.RELEASE.jar:3.2.1.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.obtainFreshBeanFactory(AbstractApplicationContext.java:537) [spring-context-3.2.1.RELEASE.jar:
3.2.1.RELEASE]
        at org.springframework.context.support.AbstractApplicationContext.refresh(AbstractApplicationContext.java:451) [spring-context-3.2.1.RELEASE.jar:3.2.1.RELEASE]
        at com.myapp.softgems.ActionBaseTestCase.<clinit>(ActionBaseTestCase.java:70) [classes:]
        ... 34 more
Caused by: java.io.FileNotFoundException: Could not open ServletContext resource [/WEB-INF/classes/struts.xml]
        at org.springframework.web.context.support.ServletContextResource.getInputStream(ServletContextResource.java:140) [spring-web-3.2.1.RELEASE.jar:3.2.1.RELEASE]
        at org.springframework.beans.factory.xml.XmlBeanDefinitionReader.loadBeanDefinitions(XmlBeanDefinitionReader.java:328) [spring-beans-3.2.1.RELEASE.jar:3.2.1.REL
EASE]
        ... 44 more

UPDATE 2:

The reason I want to be able to test an Action, is not simply to be able to right-click on it from the project explorer and than right-click "run as Junit". I actually want to run the test as part of my web application.

This is what I mean: I am trying to call the following code from one of my service classes:

DataTest test = new DataTest("testDataAction");

JUnitCore jUnitCore = new JUnitCore();
jUnitCore.run(test);

Because, in DataTest I try to execute() DataAction. That Action calls some messy db logic and eventually return some data. I need that data for my service. Instead of trying to figure out what the Action does exactly, I wan to simply execute() it and get the data. The way I'm trying to do it is by running a test that will execute() the Action.

My struts.xml is not directly in the project that I am building. Here is my situation: let's call my new project, project A; and the legacy struts project that I depend on, project B. Project A is being built by other framework (JSF & Spring). Project B uses struts for the UI. But its Actions were written in such a messy way, that a lot of the DB access logic was written directly within the actions. So if I want to use the logic from project B, and not rewrite it, a temporary solution (which I am trying to accomplish) would be to execute an Action from project B with the corresponding request data. The way to do it, is by running a test on an Action from B, within a service class from A.

Since project B was packed as a war file, my way to reuse it with Maven in project A, is to define it as an overlay in project A. The classes and other resources from the classpath of project B are packed into a jar. Then in project A I have a dependency on that jar. So struts.xml lives in the jar that contains the classes and xml's of project B. In compile time, project A can only see struts.xml within the jar of project A.

Only when I pack project A, war B is unpacked, its content (including struts.xml) is copied into the target folder of project A, and then A + B are packed together as war. (in the final war, struts.xml will be both in the jar B, and in WEB-INF/classes witin the final war.

So in practical terms, if I try to run the test by right-clicking the test and them "run as junit", it should not find struts.xml in some WEB-INF/classes on my classpath - first there's no such thing in my workspace, only in created war. Second I only try to run the test from within a war (as I've shown above). in that time, there is WEB-INF/classes/struts.xml, why could it not be found?

도움이 되었습니까?

해결책

Just create new instance of StrutsSpringObjectFactory but do it after creation of Dispatcher.

final StrutsSpringObjectFactory ssf = new StrutsSpringObjectFactory("auto",
      "true", "false", servletContext, "false", dispatcher.getContainer());

BUT take a look at struts2-junit-plugin for testing Struts2 actions. It has StrutsSpringJUnit4TestCase with convenient methods, so you do not need to do all that stuff by yourself.

다른 팁

You need to include the Struts2-Spring-plugin(*.jar)

http://struts.apache.org/release/2.3.x/docs/spring-plugin.html

The Spring Plugin works by overriding the Struts ObjectFactory to enhance the creation of core framework objects. When an object is to be created, it uses the class attribute in the Struts configuration to correspond to the id attribute in the Spring configuration. If not found, the class will try to be created as usual, then be autowired by Spring. In the case of Actions, Spring 2's bean scope feature can be used to scope an Action instance to the session, application, or a custom scope, providing advanced customization above the default per-request scoping.

라이센스 : CC-BY-SA ~와 함께 속성
제휴하지 않습니다 StackOverflow
scroll top