package org.jboss.seam.remoting; import static org.jboss.seam.ScopeType.APPLICATION; import static org.jboss.seam.annotations.Install.BUILT_IN; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; import java.util.HashMap; import java.util.Map; import java.util.regex.Matcher; import java.util.regex.Pattern; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.jboss.seam.annotations.Install; import org.jboss.seam.annotations.Name; import org.jboss.seam.annotations.Scope; import org.jboss.seam.annotations.intercept.BypassInterceptors; import org.jboss.seam.contexts.ServletLifecycle; import org.jboss.seam.log.LogProvider; import org.jboss.seam.log.Logging; import org.jboss.seam.web.AbstractResource; /** * Serves JavaScript implementation of Seam Remoting * * @author Shane Bryzak * */ @Scope(APPLICATION) @Name("org.jboss.seam.remoting.remoting") @Install(precedence = BUILT_IN) @BypassInterceptors public class Remoting extends AbstractResource { public static final int DEFAULT_POLL_TIMEOUT = 10; // 10 seconds public static final int DEFAULT_POLL_INTERVAL = 1; // 1 second private int pollTimeout = DEFAULT_POLL_TIMEOUT; private int pollInterval = DEFAULT_POLL_INTERVAL; private boolean debug = false; /** * We use a Map for this because a Servlet can serve requests for more than * one context path. */ private Map<String, byte[]> cachedConfig = new HashMap<String, byte[]>(); private static final LogProvider log = Logging.getLogProvider(Remoting.class); private static final Pattern pathPattern = Pattern.compile("/(.*?)/([^/]+)"); private static final String REMOTING_RESOURCE_PATH = "resource"; @Override public String getResourcePath() { return "/remoting"; } private synchronized void initConfig(String contextPath, HttpServletRequest request) { if (!cachedConfig.containsKey(contextPath)) { try { ServletLifecycle.beginRequest(request,getServletContext()); StringBuilder sb = new StringBuilder(); sb.append("\nSeam.Remoting.resourcePath = \""); sb.append(contextPath); sb.append(request.getServletPath()); sb.append(getResourcePath()); sb.append("\";"); sb.append("\nSeam.Remoting.debug = "); sb.append(getDebug() ? "true" : "false"); sb.append(";"); sb.append("\nSeam.Remoting.pollInterval = "); sb.append(getPollInterval()); sb.append(";"); sb.append("\nSeam.Remoting.pollTimeout = "); sb.append(getPollTimeout()); sb.append(";"); cachedConfig.put(contextPath, sb.toString().getBytes()); } finally { ServletLifecycle.endRequest(request); } } } @Override public void getResource(HttpServletRequest request, HttpServletResponse response) throws IOException { try { String pathInfo = request.getPathInfo().substring(getResourcePath().length()); RequestHandler handler = RequestHandlerFactory.getInstance() .getRequestHandler(pathInfo); if (handler != null) { handler.setServletContext(getServletContext()); handler.handle(request, response); } else { Matcher m = pathPattern.matcher(pathInfo); if (m.matches()) { String path = m.group(1); String resource = m.group(2); if (REMOTING_RESOURCE_PATH.equals(path)) { writeResource(resource, response); if ("remote.js".equals(resource)) { appendConfig(response.getOutputStream(), request .getContextPath(), request); } } response.getOutputStream().flush(); } } } catch (Exception ex) { log.error("Error", ex); } } /** * Appends various configuration options to the remoting javascript client * api. * * @param out OutputStream */ private void appendConfig(OutputStream out, String contextPath, HttpServletRequest request) throws IOException { if (!cachedConfig.containsKey(contextPath)) { initConfig(contextPath, request); } out.write(cachedConfig.get(contextPath)); } /** * * @param resourceName String * @param out OutputStream */ private void writeResource(String resourceName, HttpServletResponse response) throws IOException { // Only allow resource requests for .js files if (resourceName.endsWith(".js")) { InputStream in = this.getClass().getClassLoader().getResourceAsStream( "org/jboss/seam/remoting/" + resourceName); try { if (in != null) { response.setContentType("text/javascript"); byte[] buffer = new byte[1024]; int read = in.read(buffer); while (read != -1) { response.getOutputStream().write(buffer, 0, read); read = in.read(buffer); } } else { log.error(String.format("Resource [%s] not found.", resourceName)); } } finally { if (in != null) in.close(); } } } public int getPollTimeout() { return pollTimeout; } public void setPollTimeout(int pollTimeout) { this.pollTimeout = pollTimeout; } public int getPollInterval() { return pollInterval; } public void setPollInterval(int pollInterval) { this.pollInterval = pollInterval; } public boolean getDebug() { return debug; } public void setDebug(boolean debug) { this.debug = debug; } }