Over a million developers have joined DZone.

Tapestry Magic: Integration with cometd

· Java Zone

Discover how AppDynamics steps in to upgrade your performance game and prevent your enterprise from these top 10 Java performance problems, brought to you in partnership with AppDynamics.

Every time I work with Tapestry, I wonder how people can use Java and not use Tapestry. It is so much fun, but then, not everybody wants to have fun while working!!. Recently there was a jira filed for integration with cometd-java. I had never used cometd but thought of giving it a try. Integration was easy but testing it with gradle and eclipse was a headache and consumed much of my Sunday.

This integration will let you expose cometd services from Tapestry. These services will not be tapestry-ioc services, but will be able to access any tapestry-ioc services or objects using @Inject and co. annotations.

Cometd-java uses servlets to initialize and expose its services. I have separated the two jobs. The initialization of BayeuxServer is done by BayeuxServiceSource and the services are exposed by an CometdRequestFilter, an implementation of HttpServletRequestFilter. The code below is just copy-paste stuff taken from CometdServlet.java and AnnotationCometdServlet.java

First the easy part, the CometdRequestFilter

public class CometdRequestFilter implements HttpServletRequestFilter {

private BayeuxServerSource bayeuxServerSource;
private String path;
private Logger logger;

public CometdRequestFilter(BayeuxServerSource bayeuxServerSource,
RequestGlobals requestGlobals, Logger logger,
@Symbol(CometdConstants.CONTEXT_PATH) @Inject String path) {
this.bayeuxServerSource = bayeuxServerSource;
this.path = path.toLowerCase();
this.logger = logger;

try {
bayeuxServerSource.startServer();
logger.debug("Allowed protocols are : " + bayeuxServerSource.getAllowedTransports());
} catch (Exception e) {
e.printStackTrace();
throw new RuntimeException("Could not start Bayeux Server: "
+ e.getMessage(), e);
}
}

private void setServiceOptions(HttpServletRequest request,
HttpServletResponse response) {

}

public boolean service(HttpServletRequest request,
HttpServletResponse response, HttpServletRequestHandler handler)
throws IOException {
//Not my path, so don't handle it
if (!request.getRequestURI().startsWith(request.getContextPath() + path)) {
logger.debug("Skipping " + request.getRequestURI() + " not matching "
+ path);
return handler.service(request, response);
}

logger.debug("Processing request : " + request.getRequestURI());
if ("OPTIONS".equals(request.getMethod())) {
setServiceOptions(request, response);
return true;
}

HttpTransport transport = null;

for (HttpTransport allowedTransport : bayeuxServerSource.getAllowedTransports()) {
if (allowedTransport != null && allowedTransport.accept(request)) {
transport = allowedTransport;
break;
}
}

if (transport == null) {
logger.error("Request " + request.getRequestURI()
+ " Unknown Bayeux Transport");
response.sendError(HttpServletResponse.SC_BAD_REQUEST,
"Unknown Bayeux Transport");
} else {
BayeuxServerImpl bayeux = bayeuxServerSource.getBayeuxImpl();
try {
bayeux.setCurrentTransport(transport);
transport.setCurrentRequest(request);
transport.handle(request, response);
} catch (ServletException e) {
throw new IOException(e);
} finally {
transport.setCurrentRequest(null);
if (bayeux != null) {
bayeux.setCurrentTransport(null);
}
}
}

return true;
}

}

This filter is used to intercept all requests and if the request pertains to our sub-context/path, we consider it as a cometd request, otherwise we ask the remaining chain to handle it. In the constructor, we start the server. We don’t have to worry about the cleanup here as it is handled by the service itself.

While handling the request in service() method, we check if this is an allowed transport type and in case it is we ask the transport to handle the request. The Bayeux Server related details are placed in BayeuxServerSource.

public interface BayeuxServerSource {

BayeuxServer getBayeux();

BayeuxServerImpl getBayeuxImpl();

List<HttpTransport> getAllowedTransports();

void startServer();
public interface BayeuxServerSource {

BayeuxServer getBayeux();

BayeuxServerImpl getBayeuxImpl();

List<HttpTransport> getAllowedTransports();

void startServer();
}}

The bayeux implementation is exposed here as it is required by the filter. BayeuxServerSource service is implemented as

