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 ?!
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 !
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.)