Illyr
Illyr

Reputation: 155

Exception while using the response outputstream in a Java-backed Alfresco-Share webscript

1. Context

I want to implement an Alfresco-Share Java-backed webscript like the existing "I18N resources and messages Web Script". The main difference is that I want to use the response outputstream (not the writer).

Alfresco-Share version used: 4.1.1.

2. Test code used to reproduce the error

- Spring bean:

<bean id="webscript.test.content.get" parent="webscript" class="test.TestWebscript" />

- Java code:

package test;

import java.io.IOException;

import org.springframework.extensions.webscripts.AbstractWebScript;
import org.springframework.extensions.webscripts.WebScriptRequest;
import org.springframework.extensions.webscripts.WebScriptResponse;

public final class TestWebscript extends AbstractWebScript
{
    @Override
    public void execute(final WebScriptRequest request, final WebScriptResponse response) throws IOException
    {
        response.getOutputStream().write("test".getBytes());
    }
}

- Webscript desc file:

<?xml version="1.0" encoding="UTF-8"?>

<webscript>
  <shortname>Test webscript</shortname>
  <description>A webscript using the response outputstream</description>
  <url>/test/content</url>
  <format default="">extension</format>
  <lifecycle>draft_public_api</lifecycle>
  <authentication>guest</authentication>
  <transaction>required</transaction>
  <family>Tests</family>
</webscript>

3. Result

I have the exception below:

java.lang.IllegalStateException: getOutputStream() has already been called for this response
    at org.apache.catalina.connector.Response.getWriter(Response.java:611)
    at org.apache.catalina.connector.ResponseFacade.getWriter(ResponseFacade.java:198)
    at org.springframework.extensions.webscripts.servlet.WebScriptServletResponse.getWriter(WebScriptServletResponse.java:198)
    at org.springframework.extensions.webscripts.LocalWebScriptRuntimeContainer.executeScript(LocalWebScriptRuntimeContainer.java:241)
    at org.springframework.extensions.webscripts.AbstractRuntime.executeScript(AbstractRuntime.java:377)
    at org.springframework.extensions.webscripts.AbstractRuntime.executeScript(AbstractRuntime.java:209)
    at org.springframework.extensions.webscripts.servlet.mvc.WebScriptView.renderMergedOutputModel(WebScriptView.java:104)
    at org.springframework.web.servlet.view.AbstractView.render(AbstractView.java:250)
    at org.springframework.web.servlet.DispatcherServlet.render(DispatcherServlet.java:1047)
    at org.springframework.web.servlet.DispatcherServlet.doDispatch(DispatcherServlet.java:817)
    at org.springframework.web.servlet.DispatcherServlet.doService(DispatcherServlet.java:719)
    at org.springframework.web.servlet.FrameworkServlet.processRequest(FrameworkServlet.java:644)
    at org.springframework.web.servlet.FrameworkServlet.doGet(FrameworkServlet.java:549)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:617)
    at javax.servlet.http.HttpServlet.service(HttpServlet.java:717)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:290)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.alfresco.web.site.servlet.MTAuthenticationFilter.doFilter(MTAuthenticationFilter.java:74)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.alfresco.web.site.servlet.SSOAuthenticationFilter.doFilter(SSOAuthenticationFilter.java:355)
    at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(ApplicationFilterChain.java:235)
    at org.apache.catalina.core.ApplicationFilterChain.doFilter(ApplicationFilterChain.java:206)
    at org.apache.catalina.core.StandardWrapperValve.invoke(StandardWrapperValve.java:233)
    at org.apache.catalina.core.StandardContextValve.invoke(StandardContextValve.java:191)
    at org.apache.catalina.core.StandardHostValve.invoke(StandardHostValve.java:127)
    at org.apache.catalina.valves.ErrorReportValve.invoke(ErrorReportValve.java:102)
    at org.apache.catalina.core.StandardEngineValve.invoke(StandardEngineValve.java:109)
    at org.apache.catalina.connector.CoyoteAdapter.service(CoyoteAdapter.java:298)
    at org.apache.coyote.http11.Http11NioProcessor.process(Http11NioProcessor.java:886)
    at org.apache.coyote.http11.Http11NioProtocol$Http11ConnectionHandler.process(Http11NioProtocol.java:721)
    at org.apache.tomcat.util.net.NioEndpoint$SocketProcessor.run(NioEndpoint.java:2256)
    at java.util.concurrent.ThreadPoolExecutor$Worker.runTask(ThreadPoolExecutor.java:886)
    at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:908)
    at java.lang.Thread.run(Thread.java:662)

