Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MethodBindingProcessor.java ------------------------------------------------------------------------------ svn:eol-style = native
Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/MethodBindingProcessor.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java?rev=365442&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java (added) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java Mon Jan 2 13:29:19 2006 @@ -0,0 +1,112 @@ +/* + * Copyright 2005-2006 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.shale.remoting.impl; + +import java.lang.reflect.Method; +import java.net.URL; +import java.util.Date; +import java.util.ResourceBundle; +import javax.faces.FacesException; +import javax.faces.context.FacesContext; +import org.apache.commons.logging.Log; +import org.apache.commons.logging.LogFactory; +import org.apache.shale.remoting.Processor; + +/** + * <p>Implementation of [EMAIL PROTECTED] Processor} which serves resources from the + * web application's static resources. View identifiers shoud be a fully + * qualified path, beginning with a slash ("/") character (for example, + * <code>/org/apache/shale/remoting/MyResource.css</code>).</p> + */ +public class WebResourceProcessor extends AbstractResourceProcessor { + + + // ------------------------------------------------------------ Constructors + + + // ------------------------------------------------------ Instance Variables + + + /** + * <p><code>ResourceBundle</code> containing our localized messages.</p> + */ + private ResourceBundle bundle = ResourceBundle.getBundle("org.apache.shale.remoting.Bundle"); + + + /** + * <p>Log instance for this class.</p> + */ + private static Log log = LogFactory.getLog(WebResourceProcessor.class); + + + // -------------------------------------------------------------- Properties + + + + // -------------------------------------------------------- Abstract Methods + + + /** [EMAIL PROTECTED] */ + protected URL getResourceURL(FacesContext context, String resourceId) { + + // Disallow access to resources in reserved directories + String resourceIdUpper = resourceId.toUpperCase(); + if (resourceIdUpper.startsWith("/WEB-INF") || resourceIdUpper.startsWith("/META-INF")) { + if (log.isWarnEnabled()) { + log.warn(bundle.getString("resource.refuse")); + log.warn(resourceId); + } + return null; + } + + // Disallow access to JSP and JSP fragment sources + if(resourceIdUpper.endsWith(".JSP") || resourceIdUpper.endsWith(".JSPF")) { + if (log.isWarnEnabled()) { + log.warn(bundle.getString("resource.refuse")); + log.warn(resourceId); + } + return null; + } + + // Call getResource() on the ServletContext or PortletContext instance + Object ctxt = context.getExternalContext().getContext(); + try { + Method method = + ctxt.getClass().getMethod("getResource", + new Class[] { String.class }); + URL url = (URL) method.invoke(ctxt, new Object[] { resourceId }); + if (log.isDebugEnabled()) { + log.debug("getResource(" + resourceId + ") --> " + url); + } + return url; + } catch (Exception e) { + if (log.isErrorEnabled()) { + log.error(bundle.getString("resource.exception"), e); + log.error(resourceId); + } + return null; + } + + } + + + + // --------------------------------------------------------- Private Methods + + + +} Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/WebResourceProcessor.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/package.html URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/package.html?rev=365442&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/package.html (added) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/package.html Mon Jan 2 13:29:19 2006 @@ -0,0 +1,24 @@ +<!-- + * Copyright 2004-2005 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. +--> + +<!-- $Id$ --> + +<body> + +<p>This package contains default implementations for the APIs specified +by Shale remoting support.</p> + +</body> Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/package.html ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/impl/package.html ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/package.html URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/package.html?rev=365442&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/package.html (added) +++ struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/package.html Mon Jan 2 13:29:19 2006 @@ -0,0 +1,232 @@ +<!-- + * Copyright 2004-2005 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. +--> + +<!-- $Id$ --> + +<body> + +<p>This package contains interfaces and APIs to support access for remoting +(client side components that need to perform server side activities and/or +retrieve information on a background thread. A JavaServer Faces +<code>PhaseListener</code> is used to gain control at the end of the +<em>Restore View</em> phase of the request processing lifecycle, and +determine whether or not the <em>view identifier</em> of the requested +view now matches one of a set of configured patterns. When a match is +detected, control of this request is handed over to a configured +<a href="Processor.html">Processor</a> instance, which can take complete +responsibility for creating the response for this request (and then +calling <code>FacesContext.responseComplete()</code> to tell JSF this has been +done).</p> + +<h3>Concrete Processor Implementations</h3> + +<p>You can define your own <a href="Processor.html">Processor</a> implementations, +and map them to requests as shown below. Alternatively, the following concrete +implementations are available out of the box for your use:</p> +<ul> + <li><a href="impl/ClassResourceProcessor.html">ClassResourceProcessor</a> - + Serves static resources (such as stylesheets or JavaScript source files) + from the web application classpath. In other words, resource files can + be packaged inside a JAR file that is included in the + <code>/WEB-INF/lib</code> directory, without requiring the developer to + modify their build script to extract the resources into the webapp root + directory or a specially configured subdirectory.</li> + <li><a href="impl/WebResourceProcessor.html">WebResourceProcessor</a> - + Serves static resources (such as stylesheets or JavaScript source files) + from the web application's document root. This is essentially the same + as allowing your servlet container's default file-serving servlet to + serve the resources, but allows operation in scenarios where such a + file serving servlet has not been configured.</li> + <li><a href="impl/MethodBindingProcessor.html">MethodBindingProcessor</a> - + Translates the resource portion of the URL (see examples below) into a + JavaServer Faces <em>method binding expression</em> that points at a + public method taking no parameters, and then executes a call to this + method. As a result of evaluating the method binding, it is possible + that the JavaServer Faces <em>managed beans</em> facility will be + invoked to create and configure the bean containing the called method.</li> +</ul> + +<p>The static resource serving <a href="Processor.html">Processor</a> implentations +share the following features:</p> +<ul> + <li>Disallow access to prohibited resources (web application resources + under <code>/WEB-INF</code> or <code>/META-INF</code>, Java class + files, and JSP source files).</li> + <li>Set the content type of the response based on the extension portion + of the resource identifier. This is done by first consulting the + servlet or portlet container (which supports mapping of extensions + to content types with a <code><mime-type></code> element in the + web application deployment descriptor), and then by consulting a + fallback list of mappings for common types of resources.</li> + <li>Support browser caching of static content, by watching for incoming + requests that contain an <code>If-Modified-Since</code> HTTP header, + and returning an HTTP "not modified" (304) response if this application + has not been restarted since the resource was served to this client.</li> + <li>Requests for non-existent resources will return a standard HTTP + "not found" (404) response.</li> + <li>Standard servlet access control features may be applied to the served + resources, by using standard security constraint elements in the web + application deployment descriptor.</li> +</ul> + +<p>The dynamic logic invoked by <a href="impl/MethodBindingProcessor.html"> +MethodBindingProcessor</a> can use any technique it desires to create the +content for this response. Available options (starting with the one that is +recommended best practice) include:</p> +<ul> + <li>For a text response, acquire a <code>ResponseWriter</code> instance + and use its methods, just as a <code>Renderer</code> would: + <blockquote><pre> +FacesContext context = FacesContext.getCurrentInstance(); +ResponseWriter writer = (new ResponseFactory()).createResponseWriter(context, "text/xml"); +writer.startDocument(); +... +writer.endDocument(); +writer.close(); + </pre></blockquote></li> + <li>For a binary response, acquire a <code>ResponseStream</code> instance + and use its methods: + <blockquote><pre> +FacesContext context = FacesContext.getCurrentInstance(); +ResponseStream stream = (new ResponseFactory()).createResponseStream(context, "image/gif"); +stream.write(...); +... +stream.close(); + </pre></blockquote></l> + <li>Stash relevant model data (typically in request scope), and then forward + to a JSP page (with either JSF components or JSTL tags coupled with EL + expressions) that will produce the actual response: + <blockquote><pre> +FacesContext context = FacesContext.getCurrentInstance(); +context.getExternalContext().dispatch("/results.jsp"); + </pre></blockquote></li> + <li>Access the response object from the external context, and acquire a + writer or stream from the response object directly. Note that this + technique, unlike those illustrated above, requires you to program + specfically to either the servlet or portlet response APIs.</li> +</ul> + +<p>Before returning, the dynamic logic should call <code>responseComplete()</code> +on the <code>FacesContext</code> instance for the current request, to bypass +the remaining phases of the JavaServer Faces request processing lifecycle.</p> + +<h3>Configuring Remoting Support</h3> + +<p>In order for a request URI to be processed at all by the Shale remoting +facilities, it must match the mapping for <code>FacesServlet</code> that is +already defined in <code>/WEB-INF/web.xml</code>. Once that occurs, JSF will +have constructed a corresponding view identifier that can be retrieved by +calling: +<blockquote><pre> +String viewId = FacesContext.getCurrentInstance().getViewRoot().getViewId(); +</pre></blockquote></p> + +<p>This view identifier is then compared to a set of matching patterns which, +like servlet mappings, can be either prefix matched (<code>/foo/*</code>) or +extension matched (<code>*.foo</code>). If the view identifier matches, it will +be transformed into a corresponding <em>resource identifier</em> that is passed +to the <code>process()</code> method of an appropriate <a href="Processor.html"> +Processor</a> instance.</p> + +<p>Mapping of view identifiers to resources is configured by specifying comma +delimited lists of "pattern:classname" pairs for the following context initialization +parameters in <code>/WEB-INF/web.xml</code>. For each of these parameters that +is not specified, the value illustrated here will be the default (<strong>WARNING</strong> +- this decision may be changed later, so double check the latest documentation): +<blockquote><pre> +<context-param> + <param-name> + org.apache.shale.remoting.CLASS_RESOURCES + </param-name> + <param-value> + /static/*:org.apache.shale.remoting.impl.ClassResourceProcessor + </param-value> +</context-param> + +<context-param> + <param-name> + org.apache.shale.remoting.DYNAMIC_RESOURCES + </param-name> + <param-value> + /dynamic/*:org.apache.shale.remoting.impl.MethodBindingProcessor + </param-value> +</context-param> + +<context-param> + <param-name> + org.apache.shale.remoting.WEBAPP_RESOURCES + </param-name> + <param-value> + /webapp/*:org.apache.shale.remoting.impl.WebResourceProcessor + </param-value> +</context-param> +</pre></blockquote></p> + +<p>In addition, you can configure the following additional context initialization +parameters:</p> +<ul> + <li><code>org.apache.shale.remoting.OTHER_RESOURCES</code> - configuration + pairs as described above, which will be marked with the "other" + mechanism type. No default value.</li> + <li><code>org.apache.shale.remoting.MAPPING_CLASS</code> - fully qualified + classname of class used to create <a href="Mapping.html">Mapping</a> + instances to record configuration information. If not specified, the + <a href="impl/MappingImpl.html">MappingImpl</a> class will be used.</li> + <li><code>org.apache.shale.remoting.MAPPINGS_CLASS</code> - fully qualified + classname of class used to create a <a href="Mappings.html">Mappings</a> + instance to record configuration information. If not specified, the + <a href="impl/MappingsImpl.html">MappingsImpl</a> class will be used.</li> +</ul> + +<p>Once the configuration information has been processed (which will occur on +the first JSF request after the application has started), an instance of +<a href="Mappings.html">Mappings</a> will be stored as an application scope +parameter under the key identified by <code>Globals.MAPPINGS_ATTR</code> which +contains all of the configuration information being used. This instance might +be useful, for example, to a JSF component that wishes to dynamically determine +the appropriate mapping for class resources.</p> + + +<h3>Example URLs</h3> + +<p>Given the default resource mappings described above, and assuming that the +developer has mapped <code>FacesServlet</code> to the <code>*.faces</code> +pattern, the following request URLs will be mapped to appropriate resources +and processed as follows:</p> + +<p><code>http://localhost:8080/myapp/dynamic/foo/bar.faces</code> +<blockquote> + Constructs a method binding expression <code>#{foo.bar}</code> and then + executes it. This will cause the bean instance at attribute name <code>foo</code> + to be located (or created, if necessary), and then the public <code>bar()</code> + method, which takes no parameters, will be called on that instance. +</blockquote></p> + +<p><code>http://localhost:8080/myapp/static/mycompany/mypackage/MyScript.js.faces</code> +<blockquote> + Locates and serves a static resource (<code>/mycompany/mypackage/MyScript.js</code>) + from the web application class loader, which will therefore locate such resources + in the <code>/WEB-INF/classes</code> directory, or packaged in a JAR file in the + <code>/WEB-INF/lib</code> directory.</p> +</blockquote></p> + +<p><code>http://localhost:8080/myapp/webapp/resources/MyScript.js.faces</code> +<blockquote> + Locates and serves a static resource (<code>/resources/MyScript.js</code>) + from the document root of the web application.</p> +</blockquote></p> + +</body> Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/package.html ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/java/org/apache/shale/remoting/package.html ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/ClassResourceProcessorTestCase.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/ClassResourceProcessorTestCase.java?rev=365442&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/ClassResourceProcessorTestCase.java (added) +++ struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/ClassResourceProcessorTestCase.java Mon Jan 2 13:29:19 2006 @@ -0,0 +1,163 @@ +/* + * Copyright 2005-2006 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.shale.remoting.impl; + +import javax.faces.el.EvaluationException; +import javax.faces.el.MethodNotFoundException; +import javax.servlet.http.HttpServletResponse; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.apache.shale.test.base.AbstractJsfTestCase; +import org.apache.shale.test.mock.MockServletOutputStream; + +/** + * <p>Test case for <code>org.apache.shale.remoting.impl.ClassResourceProcessor</code>.</p> + */ +public class ClassResourceProcessorTestCase extends AbstractJsfTestCase { + + + // ------------------------------------------------------------ Constructors + + + // Construct a new instance of this test case. + public ClassResourceProcessorTestCase(String name) { + super(name); + } + + + // ------------------------------------------------------ Manifest Constants + + + private static final String INVALID_RESOURCE_ID = + "/org/apache/shale/remoting/impl/MissingData.text"; + + private static final String VALID_RESOURCE_ID = + "/org/apache/shale/remoting/impl/TestData.text"; + + private static final String VALID_RESOURCE_CONTENT = + "This is a test. It is only a test."; // Not including line delimiters! + + // ----------------------------------------------------------- Setup Methods + + + // Set up instance variables for this test case. + public void setUp() { + + threadClassLoader = Thread.currentThread().getContextClassLoader(); + Thread.currentThread().setContextClassLoader(this.getClass().getClassLoader()); + super.setUp(); + servletContext.addMimeType("text", "text/x-plain"); + processor = new ClassResourceProcessor(); + + } + + + // Return the tests included in this test case. + public static Test suite() { + + return (new TestSuite(ClassResourceProcessorTestCase.class)); + + } + + + // Tear down instance variables for this test case. + public void tearDown() { + + processor = null; + super.tearDown(); + Thread.currentThread().setContextClassLoader(threadClassLoader); + + } + + + // ------------------------------------------------------ Instance Variables + + + // The Processor instance to be tested + private ClassResourceProcessor processor = null; + + + // The cached thread context class loader + private ClassLoader threadClassLoader = null; + + + // ------------------------------------------------------------ Test Methods + + + // Test an invalid resource + public void testInvalidResource() throws Exception { + + processor.process(facesContext, INVALID_RESOURCE_ID); + assertEquals(HttpServletResponse.SC_NOT_FOUND, response.getStatus()); + assertEquals(INVALID_RESOURCE_ID, response.getMessage()); + + } + + + // Test mapping of resource identifiers to URLs + public void testMapping() { + + assertNotNull(processor.getResourceURL(facesContext, VALID_RESOURCE_ID)); + assertNull(processor.getResourceURL(facesContext, INVALID_RESOURCE_ID)); + + } + + + // Test a pristine instance of the Processor to be tested + public void testPristine() { + + assertNotNull(processor); + assertEquals("text/x-plain", servletContext.getMimeType(VALID_RESOURCE_ID)); + assertEquals("text/x-plain", servletContext.getMimeType(INVALID_RESOURCE_ID)); + + } + + + // Test a valid resource that has not been modified + public void testNotModifiedResource() throws Exception { + + long timestamp = processor.getLastModified(); + request.addDateHeader("If-Modified-Since", timestamp); + processor.process(facesContext, VALID_RESOURCE_ID); + assertEquals(HttpServletResponse.SC_NOT_MODIFIED, response.getStatus()); + + } + + + // Test a valid resource + public void testValidResource() throws Exception { + + processor.process(facesContext, VALID_RESOURCE_ID); + assertEquals("text/x-plain", response.getContentType()); + MockServletOutputStream stream = + (MockServletOutputStream) response.getOutputStream(); + assertNotNull(stream); + assertTrue(stream.size() > VALID_RESOURCE_CONTENT.length()); + byte content[] = stream.content(); + for (int i = 0; i < VALID_RESOURCE_CONTENT.length(); i++) { + byte b = (byte) ((int) VALID_RESOURCE_CONTENT.charAt(i)); + assertEquals("Byte at position " + i, b, content[i]); + } + + + } + + // --------------------------------------------------------- Support Methods + + + +} Propchange: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/ClassResourceProcessorTestCase.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/ClassResourceProcessorTestCase.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java?rev=365442&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java (added) +++ struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java Mon Jan 2 13:29:19 2006 @@ -0,0 +1,192 @@ +/* + * Copyright 2005-2006 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.shale.remoting.impl; + +import javax.faces.component.UIViewRoot; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.apache.shale.test.base.AbstractJsfTestCase; + +/** + * <p>Test case for <code>org.apache.shale.remoting.impl.MappingImpl</code>.</p> + */ +public class MappingImplTestCase extends AbstractJsfTestCase { + + + // ------------------------------------------------------------ Constructors + + + // Construct a new instance of this test case. + public MappingImplTestCase(String name) { + super(name); + } + + + // ----------------------------------------------------------- Setup Methods + + + // Set up instance variables for this test case. + public void setUp() { + + super.setUp(); + facesContext.setViewRoot(new UIViewRoot()); + mapping = new MappingImpl(); + + } + + + // Return the tests included in this test case. + public static Test suite() { + + return (new TestSuite(MappingImplTestCase.class)); + + } + + + // Tear down instance variables for this test case. + public void tearDown() { + + mapping = null; + super.tearDown(); + + } + + + // ------------------------------------------------------ Instance Variables + + + // The instance to be tested + private MappingImpl mapping = null; + + + // ------------------------------------------------------------ Test Methods + + + // Test extension mapping + public void testExtension() { + + mapping.setPattern("*.foo"); + facesContext.getViewRoot().setViewId("foo"); + assertNull(mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId(".foo"); + assertNull(mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/foo"); + assertNull(mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/foo.bar"); + assertNull(mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/bar.foo"); + assertEquals("/bar", mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/bar/baz.foo"); + assertEquals("/bar/baz", mapping.mapViewId(facesContext)); + + } + + + // Test invalid patterns + public void testInvalid() { + + try { + mapping.setPattern(""); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + ; // Expected result + } + + try { + mapping.setPattern("foo"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + ; // Expected result + } + + try { + mapping.setPattern("/foo"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + ; // Expected result + } + + try { + mapping.setPattern("/foo/"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + ; // Expected result + } + + try { + mapping.setPattern(".foo"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + ; // Expected result + } + + try { + mapping.setPattern("*.foo."); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + ; // Expected result + } + + try { + mapping.setPattern("*.foo.bar"); + fail("Should have thrown IllegalArgumentException"); + } catch (IllegalArgumentException e) { + ; // Expected result + } + + } + + + // Test prefix mapping + public void testPrefix() { + + mapping.setPattern("/foo/*"); + facesContext.getViewRoot().setViewId("foo"); + assertNull(mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/bar"); + assertNull(mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/bar/foo"); + assertNull(mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/foo"); + assertNull(mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/foo/bar"); + assertEquals("/bar", mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/foo/bar.baz"); + assertEquals("/bar.baz", mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/foo/bar/baz"); + assertEquals("/bar/baz", mapping.mapViewId(facesContext)); + facesContext.getViewRoot().setViewId("/foo/bar/baz.bop"); + assertEquals("/bar/baz.bop", mapping.mapViewId(facesContext)); + + } + + + // Test a pristine instance + public void testPristine() { + + assertNull(mapping.getMechanism()); + assertNull(mapping.getPattern()); + assertNull(mapping.getProcessor()); + + } + + + // --------------------------------------------------------- Support Methods + + + +} Propchange: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MappingImplTestCase.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorBusinessObject.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorBusinessObject.java?rev=365442&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorBusinessObject.java (added) +++ struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorBusinessObject.java Mon Jan 2 13:29:19 2006 @@ -0,0 +1,116 @@ +/* + * Copyright 2005-2006 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.shale.remoting.impl; + +import java.io.IOException; +import java.io.PrintWriter; +import javax.faces.FacesException; +import javax.faces.context.FacesContext; +import javax.faces.context.ResponseStream; +import javax.faces.context.ResponseWriter; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServletResponse; +import org.apache.shale.remoting.faces.ResponseFactory; + +/** + * <p>Business object that includes methods to produce output, when + * invoked indirectly via <code>MethodBindingProcessor</code>.</p> + */ +public class MethodBindingProcessorBusinessObject { + + + /** + * <p>Create binary output directy to the servlet response.</p> + */ + public void directStream() { + + try { + FacesContext context = FacesContext.getCurrentInstance(); + HttpServletResponse response = (HttpServletResponse) + context.getExternalContext().getResponse(); + response.setContentType("application/x-binary"); + ServletOutputStream stream = response.getOutputStream(); + for (int i = 0; i < 10; i++) { + stream.write(i); + } + } catch (IOException e) { + throw new FacesException(e); + } + + } + + + /** + * <p>Create character output directly to the servlet response.</p> + */ + public void directWriter() { + + try { + FacesContext context = FacesContext.getCurrentInstance(); + HttpServletResponse response = (HttpServletResponse) + context.getExternalContext().getResponse(); + response.setContentType("text/x-plain"); + PrintWriter writer = response.getWriter(); + for (int i = 0; i < 10; i++) { + writer.write('a' + i); + } + } catch (IOException e) { + throw new FacesException(e); + } + + } + + + /** + * <p>Create binary output indirecty via a factory.</p> + */ + public void indirectStream() { + + try { + FacesContext context = FacesContext.getCurrentInstance(); + ResponseStream stream = + (new ResponseFactory()).getResponseStream(context, "application/x-binary"); + for (int i = 0; i < 10; i++) { + stream.write(i); + } + } catch (IOException e) { + throw new FacesException(e); + } + + } + + + /** + * <p>Create character output indirectly a factory.</p> + */ + public void indirectWriter() { + + try { + FacesContext context = FacesContext.getCurrentInstance(); + ResponseWriter writer = + (new ResponseFactory()).getResponseWriter(context, "text/x-plain"); + for (int i = 0; i < 10; i++) { + writer.write('a' + i); + } + } catch (IOException e) { + throw new FacesException(e); + } + + } + + +} Propchange: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorBusinessObject.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorBusinessObject.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorTestCase.java URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorTestCase.java?rev=365442&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorTestCase.java (added) +++ struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorTestCase.java Mon Jan 2 13:29:19 2006 @@ -0,0 +1,208 @@ +/* + * Copyright 2005-2006 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.shale.remoting.impl; + +import javax.faces.el.EvaluationException; +import javax.faces.el.MethodNotFoundException; +import junit.framework.Test; +import junit.framework.TestSuite; +import org.apache.shale.test.base.AbstractJsfTestCase; +import org.apache.shale.test.mock.MockPrintWriter; +import org.apache.shale.test.mock.MockServletOutputStream; + +/** + * <p>Test case for <code>org.apache.shale.remoting.impl.MethodBindingProcessor</code>.</p> + */ +public class MethodBindingProcessorTestCase extends AbstractJsfTestCase { + + + // ------------------------------------------------------------ Constructors + + + // Construct a new instance of this test case. + public MethodBindingProcessorTestCase(String name) { + super(name); + } + + + // ----------------------------------------------------------- Setup Methods + + + // Set up instance variables for this test case. + public void setUp() { + + super.setUp(); + processor = new MethodBindingProcessor(); + servletContext.setAttribute("business", new MethodBindingProcessorBusinessObject()); + + } + + + // Return the tests included in this test case. + public static Test suite() { + + return (new TestSuite(MethodBindingProcessorTestCase.class)); + + } + + + // Tear down instance variables for this test case. + public void tearDown() { + + servletContext.removeAttribute("business"); + processor = null; + super.tearDown(); + + } + + + // ------------------------------------------------------ Instance Variables + + + // The Processor instance to be tested + private MethodBindingProcessor processor = null; + + + // ------------------------------------------------------------ Test Methods + + + // Test attempt to execute an expression with an invalid bean name + public void testInvalidBean() throws Exception { + + try { + processor.process(facesContext, "/invalid/directStream"); + fail("Should have thrown EvaluationException"); + } catch (EvaluationException e) { + ; // Expected result + } + + } + + + // Test attempt to execute an expression with an invalid method name + public void testInvalidMethod() throws Exception { + + try { + processor.process(facesContext, "/business/invalidMethod"); + fail("Should have thrown MethodNotFoundException"); + } catch (MethodNotFoundException e) { + ; // Expected result + } + + } + + + // Test mapping of resource identifiers to expressions + public void testMapping() { + + assertEquals("#{business.directStream}", + processor.mapResourceId(facesContext, "/business/directStream").getExpressionString()); + assertEquals("#{business.directWriter}", + processor.mapResourceId(facesContext, "/business/directWriter").getExpressionString()); + assertEquals("#{business.indirectStream}", + processor.mapResourceId(facesContext, "/business/indirectStream").getExpressionString()); + assertEquals("#{business.indirectWriter}", + processor.mapResourceId(facesContext, "/business/indirectWriter").getExpressionString()); + + } + + + // Test a pristine instance of the Processor to be tested + public void testPristine() { + + assertNotNull(processor); + assertNotNull(servletContext.getAttribute("business")); + assertNull(servletContext.getAttribute("invalid")); + + } + + + // Test output sent directly to the servlet response ServletOutputStream + public void testDirectStream() throws Exception { + + processor.process(facesContext, "/business/directStream"); + assertEquals("application/x-binary", response.getContentType()); + MockServletOutputStream stream = + (MockServletOutputStream) response.getOutputStream(); + assertNotNull(stream); + assertEquals(10, stream.size()); + byte content[] = stream.content(); + for (int i = 0; i < 10; i++) { + assertEquals("Byte at position " + i, (byte) i, content[i]); + } + assertTrue(facesContext.getResponseComplete()); + + } + + + // Test output sent directly to the servlet response PrintWriter + public void testDirectWriter() throws Exception { + + processor.process(facesContext, "/business/directWriter"); + assertEquals("text/x-plain", response.getContentType()); + MockPrintWriter writer = (MockPrintWriter) response.getWriter(); + assertNotNull(writer); + assertEquals(10, writer.size()); + char content[] = writer.content(); + for (int i = 0; i < 10; i++) { + assertEquals("Character at position " + i, (char) ('a' + i), content[i]); + } + assertTrue(facesContext.getResponseComplete()); + + } + + + // Test output sent indirectly to the servlet or portlet response stream + public void testIndirectStream() throws Exception { + + processor.process(facesContext, "/business/indirectStream"); + assertEquals("application/x-binary", response.getContentType()); + MockServletOutputStream stream = + (MockServletOutputStream) response.getOutputStream(); + assertNotNull(stream); + assertEquals(10, stream.size()); + byte content[] = stream.content(); + for (int i = 0; i < 10; i++) { + assertEquals("Byte at position " + i, (byte) i, content[i]); + } + assertTrue(facesContext.getResponseComplete()); + + } + + + // Test output sent indirectly to the servlet or portlet response writer + public void testIndirectWriter() throws Exception { + + processor.process(facesContext, "/business/indirectWriter"); + assertEquals("text/x-plain", response.getContentType()); + MockPrintWriter writer = (MockPrintWriter) response.getWriter(); + assertNotNull(writer); + assertEquals(10, writer.size()); + char content[] = writer.content(); + for (int i = 0; i < 10; i++) { + assertEquals("Character at position " + i, (char) ('a' + i), content[i]); + } + assertTrue(facesContext.getResponseComplete()); + + } + + + // --------------------------------------------------------- Support Methods + + + +} Propchange: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorTestCase.java ------------------------------------------------------------------------------ svn:eol-style = native Propchange: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/MethodBindingProcessorTestCase.java ------------------------------------------------------------------------------ svn:keywords = Date Author Id Revision HeadURL Added: struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/TestData.text URL: http://svn.apache.org/viewcvs/struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/TestData.text?rev=365442&view=auto ============================================================================== --- struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/TestData.text (added) +++ struts/shale/trunk/core-library/src/test/org/apache/shale/remoting/impl/TestData.text Mon Jan 2 13:29:19 2006 @@ -0,0 +1 @@ +This is a test. It is only a test. --------------------------------------------------------------------- To unsubscribe, e-mail: [EMAIL PROTECTED] For additional commands, e-mail: [EMAIL PROTECTED]