You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@struts.apache.org by tm...@apache.org on 2006/11/05 15:22:55 UTC

svn commit: r471435 - in /struts/struts2/trunk/core/src: main/java/org/apache/struts2/ main/java/org/apache/struts2/config/ main/java/org/apache/struts2/dispatcher/mapper/ main/resources/org/apache/struts2/ test/java/org/apache/struts2/dispatcher/mapper/

Author: tmjee
Date: Sun Nov  5 06:22:54 2006
New Revision: 471435

URL: http://svn.apache.org/viewvc?view=rev&rev=471435
Log:
WW-1490
 - Have a composite ActionMapper that decides which ActionMapper it contains should be used


Added:
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapper.java
    struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapperTest.java
Modified:
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java
    struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Settings.java
    struts/struts2/trunk/core/src/main/resources/org/apache/struts2/default.properties

Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java?view=diff&rev=471435&r1=471434&r2=471435
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java (original)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/StrutsConstants.java Sun Nov  5 06:22:54 2006
@@ -17,6 +17,8 @@
  */
 package org.apache.struts2;
 
+import org.apache.struts2.dispatcher.mapper.CompositeActionMapper;
+
 /**
  * This class provides a central location for framework configuration keys
  * used to retrieve and store Struts configuration settings.
@@ -134,4 +136,6 @@
     /** Whether slashes in action names are allowed or not */ 
     public static final String STRUTS_ENABLE_SLASHES_IN_ACTION_NAMES = "struts.enable.SlashesInActionNames";
      
+    /** Prefix used by {@link CompositeActionMapper} to identified its containing {@link ActionMapper} class. */
+    public static final String STRUTS_MAPPER_COMPOSITE = "struts.mapper.composite.";
 }

Modified: struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Settings.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Settings.java?view=diff&rev=471435&r1=471434&r2=471435
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Settings.java (original)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/config/Settings.java Sun Nov  5 06:22:54 2006
@@ -135,7 +135,7 @@
 
         return val;
     }
