Offline rendering of Tapestry pages and components without a HTTP servlet request
Pages and components can be rendered via the OfflineComponentRenderer which can be injected via Tapestry IOC
public class MyOfflineRenderer {
@Inject
private OfflineComponentRenderer offlineRenderer;
@Inject
private TypeCoercer typeCoercer;
public String renderPageAsString() throws Exception {
// setup the PageRenderRequestParameters
String logicalPageName = ...;
EventContext activationContext = new ArrayEventContext(typeCoercer, ...);
boolean loopback = false;
PageRenderRequestParameters params = new PageRenderRequestParameters(
logicalPageName,
activationContext,
loopback
);
// setup the RequestContext
Map<String, Object> session = new HashMap<String, Object>();
session.put("foo", "bar");
DefaultOfflineRequestContext requestContext = new DefaultOfflineRequestContext();
requestContext.setSession(session);
requestContext.setParameter("someParam", "paramValue");
requestContext.setAttribute("someAttribute", "attributeValue");
requestContext.setHeader("someHeader", "headerValue");
// render the page offline
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
Future<?> future = offlineRenderer.renderPage(requestContext, params, printWriter);
future.get();
return stringWriter.toString();
}
public JSONObject renderComponentAsJSONObject() throws Exception {
// setup the ComponentEventRequestParameters
String activePageName = ...;
String containingPageName = ...;
String nestedComponentId = ...;
String event = ...;
EventContext pageActivationContext = new ArrayEventContext(typeCoercer, ...);
EventContext eventContext = new ArrayEventContext(typeCoercer, ...);
ComponentEventRequestParameters eventParams = new ComponentEventRequestParameters(
activePageName,
containingPageName,
nestedComponentId,
event,
pageActivationContext,
eventContext
);
// setup the RequestContext
Map<String, Object> session = new HashMap<String, Object>();
session.put("foo", "bar");
DefaultOfflineRequestContext requestContext = new DefaultOfflineRequestContext();
requestContext.setSession(session);
requestContext.setXHR(true);
requestContext.setParameter("someParam", "paramValue");
requestContext.setAttribute("someAttribute", "attributeValue");
requestContext.setHeader("someHeader", "headerValue");
// render the component event offline
Future<JSONObject> future = offlineRenderer.renderComponent(requestContext, eventParams);
return future.get();
}
}
Configure the offline request symbols in your IOC Module
/**
* These are usually only required if generating links when rendering pages / components offline
*/
public static void contributeApplicationDefaults(MappedConfiguration<String, Object> config) {
// the host name of the server to which the request was sent. It is the value of the part
// before ":" in the Host header value, if any, or the resolved server name, or the
// server IP address.
config.add("tapestry-offline.serverName", ...);
// the fully qualified name of the client or the last proxy that sent the request
config.add("tapestry-offline.remoteHost", ...);
// the Internet Protocol (IP) port number of the interface on which the request was received.
config.add("tapestry-offline.localPort", ...);
// the port number to which the request was sent.
config.add("tapestry-offline.serverPort", ...);
}
Configuration options listed here
Add the following to your pom.xml:
<dependencies>
<dependency>
<groupId>org.lazan</groupId>
<artifactId>tapestry-offline</artifactId>
<version>x.y.z</version> <!-- lookup latest version at https://github.com/uklance/releases -->
</dependency>
</dependencies>
<repositories>
<repository>
<id>lazan-releases</id>
<url>https://raw.github.com/uklance/releases/master</url>
</repository>
</repositories>
First, a Request is constructed by combining the OfflineRequestContext parameter with the global OfflineRequestGlobals service. Next, a Response is created by combining the Writer / OutputStream parameter with the global OfflineResponseGlobals service. The core tapestry page / component rendering pipeline is then invoked on a seperate thread by the ParallelExecutor to prevent dirtying the current thread.