Here's my GWT-invoking-via-reflection ServerServlet. It does seem to work from the GWT perspective. But, I don't think the custom Component instantiation is working quite right (or I broke it, or I still don't have it configured correctly) -- my custom Component is instantiated, but its Client connectors don't seem to be there. More info after further due diligence!
On 1/3/08, Jerome Louvel <[EMAIL PROTECTED]> wrote: > > > Hi Rob, > > The feature is now in SVN trunk! Looking forward to your enhanced GWT > integration approach. > > Best regards, > Jerome >
/* * Copyright 2005-2007 Noelios Consulting. * * The contents of this file are subject to the terms of the Common Development * and Distribution License (the "License"). You may not use this file except in * compliance with the License. * * You can obtain a copy of the license at * http://www.opensource.org/licenses/cddl1.txt See the License for the specific * language governing permissions and limitations under the License. * * When distributing Covered Code, include this CDDL HEADER in each file and * include the License file at http://www.opensource.org/licenses/cddl1.txt If * applicable, add the following below this CDDL HEADER, with the fields * enclosed by brackets "[]" replaced with your own identifying information: * Portions Copyright [yyyy] [name of copyright owner] */ package com.noelios.restlet.ext.servlet; import java.io.IOException; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.util.Enumeration; import java.util.List; import javax.servlet.ServletConfig; import javax.servlet.ServletException; import javax.servlet.ServletRequest; import javax.servlet.ServletResponse; import javax.servlet.http.HttpServlet; import javax.servlet.http.HttpServletRequest; import javax.servlet.http.HttpServletResponse; import org.restlet.Application; import org.restlet.Component; import org.restlet.Context; import org.restlet.Server; import org.restlet.data.Protocol; import com.noelios.restlet.application.ApplicationContext; import com.noelios.restlet.component.ComponentContext; import com.noelios.restlet.http.HttpServerHelper; /** * Servlet acting like an HTTP server connector. See <a * href="/documentation/1.1/faq#02">Developper FAQ #2</a> for details on how to * integrate a Restlet application into a servlet container.<br/> Here is a * sample configuration for your Restlet webapp: * * <pre> * <?xml version="1.0" encoding="ISO-8859-1"?> * <!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"> * <web-app> * <display-name>Restlet adapter</display-name> * * <!-- Your application class name --> * <context-param> * <param-name>org.restlet.application</param-name> * <param-value>com.noelios.restlet.test.TraceApplication</param-value> * </context-param> * * <!-- Restlet adapter --> * <servlet> * <servlet-name>ServerServlet</servlet-name> * <servlet-class>com.noelios.restlet.ext.servlet.ServerServlet</servlet-class> * </servlet> * * <!-- Catch all requests --> * <servlet-mapping> * <servlet-name>ServerServlet</servlet-name> * <url-pattern>/*</url-pattern> * </servlet-mapping> * </web-app> * </pre> * * The enumeration of initParameters of your Servlet will be copied to the * "context.parameters" property of your application. This way, you can pass * additional initialization parameters to your Restlet application, and share * them with existing Servlets.<br> * <br> * It is also possible to specify a component class to be instantiated instead * of a default component. You just need to add a "org.restlet.component" * context parameter to your ServerServlet, with the qualified class name to * instantiate as value. Once instantiated, a server connector will be added to * this component and the application specified via the other context parameter * will be normally attached to its default virtual host. This allows you to * manually attach private applications to its internal router or to declare * client connectors, for example for the CLAP, FILE or HTTP protocols.<br> * <br> * To allow Restlet to handle all non-module requests under the hosted mode of * Google Web Toolkit, ServerServlet can be used in place of GWTShellServlet. * As GWTShellServlet expects, add a context parameter called "module" that * specifies the fully qualified name of a GWT module. Requests directed to * the module will be forwarded to GWTShellServlet; Restlet will handle all * other requests. * * @see <a href="http://java.sun.com/j2ee/">J2EE home page</a> * @author Jerome Louvel ([EMAIL PROTECTED]) */ public class ServerServlet extends HttpServlet { /** * The Servlet context initialization parameter's name containing the name * of the Servlet context attribute that should be used to store the Restlet * Application instance. */ private static final String NAME_APPLICATION_ATTRIBUTE = "org.restlet.attribute.application"; /** The default value for the NAME_APPLICATION_ATTRIBUTE parameter. */ private static final String NAME_APPLICATION_ATTRIBUTE_DEFAULT = "com.noelios.restlet.ext.servlet.ServerServlet.application"; /** * The Servlet context initialization parameter's name containing the name * of the Servlet context attribute that should be used to store the Restlet * Component instance. */ private static final String NAME_COMPONENT_ATTRIBUTE = "org.restlet.attribute.component"; /** The default value for the NAME_COMPONENT_ATTRIBUTE parameter. */ private static final String NAME_COMPONENT_ATTRIBUTE_DEFAULT = "com.noelios.restlet.ext.servlet.ServerServlet.component"; /** * The Servlet context initialization parameter's name containing the name * of the Servlet context attribute that should be used to store the HTTP * server connector instance. */ private static final String NAME_SERVER_ATTRIBUTE = "org.restlet.attribute.server"; /** The default value for the NAME_SERVER_ATTRIBUTE parameter. */ private static final String NAME_SERVER_ATTRIBUTE_DEFAULT = "com.noelios.restlet.ext.servlet.ServerServlet.server"; /** * Name of the attribute key containing a reference to the current * application. */ private static final String COMPONENT_KEY = "org.restlet.component"; /** Serial version identifier. */ private static final long serialVersionUID = 1L; /** The associated Restlet application. */ private volatile transient Application application; /** The associated Restlet component. */ private volatile transient Component component; /** The associated HTTP server helper. */ private volatile transient HttpServerHelper helper; /** The GWT Shell Servlet to composite for GWT module support. */ private volatile transient Object gwtShellServlet; /** The service method for the GWT Shell Servlet. */ private volatile transient Method gwtShellServletServiceMethod; /** Signify whether GWT module support is enabled. */ private volatile boolean gwtSupported = false; /** Name of the GWT module specified in the servlet configuration. */ private volatile String gwtModule; /** * Constructor. */ public ServerServlet() { this.application = null; this.component = null; this.helper = null; } /** * Creates the single Application used by this Servlet. * * @param context * The Context for the Application * * @return The newly created Application or null if unable to create */ @SuppressWarnings("unchecked") public Application createApplication(Context context) { Application application = null; // Try to instantiate a new target application // First, find the application class name String applicationClassName = getInitParameter(Application.KEY, null); // Load the application class using the given class name if (applicationClassName != null) { try { // According to // http://www.caucho.com/resin-3.0/webapp/faq.xtp#Class.forName()-doesn't-seem-to-work-right // this approach may need to used when loading classes. Class<?> targetClass; ClassLoader loader = Thread.currentThread() .getContextClassLoader(); if (loader != null) targetClass = Class.forName(applicationClassName, false, loader); else targetClass = Class.forName(applicationClassName); try { // Create a new instance of the application class by // invoking the constructor with the Context parameter. application = (Application) targetClass.getConstructor( Context.class).newInstance( new ServletContextAdapter(this, context)); } catch (NoSuchMethodException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't invoke the constructor of the target class. Please check this class has a constructor with a single parameter of type Context. The empty constructor and the context setter will be used instead.", e); // The constructor with the Context parameter does not // exist. Instantiate an application with the default // constructor then invoke the setContext method. application = (Application) targetClass.getConstructor() .newInstance(); // Set the context based on the Servlet's context ApplicationContext applicationContext = (ApplicationContext) application .getContext(); application.setContext(new ApplicationContext(application, new ServletContextAdapter(this, context), applicationContext.getLogger())); } } catch (ClassNotFoundException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't find the target class. Please check that your classpath includes " + applicationClassName, e); } catch (InstantiationException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't instantiate the target class. Please check this class has an empty constructor " + applicationClassName, e); } catch (IllegalAccessException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't instantiate the target class. Please check that you have to proper access rights to " + applicationClassName, e); } catch (NoSuchMethodException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't invoke the constructor of the target class. Please check this class has a constructor with a single parameter of Context " + applicationClassName, e); } catch (InvocationTargetException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't instantiate the target class. An exception was thrown while creating " + applicationClassName, e); } } if (application != null) { ApplicationContext applicationContext = (ApplicationContext) application .getContext(); // Set the special WAR client applicationContext.setWarClient(new ServletWarClient( applicationContext, this.getServletConfig() .getServletContext())); // Copy all the servlet parameters into the context String initParam; // Copy all the Servlet component initialization parameters javax.servlet.ServletConfig servletConfig = getServletConfig(); for (Enumeration<String> enum1 = servletConfig .getInitParameterNames(); enum1.hasMoreElements();) { initParam = (String) enum1.nextElement(); applicationContext.getParameters().add(initParam, servletConfig.getInitParameter(initParam)); } // Copy all the Servlet application initialization parameters for (Enumeration<String> enum1 = getServletContext() .getInitParameterNames(); enum1.hasMoreElements();) { initParam = (String) enum1.nextElement(); applicationContext.getParameters().add(initParam, getServletContext().getInitParameter(initParam)); } } return application; } /** * Creates the single Component used by this Servlet. * * @return The newly created Component or null if unable to create */ @SuppressWarnings("unchecked") public Component createComponent() { Component component = null; // Try to instantiate a new target component // First, find the component class name String componentClassName = getInitParameter(COMPONENT_KEY, null); // Load the component class using the given class name if (componentClassName != null) { try { // According to // http://www.caucho.com/resin-3.0/webapp/faq.xtp#Class.forName()-doesn't-seem-to-work-right // this approach may need to used when loading classes. Class<?> targetClass; ClassLoader loader = Thread.currentThread() .getContextClassLoader(); if (loader != null) targetClass = Class.forName(componentClassName, false, loader); else targetClass = Class.forName(componentClassName); // Create a new instance of the component class by // invoking the constructor with the Context parameter. component = (Component) targetClass.newInstance(); } catch (ClassNotFoundException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't find the target class. Please check that your classpath includes " + componentClassName, e); } catch (InstantiationException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't instantiate the target class. Please check this class has an empty constructor " + componentClassName, e); } catch (IllegalAccessException e) { log( "[Noelios Restlet Engine] - The ServerServlet couldn't instantiate the target class. Please check that you have to proper access rights to " + componentClassName, e); } } if (component != null) { ComponentContext componentContext = (ComponentContext) component .getContext(); // Set the special WAR client // componentContext.setWarClient(new ServletWarClient( // componentContext, this.getServletConfig() // .getServletContext())); // Copy all the servlet parameters into the context String initParam; // Copy all the Servlet container initialization parameters javax.servlet.ServletConfig servletConfig = getServletConfig(); for (Enumeration<String> enum1 = servletConfig .getInitParameterNames(); enum1.hasMoreElements();) { initParam = (String) enum1.nextElement(); componentContext.getParameters().add(initParam, servletConfig.getInitParameter(initParam)); } // Copy all the Servlet application initialization parameters for (Enumeration<String> enum1 = getServletContext() .getInitParameterNames(); enum1.hasMoreElements();) { initParam = (String) enum1.nextElement(); componentContext.getParameters().add(initParam, getServletContext().getInitParameter(initParam)); } } else { component = new Component(); } return component; } /** * Creates the associated HTTP server handling calls. * * @param request * The HTTP Servlet request. * @return The new HTTP server handling calls. */ public HttpServerHelper createServer(HttpServletRequest request) { HttpServerHelper result = null; Component component = getComponent(); Application application = getApplication(); if ((component != null) && (application != null)) { // First, let's create a pseudo server Server server = new Server(component.getContext(), (List<Protocol>) null, request.getLocalAddr(), request .getLocalPort(), component); result = new HttpServerHelper(server); // Attach the application String uriPattern = request.getContextPath() + request.getServletPath(); component.getDefaultHost().attach(uriPattern, application); } return result; } @Override public void destroy() { if (gwtSupported) { destroyGWTShellServlet(); } if ((getApplication() != null) && (getApplication().isStarted())) { try { getApplication().stop(); } catch (Exception e) { log("Error during the stopping of the Restlet Application", e); } } super.destroy(); } /** * Returns the application. It creates a new one if none exists. * * @return The application. */ public Application getApplication() { Application result = this.application; if (result == null) { synchronized (ServerServlet.class) { // Find the attribute name to use to store the application String applicationAttributeName = getInitParameter( NAME_APPLICATION_ATTRIBUTE, NAME_APPLICATION_ATTRIBUTE_DEFAULT); // Look up the attribute for a target result = (Application) getServletContext().getAttribute( applicationAttributeName); if (result == null) { result = createApplication(getComponent().getContext()); getServletContext().setAttribute(applicationAttributeName, result); } this.application = result; } } return result; } /** * Returns the component. It creates a new one if none exists. * * @return The component. */ public Component getComponent() { Component result = this.component; if (result == null) { synchronized (ServerServlet.class) { // Find the attribute name to use to store the component String componentAttributeName = getInitParameter( NAME_COMPONENT_ATTRIBUTE, NAME_COMPONENT_ATTRIBUTE_DEFAULT); // Look up the attribute for a target result = (Component) getServletContext().getAttribute( componentAttributeName); if (result == null) { result = createComponent(); getServletContext().setAttribute(componentAttributeName, result); } this.component = result; } } return result; } /** * Returns the value of a given initialization parameter, first from the * Servlet configuration, then from the Web Application context. * * @param name * The parameter name. * @param defaultValue * The default to use in case the parameter is not found. * @return The value of the parameter or null. */ public String getInitParameter(String name, String defaultValue) { String result = getServletConfig().getInitParameter(name); if (result == null) { result = getServletConfig().getServletContext().getInitParameter( name); } if (result == null) { result = defaultValue; } return result; } /** * Returns the associated HTTP server handling calls. It creates a new one * if none exists. * * @param request * The HTTP Servlet request. * @return The HTTP server handling calls. */ public HttpServerHelper getServer(HttpServletRequest request) { HttpServerHelper result = this.helper; if (result == null) { synchronized (ServerServlet.class) { // Find the attribute name to use to store the server reference String serverAttributeName = getInitParameter( NAME_SERVER_ATTRIBUTE, NAME_SERVER_ATTRIBUTE_DEFAULT); // Look up the attribute for a target result = (HttpServerHelper) getServletContext().getAttribute( serverAttributeName); if (result == null) { result = createServer(request); getServletContext().setAttribute(serverAttributeName, result); } this.helper = result; } } return result; } @Override public void init() throws ServletException { gwtModule = getInitParameter("module"); if(gwtModule!=null){ gwtSupported = true; instantiateGWTShellServlet(); initGWTShellServlet(getServletConfig()); } if ((getApplication() != null) && (getApplication().isStopped())) { try { getApplication().start(); } catch (Exception e) { log("Error during the starting of the Restlet Application", e); } } } /** * Services a HTTP Servlet request as an uniform call. * * @param request * The HTTP Servlet request. * @param response * The HTTP Servlet response. */ public void service(HttpServletRequest request, HttpServletResponse response) throws ServletException, IOException { if(gwtSupported){ String path = request.getPathInfo(); if(path.startsWith("/"+gwtModule)){ serviceGWTShellServlet(request,response); return; } } HttpServerHelper helper = getServer(request); if (helper != null) { helper .handle(new ServletCall(helper.getServer(), request, response)); } else { log("[Noelios Restlet Engine] - Unable to get the Restlet HTTP server connector. Status code 500 returned."); response.sendError(500); } } /** * Instantiates the GWT Shell Servlet using reflection. */ private void instantiateGWTShellServlet(){ try{ gwtShellServlet = Class.forName("com.google.gwt.dev.shell.GWTShellServlet").newInstance(); gwtShellServletServiceMethod = gwtShellServlet.getClass().getMethod("service",ServletRequest.class,ServletResponse.class); } catch (IllegalAccessException x) { log("[Noelios Restlet Engine] - Unable to instantiate GWTShellServlet",x); } catch (ClassNotFoundException x) { log("[Noelios Restlet Engine] - Unable to instantiate GWTShellServlet",x); } catch (InstantiationException x) { log("[Noelios Restlet Engine] - Unable to instantiate GWTShellServlet",x); } catch (NoSuchMethodException x) { log("[Noelios Restlet Engine] - Unable to find service method for GWTShellServlet",x); } } /** * Initialize the GWT Shell Servlet using reflection. */ private void initGWTShellServlet(ServletConfig servletConfig){ try{ gwtShellServlet.getClass().getMethod("init", ServletConfig.class).invoke(gwtShellServlet,servletConfig); } catch (IllegalAccessException x) { log("[Noelios Restlet Engine] - Unable to init GWTShellServlet",x); } catch (InvocationTargetException x) { log("[Noelios Restlet Engine] - Unable to init GWTShellServlet",x); } catch (NoSuchMethodException x) { log("[Noelios Restlet Engine] - Unable to init GWTShellServlet",x); } } /** * Destroy the GWT Shell Servlet using reflection. */ private void destroyGWTShellServlet(){ try{ gwtShellServlet.getClass().getMethod("destroy").invoke(gwtShellServlet); } catch (IllegalAccessException x) { log("[Noelios Restlet Engine] - Unable to destroy GWTShellServlet",x); } catch (InvocationTargetException x) { log("[Noelios Restlet Engine] - Unable to destroy GWTShellServlet",x); } catch (NoSuchMethodException x) { log("[Noelios Restlet Engine] - Unable to destroy GWTShellServlet",x); } } /** * Services the GWT Shell Servlet using reflection. */ private void serviceGWTShellServlet(ServletRequest request, ServletResponse response){ try{ gwtShellServletServiceMethod.invoke(gwtShellServlet,request,response); } catch (IllegalAccessException x) { log("[Noelios Restlet Engine] - Unable to call service(request,response) in GWTShellServlet",x); } catch (InvocationTargetException x) { log("[Noelios Restlet Engine] - Unable to call service(request,response) in GWTShellServlet",x); } } }

