/* * Copyright 2009 Richard Zschech. * * Licensed under the Apache License, Version 2.0 (the "License"); you may not * use this file except in compliance with the License. You may obtain a copy of * the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations under * the License. */ package net.zschech.gwt.comet.server.impl; import java.io.IOException; import java.io.OutputStream; import java.lang.reflect.Field; import java.util.concurrent.ScheduledFuture; import javax.servlet.ServletContext; import javax.servlet.ServletException; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpSession; public abstract class AsyncServlet { public static final String SERVLET_CONTEXT_KEY = AsyncServlet.class.getName(); public static AsyncServlet initialize(ServletContext context) { synchronized (context) { AsyncServlet async = (AsyncServlet) context.getAttribute(SERVLET_CONTEXT_KEY); if (async == null) { String serverInfo = context.getServerInfo(); String server = context.getInitParameter(SERVLET_CONTEXT_KEY); if (server == null) { if (serverInfo.startsWith("jetty-6") || serverInfo.startsWith("jetty/6")) { // e.g. jetty-6.1.x server = "Jetty6"; } else if (serverInfo.startsWith("jetty/7")) { server = "Jetty7"; } else if (serverInfo.startsWith("Apache Tomcat/5.5.")) { // e.g. Apache Tomcat/5.5.26 server = "Catalina55"; } else if (serverInfo.startsWith("Apache Tomcat/6.")) { // e.g. Apache Tomcat/6.0.18 server = "Catalina60"; } else if (serverInfo.startsWith("Grizzly/")) { server = "Grizzly"; } else if (serverInfo.startsWith("GlassFish ")) { server = "GlassFish"; } else if (serverInfo.startsWith("Google App Engine/")) { server = "GAE"; } } if (server != null) { context.log("Creating " + server + " async servlet handler for server " + serverInfo); try { async = (AsyncServlet) Class.forName("net.zschech.gwt.comet.server.impl." + server + "AsyncServlet").newInstance(); } catch (Throwable e) { context.log("Error creating " + server + " async servlet handler for server " + serverInfo + ". Falling back to default blocking async servlet handler.", e); async = new BlockingAsyncServlet(); } } else { context.log("Creating blocking async servlet handler for server " + serverInfo); async = new BlockingAsyncServlet(); } try { try { async.init(context); } catch (Throwable e) { context.log("Error initiating " + server + " async servlet handler for server " + serverInfo + ". Falling back to default blocking async servlet handler.", e); context.log("Creating blocking async servlet handler for server " + serverInfo); async = new BlockingAsyncServlet(); async.init(context); } context.setAttribute(SERVLET_CONTEXT_KEY, async); } catch (ServletException e) { throw new Error("Error setting up async servlet"); } } return async; } } public static void destroy(ServletContext context) { synchronized (context) { AsyncServlet async = (AsyncServlet) context.getAttribute(SERVLET_CONTEXT_KEY); if (async != null) { async.shutdown(); } } } private ServletContext context; /** * Override for web-server specific initialisation * @throws ServletException */ protected void init(ServletContext context) throws ServletException { this.context = context; } /** * Override for web-server specific shutdown */ protected void shutdown() { } /** * @return the servlet context associated with this AsyncServlet */ protected ServletContext getServletContext() { return context; } /** * Log a message to the servlet context * @see ServletContext#log(String) * @param message */ protected void log(String message) { context.log(message); } /** * Log a message to the servlet context * @see ServletContext#log(String, Throwable) * @param message * @param throwable */ protected void log(String message, Throwable throwable) { context.log(message, throwable); } /** * Gets a web-server specific wrapper for the servlet response output stream. * @param outputStream * @return the web-server specific wrapper */ public OutputStream getOutputStream(OutputStream outputStream) { return outputStream; } public abstract Object suspend(CometServletResponseImpl response, CometSessionImpl session, HttpServletRequest request) throws IOException; public abstract void terminate(CometServletResponseImpl response, CometSessionImpl session, boolean serverInitiated, Object suspendInfo); public abstract void invalidate(CometSessionImpl session); public abstract void enqueued(CometSessionImpl session); /** * web-server specific implementation of updating the access time of the HTTP session * @param httpSession * @return true if the access time was updated successfully */ protected boolean access(HttpSession httpSession) { return false; } /** * web-server specific implementation of scheduling a heartbeat * @param response * @param session * @return null if no scheduling is required */ public ScheduledFuture<?> scheduleHeartbeat(CometServletResponseImpl response, CometSessionImpl session) { return null; } /** * web-server specific implementation of scheduling a session keep alive * @param response * @param session * @return null if no scheduling is required */ public ScheduledFuture<?> scheduleSessionKeepAlive(CometServletResponseImpl response, CometSessionImpl session) { return null; } protected Object get(String path, Object object) { try { for (String property : path.split("\\.")) { Class<?> c = object.getClass(); while (true) { try { Field field = c.getDeclaredField(property); field.setAccessible(true); object = field.get(object); break; } catch (NoSuchFieldException e) { c = c.getSuperclass(); if (c == null) { throw e; } } } } return object; } catch (Exception e) { log("Error accessing underlying objects " + path + " from " + object.getClass().getCanonicalName(), e); return null; } } }