Author: felipeal
Date: Sat Apr 15 17:26:20 2006
New Revision: 394406

URL: http://svn.apache.org/viewcvs?rev=394406&view=rev
Log:
CACTUS-151: this file was submitted on Xuan's patch, but I forgot to include it 
earlier

Added:
    
jakarta/cactus/trunk/framework/src/java/share-13-14/org/apache/cactus/extension/jetty/Jetty4xTestSetup.java

Added: 
jakarta/cactus/trunk/framework/src/java/share-13-14/org/apache/cactus/extension/jetty/Jetty4xTestSetup.java
URL: 
http://svn.apache.org/viewcvs/jakarta/cactus/trunk/framework/src/java/share-13-14/org/apache/cactus/extension/jetty/Jetty4xTestSetup.java?rev=394406&view=auto
==============================================================================
--- 
jakarta/cactus/trunk/framework/src/java/share-13-14/org/apache/cactus/extension/jetty/Jetty4xTestSetup.java
 (added)
+++ 
jakarta/cactus/trunk/framework/src/java/share-13-14/org/apache/cactus/extension/jetty/Jetty4xTestSetup.java
 Sat Apr 15 17:26:20 2006
@@ -0,0 +1,586 @@
+/* 
+ * ========================================================================
+ * 
+ * Copyright 2001-2004 The Apache Software Foundation.
+ *
+ * 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 org.apache.cactus.extension.jetty;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.net.HttpURLConnection;
+import java.net.URL;
+
+import junit.extensions.TestSetup;
+import junit.framework.Protectable;
+import junit.framework.Test;
+import junit.framework.TestResult;
+
+import org.apache.cactus.internal.configuration.BaseConfiguration;
+import org.apache.cactus.internal.configuration.Configuration;
+import org.apache.cactus.internal.configuration.DefaultFilterConfiguration;
+import org.apache.cactus.internal.configuration.DefaultServletConfiguration;
+import org.apache.cactus.internal.configuration.FilterConfiguration;
+import org.apache.cactus.internal.configuration.ServletConfiguration;
+import org.apache.cactus.internal.util.ClassLoaderUtils;
+import org.apache.cactus.server.FilterTestRedirector;
+import org.apache.cactus.server.ServletTestRedirector;
+
+
+/**
+ * Custom JUnit test setup to use to automatically start Jetty. Example:<br/>
+ * <code><pre>
+ * public static Test suite()
+ * {
+ *     TestSuite suite = new TestSuite(Myclass.class);
+ *     return new JettyTestSetup(suite);
+ * }
+ * </pre></code>
+ * 
+ * @version $Id: JettyTestSetup.java,v 1.2 2004/08/17 10:35:57 vmassol Exp $
+ */
+public class Jetty4xTestSetup extends TestSetup
+{
+    /**
+     * Name of optional system property that points to a Jetty XML
+     * configuration file.
+     */
+    private static final String CACTUS_JETTY_CONFIG_PROPERTY = 
+        "cactus.jetty.config";
+
+    /**
+     * Name of optional system property that gives the directory
+     * where JSPs and other resources are located. 
+     */
+    private static final String CACTUS_JETTY_RESOURCE_DIR_PROPERTY = 
+        "cactus.jetty.resourceDir";
+
+    /**
+     * The configuration file to be used for initializing Jetty.
+     */
+    private File configFile;
+
+    /**
+     * The directory containing the resources of the web-application.
+     */
+    private File resourceDir;
+
+    /**
+     * The Jetty server object representing the running instance. It is
+     * used to stop Jetty in [EMAIL PROTECTED] #tearDown()}.
+     */
+    private Object server; 
+
+    /**
+     * Whether the container had already been running before.
+     */
+    private boolean alreadyRunning;
+
+    /**
+     * Whether the container is running or not.
+     */
+    private boolean isRunning = false;
+
+    /**
+     * Whether the container should be stopped on tearDown even though
+     * it was not started by us.
+     */
+    private boolean forceShutdown = false;
+    
+    /**
+     * The Servlet configuration object used to configure Jetty. 
+     */
+    private ServletConfiguration servletConfiguration;
+
+    /**
+     * The Filter configuration object used to configure Jetty. 
+     */
+    private FilterConfiguration filterConfiguration;
+
+    /**
+     * The base configuration object used to configure Jetty. 
+     */
+    private Configuration baseConfiguration;
+    
+    /**
+     * @param theTest the test we are decorating (usually a test suite)
+     */
+    public Jetty4xTestSetup(Test theTest)
+    {
+        super(theTest);
+        this.baseConfiguration = new BaseConfiguration();
+        this.servletConfiguration = new DefaultServletConfiguration();
+        this.filterConfiguration = new DefaultFilterConfiguration();
+    }
+
+    /**
+     * @param theTest the test we are decorating (usually a test suite)
+     * @param theBaseConfiguration the base configuration object used to
+     *        configure Jetty
+     * @param theServletConfiguration the servlet configuration object used
+     *        to configure Jetty
+     * @param theFilterConfiguration the filter configuration object used
+     *       to configure Jetty
+     */
+    public Jetty4xTestSetup(Test theTest, 
+        Configuration theBaseConfiguration,
+        ServletConfiguration theServletConfiguration,
+        FilterConfiguration theFilterConfiguration)
+    {
+        this(theTest);
+        this.baseConfiguration = theBaseConfiguration;
+        this.servletConfiguration = theServletConfiguration;
+        this.filterConfiguration = theFilterConfiguration;
+    }
+    
+    /**
+     * Make sure that [EMAIL PROTECTED] #tearDown} is called if [EMAIL 
PROTECTED] #setUp} fails
+     * to start the container properly. The default 
+     * [EMAIL PROTECTED] TestSetup#run(TestResult)} method does not provide 
this feature
+     * unfortunately.
+     *  
+     * @see TestSetup#run(TestResult)
+     */
+    public void run(final TestResult theResult)
+    {
+        Protectable p = new Protectable()
+        {
+            public void protect() throws Exception
+            {
+                try
+                {
+                    setUp();
+                    basicRun(theResult);
+                }
+                finally
+                {
+                    tearDown();
+                }
+            }
+        };
+        theResult.runProtected(this, p);
+    }  
+    
+    /**
+     * Start an embedded Jetty server. It is allowed to pass a Jetty XML as
+     * a system property (<code>cactus.jetty.config</code>) to further 
+     * configure Jetty. Example: 
+     * <code>-Dcactus.jetty.config=./jetty.xml</code>.
+     *
+     * @exception Exception if an error happens during initialization
+     */
+    protected void setUp() throws Exception
+    {
+        // Try connecting in case the server is already running. If so, does
+        // nothing
+        URL contextURL = new URL(this.baseConfiguration.getContextURL()
+            + "/" + this.servletConfiguration.getDefaultRedirectorName()
+            + "?Cactus_Service=RUN_TEST");
+        this.alreadyRunning = isAvailable(testConnectivity(contextURL));
+        if (this.alreadyRunning)
+        {
+            // Server is already running. Record this information so that we
+            // don't stop it afterwards.
+            this.isRunning = true;
+            return;
+        }
+
+        // Note: We are currently using reflection in order not to need Jetty
+        // to compile Cactus. If the code becomes more complex or we need to 
+        // add other initializer, it will be worth considering moving them
+        // to a separate "extension" subproject which will need additional jars
+        // in its classpath (using the same mechanism as the Ant project is
+        // using to conditionally compile tasks).
+
+        // Create a Jetty Server object and configure a listener
+        this.server = createServer(this.baseConfiguration);
+
+        // Create a Jetty context.
+        Object context = createContext(this.server, this.baseConfiguration);
+        
+        // Add the Cactus Servlet redirector
+        addServletRedirector(context, this.servletConfiguration);
+
+        // Add the Cactus Jsp redirector
+        addJspRedirector(context);
+
+        // Add the Cactus Filter redirector
+        addFilterRedirector(context, this.filterConfiguration);
+
+        // Configure Jetty with an XML file if one has been specified on the
+        // command line.
+        if (getConfigFile() != null)
+        {
+            this.server.getClass().getMethod("configure", 
+                new Class[] {String.class}).invoke(
+                    this.server, new Object[] {getConfigFile().toString()});
+        }
+
+        // Start the Jetty server
+        this.server.getClass().getMethod("start", null).invoke(
+            this.server, null);
+
+        this.isRunning = true;
+    }
+
+    /**
+     * Stop the running Jetty server.
+     * 
+     * @exception Exception if an error happens during the shutdown
+     */
+    protected void tearDown() throws Exception
+    { 
+        // Don't shut down a container that has not been started by us
+        if (!this.forceShutdown && this.alreadyRunning)
+        {
+            return;
+        }
+
+        if (this.server != null)
+        { 
+            // First, verify if the server is running
+            boolean started = ((Boolean) this.server.getClass().getMethod(
+                "isStarted", null).invoke(this.server, null)).booleanValue(); 
+
+            // Stop and destroy the Jetty server, if started
+            if (started)
+            {
+                // Stop all listener and contexts
+                this.server.getClass().getMethod("stop", null).invoke(
+                    this.server, null);
+
+                // Destroy a stopped server. Remove all components and send 
+                // notifications to all event listeners.  
+                this.server.getClass().getMethod("destroy", null).invoke(
+                    this.server, null);
+            }
+        } 
+
+        this.isRunning = false;
+    }
+
+    /**
+     * Sets the configuration file to use for initializing Jetty.
+     * 
+     * @param theConfigFile The configuration file to set
+     */
+    public final void setConfigFile(File theConfigFile)
+    {
+        this.configFile = theConfigFile;
+    }
+
+    /**
+     * Sets the directory in which Jetty will look for the web-application
+     * resources.
+     * 
+     * @param theResourceDir The resource directory to set
+     */
+    public final void setResourceDir(File theResourceDir)
+    {
+        this.resourceDir = theResourceDir;
+    }
+
+    /**
+     * @param isForcedShutdown if true the container will be stopped even
+     *        if it has not been started by us
+     */
+    public final void setForceShutdown(boolean isForcedShutdown)
+    {
+        this.forceShutdown = isForcedShutdown;
+    }
+    
+    /**
+     * @return The resource directory, or <code>null</code> if it has not been
+     *         set
+     */
+    protected final File getConfigFile()
+    {
+        if (this.configFile == null)
+        {
+            String configFileProperty = System.getProperty(
+                CACTUS_JETTY_CONFIG_PROPERTY);
+            if (configFileProperty != null)
+            {
+                this.configFile = new File(configFileProperty);
+            }
+        }
+        return this.configFile;
+    }
+
+    /**
+     * @return The resource directory, or <code>null</code> if it has not been
+     *         set
+     */
+    protected final File getResourceDir()
+    {
+        if (this.resourceDir == null)
+        {
+            String resourceDirProperty = System.getProperty(
+                CACTUS_JETTY_RESOURCE_DIR_PROPERTY);
+            if (resourceDirProperty != null)
+            {
+                this.resourceDir = new File(resourceDirProperty);
+            }
+        }
+        return this.resourceDir;
+    }
+
+    /**
+     * Create a Jetty server object and configures a listener on the
+     * port defined in the Cactus context URL property.
+     * 
+     * @param theConfiguration the base Cactus configuration
+     * @return the Jetty <code>Server</code> object
+     * 
+     * @exception Exception if an error happens during initialization
+     */
+    private Object createServer(Configuration theConfiguration) 
+        throws Exception
+    {
+        // Create Jetty Server object
+        Class serverClass = ClassLoaderUtils.loadClass(
+            "org.mortbay.jetty.Server", this.getClass());
+        Object server = serverClass.newInstance();
+
+        URL contextURL = new URL(theConfiguration.getContextURL());
+
+        // Add a listener on the port defined in the Cactus configuration
+        server.getClass().getMethod("addListener", 
+            new Class[] {String.class})
+            .invoke(server, new Object[] {"" + contextURL.getPort()});
+
+        return server;
+    }
+
+    /**
+     * Create a Jetty Context. We use a <code>WebApplicationContext</code>
+     * because we need to use Servlet Filters.
+     * 
+     * @param theServer the Jetty Server object
+     * @param theConfiguration the base Cactus configuration
+     * @return Object the <code>WebApplicationContext</code> object
+     * 
+     * @exception Exception if an error happens during initialization
+     */
+    private Object createContext(Object theServer,
+        Configuration theConfiguration) throws Exception
+    {
+        // Add a web application. This creates a WebApplicationContext.
+        // Note: We do not put any WEB-INF/, lib/ nor classes/ directory
+        // in the webapp.
+        URL contextURL = new URL(theConfiguration.getContextURL());
+
+        if (getResourceDir() != null)
+        {
+            theServer.getClass().getMethod("addWebApplication", 
+                new Class[] {String.class, String.class})
+                .invoke(theServer, new Object[] {contextURL.getPath(), 
+                    getResourceDir().toString()});
+        }
+        
+        // Retrieves the WebApplication context created by the
+        // "addWebApplication". We need it to be able to manually configure
+        // other items in the context.
+        Object context = theServer.getClass().getMethod(
+            "getContext", new Class[] {String.class})
+            .invoke(theServer, new Object[] {contextURL.getPath()});
+
+        return context;
+    }
+    
+    /**
+     * Adds the Cactus Servlet redirector configuration
+     * 
+     * @param theContext the Jetty context under which to add the configuration
+     * @param theConfiguration the Cactus Servlet configuration
+     * 
+     * @exception Exception if an error happens during initialization
+     */
+    private void addServletRedirector(Object theContext,
+        ServletConfiguration theConfiguration) throws Exception
+    {
+        theContext.getClass().getMethod("addServlet", 
+            new Class[] {String.class, String.class, String.class})
+            .invoke(theContext, 
+            new Object[] {theConfiguration.getDefaultRedirectorName(),
+            "/" + theConfiguration.getDefaultRedirectorName(), 
+            ServletTestRedirector.class.getName()});
+    }
+    
+    /**
+     * Adds the Cactus Jsp redirector configuration. We only add it if the
+     * CACTUS_JETTY_RESOURCE_DIR_PROPERTY has been provided by the user. This
+     * is because JSPs need to be attached to a WebApplicationHandler in Jetty.
+     * 
+     * @param theContext the Jetty context under which to add the configuration
+     * 
+     * @exception Exception if an error happens during initialization
+     */
+    private void addJspRedirector(Object theContext) throws Exception
+    {
+        if (getResourceDir() != null)
+        {
+            theContext.getClass().getMethod("addServlet", 
+                new Class[] {String.class, String.class})
+                .invoke(theContext, 
+                new Object[] {"*.jsp", 
+                "org.apache.jasper.servlet.JspServlet"});
+
+            // Get the WebApplicationHandler object in order to be able to 
+            // call the addServlet() method that accpets a forced path.
+            Object handler = theContext.getClass().getMethod(
+                "getWebApplicationHandler", 
+                new Class[] {}).invoke(theContext, new Object[] {});
+
+            handler.getClass().getMethod("addServlet", 
+                new Class[] {String.class, String.class, String.class, 
+                    String.class})
+                .invoke(handler, 
+                new Object[] {
+                    "JspRedirector",
+                    "/JspRedirector",
+                    "org.apache.jasper.servlet.JspServlet",
+                    "/jspRedirector.jsp"});
+        }
+    }
+
+    /**
+     * Adds the Cactus Filter redirector configuration. We only add it if the
+     * CACTUS_JETTY_RESOURCE_DIR_PROPERTY has been provided by the user. This
+     * is because Filters need to be attached to a WebApplicationHandler in 
+     * Jetty.
+     * 
+     * @param theContext the Jetty context under which to add the configuration
+     * @param theConfiguration the Cactus Filter configuration
+     * 
+     * @exception Exception if an error happens during initialization
+     */
+    private void addFilterRedirector(Object theContext,
+        FilterConfiguration theConfiguration) throws Exception
+    {
+        if (getResourceDir() != null)
+        {
+            // Get the WebApplicationHandler object in order to be able to add
+            // the Cactus Filter redirector
+            Object handler = theContext.getClass().getMethod(
+                "getWebApplicationHandler", 
+                new Class[] {}).invoke(theContext, new Object[] {});
+    
+            Object filterHolder = handler.getClass().getMethod("defineFilter",
+                new Class[] {String.class, String.class})
+                .invoke(handler, 
+                new Object[] {theConfiguration.getDefaultRedirectorName(),
+                FilterTestRedirector.class.getName()});        
+            
+            filterHolder.getClass().getMethod("addAppliesTo",
+                new Class[] {String.class})
+                .invoke(filterHolder, new Object[] {"REQUEST"});        
+    
+            // Map the Cactus Filter redirector to a path
+            handler.getClass().getMethod("mapPathToFilter", 
+                new Class[] {String.class, String.class})
+                .invoke(handler, 
+                new Object[] {"/" 
+                + theConfiguration.getDefaultRedirectorName(),
+                theConfiguration.getDefaultRedirectorName()});
+        }
+    }
+
+    /**
+     * Tests whether we are able to connect to the HTTP server identified by 
the
+     * specified URL.
+     * 
+     * @param theUrl The URL to check
+     * @return the HTTP response code or -1 if no connection could be 
+     *         established
+     */
+    protected int testConnectivity(URL theUrl)
+    {
+        int code;
+        try
+        {
+            HttpURLConnection connection = 
+                (HttpURLConnection) theUrl.openConnection();
+            connection.setRequestProperty("Connection", "close");
+            connection.connect();
+            readFully(connection);
+            connection.disconnect();
+            code = connection.getResponseCode();
+        }
+        catch (IOException e)
+        {
+            code = -1;
+        }
+        return code;
+    }
+
+    /**
+     * Tests whether an HTTP return code corresponds to a valid connection
+     * to the test URL or not. Success is 200 up to but excluding 300.
+     * 
+     * @param theCode the HTTP response code to verify
+     * @return <code>true</code> if the test URL could be called without error,
+     *         <code>false</code> otherwise
+     */
+    protected boolean isAvailable(int theCode)
+    {
+        boolean result;
+        if ((theCode != -1) && (theCode < 300)) 
+        {
+            result = true;            
+        }
+        else
+        {
+            result = false;
+        }
+        return result;
+    }
+
+    /**
+     * Fully reads the input stream from the passed HTTP URL connection to
+     * prevent (harmless) server-side exception.
+     *
+     * @param theConnection the HTTP URL connection to read from
+     * @exception IOException if an error happens during the read
+     */
+    protected void readFully(HttpURLConnection theConnection)
+        throws IOException
+    {
+        // Only read if there is data to read ... The problem is that not
+        // all servers return a content-length header. If there is no header
+        // getContentLength() returns -1. It seems to work and it seems
+        // that all servers that return no content-length header also do
+        // not block on read() operations!
+        if (theConnection.getContentLength() != 0)
+        {
+            byte[] buf = new byte[256];
+            InputStream in = theConnection.getInputStream();
+            while (in.read(buf) != -1)
+            {
+                // Make sure we read all the data in the stream
+            }
+        }
+    }
+
+    /**
+     * @return true if the server is running or false otherwise
+     */
+    protected boolean isRunning()
+    {
+        return this.isRunning;
+    }
+}



---------------------------------------------------------------------
To unsubscribe, e-mail: [EMAIL PROTECTED]
For additional commands, e-mail: [EMAIL PROTECTED]

Reply via email to