1 /*
2 * Licensed to the Apache Software Foundation (ASF) under one or more
3 * contributor license agreements. See the NOTICE file distributed with
4 * this work for additional information regarding copyright ownership.
5 * The ASF licenses this file to You under the Apache License, Version 2.0
6 * (the "License"); you may not use this file except in compliance with
7 * the License. You may obtain a copy of the License at
8 *
9 * http://www.apache.org/licenses/LICENSE-2.0
10 *
11 * Unless required by applicable law or agreed to in writing, software
12 * distributed under the License is distributed on an "AS IS" BASIS,
13 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 * See the License for the specific language governing permissions and
15 * limitations under the License.
16 */
17
18 package org.apache.tomcat.util.http.mapper;
19
20 import javax.naming.NamingException;
21 import javax.naming.directory.DirContext;
22
23 import org.apache.tomcat.util.buf.CharChunk;
24 import org.apache.tomcat.util.buf.MessageBytes;
25 import org.apache.tomcat.util.buf.Ascii;
26 import java.util.List;
27 import java.util.ArrayList;
28
29 /**
30 * Mapper, which implements the servlet API mapping rules (which are derived
31 * from the HTTP rules).
32 *
33 * @author Remy Maucherat
34 */
35 public final class Mapper {
36
37
38 private static org.apache.juli.logging.Log logger =
39 org.apache.juli.logging.LogFactory.getLog(Mapper.class);
40 // ----------------------------------------------------- Instance Variables
41
42
43 /**
44 * Array containing the virtual hosts definitions.
45 */
46 protected Host[] hosts = new Host[0];
47
48
49 /**
50 * Default host name.
51 */
52 protected String defaultHostName = null;
53
54 /**
55 * Context associated with this wrapper, used for wrapper mapping.
56 */
57 protected Context context = new Context();
58
59
60 // --------------------------------------------------------- Public Methods
61
62
63 /**
64 * Get default host.
65 *
66 * @return Default host name
67 */
68 public String getDefaultHostName() {
69 return defaultHostName;
70 }
71
72
73 /**
74 * Set default host.
75 *
76 * @param defaultHostName Default host name
77 */
78 public void setDefaultHostName(String defaultHostName) {
79 this.defaultHostName = defaultHostName;
80 }
81
82 /**
83 * Add a new host to the mapper.
84 *
85 * @param name Virtual host name
86 * @param host Host object
87 */
88 public synchronized void addHost(String name, String[] aliases,
89 Object host) {
90 Host[] newHosts = new Host[hosts.length + 1];
91 Host newHost = new Host();
92 ContextList contextList = new ContextList();
93 newHost.name = name;
94 newHost.contextList = contextList;
95 newHost.object = host;
96 if (insertMap(hosts, newHosts, newHost)) {
97 hosts = newHosts;
98 }
99 for (int i = 0; i < aliases.length; i++) {
100 newHosts = new Host[hosts.length + 1];
101 newHost = new Host();
102 newHost.name = aliases[i];
103 newHost.contextList = contextList;
104 newHost.object = host;
105 if (insertMap(hosts, newHosts, newHost)) {
106 hosts = newHosts;
107 }
108 }
109 }
110
111
112 /**
113 * Remove a host from the mapper.
114 *
115 * @param name Virtual host name
116 */
117 public synchronized void removeHost(String name) {
118 // Find and remove the old host
119 int pos = find(hosts, name);
120 if (pos < 0) {
121 return;
122 }
123 Object host = hosts[pos].object;
124 Host[] newHosts = new Host[hosts.length - 1];
125 if (removeMap(hosts, newHosts, name)) {
126 hosts = newHosts;
127 }
128 // Remove all aliases (they will map to the same host object)
129 for (int i = 0; i < newHosts.length; i++) {
130 if (newHosts[i].object == host) {
131 Host[] newHosts2 = new Host[hosts.length - 1];
132 if (removeMap(hosts, newHosts2, newHosts[i].name)) {
133 hosts = newHosts2;
134 }
135 }
136 }
137 }
138
139 public String[] getHosts() {
140 String hostN[] = new String[hosts.length];
141 for( int i = 0; i < hosts.length; i++ ) {
142 hostN[i] = hosts[i].name;
143 }
144 return hostN;
145 }
146
147
148 /**
149 * Set context, used for wrapper mapping (request dispatcher).
150 *
151 * @param welcomeResources Welcome files defined for this context
152 * @param resources Static resources of the context
153 */
154 public void setContext(String path, String[] welcomeResources,
155 javax.naming.Context resources) {
156 context.name = path;
157 context.welcomeResources = welcomeResources;
158 context.resources = resources;
159 }
160
161
162 /**
163 * Add a new Context to an existing Host.
164 *
165 * @param hostName Virtual host name this context belongs to
166 * @param path Context path
167 * @param context Context object
168 * @param welcomeResources Welcome files defined for this context
169 * @param resources Static resources of the context
170 */
171 public void addContext
172 (String hostName, String path, Object context,
173 String[] welcomeResources, javax.naming.Context resources) {
174
175 Host[] hosts = this.hosts;
176 int pos = find(hosts, hostName);
177 if( pos <0 ) {
178 addHost(hostName, new String[0], "");
179 hosts = this.hosts;
180 pos = find(hosts, hostName);
181 }
182 if (pos < 0) {
183 logger.error("No host found: " + hostName);
184 }
185 Host host = hosts[pos];
186 if (host.name.equals(hostName)) {
187 int slashCount = slashCount(path);
188 synchronized (host) {
189 Context[] contexts = host.contextList.contexts;
190 // Update nesting
191 if (slashCount > host.contextList.nesting) {
192 host.contextList.nesting = slashCount;
193 }
194 Context[] newContexts = new Context[contexts.length + 1];
195 Context newContext = new Context();
196 newContext.name = path;
197 newContext.object = context;
198 newContext.welcomeResources = welcomeResources;
199 newContext.resources = resources;
200 if (insertMap(contexts, newContexts, newContext)) {
201 host.contextList.contexts = newContexts;
202 }
203 }
204 }
205
206 }
207
208
209 /**
210 * Remove a context from an existing host.
211 *
212 * @param hostName Virtual host name this context belongs to
213 * @param path Context path
214 */
215 public void removeContext(String hostName, String path) {
216 Host[] hosts = this.hosts;
217 int pos = find(hosts, hostName);
218 if (pos < 0) {
219 return;
220 }
221 Host host = hosts[pos];
222 if (host.name.equals(hostName)) {
223 synchronized (host) {
224 Context[] contexts = host.contextList.contexts;
225 if( contexts.length == 0 ){
226 return;
227 }
228 Context[] newContexts = new Context[contexts.length - 1];
229 if (removeMap(contexts, newContexts, path)) {
230 host.contextList.contexts = newContexts;
231 // Recalculate nesting
232 host.contextList.nesting = 0;
233 for (int i = 0; i < newContexts.length; i++) {
234 int slashCount = slashCount(newContexts[i].name);
235 if (slashCount > host.contextList.nesting) {
236 host.contextList.nesting = slashCount;
237 }
238 }
239 }
240 }
241 }
242 }
243
244
245 /**
246 * Return all contexts, in //HOST/PATH form
247 *
248 * @return The context names
249 */
250 public String[] getContextNames() {
251 List list=new ArrayList();
252 for( int i=0; i<hosts.length; i++ ) {
253 for( int j=0; j<hosts[i].contextList.contexts.length; j++ ) {
254 String cname=hosts[i].contextList.contexts[j].name;
255 list.add("//" + hosts[i].name +
256 (cname.startsWith("/") ? cname : "/"));
257 }
258 }
259 String res[] = new String[list.size()];
260 return (String[])list.toArray(res);
261 }
262
263
264 /**
265 * Add a new Wrapper to an existing Context.
266 *
267 * @param hostName Virtual host name this wrapper belongs to
268 * @param contextPath Context path this wrapper belongs to
269 * @param path Wrapper mapping
270 * @param wrapper Wrapper object
271 */
272 public void addWrapper(String hostName, String contextPath, String path,
273 Object wrapper) {
274 addWrapper(hostName, contextPath, path, wrapper, false);
275 }
276
277
278 public void addWrapper(String hostName, String contextPath, String path,
279 Object wrapper, boolean jspWildCard) {
280 Host[] hosts = this.hosts;
281 int pos = find(hosts, hostName);
282 if (pos < 0) {
283 return;
284 }
285 Host host = hosts[pos];
286 if (host.name.equals(hostName)) {
287 Context[] contexts = host.contextList.contexts;
288 int pos2 = find(contexts, contextPath);
289 if( pos2<0 ) {
290 logger.error("No context found: " + contextPath );
291 return;
292 }
293 Context context = contexts[pos2];
294 if (context.name.equals(contextPath)) {
295 addWrapper(context, path, wrapper, jspWildCard);
296 }
297 }
298 }
299
300
301 /**
302 * Add a wrapper to the context associated with this wrapper.
303 *
304 * @param path Wrapper mapping
305 * @param wrapper The Wrapper object
306 */
307 public void addWrapper(String path, Object wrapper) {
308 addWrapper(context, path, wrapper);
309 }
310
311
312 public void addWrapper(String path, Object wrapper, boolean jspWildCard) {
313 addWrapper(context, path, wrapper, jspWildCard);
314 }
315
316
317 protected void addWrapper(Context context, String path, Object wrapper) {
318 addWrapper(context, path, wrapper, false);
319 }
320
321
322 /**
323 * Adds a wrapper to the given context.
324 *
325 * @param context The context to which to add the wrapper
326 * @param path Wrapper mapping
327 * @param wrapper The Wrapper object
328 * @param jspWildCard true if the wrapper corresponds to the JspServlet
329 * and the mapping path contains a wildcard; false otherwise
330 */
331 protected void addWrapper(Context context, String path, Object wrapper,
332 boolean jspWildCard) {
333
334 synchronized (context) {
335 Wrapper newWrapper = new Wrapper();
336 newWrapper.object = wrapper;
337 newWrapper.jspWildCard = jspWildCard;
338 if (path.endsWith("/*")) {
339 // Wildcard wrapper
340 newWrapper.name = path.substring(0, path.length() - 2);
341 Wrapper[] oldWrappers = context.wildcardWrappers;
342 Wrapper[] newWrappers =
343 new Wrapper[oldWrappers.length + 1];
344 if (insertMap(oldWrappers, newWrappers, newWrapper)) {
345 context.wildcardWrappers = newWrappers;
346 int slashCount = slashCount(newWrapper.name);
347 if (slashCount > context.nesting) {
348 context.nesting = slashCount;
349 }
350 }
351 } else if (path.startsWith("*.")) {
352 // Extension wrapper
353 newWrapper.name = path.substring(2);
354 Wrapper[] oldWrappers = context.extensionWrappers;
355 Wrapper[] newWrappers =
356 new Wrapper[oldWrappers.length + 1];
357 if (insertMap(oldWrappers, newWrappers, newWrapper)) {
358 context.extensionWrappers = newWrappers;
359 }
360 } else if (path.equals("/")) {
361 // Default wrapper
362 newWrapper.name = "";
363 context.defaultWrapper = newWrapper;
364 } else {
365 // Exact wrapper
366 newWrapper.name = path;
367 Wrapper[] oldWrappers = context.exactWrappers;
368 Wrapper[] newWrappers =
369 new Wrapper[oldWrappers.length + 1];
370 if (insertMap(oldWrappers, newWrappers, newWrapper)) {
371 context.exactWrappers = newWrappers;
372 }
373 }
374 }
375 }
376
377
378 /**
379 * Remove a wrapper from the context associated with this wrapper.
380 *
381 * @param path Wrapper mapping
382 */
383 public void removeWrapper(String path) {
384 removeWrapper(context, path);
385 }
386
387
388 /**
389 * Remove a wrapper from an existing context.
390 *
391 * @param hostName Virtual host name this wrapper belongs to
392 * @param contextPath Context path this wrapper belongs to
393 * @param path Wrapper mapping
394 */
395 public void removeWrapper
396 (String hostName, String contextPath, String path) {
397 Host[] hosts = this.hosts;
398 int pos = find(hosts, hostName);
399 if (pos < 0) {
400 return;
401 }
402 Host host = hosts[pos];
403 if (host.name.equals(hostName)) {
404 Context[] contexts = host.contextList.contexts;
405 int pos2 = find(contexts, contextPath);
406 if (pos2 < 0) {
407 return;
408 }
409 Context context = contexts[pos2];
410 if (context.name.equals(contextPath)) {
411 removeWrapper(context, path);
412 }
413 }
414 }
415
416 protected void removeWrapper(Context context, String path) {
417 synchronized (context) {
418 if (path.endsWith("/*")) {
419 // Wildcard wrapper
420 String name = path.substring(0, path.length() - 2);
421 Wrapper[] oldWrappers = context.wildcardWrappers;
422 Wrapper[] newWrappers =
423 new Wrapper[oldWrappers.length - 1];
424 if (removeMap(oldWrappers, newWrappers, name)) {
425 // Recalculate nesting
426 context.nesting = 0;
427 for (int i = 0; i < newWrappers.length; i++) {
428 int slashCount = slashCount(newWrappers[i].name);
429 if (slashCount > context.nesting) {
430 context.nesting = slashCount;
431 }
432 }
433 context.wildcardWrappers = newWrappers;
434 }
435 } else if (path.startsWith("*.")) {
436 // Extension wrapper
437 String name = path.substring(2);
438 Wrapper[] oldWrappers = context.extensionWrappers;
439 Wrapper[] newWrappers =
440 new Wrapper[oldWrappers.length - 1];
441 if (removeMap(oldWrappers, newWrappers, name)) {
442 context.extensionWrappers = newWrappers;
443 }
444 } else if (path.equals("/")) {
445 // Default wrapper
446 context.defaultWrapper = null;
447 } else {
448 // Exact wrapper
449 String name = path;
450 Wrapper[] oldWrappers = context.exactWrappers;
451 Wrapper[] newWrappers =
452 new Wrapper[oldWrappers.length - 1];
453 if (removeMap(oldWrappers, newWrappers, name)) {
454 context.exactWrappers = newWrappers;
455 }
456 }
457 }
458 }
459
460 public String getWrappersString( String host, String context ) {
461 String names[]=getWrapperNames(host, context);
462 StringBuffer sb=new StringBuffer();
463 for( int i=0; i<names.length; i++ ) {
464 sb.append(names[i]).append(":");
465 }
466 return sb.toString();
467 }
468
469 public String[] getWrapperNames( String host, String context ) {
470 List list=new ArrayList();
471 if( host==null ) host="";
472 if( context==null ) context="";
473 for( int i=0; i<hosts.length; i++ ) {
474 if( ! host.equals( hosts[i].name ))
475 continue;
476 for( int j=0; j<hosts[i].contextList.contexts.length; j++ ) {
477 if( ! context.equals( hosts[i].contextList.contexts[j].name))
478 continue;
479 // found the context
480 Context ctx=hosts[i].contextList.contexts[j];
481 list.add( ctx.defaultWrapper.path);
482 for( int k=0; k<ctx.exactWrappers.length; k++ ) {
483 list.add( ctx.exactWrappers[k].path);
484 }
485 for( int k=0; k<ctx.wildcardWrappers.length; k++ ) {
486 list.add( ctx.wildcardWrappers[k].path + "*");
487 }
488 for( int k=0; k<ctx.extensionWrappers.length; k++ ) {
489 list.add( "*." + ctx.extensionWrappers[k].path);
490 }
491 }
492 }
493 String res[]=new String[list.size()];
494 return (String[])list.toArray(res);
495 }
496
497
498
499 /**
500 * Map the specified host name and URI, mutating the given mapping data.
501 *
502 * @param host Virtual host name
503 * @param uri URI
504 * @param mappingData This structure will contain the result of the mapping
505 * operation
506 */
507 public void map(MessageBytes host, MessageBytes uri,
508 MappingData mappingData)
509 throws Exception {
510
511 if (host.isNull()) {
512 host.getCharChunk().append(defaultHostName);
513 }
514 host.toChars();
515 uri.toChars();
516 internalMap(host.getCharChunk(), uri.getCharChunk(), mappingData);
517
518 }
519
520
521 /**
522 * Map the specified URI relative to the context,
523 * mutating the given mapping data.
524 *
525 * @param uri URI
526 * @param mappingData This structure will contain the result of the mapping
527 * operation
528 */
529 public void map(MessageBytes uri, MappingData mappingData)
530 throws Exception {
531
532 uri.toChars();
533 CharChunk uricc = uri.getCharChunk();
534 uricc.setLimit(-1);
535 internalMapWrapper(context, uricc, mappingData);
536
537 }
538
539
540 // -------------------------------------------------------- Private Methods
541
542
543 /**
544 * Map the specified URI.
545 */
546 private final void internalMap(CharChunk host, CharChunk uri,
547 MappingData mappingData)
548 throws Exception {
549
550 uri.setLimit(-1);
551
552 Context[] contexts = null;
553 Context context = null;
554 int nesting = 0;
555
556 // Virtual host mapping
557 if (mappingData.host == null) {
558 Host[] hosts = this.hosts;
559 int pos = findIgnoreCase(hosts, host);
560 if ((pos != -1) && (host.equalsIgnoreCase(hosts[pos].name))) {
561 mappingData.host = hosts[pos].object;
562 contexts = hosts[pos].contextList.contexts;
563 nesting = hosts[pos].contextList.nesting;
564 } else {
565 if (defaultHostName == null) {
566 return;
567 }
568 pos = find(hosts, defaultHostName);
569 if ((pos != -1) && (defaultHostName.equals(hosts[pos].name))) {
570 mappingData.host = hosts[pos].object;
571 contexts = hosts[pos].contextList.contexts;
572 nesting = hosts[pos].contextList.nesting;
573 } else {
574 return;
575 }
576 }
577 }
578
579 // Context mapping
580 if (mappingData.context == null) {
581 int pos = find(contexts, uri);
582 if (pos == -1) {
583 return;
584 }
585
586 int lastSlash = -1;
587 int uriEnd = uri.getEnd();
588 int length = -1;
589 boolean found = false;
590 while (pos >= 0) {
591 if (uri.startsWith(contexts[pos].name)) {
592 length = contexts[pos].name.length();
593 if (uri.getLength() == length) {
594 found = true;
595 break;
596 } else if (uri.startsWithIgnoreCase("/", length)) {
597 found = true;
598 break;
599 }
600 }
601 if (lastSlash == -1) {
602 lastSlash = nthSlash(uri, nesting + 1);
603 } else {
604 lastSlash = lastSlash(uri);
605 }
606 uri.setEnd(lastSlash);
607 pos = find(contexts, uri);
608 }
609 uri.setEnd(uriEnd);
610
611 if (!found) {
612 if (contexts[0].name.equals("")) {
613 context = contexts[0];
614 }
615 } else {
616 context = contexts[pos];
617 }
618 if (context != null) {
619 mappingData.context = context.object;
620 mappingData.contextPath.setString(context.name);
621 }
622 }
623
624 // Wrapper mapping
625 if ((context != null) && (mappingData.wrapper == null)) {
626 internalMapWrapper(context, uri, mappingData);
627 }
628
629 }
630
631
632 /**
633 * Wrapper mapping.
634 */
635 private final void internalMapWrapper(Context context, CharChunk path,
636 MappingData mappingData)
637 throws Exception {
638
639 int pathOffset = path.getOffset();
640 int pathEnd = path.getEnd();
641 int servletPath = pathOffset;
642 boolean noServletPath = false;
643
644 int length = context.name.length();
645 if (length != (pathEnd - pathOffset)) {
646 servletPath = pathOffset + length;
647 } else {
648 noServletPath = true;
649 path.append('/');
650 pathOffset = path.getOffset();
651 pathEnd = path.getEnd();
652 servletPath = pathOffset+length;
653 }
654
655 path.setOffset(servletPath);
656
657 // Rule 1 -- Exact Match
658 Wrapper[] exactWrappers = context.exactWrappers;
659 internalMapExactWrapper(exactWrappers, path, mappingData);
660
661 // Rule 2 -- Prefix Match
662 boolean checkJspWelcomeFiles = false;
663 Wrapper[] wildcardWrappers = context.wildcardWrappers;
664 if (mappingData.wrapper == null) {
665 internalMapWildcardWrapper(wildcardWrappers, context.nesting,
666 path, mappingData);
667 if (mappingData.wrapper != null && mappingData.jspWildCard) {
668 char[] buf = path.getBuffer();
669 if (buf[pathEnd - 1] == '/') {
670 /*
671 * Path ending in '/' was mapped to JSP servlet based on
672 * wildcard match (e.g., as specified in url-pattern of a
673 * jsp-property-group.
674 * Force the context's welcome files, which are interpreted
675 * as JSP files (since they match the url-pattern), to be
676 * considered. See Bugzilla 27664.
677 */
678 mappingData.wrapper = null;
679 checkJspWelcomeFiles = true;
680 } else {
681 // See Bugzilla 27704
682 mappingData.wrapperPath.setChars(buf, path.getStart(),
683 path.getLength());
684 mappingData.pathInfo.recycle();
685 }
686 }
687 }
688
689 if(mappingData.wrapper == null && noServletPath) {
690 // The path is empty, redirect to "/"
691 mappingData.redirectPath.setChars
692 (path.getBuffer(), pathOffset, pathEnd);
693 path.setEnd(pathEnd - 1);
694 return;
695 }
696
697 // Rule 3 -- Extension Match
698 Wrapper[] extensionWrappers = context.extensionWrappers;
699 if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
700 internalMapExtensionWrapper(extensionWrappers, path, mappingData);
701 }
702
703 // Rule 4 -- Welcome resources processing for servlets
704 if (mappingData.wrapper == null) {
705 boolean checkWelcomeFiles = checkJspWelcomeFiles;
706 if (!checkWelcomeFiles) {
707 char[] buf = path.getBuffer();
708 checkWelcomeFiles = (buf[pathEnd - 1] == '/');
709 }
710 if (checkWelcomeFiles) {
711 for (int i = 0; (i < context.welcomeResources.length)
712 && (mappingData.wrapper == null); i++) {
713 path.setOffset(pathOffset);
714 path.setEnd(pathEnd);
715 path.append(context.welcomeResources[i], 0,
716 context.welcomeResources[i].length());
717 path.setOffset(servletPath);
718
719 // Rule 4a -- Welcome resources processing for exact macth
720 internalMapExactWrapper(exactWrappers, path, mappingData);
721
722 // Rule 4b -- Welcome resources processing for prefix match
723 if (mappingData.wrapper == null) {
724 internalMapWildcardWrapper
725 (wildcardWrappers, context.nesting,
726 path, mappingData);
727 }
728
729 // Rule 4c -- Welcome resources processing
730 // for physical folder
731 if (mappingData.wrapper == null
732 && context.resources != null) {
733 Object file = null;
734 String pathStr = path.toString();
735 try {
736 file = context.resources.lookup(pathStr);
737 } catch(NamingException nex) {
738 // Swallow not found, since this is normal
739 }
740 if (file != null && !(file instanceof DirContext) ) {
741 internalMapExtensionWrapper(extensionWrappers,
742 path, mappingData);
743 if (mappingData.wrapper == null
744 && context.defaultWrapper != null) {
745 mappingData.wrapper =
746 context.defaultWrapper.object;
747 mappingData.requestPath.setChars
748 (path.getBuffer(), path.getStart(),
749 path.getLength());
750 mappingData.wrapperPath.setChars
751 (path.getBuffer(), path.getStart(),
752 path.getLength());
753 mappingData.requestPath.setString(pathStr);
754 mappingData.wrapperPath.setString(pathStr);
755 }
756 }
757 }
758 }
759
760 path.setOffset(servletPath);
761 path.setEnd(pathEnd);
762 }
763
764 }
765
766
767 // Rule 7 -- Default servlet
768 if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
769 if (context.defaultWrapper != null) {
770 mappingData.wrapper = context.defaultWrapper.object;
771 mappingData.requestPath.setChars
772 (path.getBuffer(), path.getStart(), path.getLength());
773 mappingData.wrapperPath.setChars
774 (path.getBuffer(), path.getStart(), path.getLength());
775 }
776 // Redirection to a folder
777 char[] buf = path.getBuffer();
778 if (context.resources != null && buf[pathEnd -1 ] != '/') {
779 Object file = null;
780 String pathStr = path.toString();
781 try {
782 file = context.resources.lookup(pathStr);
783 } catch(NamingException nex) {
784 // Swallow, since someone else handles the 404
785 }
786 if (file != null && file instanceof DirContext) {
787 // Note: this mutates the path: do not do any processing
788 // after this (since we set the redirectPath, there
789 // shouldn't be any)
790 path.setOffset(pathOffset);
791 path.append('/');
792 mappingData.redirectPath.setChars
793 (path.getBuffer(), path.getStart(), path.getLength());
794 } else {
795 mappingData.requestPath.setString(pathStr);
796 mappingData.wrapperPath.setString(pathStr);
797 }
798 }
799 }
800
801 path.setOffset(pathOffset);
802 path.setEnd(pathEnd);
803
804 }
805
806
807 /**
808 * Exact mapping.
809 */
810 private final void internalMapExactWrapper
811 (Wrapper[] wrappers, CharChunk path, MappingData mappingData) {
812 int pos = find(wrappers, path);
813 if ((pos != -1) && (path.equals(wrappers[pos].name))) {
814 mappingData.requestPath.setString(wrappers[pos].name);
815 mappingData.wrapperPath.setString(wrappers[pos].name);
816 mappingData.wrapper = wrappers[pos].object;
817 }
818 }
819
820
821 /**
822 * Wildcard mapping.
823 */
824 private final void internalMapWildcardWrapper
825 (Wrapper[] wrappers, int nesting, CharChunk path,
826 MappingData mappingData) {
827
828 int pathEnd = path.getEnd();
829 int pathOffset = path.getOffset();
830
831 int lastSlash = -1;
832 int length = -1;
833 int pos = find(wrappers, path);
834 if (pos != -1) {
835 boolean found = false;
836 while (pos >= 0) {
837 if (path.startsWith(wrappers[pos].name)) {
838 length = wrappers[pos].name.length();
839 if (path.getLength() == length) {
840 found = true;
841 break;
842 } else if (path.startsWithIgnoreCase("/", length)) {
843 found = true;
844 break;
845 }
846 }
847 if (lastSlash == -1) {
848 lastSlash = nthSlash(path, nesting + 1);
849 } else {
850 lastSlash = lastSlash(path);
851 }
852 path.setEnd(lastSlash);
853 pos = find(wrappers, path);
854 }
855 path.setEnd(pathEnd);
856 if (found) {
857 mappingData.wrapperPath.setString(wrappers[pos].name);
858 if (path.getLength() > length) {
859 mappingData.pathInfo.setChars
860 (path.getBuffer(),
861 path.getOffset() + length,
862 path.getLength() - length);
863 }
864 mappingData.requestPath.setChars
865 (path.getBuffer(), path.getOffset(), path.getLength());
866 mappingData.wrapper = wrappers[pos].object;
867 mappingData.jspWildCard = wrappers[pos].jspWildCard;
868 }
869 }
870 }
871
872
873 /**
874 * Extension mappings.
875 */
876 private final void internalMapExtensionWrapper
877 (Wrapper[] wrappers, CharChunk path, MappingData mappingData) {
878 char[] buf = path.getBuffer();
879 int pathEnd = path.getEnd();
880 int servletPath = path.getOffset();
881 int slash = -1;
882 for (int i = pathEnd - 1; i >= servletPath; i--) {
883 if (buf[i] == '/') {
884 slash = i;
885 break;
886 }
887 }
888 if (slash >= 0) {
889 int period = -1;
890 for (int i = pathEnd - 1; i > slash; i--) {
891 if (buf[i] == '.') {
892 period = i;
893 break;
894 }
895 }
896 if (period >= 0) {
897 path.setOffset(period + 1);
898 path.setEnd(pathEnd);
899 int pos = find(wrappers, path);
900 if ((pos != -1)
901 && (path.equals(wrappers[pos].name))) {
902 mappingData.wrapperPath.setChars
903 (buf, servletPath, pathEnd - servletPath);
904 mappingData.requestPath.setChars
905 (buf, servletPath, pathEnd - servletPath);
906 mappingData.wrapper = wrappers[pos].object;
907 }
908 path.setOffset(servletPath);
909 path.setEnd(pathEnd);
910 }
911 }
912 }
913
914
915 /**
916 * Find a map elemnt given its name in a sorted array of map elements.
917 * This will return the index for the closest inferior or equal item in the
918 * given array.
919 */
920 private static final int find(MapElement[] map, CharChunk name) {
921 return find(map, name, name.getStart(), name.getEnd());
922 }
923
924
925 /**
926 * Find a map elemnt given its name in a sorted array of map elements.
927 * This will return the index for the closest inferior or equal item in the
928 * given array.
929 */
930 private static final int find(MapElement[] map, CharChunk name,
931 int start, int end) {
932
933 int a = 0;
934 int b = map.length - 1;
935
936 // Special cases: -1 and 0
937 if (b == -1) {
938 return -1;
939 }
940
941 if (compare(name, start, end, map[0].name) < 0 ) {
942 return -1;
943 }
944 if (b == 0) {
945 return 0;
946 }
947
948 int i = 0;
949 while (true) {
950 i = (b + a) / 2;
951 int result = compare(name, start, end, map[i].name);
952 if (result == 1) {
953 a = i;
954 } else if (result == 0) {
955 return i;
956 } else {
957 b = i;
958 }
959 if ((b - a) == 1) {
960 int result2 = compare(name, start, end, map[b].name);
961 if (result2 < 0) {
962 return a;
963 } else {
964 return b;
965 }
966 }
967 }
968
969 }
970
971 /**
972 * Find a map elemnt given its name in a sorted array of map elements.
973 * This will return the index for the closest inferior or equal item in the
974 * given array.
975 */
976 private static final int findIgnoreCase(MapElement[] map, CharChunk name) {
977 return findIgnoreCase(map, name, name.getStart(), name.getEnd());
978 }
979
980
981 /**
982 * Find a map elemnt given its name in a sorted array of map elements.
983 * This will return the index for the closest inferior or equal item in the
984 * given array.
985 */
986 private static final int findIgnoreCase(MapElement[] map, CharChunk name,
987 int start, int end) {
988
989 int a = 0;
990 int b = map.length - 1;
991
992 // Special cases: -1 and 0
993 if (b == -1) {
994 return -1;
995 }
996 if (compareIgnoreCase(name, start, end, map[0].name) < 0 ) {
997 return -1;
998 }
999 if (b == 0) {
1000 return 0;
1001 }
1002
1003 int i = 0;
1004 while (true) {
1005 i = (b + a) / 2;
1006 int result = compareIgnoreCase(name, start, end, map[i].name);
1007 if (result == 1) {
1008 a = i;
1009 } else if (result == 0) {
1010 return i;
1011 } else {
1012 b = i;
1013 }
1014 if ((b - a) == 1) {
1015 int result2 = compareIgnoreCase(name, start, end, map[b].name);
1016 if (result2 < 0) {
1017 return a;
1018 } else {
1019 return b;
1020 }
1021 }
1022 }
1023
1024 }
1025
1026
1027 /**
1028 * Find a map elemnt given its name in a sorted array of map elements.
1029 * This will return the index for the closest inferior or equal item in the
1030 * given array.
1031 */
1032 private static final int find(MapElement[] map, String name) {
1033
1034 int a = 0;
1035 int b = map.length - 1;
1036
1037 // Special cases: -1 and 0
1038 if (b == -1) {
1039 return -1;
1040 }
1041
1042 if (name.compareTo(map[0].name) < 0) {
1043 return -1;
1044 }
1045 if (b == 0) {
1046 return 0;
1047 }
1048
1049 int i = 0;
1050 while (true) {
1051 i = (b + a) / 2;
1052 int result = name.compareTo(map[i].name);
1053 if (result > 0) {
1054 a = i;
1055 } else if (result == 0) {
1056 return i;
1057 } else {
1058 b = i;
1059 }
1060 if ((b - a) == 1) {
1061 int result2 = name.compareTo(map[b].name);
1062 if (result2 < 0) {
1063 return a;
1064 } else {
1065 return b;
1066 }
1067 }
1068 }
1069
1070 }
1071
1072
1073 /**
1074 * Compare given char chunk with String.
1075 * Return -1, 0 or +1 if inferior, equal, or superior to the String.
1076 */
1077 private static final int compare(CharChunk name, int start, int end,
1078 String compareTo) {
1079 int result = 0;
1080 char[] c = name.getBuffer();
1081 int len = compareTo.length();
1082 if ((end - start) < len) {
1083 len = end - start;
1084 }
1085 for (int i = 0; (i < len) && (result == 0); i++) {
1086 if (c[i + start] > compareTo.charAt(i)) {
1087 result = 1;
1088 } else if (c[i + start] < compareTo.charAt(i)) {
1089 result = -1;
1090 }
1091 }
1092 if (result == 0) {
1093 if (compareTo.length() > (end - start)) {
1094 result = -1;
1095 } else if (compareTo.length() < (end - start)) {
1096 result = 1;
1097 }
1098 }
1099 return result;
1100 }
1101
1102
1103 /**
1104 * Compare given char chunk with String ignoring case.
1105 * Return -1, 0 or +1 if inferior, equal, or superior to the String.
1106 */
1107 private static final int compareIgnoreCase(CharChunk name, int start, int end,
1108 String compareTo) {
1109 int result = 0;
1110 char[] c = name.getBuffer();
1111 int len = compareTo.length();
1112 if ((end - start) < len) {
1113 len = end - start;
1114 }
1115 for (int i = 0; (i < len) && (result == 0); i++) {
1116 if (Ascii.toLower(c[i + start]) > Ascii.toLower(compareTo.charAt(i))) {
1117 result = 1;
1118 } else if (Ascii.toLower(c[i + start]) < Ascii.toLower(compareTo.charAt(i))) {
1119 result = -1;
1120 }
1121 }
1122 if (result == 0) {
1123 if (compareTo.length() > (end - start)) {
1124 result = -1;
1125 } else if (compareTo.length() < (end - start)) {
1126 result = 1;
1127 }
1128 }
1129 return result;
1130 }
1131
1132
1133 /**
1134 * Find the position of the last slash in the given char chunk.
1135 */
1136 private static final int lastSlash(CharChunk name) {
1137
1138 char[] c = name.getBuffer();
1139 int end = name.getEnd();
1140 int start = name.getStart();
1141 int pos = end;
1142
1143 while (pos > start) {
1144 if (c[--pos] == '/') {
1145 break;
1146 }
1147 }
1148
1149 return (pos);
1150
1151 }
1152
1153
1154 /**
1155 * Find the position of the nth slash, in the given char chunk.
1156 */
1157 private static final int nthSlash(CharChunk name, int n) {
1158
1159 char[] c = name.getBuffer();
1160 int end = name.getEnd();
1161 int start = name.getStart();
1162 int pos = start;
1163 int count = 0;
1164
1165 while (pos < end) {
1166 if ((c[pos++] == '/') && ((++count) == n)) {
1167 pos--;
1168 break;
1169 }
1170 }
1171
1172 return (pos);
1173
1174 }
1175
1176
1177 /**
1178 * Return the slash count in a given string.
1179 */
1180 private static final int slashCount(String name) {
1181 int pos = -1;
1182 int count = 0;
1183 while ((pos = name.indexOf('/', pos + 1)) != -1) {
1184 count++;
1185 }
1186 return count;
1187 }
1188
1189
1190 /**
1191 * Insert into the right place in a sorted MapElement array, and prevent
1192 * duplicates.
1193 */
1194 private static final boolean insertMap
1195 (MapElement[] oldMap, MapElement[] newMap, MapElement newElement) {
1196 int pos = find(oldMap, newElement.name);
1197 if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
1198 return false;
1199 }
1200 System.arraycopy(oldMap, 0, newMap, 0, pos + 1);
1201 newMap[pos + 1] = newElement;
1202 System.arraycopy
1203 (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1);
1204 return true;
1205 }
1206
1207
1208 /**
1209 * Insert into the right place in a sorted MapElement array.
1210 */
1211 private static final boolean removeMap
1212 (MapElement[] oldMap, MapElement[] newMap, String name) {
1213 int pos = find(oldMap, name);
1214 if ((pos != -1) && (name.equals(oldMap[pos].name))) {
1215 System.arraycopy(oldMap, 0, newMap, 0, pos);
1216 System.arraycopy(oldMap, pos + 1, newMap, pos,
1217 oldMap.length - pos - 1);
1218 return true;
1219 }
1220 return false;
1221 }
1222
1223
1224 // ------------------------------------------------- MapElement Inner Class
1225
1226
1227 protected static abstract class MapElement {
1228
1229 public String name = null;
1230 public Object object = null;
1231
1232 }
1233
1234
1235 // ------------------------------------------------------- Host Inner Class
1236
1237
1238 protected static final class Host
1239 extends MapElement {
1240
1241 public ContextList contextList = null;
1242
1243 }
1244
1245
1246 // ------------------------------------------------ ContextList Inner Class
1247
1248
1249 protected static final class ContextList {
1250
1251 public Context[] contexts = new Context[0];
1252 public int nesting = 0;
1253
1254 }
1255
1256
1257 // ---------------------------------------------------- Context Inner Class
1258
1259
1260 protected static final class Context
1261 extends MapElement {
1262
1263 public String path = null;
1264 public String[] welcomeResources = new String[0];
1265 public javax.naming.Context resources = null;
1266 public Wrapper defaultWrapper = null;
1267 public Wrapper[] exactWrappers = new Wrapper[0];
1268 public Wrapper[] wildcardWrappers = new Wrapper[0];
1269 public Wrapper[] extensionWrappers = new Wrapper[0];
1270 public int nesting = 0;
1271
1272 }
1273
1274
1275 // ---------------------------------------------------- Wrapper Inner Class
1276
1277
1278 protected static class Wrapper
1279 extends MapElement {
1280
1281 public String path = null;
1282 public boolean jspWildCard = false;
1283 }
1284
1285
1286 // -------------------------------------------------------- Testing Methods
1287
1288 // FIXME: Externalize this
1289 /*
1290 public static void main(String args[]) {
1291
1292 try {
1293
1294 Mapper mapper = new Mapper();
1295 System.out.println("Start");
1296
1297 mapper.addHost("sjbjdvwsbvhrb", new String[0], "blah1");
1298 mapper.addHost("sjbjdvwsbvhr/", new String[0], "blah1");
1299 mapper.addHost("wekhfewuifweuibf", new String[0], "blah2");
1300 mapper.addHost("ylwrehirkuewh", new String[0], "blah3");
1301 mapper.addHost("iohgeoihro", new String[0], "blah4");
1302 mapper.addHost("fwehoihoihwfeo", new String[0], "blah5");
1303 mapper.addHost("owefojiwefoi", new String[0], "blah6");
1304 mapper.addHost("iowejoiejfoiew", new String[0], "blah7");
1305 mapper.addHost("iowejoiejfoiew", new String[0], "blah17");
1306 mapper.addHost("ohewoihfewoih", new String[0], "blah8");
1307 mapper.addHost("fewohfoweoih", new String[0], "blah9");
1308 mapper.addHost("ttthtiuhwoih", new String[0], "blah10");
1309 mapper.addHost("lkwefjwojweffewoih", new String[0], "blah11");
1310 mapper.addHost("zzzuyopjvewpovewjhfewoih", new String[0], "blah12");
1311 mapper.addHost("xxxxgqwiwoih", new String[0], "blah13");
1312 mapper.addHost("qwigqwiwoih", new String[0], "blah14");
1313
1314 System.out.println("Map:");
1315 for (int i = 0; i < mapper.hosts.length; i++) {
1316 System.out.println(mapper.hosts[i].name);
1317 }
1318
1319 mapper.setDefaultHostName("ylwrehirkuewh");
1320
1321 String[] welcomes = new String[2];
1322 welcomes[0] = "boo/baba";
1323 welcomes[1] = "bobou";
1324
1325 mapper.addContext("iowejoiejfoiew", "", "context0", new String[0], null);
1326 mapper.addContext("iowejoiejfoiew", "/foo", "context1", new String[0], null);
1327 mapper.addContext("iowejoiejfoiew", "/foo/bar", "context2", welcomes, null);
1328 mapper.addContext("iowejoiejfoiew", "/foo/bar/bla", "context3", new String[0], null);
1329
1330 mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/fo/*", "wrapper0");
1331 mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/", "wrapper1");
1332 mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blh", "wrapper2");
1333 mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.jsp", "wrapper3");
1334 mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bou/*", "wrapper4");
1335 mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "/blah/bobou/*", "wrapper5");
1336 mapper.addWrapper("iowejoiejfoiew", "/foo/bar", "*.htm", "wrapper6");
1337
1338 MappingData mappingData = new MappingData();
1339 MessageBytes host = MessageBytes.newInstance();
1340 host.setString("iowejoiejfoiew");
1341 MessageBytes uri = MessageBytes.newInstance();
1342 uri.setString("/foo/bar/blah/bobou/foo");
1343 uri.toChars();
1344 uri.getCharChunk().setLimit(-1);
1345
1346 mapper.map(host, uri, mappingData);
1347 System.out.println("MD Host:" + mappingData.host);
1348 System.out.println("MD Context:" + mappingData.context);
1349 System.out.println("MD Wrapper:" + mappingData.wrapper);
1350
1351 System.out.println("contextPath:" + mappingData.contextPath);
1352 System.out.println("wrapperPath:" + mappingData.wrapperPath);
1353 System.out.println("pathInfo:" + mappingData.pathInfo);
1354 System.out.println("redirectPath:" + mappingData.redirectPath);
1355
1356 mappingData.recycle();
1357 mapper.map(host, uri, mappingData);
1358 System.out.println("MD Host:" + mappingData.host);
1359 System.out.println("MD Context:" + mappingData.context);
1360 System.out.println("MD Wrapper:" + mappingData.wrapper);
1361
1362 System.out.println("contextPath:" + mappingData.contextPath);
1363 System.out.println("wrapperPath:" + mappingData.wrapperPath);
1364 System.out.println("pathInfo:" + mappingData.pathInfo);
1365 System.out.println("redirectPath:" + mappingData.redirectPath);
1366
1367 for (int i = 0; i < 1000000; i++) {
1368 mappingData.recycle();
1369 mapper.map(host, uri, mappingData);
1370 }
1371
1372 long time = System.currentTimeMillis();
1373 for (int i = 0; i < 1000000; i++) {
1374 mappingData.recycle();
1375 mapper.map(host, uri, mappingData);
1376 }
1377 System.out.println("Elapsed:" + (System.currentTimeMillis() - time));
1378
1379 System.out.println("MD Host:" + mappingData.host);
1380 System.out.println("MD Context:" + mappingData.context);
1381 System.out.println("MD Wrapper:" + mappingData.wrapper);
1382
1383 System.out.println("contextPath:" + mappingData.contextPath);
1384 System.out.println("wrapperPath:" + mappingData.wrapperPath);
1385 System.out.println("requestPath:" + mappingData.requestPath);
1386 System.out.println("pathInfo:" + mappingData.pathInfo);
1387 System.out.println("redirectPath:" + mappingData.redirectPath);
1388
1389 } catch (Exception e) {
1390 e.printStackTrace();
1391 }
1392
1393 }
1394 */
1395
1396
1397 }