Search code examples
jspservletswicketwicket-1.5requestdispatcher

Wicket 1.5 and JSP/servlet wrapping


In the project I'm on we want to upgrade from Wicket 1.4 to 1.5. After some work we got most things working OK.

However one major thing is not working yet. There's a need to wrap old JSP/servlets into the new Wicket based application and the old 1.4-approach is not working anymore.

Simplified html output in 1.4

<body>
    <div id="container">
        wrappedContentFromJsp
    </div>  
<body>

Simplified html output in 1.5

<body>
    wrappedContentFromJsp   
    <div id="container">        
    </div>  
<body>

So, all the JSP content renders outside the tag that we like to wrap it in.

The wrapping magic happens in our internal AbstractServletWrapperPanel and the WebMarkupContainer.onRender(MarkupStream markupStream) override. However, in Wicket 1.5 we can't invoke markupStream.next() since it's no longer provided. I have not found a way around this yet.

Working code for 1.4 with a sample panel implementation as reference:

public abstract class AbstractServletWrapperPanel extends Panel {

    public AbstractServletWrapperPanel(String id, final  String servletName, String tagId) {
        super(id);
        add(new WebMarkupContainer(tagId) {

            @Override
            protected void onRender(MarkupStream markupStream) {
                markupStream.next();
                try {
                    WebRequestCycle cycle = (WebRequestCycle) RequestCycle.get();
                    ServletRequest request = cycle.getWebRequest().getHttpServletRequest();
                    ServletResponse response = cycle.getWebResponse().getHttpServletResponse();
                    ServletContext context = ((WebApplication) Application.get()).getServletContext();
                    RequestDispatcher rd = context.getNamedDispatcher(servletName);
                    if (rd != null) {
                        rd.include(request, response);
                    } else {
                        // handling...
                    }
                } catch (Exception e) {
                    // handling...
                }
            }
        });
    }
}

//Impl
public class WrapperPanel extends AbstractServletWrapperPanel {
    private static final long serialVersionUID = 1L;

    public WrapperPanel(String id, final String servletName) {
        super(id, servletName, "wrappedContentId");
    }   
}

//WrapperPanel html
<body>
    <wicket:panel>
        <wicket:container wicket:id="wrappedContentId"/>
    </wicket:panel>
</body>

In the 1.5 version I'm getting the request and response via (HttpServletRequest)RequestCycle.get().getRequest().getContainerRequest() and (HttpServletResponse)RequestCycle.get().getResponse().getContainerResponse()

Then I've tried to:

  • use the onRender()-magic without markupStream.next() that's no longer provided in 1.5
  • move it to onComponentTagBody(MarkupStream markupStream, ComponentTag tag)
    • Note: To invoke onComponentTagBody() I had to open up the wicket:container tag <wicket:container wicket:id="wrappedContentId"></wicket:container>. I also tried without invoking markupStream.next() as that step is performed in Component.internalRenderComponent() just before onComponentTagBody is invoked at all.
  • move it to onComponentTag(ComponentTag tag)
  • combined above with setting setRenderBodyOnly(true) in the WebMarkupContatiner.onInitialize()
  • use a <div> tag instead of a wicket:container
  • use debug mode to track down the rendering process of 1.5. But still, I guess I'm missing out some key part of the new 1.5 way of rendering components.

Since it's not an option to migrate all that JSP functionality to Wicket anytime soon this is kind of a showstopper for us at the moment.

For reference, the 1.4 way of doing this is much similar to the approach I found in the article jsp-and-wicket-sitting-in-a-tree and on the Wicket wiki

Any help solving this issue would be very appreciated!

[EDIT]
After a suggestion from TheStijn I've also tried invoking getAssociatedMarkupStream() from onRender() but that raises the following error: org.apache.wicket.markup.MarkupNotFoundException: Markup of type 'html' for component '... AbstractServletWrapperPanel$1' not found.


Solution

  • After great help and pointers from Martin Grigorov I found the solution to this. To follow the process of getting there, hava look at the user's forum

    Please note that following is just the raw output of trying to get it to work at all, there might be some nicer way to "package" it. So take it for what it is.

    //Stripped code for clarity
    
    @Override
    public void onComponentTagBody(MarkupStream markupStream, ComponentTag tag) {
    
     //Set up mock response and dispatch.   
     ServletContext context = WebApplication.get().getServletContext(); 
     ServletRequest request = (HttpServletRequest)RequestCycle.get().getRequest().getContainerRequest();
     MockResponse mockResponse = new MockResponse((HttpServletResponse)RequestCycle.get().getResponse().getContainerResponse());
     RequestDispatcher rd = context.getNamedDispatcher(aServletName);
     rd.include(request, mockResponse);
    
     //As in Wicket's Include.onComponentTagBody(MarkupStream markupStream, ComponentTag tag)
     replaceComponentTagBody(markupStream, tag, mockResponse.getOutput());
    }
    
    
    private static class MockResponse extends HttpServletResponseWrapper {    
     ServletOutputStream servletStream;
     ByteArrayOutputStream byteStream;
    
     public MockResponse(HttpServletResponse response) {
      super(response);      
      byteStream = new ByteArrayOutputStream();
      servletStream = new ServletOutputStream() {
    
        @Override
        public void write(int b) {
           byteStream.write(b);
         }
       };
     }
    
     @Override
     public ServletOutputStream getOutputStream() {
      return servletStream;
     }
    
     public String getOutput() {
        //NOTE: Remember to clean up IO in real usage...
        return byteStream.toString("UTF-8"); //Provide desired encoding
      }  
    }
    

    So why didn't I use org.apache.wicket.protocol.http.mockMockHttpServletResponse instead? For some reason neither getWriter() or getOutputStream() got invoked, but I might look further into that later.