-
+    
     /**
      * Returns an Iterator of all properties names.
      *

Added: struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapper.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapper.java?view=auto&rev=471435
==============================================================================
--- struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapper.java (added)
+++ struts/struts2/trunk/core/src/main/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapper.java Sun Nov  5 06:22:54 2006
@@ -0,0 +1,254 @@
+/*
+ * $Id: ActionMapper.java 449367 2006-09-24 06:49:04Z mrdon $
+ *
+ * Copyright 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.struts2.dispatcher.mapper;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.commons.logging.Log;
+import org.apache.commons.logging.LogFactory;
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+
+import com.opensymphony.xwork2.ObjectFactory;
+import com.opensymphony.xwork2.config.ConfigurationManager;
+import com.opensymphony.xwork2.util.FileManager;
+
+/**
+ * A composite action mapper that is capable of delegating to a series of {@link ActionMapper} if the former 
+ * failed to obtained a valid {@link ActionMapping} or uri.
+ * <p/>
+ * It is configured through struts.properties. 
+ * <p/>
+ * For example, with the following entries in struts.properties
+ * <pre>
+ * struts.mapper.class=org.apache.struts2.dispatcher.mapper.CompositeActionMapper
+ * struts.mapper.composite.1=org.apache.struts2.dispatcher.mapper.DefaultActionMapper
+ * struts.mapper.composite.2=org.apache.struts2.dispatcher.mapper.RestfulActionMapper
+ * struts.mapper.composite.3=org.apache.struts2.dispatcher.mapper.Restful2ActionMapper
+ * </pre>
+ * When {@link CompositeActionMapper#getMapping(HttpServletRequest, ConfigurationManager)} or 
+ * {@link CompositeActionMapper#getUriFromActionMapping(ActionMapping)} is invoked, 
+ * {@link CompositeActionMapper} would go through these {@link ActionMapper}s in sequence 
+ * starting from {@link ActionMapper} identified by 'struts.mapper.composite.1', followed by 
+ * 'struts.mapper.composite.2' and finally 'struts.mapper.composite.3' (in this case) until either
+ * one of the {@link ActionMapper} return a valid result (not null) or it runs out of {@link ActionMapper}
+ * in which case it will just return null for both 
+ * {@link CompositeActionMapper#getMapping(HttpServletRequest, ConfigurationManager)} and 
+ * {@link CompositeActionMapper#getUriFromActionMapping(ActionMapping)} methods.
+ * 
+ * <p/>
+ * 
+ * @see ActionMapper
+ * @see ActionMapperFactory
+ * @see ActionMapping
+ * @see IndividualActionMapperEntry
+ * 
+ * @version $Date$ $Id$
+ */
+public class CompositeActionMapper implements ActionMapper {
+
+	private static final Log LOG = LogFactory.getLog(CompositeActionMapper.class);
+	
+	protected List<IndividualActionMapperEntry> orderedActionMappers;
+	
+	
+	public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
+		
+		for (IndividualActionMapperEntry actionMapperEntry: getOrderedActionMapperEntries()) {
+			ActionMapping actionMapping = actionMapperEntry.actionMapper.getMapping(request, configManager);
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Using ActionMapper from entry ["+actionMapperEntry.propertyName+"="+actionMapperEntry.propertyValue+"]");
+			}
+			if (actionMapping == null) {
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("ActionMapper from entry ["+actionMapperEntry.propertyName+"="+actionMapperEntry.propertyValue+"] failed to return an ActionMapping (null)");
+				}
+			}
+			else {
+				return actionMapping;
+			}
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("exhausted from ActionMapper that could return an ActionMapping");
+		}
+		return null;
+	}
+
+	public String getUriFromActionMapping(ActionMapping mapping) {
+		
+		for (IndividualActionMapperEntry actionMapperEntry: getOrderedActionMapperEntries()) {
+			String uri = actionMapperEntry.actionMapper.getUriFromActionMapping(mapping);
+			if (LOG.isDebugEnabled()) {
+				LOG.debug("Using ActionMapper from entry ["+actionMapperEntry.propertyName+"="+actionMapperEntry.propertyValue+"]");
+			}
+			if (uri == null) {
+				if (LOG.isDebugEnabled()) {
+					LOG.debug("ActionMapper from entry ["+actionMapperEntry.propertyName+"="+actionMapperEntry.propertyValue+"] failed to return a uri (null)");
+				}
+			}
+			else {
+				return uri;
+			}
+		}
+		if (LOG.isDebugEnabled()) {
+			LOG.debug("exhausted from ActionMapper that could return a uri");
+		}
+		return null;
+	}
+	
+	
+	protected List<IndividualActionMapperEntry> getOrderedActionMapperEntries() {
+		if (this.orderedActionMappers == null || FileManager.isReloadingConfigs()) {
+			
+			List<IndividualActionMapperEntry> actionMapperEntriesContainer = new ArrayList<IndividualActionMapperEntry>();
+			Iterator settings = Settings.list();
+			while(settings.hasNext()) {
+				String setting = settings.next().toString();
+				if (setting.startsWith(StrutsConstants.STRUTS_MAPPER_COMPOSITE)) {
+					try {
+						int order = Integer.valueOf(setting.substring(StrutsConstants.STRUTS_MAPPER_COMPOSITE.length(), setting.length()));
+						String propertyValue = Settings.get(setting);
+						if (propertyValue != null && propertyValue.trim().length() > 0) {
+							actionMapperEntriesContainer.add(
+									new IndividualActionMapperEntry(order, setting, propertyValue));
+						}
+						else {
+							LOG.warn("Ignoring property "+setting+" that contains no value");
+						}
+					}
+					catch(NumberFormatException e) {
+						LOG.warn("Ignoring malformed property "+setting);
+					}
+				}
+			}
+		
+			Collections.sort(actionMapperEntriesContainer, new Comparator<IndividualActionMapperEntry>() {
+				public int compare(IndividualActionMapperEntry o1, IndividualActionMapperEntry o2) {
+					return o1.compareTo(o2);
+				}
+			});
+		
+		
+			ObjectFactory objectFactory = ObjectFactory.getObjectFactory();
+			List<IndividualActionMapperEntry> result = new ArrayList<IndividualActionMapperEntry>();
+			for (IndividualActionMapperEntry entry: actionMapperEntriesContainer) {
+				String actionMapperClassName = entry.propertyValue;
+				try {
+					// Let us get ClassCastException if it does not implement ActionMapper
+					ActionMapper actionMapper = (ActionMapper) objectFactory.buildBean(actionMapperClassName, null);
+					result.add(new IndividualActionMapperEntry(entry.order, entry.propertyName, entry.propertyValue, actionMapper));
+				}
+				catch(Exception e) {
+					LOG.warn("failed to create action mapper "+actionMapperClassName+", ignoring it", e);
+				}
+			}
+		
+			this.orderedActionMappers = result;
+		}
+		
+		return this.orderedActionMappers;
+	}
+	
+	
+	/**
+	 * A value object (holder) that holds information regarding {@link ActionMapper} this {@link CompositeActionMapper}
+	 * is capable of delegating to.
+	 * <p/>
+	 * The information stored are :-
+	 * <ul>
+	 * 	<li> order</li>
+	 * 	<li> propertyValue</li>
+	 * 	<li> propertyName</li>
+	 * 	<li> actionMapper</li>
+	 * </ul>
+	 * 
+	 * eg. if we have the following entry in struts.properties
+	 * <pre>
+	 * struts.mapper.composite.1=foo.bar.ActionMapper1
+	 * struts.mapper.composite.2=foo.bar.ActionMapper2
+	 * struts.mapper.composite.3=foo.bar.ActionMapper3
+	 * </pre>
+	 * 
+	 * <table border="1">
+	 * 	<tr>
+	 * 		<td>order</td>
+	 *    	<td>propertyName</td>
+	 *      <td>propertyValue</td>
+	 *      <td>actionMapper</td>
+	 *  </tr>
+	 *  <tr>
+	 *  	<td>1</td>
+	 *      <td>struts.mapper.composite.1</td>
+	 *      <td>foo.bar.ActionMapper1</td>
+	 *      <td>instance of foo.bar.ActionMapper1</td>
+	 *  </tr>
+	 *  <tr>
+	 *  	<td>2</td>
+	 *      <td>struts.mapper.composite.2</td>
+	 *      <td>foo.bar.ActionMapper2</td>
+	 *      <td>instance of foo.bar.ActionMapper2</td>
+	 *  </tr>
+	 *  <tr>
+	 *  	<td>3</td>
+	 *      <td>struts.mapper.composite.3</td>
+	 *      <td>foo.bar.ActionMapper3</td>
+	 *      <td>instance of foo.bar.ActionMapper3</td>
+	 *  </tr>
+	 * </table>
+	 * 
+	 * @version $Date$ $Id$
+	 */
+	public class IndividualActionMapperEntry implements Comparable<IndividualActionMapperEntry> {
+		
+		public Integer order;
+		public String propertyValue;
+		public String propertyName;
+		public ActionMapper actionMapper;
+		
+		
+		private IndividualActionMapperEntry(Integer order, String propertyName, String propertyValue) {
+			assert(order != null);
+			assert(propertyValue != null);
+			assert(propertyName != null);
+			this.order = order;
+			this.propertyValue = propertyValue;
+			this.propertyName = propertyName;
+		}
+		
+		public IndividualActionMapperEntry(Integer order, String propertyName, String propertyValue, ActionMapper actionMapper) {
+			assert(order != null);
+			assert(propertyValue != null);
+			assert(propertyName != null);
+			assert(actionMapper != null);
+			this.order = order;
+			this.propertyValue = propertyValue;
+			this.propertyName = propertyName;
+			this.actionMapper = actionMapper;
+		}
+
+		public int compareTo(IndividualActionMapperEntry o) {
+			return order - o.order;
+		}
+	}
+}

