Search code examples
javaspringstruts2autowiredfileinputstream

Define a bean of java.io.FileInputStream in Spring makes the Struts2 stream result not work


We are using the Struts2 with spring in a project.

Consider a simple action with stream result

@Action(value = "sample-export", 
        results = { @Result(name = "success", type = "stream", params = {
        "inputName", "exportInputStream", "contentType",
        "${exportContentType}; charset=UTF-8", "Content-Disposition",
        "attachment; filename=\"${filename}\"", "contentDisposition",
        "attachment; filename=\"${filename}\"", "bufferSize", "2048" }) })
public String export() throws ClientException {
    //buildExportInputStream() creates and returns new ByteArrayOutputStream by using jasper
    exportInputStream = buildExportInputStream();
    LOG.debug("Exporting to {} file ", getFilename());

    return SUCCESS;

}

This works fine.

Something strange happens when I add below line in spring-applicaiton-context.xml !!

  <bean id="sampleStream" class="java.io.FileInputStream" >
        <constructor-arg  value="c:/sample.jks"/>  
  </bean>

After adding above line the inputStream of action will be a zero size file! When I set a break point in StreamResult, doExecute method the inputStream.read(oBuff) is always -1.


The created bean (sampleStream) will be referenced by other beans by using ref for example <ref bean="sampleStream"/>. When I change this structure and use sampleStream as an inline parameter it works fine:

<bean id="anotherBean" class="foo.bar">
        <bean class="org.xml.sax.InputSource">
            <constructor-arg index="0" >
                    <bean class="java.io.FileInputStream">
                        <constructor-arg index="0" type="java.lang.String" value="c:/sample.jks"/>
                    </bean>
            </constructor-arg>
        </bean>
    </property>
</bean>

Or this will also fix it:

<bean id="sampleStream" class="java.io.FileInputStream" autowire-candidate="false">
            <constructor-arg  value="c:/sample.jks"/> 
  </bean>

Well can you help me to find what is going wrong ?! Why defining a bean of FileInputStream will cause this happens ?!

Updated

I have found that the sampleStream will be autowired to org.apache.struts2.dispatcher.StreamResult and I see this log:

DEBUG ort.DefaultListableBeanFactory Returning cached instance of singleton bean 'sampleStream'
DEBUG ort.DefaultListableBeanFactory Autowiring by type from bean name 'org.apache.struts2.dispatcher.StreamResult' via constructor to bean named 'sampleStream'

This mistaken autowired is the source of problem ! Is there any workaround. And by the way, why the sampleStream is autowired here !

https://issues.apache.org/jira/browse/WW-4559


Solution

  • Currently, Struts2 Spring object factory by default will try to determine the best strategy for autowiring components, which is legacy behavior by the way.

    With this behavior an instance of StreamResult is created with StreamResult(InputStream in) constructor and Spring will try to inject constructor arguments as well.

    To avoid this set struts.objectFactory.spring.autoWire.alwaysRespect constant to true in your struts.xml file.

    <constant name="struts.objectFactory.spring.autoWire.alwaysRespect" value="true" />
    

    This will ensure that the autowire strategy is always respected. (Default is by name.)