1   /*
2    * Copyright 2004-2008 the Seasar Foundation and the Others.
3    *
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    *
8    *     http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,
13   * either express or implied. See the License for the specific language
14   * governing permissions and limitations under the License.
15   */
16  package org.seasar.cubby.routing.impl;
17  
18  import java.util.ArrayList;
19  import java.util.HashMap;
20  import java.util.Iterator;
21  import java.util.List;
22  import java.util.Map;
23  
24  import junit.framework.Assert;
25  
26  import org.seasar.cubby.CubbyConstants;
27  import org.seasar.cubby.action.Action;
28  import org.seasar.cubby.action.ActionResult;
29  import org.seasar.cubby.action.RequestMethod;
30  import org.seasar.cubby.controller.ClassDetector;
31  import org.seasar.cubby.exception.IllegalRoutingRuntimeException;
32  import org.seasar.cubby.routing.InternalForwardInfo;
33  import org.seasar.cubby.routing.Routing;
34  import org.seasar.extension.unit.S2TestCase;
35  import org.seasar.framework.util.ClassUtil;
36  
37  public class PathResolverImplTest extends S2TestCase {
38  
39  	private PathResolverImpl pathResolver;
40  
41  	@Override
42  	protected void setUp() throws Exception {
43  		super.setUp();
44  		include(this.getClass().getName().replaceAll("\\.", "/") + ".dicon");
45  	}
46  
47  	public void testGetRoutings() {
48  		List<Routing> routings = new ArrayList<Routing>(pathResolver
49  				.getRoutings());
50  		assertEquals(25, routings.size());
51  	}
52  
53  	public void testAdd() {
54  		pathResolver.add("/wiki/edit/{name,.+}", MockAction.class, "update");
55  		pathResolver.add("/wiki/{name,.+}", MockAction.class, "update2",
56  				RequestMethod.POST);
57  		pathResolver.add("/wiki/{name,.+}", MockAction.class, "name",
58  				RequestMethod.PUT);
59  
60  		List<Routing> routings = new ArrayList<Routing>(pathResolver
61  				.getRoutings());
62  		assertEquals(29, routings.size());
63  
64  		Iterator<Routing> iterator = routings.iterator();
65  
66  		Routing routing1 = iterator.next();
67  		assertEquals("^/wiki/edit/(.+)$", routing1.getPattern().pattern());
68  		assertEquals(RequestMethod.GET, routing1.getRequestMethod());
69  		assertEquals(0, routing1.getPriority());
70  
71  		Routing routing2 = iterator.next();
72  		assertEquals("^/wiki/edit/(.+)$", routing2.getPattern().pattern());
73  		assertEquals(RequestMethod.POST, routing2.getRequestMethod());
74  		assertEquals(1, routing2.getPriority());
75  
76  		Routing routing3 = iterator.next();
77  		assertEquals("^/wiki/(.+)$", routing3.getPattern().pattern());
78  		assertEquals(RequestMethod.POST, routing3.getRequestMethod());
79  		assertEquals(2, routing3.getPriority());
80  
81  		Routing routing4 = iterator.next();
82  		assertEquals("^/wiki/(.+)$", routing4.getPattern().pattern());
83  		assertEquals(RequestMethod.PUT, routing4.getRequestMethod());
84  		assertEquals(3, routing4.getPriority());
85  
86  		Routing routing5 = iterator.next();
87  		assertEquals("^/mockPriority/update$", routing5.getPattern().pattern());
88  		assertEquals(100, routing5.getPriority());
89  		assertEquals(RequestMethod.PUT, routing5.getRequestMethod());
90  	}
91  
92  	public void testRoot1() {
93  		InternalForwardInfo info = pathResolver.getInternalForwardInfo("/",
94  				"GET", "UTF-8");
95  		assertNotNull(info);
96  		assertEquals(CubbyConstants.INTERNAL_FORWARD_DIRECTORY, info
97  				.getInternalForwardPath());
98  		Map<String, Routing> routings = info.getOnSubmitRoutings();
99  		assertNotNull(routings);
100 		assertEquals(1, routings.size());
101 		Routing routing = routings.get(null);
102 		assertEquals(MockRootAction.class, routing.getActionClass());
103 		assertEquals(ClassUtil.getMethod(MockRootAction.class, "index",
104 				new Class[0]), routing.getMethod());
105 		assertEquals(0, routing.getUriParameterNames().size());
106 	}
107 
108 	public void testRoot2() {
109 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
110 				"/dummy1", "GET", "UTF-8");
111 		assertNotNull(info);
112 		assertEquals(CubbyConstants.INTERNAL_FORWARD_DIRECTORY, info
113 				.getInternalForwardPath());
114 		Map<String, Routing> routings = info.getOnSubmitRoutings();
115 		assertNotNull(routings);
116 		assertEquals(1, routings.size());
117 		Routing routing = routings.get(null);
118 		assertEquals(MockRootAction.class, routing.getActionClass());
119 		assertEquals(ClassUtil.getMethod(MockRootAction.class, "dummy1",
120 				new Class[0]), routing.getMethod());
121 		assertEquals(0, routing.getUriParameterNames().size());
122 	}
123 
124 	public void testDefault1() {
125 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
126 				"/mock/update", "GET", "UTF-8");
127 		assertNotNull(info);
128 		assertEquals(CubbyConstants.INTERNAL_FORWARD_DIRECTORY, info
129 				.getInternalForwardPath());
130 		Map<String, Routing> routings = info.getOnSubmitRoutings();
131 		assertNotNull(routings);
132 		assertEquals(1, routings.size());
133 		Routing routing = routings.get(null);
134 		assertEquals(MockAction.class, routing.getActionClass());
135 		assertEquals(ClassUtil.getMethod(MockAction.class, "update",
136 				new Class[0]), routing.getMethod());
137 		assertEquals(0, routing.getUriParameterNames().size());
138 	}
139 
140 	public void testDefault2() {
141 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
142 				"/mock/create", "GET", "UTF-8");
143 		assertNotNull(info);
144 		assertEquals(CubbyConstants.INTERNAL_FORWARD_DIRECTORY, info
145 				.getInternalForwardPath());
146 		Map<String, Routing> routings = info.getOnSubmitRoutings();
147 		assertNotNull(routings);
148 		assertEquals(1, routings.size());
149 		Routing routing = routings.get(null);
150 		assertEquals(MockAction.class, routing.getActionClass());
151 		assertEquals(ClassUtil.getMethod(MockAction.class, "insert",
152 				new Class[0]), routing.getMethod());
153 		assertEquals(0, routing.getUriParameterNames().size());
154 	}
155 
156 	public void testDefault3() {
157 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
158 				"/mock/delete/10", "GET", "UTF-8");
159 		assertNotNull(info);
160 		assertTrue(info.getInternalForwardPath().startsWith(
161 				CubbyConstants.INTERNAL_FORWARD_DIRECTORY));
162 		Map<String, Routing> routings = info.getOnSubmitRoutings();
163 		assertNotNull(routings);
164 		assertEquals(1, routings.size());
165 		Routing routing = routings.get(null);
166 		assertEquals(MockAction.class, routing.getActionClass());
167 		assertEquals(ClassUtil.getMethod(MockAction.class, "delete",
168 				new Class[0]), routing.getMethod());
169 		assertEquals(1, routing.getUriParameterNames().size());
170 		Query query = new Query(info.getInternalForwardPath());
171 		assertEquals(1, query.getParameterSize());
172 		assertEquals(1, query.getParam("value").size());
173 		assertEquals("10", query.getParam("value").get(0));
174 	}
175 
176 	public void testDefault4() {
177 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
178 				"/mock/delete/a", "GET", "UTF-8");
179 		assertNull(info);
180 	}
181 
182 	public void testDefault5() {
183 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
184 				"/mock/cubby", "GET", "UTf-8");
185 		assertNotNull(info);
186 		assertTrue(info.getInternalForwardPath().startsWith(
187 				CubbyConstants.INTERNAL_FORWARD_DIRECTORY));
188 		Map<String, Routing> routings = info.getOnSubmitRoutings();
189 		assertNotNull(routings);
190 		assertEquals(1, routings.size());
191 		Routing routing = routings.get(null);
192 		assertEquals(MockAction.class, routing.getActionClass());
193 		assertEquals(ClassUtil
194 				.getMethod(MockAction.class, "name", new Class[0]), routing
195 				.getMethod());
196 		assertEquals(1, routing.getUriParameterNames().size());
197 		Query query = new Query(info.getInternalForwardPath());
198 		assertEquals(1, query.getParam("name").size());
199 		assertEquals("cubby", query.getParam("name").get(0));
200 	}
201 
202 	public void testPath1() {
203 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
204 				"/foo/4/update", "GET", "UTF-8");
205 		assertNotNull(info);
206 		assertTrue(info.getInternalForwardPath().startsWith(
207 				CubbyConstants.INTERNAL_FORWARD_DIRECTORY));
208 		Map<String, Routing> routings = info.getOnSubmitRoutings();
209 		assertNotNull(routings);
210 		assertEquals(1, routings.size());
211 		Routing routing = routings.get(null);
212 		assertEquals(MockPathAction.class, routing.getActionClass());
213 		assertEquals(ClassUtil.getMethod(MockPathAction.class, "update",
214 				new Class[0]), routing.getMethod());
215 		assertEquals(1, routing.getUriParameterNames().size());
216 		Query query = new Query(info.getInternalForwardPath());
217 		assertEquals(1, query.getParameterSize());
218 		assertEquals(1, query.getParam("id").size());
219 		assertEquals("4", query.getParam("id").get(0));
220 	}
221 
222 	public void testPath2() {
223 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
224 				"/foo/4/create", "GET", "UTF-8");
225 		assertNotNull(info);
226 		assertTrue(info.getInternalForwardPath().startsWith(
227 				CubbyConstants.INTERNAL_FORWARD_DIRECTORY));
228 		Map<String, Routing> routings = info.getOnSubmitRoutings();
229 		assertNotNull(routings);
230 		assertEquals(1, routings.size());
231 		Routing routing = routings.get(null);
232 		assertEquals(MockPathAction.class, routing.getActionClass());
233 		assertEquals(ClassUtil.getMethod(MockPathAction.class, "insert",
234 				new Class[0]), routing.getMethod());
235 		assertEquals(1, routing.getUriParameterNames().size());
236 		Query query = new Query(info.getInternalForwardPath());
237 		assertEquals(1, query.getParameterSize());
238 		assertEquals(1, query.getParam("id").size());
239 		assertEquals("4", query.getParam("id").get(0));
240 	}
241 
242 	public void testPath3() {
243 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
244 				"/foo/4/delete/10", "GET", "UTF-8");
245 		assertNotNull(info);
246 		assertTrue(info.getInternalForwardPath().startsWith(
247 				CubbyConstants.INTERNAL_FORWARD_DIRECTORY));
248 		Map<String, Routing> routings = info.getOnSubmitRoutings();
249 		assertNotNull(routings);
250 		assertEquals(1, routings.size());
251 		Routing routing = routings.get(null);
252 		assertEquals(MockPathAction.class, routing.getActionClass());
253 		assertEquals(ClassUtil.getMethod(MockPathAction.class, "delete",
254 				new Class[0]), routing.getMethod());
255 		assertEquals(2, routing.getUriParameterNames().size());
256 		Query query = new Query(info.getInternalForwardPath());
257 		assertEquals(2, query.getParameterSize());
258 		assertEquals(1, query.getParam("id").size());
259 		assertEquals("4", query.getParam("id").get(0));
260 		assertEquals(1, query.getParam("value").size());
261 		assertEquals("10", query.getParam("value").get(0));
262 	}
263 
264 	public void testPath4() {
265 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
266 				"/foo/4/delete/a", "GET", "UTF-8");
267 		assertNull(info);
268 	}
269 
270 	public void testPath5() {
271 		InternalForwardInfo info = pathResolver.getInternalForwardInfo(
272 				"/foo/4/cubby", "GET", "UTF-8");
273 		assertNotNull(info);
274 		assertTrue(info.getInternalForwardPath().startsWith(
275 				CubbyConstants.INTERNAL_FORWARD_DIRECTORY));
276 		Map<String, Routing> routings = info.getOnSubmitRoutings();
277 		assertNotNull(routings);
278 		assertEquals(1, routings.size());
279 		Routing routing = routings.get(null);
280 		assertEquals(MockPathAction.class, routing.getActionClass());
281 		assertEquals(ClassUtil.getMethod(MockPathAction.class, "name",
282 				new Class[0]), routing.getMethod());
283 		assertEquals(2, routing.getUriParameterNames().size());
284 		Query query = new Query(info.getInternalForwardPath());
285 		assertEquals(1, query.getParam("id").size());
286 		assertEquals("4", query.getParam("id").get(0));
287 		assertEquals(1, query.getParam("name").size());
288 		assertEquals("cubby", query.getParam("name").get(0));
289 	}
290 	
291 	public void testAddAbstractClass() throws Exception {
292 		pathResolver.setClassDetector(new ClassDetector() { public void detect() {} });
293 		
294 		try {
295 			pathResolver.add("/parent/m1", ParentAction.class, "m1");
296 			fail();
297 		} catch (IllegalRoutingRuntimeException e) {
298 			assertEquals("アクションクラスではないクラスを登録するとエラー(抽象クラス)", "ECUB0002", e.getMessageCode());
299 		}
300 		try {
301 			pathResolver.add("/child/m3", ChildAction.class, "m3");
302 			fail();
303 		} catch (IllegalRoutingRuntimeException e) {
304 			assertEquals("アクションメソッドではないメソッドを登録するとエラー(戻り値がObject)", "ECUB0003", e.getMessageCode());
305 		}
306 		pathResolver.add("/child/m1", ChildAction.class, "m1");
307 		pathResolver.add("/child/m2", ChildAction.class, "m2");
308 		assertEquals("正常に登録できたルーティング情報の数", 4, pathResolver.getRoutings().size());
309 		assertRouting(pathResolver.getRoutings().get(0), "/child/m1", RequestMethod.GET, ChildAction.class, "m1");
310 		assertRouting(pathResolver.getRoutings().get(1), "/child/m1", RequestMethod.POST, ChildAction.class, "m1");
311 		assertRouting(pathResolver.getRoutings().get(2), "/child/m2", RequestMethod.GET, ChildAction.class, "m2");
312 		assertRouting(pathResolver.getRoutings().get(3), "/child/m2", RequestMethod.POST, ChildAction.class, "m2");
313 	}
314 	
315 	private void assertRouting(Routing routing, String path,
316 			RequestMethod requestMethod, Class<? extends Action> actionClass, String actionMethod) {
317 		assertEquals(path, routing.getActionPath());
318 		assertEquals(requestMethod, routing.getRequestMethod());
319 		assertEquals(actionClass, routing.getActionClass());
320 		assertEquals(actionMethod, routing.getMethod().getName());
321 	}
322 
323 	public abstract class ParentAction extends Action {
324 		public ActionResult m1() { return null; }
325 	}
326 	
327 	public class ChildAction extends ParentAction {
328 		public ActionResult m2() { return null; }
329 		public Object m3() { return null; }
330 	}
331 	
332 	class Query {
333 		private String path;
334 		private Map<String, List<String>> params;
335 
336 		public Query(String path) {
337 			String[] tokens = path.split("\\?", 2);
338 			switch (tokens.length) {
339 			case 1:
340 				this.path = tokens[0];
341 				this.params = new HashMap<String, List<String>>();
342 				break;
343 			case 2:
344 				this.path = tokens[0];
345 				this.params = parseQueryString(tokens[1]);
346 
347 				break;
348 			default:
349 				Assert.fail("illegal path " + path);
350 				break;
351 			}
352 		}
353 
354 		private Map<String, List<String>> parseQueryString(String queryString) {
355 			Map<String, List<String>> params = new HashMap<String, List<String>>();
356 			String[] tokens = queryString.split("&");
357 			for (String token : tokens) {
358 				String[] param = parseQueryParameter(token);
359 				String name = param[0];
360 				String value = param[1];
361 				List<String> values;
362 				if (params.containsKey(name)) {
363 					values = params.get(name);
364 				} else {
365 					values = new ArrayList<String>();
366 					params.put(name, values);
367 				}
368 				values.add(value);
369 			}
370 			return params;
371 		}
372 
373 		private String[] parseQueryParameter(String token) {
374 			return token.split("=");
375 		}
376 
377 		public String getPath() {
378 			return path;
379 		}
380 
381 		public List<String> getParam(String name) {
382 			return params.get(name);
383 		}
384 
385 		public boolean isEmptyParameter() {
386 			return params.isEmpty();
387 		}
388 
389 		public int getParameterSize() {
390 			return params.size();
391 		}
392 	}
393 
394 }