Modified: struts/struts2/trunk/core/src/main/resources/org/apache/struts2/default.properties
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/main/resources/org/apache/struts2/default.properties?view=diff&rev=471435&r1=471434&r2=471435
==============================================================================
--- struts/struts2/trunk/core/src/main/resources/org/apache/struts2/default.properties (original)
+++ struts/struts2/trunk/core/src/main/resources/org/apache/struts2/default.properties Sun Nov  5 06:22:54 2006
@@ -49,6 +49,15 @@
 
 ### How request URLs are mapped to and from actions
 struts.mapper.class=org.apache.struts2.dispatcher.mapper.DefaultActionMapper
+### The above line is to be commented and following are to be uncommented to 
+### enable CompositeActionMapper
+### - With CompositeActionMapper one could specified many ActionMapper instance, where
+###   each of them will be chosen according to the order. Lower order number has
+###   higher precedence
+#struts.mapper.class=org.apache.struts2.dispatcher.mapper.CompositeActionMapper
+#struts.mapper.composite.1=org.apache.struts2.dispatcher.mapper.DefaultActionMapper
+#struts.mapper.composite.2=foo.bar.MyActionMapper
+#struts.mapper.composite.3=foo.bar.MyAnotherActionMapper
 
 ### Used by the DefaultActionMapper
 ### You may provide a comma separated list, e.g. struts.action.extension=action,jnlp,do