4. Question

Is there any way to use the webscript outputstream response?

Upvotes: 3

Views: 1218

Answers (3)

Sorin Postelnicu
Sorin Postelnicu

Reputation: 1301

I had exactly the same problem (while using Spring Sur 1.2.0, but the same problem is still valid for the latest Trunk version),

and my solution was to patch Spring Surf as documented in the following JIRA issue: https://issues.alfresco.com/jira/browse/ALF-21949

and in the following forum thread: https://community.alfresco.com/thread/202736-webscript-to-export-nodes-to-excel-cannot-call-getwriter-after-getoutputstream


1) I created a new class inside spring-webscripts-1.2.0.jar :

package org.springframework.extensions.webscripts;

/**
 * Represents a type of {@link WebScript} which directly streams the content (such as a binary file) to the {@link WebScriptResponse#getOutputStream()}.
 * <p>
 * If you want to implement the streaming of the content directly to the OutputStream obtained from the {@link WebScriptResponse},
 * then subclass this abstract class and override the method {@link AbstractWebScript#execute(WebScriptRequest, WebScriptResponse)}.
 */
public abstract class OutputStreamWebScript extends AbstractWebScript {
}

.

As you can see, this is just an empty "marker class".

2) I have modified the following class inside spring-surf-1.2.0.jar : org.springframework.extensions.webscripts.LocalWebScriptRuntimeContainer (the one which was causing the exception):

2a) I have added a new method after executeScript(WebScriptRequest scriptReq, WebScriptResponse scriptRes, Authenticator auth) :

 private void executeScriptWithExtensibilityModel(WebScriptRequest scriptReq, WebScriptResponse scriptRes, Authenticator auth) throws IOException
 {
      WebScript script = scriptReq.getServiceMatch().getWebScript();

      if (script instanceof OutputStreamWebScript)
      {
           // This type of WebScript streams directly the content to the OutputStream of the WebScriptResponse,
           // so we must not apply any extensibility model, but call through to the parent container to perform the WebScript processing
           super.executeScript(scriptReq, scriptRes, auth);
      }
      else
      {
           // For all the other types of WebScripts, apply the extensibility model as needed
           ExtensibilityModel extModel = openExtensibilityModel();
           super.executeScript(scriptReq, scriptRes, auth);
           closeExtensibilityModel(extModel, scriptRes.getWriter());
      }
 }

.

2b) I replaced the following lines inside executeScript(WebScriptRequest scriptReq, WebScriptResponse scriptRes, Authenticator auth):

        try
        {
            // call through to the parent container to perform the WebScript processing
            ExtensibilityModel extModel = openExtensibilityModel();
            super.executeScript(scriptReq, scriptRes, auth);
            closeExtensibilityModel(extModel, scriptRes.getWriter());
        }

with these lines:

           try
           {
                // call through to the parent container to perform the WebScript processing, applying any ExtensibilityModel
                executeScriptWithExtensibilityModel(scriptReq, scriptRes, auth);
           }

.

Maybe someone with write-access to the spring-surf and spring-webscripts repository will commit these improvements, so then anyone else can use them in the future.

Upvotes: 0

shkwav
shkwav

Reputation: 126

This is a limitation of the WebScript Runtime on the Share web-tier. Share has a powerful component framework and extensibility model that wraps all WebScripts - enabling advanced customisations at various points in the JavaScript code, templates etc. Unfortunately treating the WebScripts as components that have their output merged together on a single page means that the Runtime controls the OutputStream and Writer - the Writer you retrieve is not the usual Servlet Writer at all but a wrapper one.

Upvotes: 5

billerby
billerby

Reputation: 1978

I've subclassed org.alfresco.repo.web.scripts.content.StreamContent instead of AbstractWebScript in similiar situations.

Upvotes: 0

Related Questions