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:
markupStream.next()
that's no longer provided in 1.5 onComponentTagBody(MarkupStream markupStream, ComponentTag 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.onComponentTag(ComponentTag tag)
setRenderBodyOnly(true)
in the WebMarkupContatiner.onInitialize()
<div>
tag instead of a wicket:container
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.
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.