Added: struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapperTest.java
URL: http://svn.apache.org/viewvc/struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapperTest.java?view=auto&rev=471435
==============================================================================
--- struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapperTest.java (added)
+++ struts/struts2/trunk/core/src/test/java/org/apache/struts2/dispatcher/mapper/CompositeActionMapperTest.java Sun Nov  5 06:22:54 2006
@@ -0,0 +1,347 @@
+/*
+ * $Id: ActionMapper.java 449367 2006-09-24 06:49:04Z mrdon $
+ *
+ * Copyright 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.struts2.dispatcher.mapper;
+
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+import java.util.List;
+import java.util.Map;
+
+import javax.servlet.http.HttpServletRequest;
+
+import org.apache.struts2.StrutsConstants;
+import org.apache.struts2.config.Settings;
+import org.apache.struts2.dispatcher.mapper.CompositeActionMapper.IndividualActionMapperEntry;
+import org.springframework.mock.web.MockHttpServletRequest;
+
+import com.opensymphony.xwork2.config.ConfigurationManager;
+
+import junit.framework.TestCase;
+
+/**
+ * 
+ * @version $Date$ $Id$
+ */
+public class CompositeActionMapperTest extends TestCase {
+	
+	/**
+	 * Test with empty settings (settings with no entries of interest)
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetOrderActionMapperEntries1() throws Exception {
+		CompositeActionMapper compositeActionMapper = new CompositeActionMapper();
+		List<IndividualActionMapperEntry> result = 
+			compositeActionMapper.getOrderedActionMapperEntries();
+		
+		assertEquals(result.size(), 0);
+	}
+	
+	/**
+	 * Test with a normal settings.
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetOrderActionMapperEntries2() throws Exception {
+		CompositeActionMapper compositeActionMapper = new CompositeActionMapper();
+		
+		Settings old = Settings.getInstance();
+		try {
+			Settings.setInstance(new InnerSettings());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1", InnerActionMapper1.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"2", InnerActionMapper2.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3", InnerActionMapper3.class.getName());
+		
+			List<IndividualActionMapperEntry> result = 
+				compositeActionMapper.getOrderedActionMapperEntries();
+		
+			assertEquals(result.size(), 3);
+			
+			IndividualActionMapperEntry e = null;
+			Iterator<IndividualActionMapperEntry> i = result.iterator();
+			
+			// 1
+			e = i.next();
+			
+			assertEquals(e.order, new Integer(1));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1");
+			assertEquals(e.propertyValue, InnerActionMapper1.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper1.class);
+			
+			// 2
+			e = i.next();
+			
+			assertEquals(e.order, new Integer(2));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"2");
+			assertEquals(e.propertyValue, InnerActionMapper2.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper2.class);
+			
+			// 3
+			e = i.next();
+			assertEquals(e.order, new Integer(3));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3");
+			assertEquals(e.propertyValue, InnerActionMapper3.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper3.class);
+		}
+		finally {
+			Settings.setInstance(old);
+		}
+	}
+	
+	/**
+	 * Test with settings where entries are out-of-order, it needs to be able to retrieve them
+	 * back in proper order.
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetOrderActionMapperEntries3() throws Exception {
+		CompositeActionMapper compositeActionMapper = new CompositeActionMapper();
+		
+		Settings old = Settings.getInstance();
+		try {
+			Settings.setInstance(new InnerSettings());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3", InnerActionMapper3.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"2", InnerActionMapper2.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1", InnerActionMapper1.class.getName());
+		
+			List<IndividualActionMapperEntry> result = 
+				compositeActionMapper.getOrderedActionMapperEntries();
+		
+			assertEquals(result.size(), 3);
+			
+			IndividualActionMapperEntry e = null;
+			Iterator<IndividualActionMapperEntry> i = result.iterator();
+			
+			// 1
+			e = i.next();
+			
+			assertEquals(e.order, new Integer(1));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1");
+			assertEquals(e.propertyValue, InnerActionMapper1.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper1.class);
+			
+			// 2
+			e = i.next();
+			
+			assertEquals(e.order, new Integer(2));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"2");
+			assertEquals(e.propertyValue, InnerActionMapper2.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper2.class);
+			
+			// 3
+			e = i.next();
+			assertEquals(e.order, new Integer(3));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3");
+			assertEquals(e.propertyValue, InnerActionMapper3.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper3.class);
+		}
+		finally {
+			Settings.setInstance(old);
+		}
+	}
+	
+	/**
+	 * Test with a bad entry
+	 * 
+	 * @throws Exception
+	 */
+	public void testGetOrderActionMapperEntries4() throws Exception {
+		CompositeActionMapper compositeActionMapper = new CompositeActionMapper();
+		
+		Settings old = Settings.getInstance();
+		try {
+			Settings.setInstance(new InnerSettings());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1", InnerActionMapper1.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"NotANumber", InnerActionMapper2.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3", InnerActionMapper3.class.getName());
+		
+			List<IndividualActionMapperEntry> result = 
+				compositeActionMapper.getOrderedActionMapperEntries();
+		
+			assertEquals(result.size(), 2);
+			
+			IndividualActionMapperEntry e = null;
+			Iterator<IndividualActionMapperEntry> i = result.iterator();
+			
+			// 1
+			e = i.next();
+			
+			assertEquals(e.order, new Integer(1));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1");
+			assertEquals(e.propertyValue, InnerActionMapper1.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper1.class);
+			
+			// 2
+			e = i.next();
+			assertEquals(e.order, new Integer(3));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3");
+			assertEquals(e.propertyValue, InnerActionMapper3.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper3.class);
+		}
+		finally {
+			Settings.setInstance(old);
+		}
+	}
+	
+	/**
+	 * Test with an entry where the action mapper class is bogus.
+	 * @throws Exception
+	 */
+	public void testGetOrderActionMapperEntries5() throws Exception {
+		CompositeActionMapper compositeActionMapper = new CompositeActionMapper();
+		
+		Settings old = Settings.getInstance();
+		try {
+			Settings.setInstance(new InnerSettings());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1", InnerActionMapper1.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"2", "bogus.class.name");
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3", InnerActionMapper3.class.getName());
+		
+			List<IndividualActionMapperEntry> result = 
+				compositeActionMapper.getOrderedActionMapperEntries();
+		
+			assertEquals(result.size(), 2);
+			
+			IndividualActionMapperEntry e = null;
+			Iterator<IndividualActionMapperEntry> i = result.iterator();
+			
+			// 1
+			e = i.next();
+			
+			assertEquals(e.order, new Integer(1));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1");
+			assertEquals(e.propertyValue, InnerActionMapper1.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper1.class);
+			
+			
+			// 2
+			e = i.next();
+			assertEquals(e.order, new Integer(3));
+			assertEquals(e.propertyName, StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3");
+			assertEquals(e.propertyValue, InnerActionMapper3.class.getName());
+			assertEquals(e.actionMapper.getClass(), InnerActionMapper3.class);
+		}
+		finally {
+			Settings.setInstance(old);
+		}
+	}
+	
+	
+	
+	public void testGetActionMappingAndUri1() throws Exception {
+		CompositeActionMapper compositeActionMapper = new CompositeActionMapper();
+		
+		Settings old = Settings.getInstance();
+		try {
+			Settings.setInstance(new InnerSettings());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1", InnerActionMapper1.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"2", InnerActionMapper2.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"3", InnerActionMapper3.class.getName());
+		
+			
+			ActionMapping actionMapping = compositeActionMapper.getMapping(new MockHttpServletRequest(), new ConfigurationManager());
+			String uri = compositeActionMapper.getUriFromActionMapping(new ActionMapping());
+			
+			assertNotNull(actionMapping);
+			assertNotNull(uri);
+			assertTrue(actionMapping == InnerActionMapper3.actionMapping);
+			assertTrue(uri == InnerActionMapper3.uri);
+		}
+		finally {
+			Settings.setInstance(old);
+		}
+	}
+	
+	public void testGetActionMappingAndUri2() throws Exception {
+		CompositeActionMapper compositeActionMapper = new CompositeActionMapper();
+		
+		Settings old = Settings.getInstance();
+		try {
+			Settings.setInstance(new InnerSettings());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"1", InnerActionMapper1.class.getName());
+			Settings.set(StrutsConstants.STRUTS_MAPPER_COMPOSITE+"2", InnerActionMapper2.class.getName());
+		
+			
+			ActionMapping actionMapping = compositeActionMapper.getMapping(new MockHttpServletRequest(), new ConfigurationManager());
+			String uri = compositeActionMapper.getUriFromActionMapping(new ActionMapping());
+			
+			assertNull(actionMapping);
+			assertNull(uri);
+		}
+		finally {
+			Settings.setInstance(old);
+		}
+	}
+	
+	
+	public static class InnerActionMapper1 implements ActionMapper {
+		public static ActionMapping actionMapping = new ActionMapping();
+		public static String uri="uri1";
+		
+		public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
+			return null;
+		}
+		public String getUriFromActionMapping(ActionMapping mapping) {
+			return null;
+		}
+	}
+	public static class InnerActionMapper2 implements ActionMapper {
+		public static ActionMapping actionMapping = new ActionMapping();
+		public static String uri="uri2";
+		
+		public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
+			return null;
+		}
+		public String getUriFromActionMapping(ActionMapping mapping) {
+			return null;
+		}
+	}
+	public static class InnerActionMapper3 implements ActionMapper {
+		public static ActionMapping actionMapping = new ActionMapping();
+		public static String uri = "uri3";
+		
+		public ActionMapping getMapping(HttpServletRequest request, ConfigurationManager configManager) {
+			return actionMapping;
+		}
+		public String getUriFromActionMapping(ActionMapping mapping) {
+			return uri;
+		}
+	}
+	
+	class InnerSettings extends Settings {
+		private Map<String, String> _impl = new LinkedHashMap<String, String>();
+		
+		@Override
+		public boolean isSetImpl(String name) {
+			return _impl.containsKey(name);
+		}
+		@Override
+		public void setImpl(String name, String value) throws IllegalArgumentException, UnsupportedOperationException {
+			_impl.put(name, value);
+		}
+		@Override
+		public String getImpl(String name) throws IllegalArgumentException {
+			return (String) _impl.get(name);
+		}
+		@Override
+		public Iterator listImpl() {
+			return _impl.keySet().iterator();
+		}
+	}
+	
+}