public class BayeuxServerSourceImpl implements BayeuxServerSource,
RegistryShutdownListener {

private BayeuxServerImpl bayeux;

private List<HttpTransport> allowedTransports;

private List<Object> annotationServices;

private ServerAnnotationProcessor annotationProcessor;

private ObjectLocator locator;

private List<BayeuxServerConfigurer> configurers;

private Logger logger;

private boolean initialized;

public BayeuxServerSourceImpl(List<BayeuxServerConfigurer> configurers,
ObjectLocator locator, Logger logger) {
bayeux = newBayeuxServer();
this.locator = locator;
this.configurers = configurers;
this.logger = logger;
annotationServices = new ArrayList<Object>();
allowedTransports = new ArrayList<HttpTransport>();
initialized = false;
}

public synchronized void startServer(){
if(initialized){
throw new RuntimeException("The Bayeux Server is already started");
}

List<Class<?>> annotationServiceClasses = new ArrayList<Class<?>>();
logger.debug("Configuring Bayeux Server using configurers");
for(final BayeuxServerConfigurer configurer: configurers){
configurer.configure(bayeux, annotationServiceClasses);
}

try {
logger.debug("Starting server");
bayeux.start();
} catch (Exception e) {
throw new RuntimeException(e);
}

addAllowedTransports();

setupAnnotatedServices(annotationServiceClasses);

initialized = true;
}

private void addAllowedTransports() {
logger.debug("Adding allowed transports");
for (String transportName : bayeux.getAllowedTransports()) {
ServerTransport transport = bayeux.getTransport(transportName);
if (transport instanceof HttpTransport) {
logger.debug("Adding transport " + transportName);
allowedTransports.add((HttpTransport) transport);
}
}
}

private void setupAnnotatedServices(List<Class<?>> annotationServiceClasses) {
logger.debug("Setting annotation services processor");
annotationProcessor = new ServerAnnotationProcessor(getBayeux());

for (Class<?> service : annotationServiceClasses) {
Object object = locator.autobuild(service);
logger.info("Building service for interface " + service);
annotationProcessor.process(object);
annotationServices.add(object);
}
}

protected BayeuxServerImpl newBayeuxServer() {
return new BayeuxServerImpl();
}

public BayeuxServer getBayeux() {
return bayeux;
}

public BayeuxServerImpl getBayeuxImpl() {
return bayeux;
}

public List<HttpTransport> getAllowedTransports() {
return allowedTransports;
}

public void registryDidShutdown() {
if (!initialized) {
return;
}
cleanupAnnotatedServices();
cleanupSessions();

try {
bayeux.stop();
} catch (Exception e) {
throw new RuntimeException(e);
} finally {
bayeux = null;
}

allowedTransports.clear();
}

private void cleanupAnnotatedServices() {
if (annotationProcessor != null) {
for (Object service : annotationServices) {
logger.debug("Deprocessing " + service);
annotationProcessor.deprocess(service);
}
}
}

private void cleanupSessions() {
for (ServerSession session : bayeux.getSessions()) {
logger.debug("Cleaning up session : " + session);
((ServerSessionImpl) session).cancelSchedule();
}

}
}

This is just startup and cleanup stuff for the service. The cometd annotation support has also been incorporated. The only magic from Tapestry side is ObjectLocator.autobuild(). This method does all the injection stuff and gives us an instance which is then passed on to the cometd’s ServerAnnotationProvider for further processing.

The contributions/initialization to BayeuxServerSource are made through BayeuxServerConfigurer.

public interface BayeuxServerConfigurer {
void configure(BayeuxServerImpl bayeuxServerImpl, List<Class<?>> annotatedServices);
}

Finally, the module file

public class TapestryCometdModule {

public BayeuxServerSource buildBayeuxServerSource(
List<BayeuxServerConfigurer> configurers, ObjectLocator locator,
Logger logger,
RegistryShutdownHub registryShutdownHub) {
BayeuxServerSourceImpl impl = new BayeuxServerSourceImpl(configurers, locator, logger);
registryShutdownHub.addRegistryShutdownListener(impl);
return impl;
}

public static void contributeHttpServletRequestHandler(
OrderedConfiguration<HttpServletRequestFilter> filters) {
filters.addInstance("Cometd", CometdRequestFilter.class);
}

public BayeuxServer buildBayeuxServer(BayeuxServerSource bayeuxServerSource,
PropertyShadowBuilder shadowBuilder) {
return shadowBuilder.build(bayeuxServerSource, "bayeux",
BayeuxServer.class);
}

public static void contributeFactoryDefaults(
MappedConfiguration<String, String> configuration) {
configuration.add(CometdConstants.CONTEXT_PATH, "/cometd/");
}

}

There is not much going on here, we create an instance of BayeuxServerSource, pass it to RegistryShutdownHub for proper cleanup and expose two of its methods as services using ShadowBuilder.

You can find the source code along with tests here

 

From http://tawus.wordpress.com/2011/06/13/tapestry-magic-15-integration-with-cometd/

The Java Zone is brought to you in partnership with AppDynamics. AppDynamics helps you gain the fundamentals behind application performance, and implement best practices so you can proactively analyze and act on performance problems as they arise, and more specifically with your Java applications. Start a Free Trial.

Topics:

{{ parent.title || parent.header.title}}

{{ parent.tldr }}

{{ parent.urlSource.name }}