001package org.apache.turbine.services.urlmapper; 002 003/* 004 * Licensed to the Apache Software Foundation (ASF) under one 005 * or more contributor license agreements. See the NOTICE file 006 * distributed with this work for additional information 007 * regarding copyright ownership. The ASF licenses this file 008 * to you under the Apache License, Version 2.0 (the 009 * "License"); you may not use this file except in compliance 010 * with the License. You may obtain a copy of the License at 011 * 012 * http://www.apache.org/licenses/LICENSE-2.0 013 * 014 * Unless required by applicable law or agreed to in writing, 015 * software distributed under the License is distributed on an 016 * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY 017 * KIND, either express or implied. See the License for the 018 * specific language governing permissions and limitations 019 * under the License. 020 */ 021 022import static org.junit.jupiter.api.Assertions.assertEquals; 023import static org.junit.jupiter.api.Assertions.assertNotNull; 024import static org.junit.jupiter.api.Assertions.assertTrue; 025import static org.junit.jupiter.api.Assertions.fail; 026 027import java.util.ArrayList; 028import java.util.List; 029import java.util.Spliterator; 030import java.util.SplittableRandom; 031import java.util.concurrent.atomic.AtomicInteger; 032import java.util.concurrent.atomic.AtomicLong; 033import java.util.function.IntConsumer; 034import java.util.stream.IntStream; 035 036import javax.servlet.http.HttpServletRequest; 037import javax.servlet.http.HttpServletResponse; 038 039import org.apache.fulcrum.parser.ParameterParser; 040import org.apache.turbine.Turbine; 041import org.apache.turbine.pipeline.PipelineData; 042import org.apache.turbine.services.TurbineServices; 043import org.apache.turbine.test.BaseTestCase; 044import org.apache.turbine.util.RunData; 045import org.apache.turbine.util.TurbineConfig; 046import org.apache.turbine.util.uri.TemplateURI; 047import org.junit.jupiter.api.AfterEach; 048import org.junit.jupiter.api.BeforeEach; 049import org.junit.jupiter.api.Tag; 050import org.junit.jupiter.api.Test; 051import org.mockito.Mockito; 052 053public class TurbineURLMapperServiceTest extends BaseTestCase 054{ 055 private TurbineConfig tc = null; 056 057 private URLMapperService urlMapper = null; 058 059 @BeforeEach 060 public void setUp() throws Exception 061 { 062 tc = 063 new TurbineConfig( 064 ".", 065 "/conf/test/TurbineURLMapperServiceTest.properties"); 066 tc.initialize(); 067 068 urlMapper = (URLMapperService) TurbineServices.getInstance().getService(URLMapperService.SERVICE_NAME); 069 } 070 071 @AfterEach 072 public void tearDown() throws Exception 073 { 074 if (tc != null) 075 { 076 tc.dispose(); 077 } 078 } 079 080 /** 081 * Tests 082 * 083 * <code>scheme://bob/wow/damn2/bookId/123</code> 084 * <code>scheme://bob/wow/book/123</code> 085 * <p> 086 * and 087 * 088 * <code>scheme://bob/wow/damn2/bookId/123/template/Book.vm?detail=1&detail=2&view=collapsed</code> 089 * <code>scheme://bob/wow/book/123/1?view=collapsed</code> 090 * 091 * @throws Exception 092 */ 093 @Test 094 public void testMapToURL() throws Exception 095 { 096 assertNotNull(urlMapper); 097 HttpServletRequest request = getMockRequest(); 098 HttpServletResponse response = Mockito.mock(HttpServletResponse.class); 099 100 PipelineData pipelineData = getPipelineData(request, response, tc.getTurbine().getServletConfig()); 101 assertNotNull(pipelineData); 102 103 TemplateURI uri = new TemplateURI(pipelineData.getRunData()); 104 uri.clearResponse(); // avoid encoding on mocked HTTPServletResponse 105 uri.addPathInfo("bookId", 123); 106 uri.setTemplate("Book.vm"); 107 uri.addQueryData("detail", 0); 108 109 urlMapper.mapToURL(uri); 110 assertEquals("/wow/book/123", uri.getRelativeLink()); 111 assertTrue(uri.getPathInfo().isEmpty()); 112 assertTrue(uri.getQueryData().isEmpty()); 113 114 uri = new TemplateURI(pipelineData.getRunData()); 115 uri.clearResponse(); // avoid encoding on mocked HTTPServletResponse 116 uri.addPathInfo("bookId", 123); 117 uri.setTemplate("Book.vm"); 118 uri.addQueryData("detail", 1); 119 uri.addQueryData("detail", 2); 120 uri.addQueryData("view", "collapsed"); 121 122 urlMapper.mapToURL(uri); 123 assertEquals("/wow/book/123/1?view=collapsed", uri.getRelativeLink()); 124 assertTrue(uri.getPathInfo().isEmpty()); 125 assertEquals(1, uri.getQueryData().size()); 126 } 127 128 /** 129 * Tests 130 * 131 * <code>scheme:///app/book/123/4</code> 132 * <code>scheme:///wow/damn2/detail/4/bookId/123</code> 133 * 134 * @throws Exception 135 */ 136 @Test 137 public void testMapFromURL() throws Exception 138 { 139 assertNotNull(urlMapper); 140 HttpServletRequest request = getMockRequest(); 141 HttpServletResponse response = Mockito.mock(HttpServletResponse.class); 142 143 PipelineData pipelineData = getPipelineData(request, response, tc.getTurbine().getServletConfig()); 144 assertNotNull(pipelineData); 145 ParameterParser pp = pipelineData.get(Turbine.class, ParameterParser.class); 146 assertNotNull(pp); 147 assertTrue(pp.keySet().isEmpty()); 148 149 urlMapper.mapFromURL("/app/book/123/4", pp); 150 151 assertEquals(3, pp.keySet().size()); 152 assertEquals(123, pp.getInt("bookId")); 153 assertEquals("Book.vm", pp.getString("template")); 154 assertEquals(4, pp.getInt("detail")); 155 156 // double check 157 TemplateURI uri = new TemplateURI(pipelineData.getRunData()); 158 uri.clearResponse(); 159 uri.addPathInfo(pp); 160 assertEquals("/wow/damn2/detail/4/bookId/123", uri.getRelativeLink()); 161 urlMapper.mapToURL(uri); 162 assertEquals("/wow/book/123/4", uri.getRelativeLink()); 163 } 164 165 166 @Tag("performance") 167 @Test 168 public void testPerformance() throws Exception 169 { 170 assertNotNull(urlMapper); 171 int templateURIs = 5; 172 List<AtomicLong> counterSum = new ArrayList<>(); 173 List<AtomicInteger> counters = new ArrayList<>(); 174 for (int i = 0; i < templateURIs; i++) 175 { 176 counters.add(i, new AtomicInteger(0)); 177 counterSum.add(i, new AtomicLong(0L)); 178 } 179 int calls = 10_000; // above 1024, set max total of parser pool2 in fulcrum component configuration .. 180 boolean parallel = false; 181 IntStream range = IntStream.range(0, calls); 182 if (parallel) 183 { 184 range = range.parallel(); 185 } 186 187 SplittableRandom sr = new SplittableRandom(); 188 189// range 190// .peek(e -> System.out.println("current value: " + e)) 191// .forEach( actionInt -> { 192// runCheck(templateURIs, counterSum, counters, parallel, sr); 193// }); 194 195 Spliterator.OfInt spliterator1 = range.spliterator(); 196 Spliterator.OfInt spliterator2 = spliterator1.trySplit(); 197 198 System.out.println("s1 estimateSize: " + spliterator1.estimateSize()); 199 spliterator1.forEachRemaining((IntConsumer) i -> 200 { 201 runCheck(templateURIs, counterSum, counters, parallel, sr); 202 }); 203 System.out.println("s2 estimateSize: " + spliterator2.estimateSize()); 204 spliterator2.forEachRemaining((IntConsumer) i -> 205 { 206 runCheck(templateURIs, counterSum, counters, parallel, sr); 207 }); 208 209 for (int i = 0; i < counters.size() - 1; i++) 210 { 211 long time = counterSum.get(i).longValue() / 1_000_000; 212 int count = counters.get(i).get(); 213 TemplateURI turi = getURI(i); 214 String relativeLink = turi.getRelativeLink(); 215 callMapToUrl(turi); 216 System.out.printf("time = %dms (%d calls),average time = %5.3fmics, uri=%s, map=%s%n", time, count, 217 count > 0 ? ((double) time * 1000 / count) : 0, 218 relativeLink, turi.getRelativeLink()); 219 } 220 System.out.printf("total time = %dms (%d total calls) parallel:%s%n", 221 counterSum.stream().mapToInt(AtomicLong::intValue).sum() / 1_000_000, 222 counters.stream().mapToInt(AtomicInteger::intValue).sum(), 223 parallel 224 ); 225 } 226 227 private void runCheck(int templateURIs, List<AtomicLong> counterSum, List<AtomicInteger> counters, boolean parallel, 228 SplittableRandom sr) 229 { 230 int randomNum = sr.nextInt(templateURIs); 231 TemplateURI turi = getURI(randomNum); 232 long time = System.nanoTime(); 233 try 234 { 235 callMapToUrl(turi); 236 } 237 finally 238 { 239 time = System.nanoTime() - time; 240 counterSum.get(randomNum).addAndGet(time); 241 counters.get(randomNum).incrementAndGet(); 242 } 243 } 244 245 /** 246 * to get a fresh URI 247 * 248 * @param tnr 249 * @return 250 */ 251 private TemplateURI getURI(int tnr) 252 { 253 TemplateURI turi = null; 254 switch (tnr) 255 { 256 case 0: 257 turi = getURI1(); 258 break; 259 case 1: 260 turi = getURI2(); 261 break; 262 case 2: 263 turi = getURI3(); 264 break; 265 case 3: 266 turi = getURI4(); 267 break; 268 case 4: 269 turi = getURI5(); 270 break; 271 default: 272 break; 273 } 274 return turi; 275 } 276 277 private TemplateURI getURI1() 278 { 279 TemplateURI uri = new TemplateURI(getRunData()); 280 uri.clearResponse(); // avoid encoding on mocked HTTPServletResponse 281 uri.addPathInfo("bookId", 123); 282 uri.setTemplate("Book.vm"); 283 uri.addQueryData("detail", 0); 284 return uri; 285 } 286 287 private TemplateURI getURI2() 288 { 289 TemplateURI uri2 = new TemplateURI(getRunData()); 290 uri2.clearResponse(); 291 uri2.addPathInfo("bookId", 123); 292 uri2.setTemplate("Book.vm"); 293 uri2.addQueryData("detail", 1); 294 uri2.addQueryData("detail", 2); 295 uri2.addQueryData("view", "collapsed"); 296 return uri2; 297 } 298 299 private TemplateURI getURI3() 300 { 301 TemplateURI uri3 = new TemplateURI(getRunData()); 302 uri3.clearResponse(); 303 uri3.addPathInfo("id", 1234); 304 uri3.addPathInfo("role", "guest"); 305 uri3.addPathInfo("language", "de"); 306 return uri3; 307 } 308 309 private TemplateURI getURI4() 310 { 311 TemplateURI uri4 = new TemplateURI(getRunData()); 312 uri4.clearResponse(); 313 uri4.addPathInfo("js_pane", "random-id-123-abc"); 314 uri4.addPathInfo("role", "anon"); 315 uri4.addPathInfo("media-type", "html"); 316 uri4.setTemplate("Registerone.vm"); 317 return uri4; 318 } 319 320 private TemplateURI getURI5() 321 { 322 TemplateURI uri5 = new TemplateURI(getRunData()); 323 uri5.clearResponse(); 324 uri5.addPathInfo("js_pane", "another-random-id-876-dfg"); 325 uri5.addPathInfo("role", "anon"); 326 uri5.addPathInfo("media-type", "html"); 327 uri5.addPathInfo("page", "Contact"); 328 return uri5; 329 } 330 331 private RunData getRunData() 332 { 333 HttpServletRequest request = getMockRequest(); 334 HttpServletResponse response = Mockito.mock(HttpServletResponse.class); 335 try 336 { 337 PipelineData pipelineData = getPipelineData(request, response, tc.getTurbine().getServletConfig()); 338 assertNotNull(pipelineData); 339 return pipelineData.getRunData(); 340 } 341 catch (Exception e) 342 { 343 fail(); 344 } 345 return null; 346 } 347 348 private void callMapToUrl(TemplateURI uri) 349 { 350 urlMapper.mapToURL(uri); 351 assertTrue(uri.getPathInfo().isEmpty(), "path is not empty:" + uri.getPathInfo()); 352 } 353}