1
17 package org.apache.catalina.mapper;
18
19 import java.io.IOException;
20 import java.util.ArrayList;
21 import java.util.Arrays;
22 import java.util.Collection;
23 import java.util.List;
24 import java.util.Map;
25 import java.util.concurrent.ConcurrentHashMap;
26 import java.util.concurrent.CopyOnWriteArrayList;
27
28 import javax.servlet.http.MappingMatch;
29
30 import org.apache.catalina.Context;
31 import org.apache.catalina.Host;
32 import org.apache.catalina.WebResource;
33 import org.apache.catalina.WebResourceRoot;
34 import org.apache.catalina.Wrapper;
35 import org.apache.juli.logging.Log;
36 import org.apache.juli.logging.LogFactory;
37 import org.apache.tomcat.util.buf.Ascii;
38 import org.apache.tomcat.util.buf.CharChunk;
39 import org.apache.tomcat.util.buf.MessageBytes;
40 import org.apache.tomcat.util.res.StringManager;
41
42
48 public final class Mapper {
49
50
51 private static final Log log = LogFactory.getLog(Mapper.class);
52
53 private static final StringManager sm = StringManager.getManager(Mapper.class);
54
55
56
57
58
61
62 volatile MappedHost[] hosts = new MappedHost[0];
63
64
65
68 private volatile String defaultHostName = null;
69 private volatile MappedHost defaultHost = null;
70
71
72
76 private final Map<Context, ContextVersion> contextObjectToContextVersionMap =
77 new ConcurrentHashMap<>();
78
79
80
81
82
87 public synchronized void setDefaultHostName(String defaultHostName) {
88 this.defaultHostName = renameWildcardHost(defaultHostName);
89 if (this.defaultHostName == null) {
90 defaultHost = null;
91 } else {
92 defaultHost = exactFind(hosts, this.defaultHostName);
93 }
94 }
95
96
97
104 public synchronized void addHost(String name, String[] aliases,
105 Host host) {
106 name = renameWildcardHost(name);
107 MappedHost[] newHosts = new MappedHost[hosts.length + 1];
108 MappedHost newHost = new MappedHost(name, host);
109 if (insertMap(hosts, newHosts, newHost)) {
110 hosts = newHosts;
111 if (newHost.name.equals(defaultHostName)) {
112 defaultHost = newHost;
113 }
114 if (log.isDebugEnabled()) {
115 log.debug(sm.getString("mapper.addHost.success", name));
116 }
117 } else {
118 MappedHost duplicate = hosts[find(hosts, name)];
119 if (duplicate.object == host) {
120
121
122 if (log.isDebugEnabled()) {
123 log.debug(sm.getString("mapper.addHost.sameHost", name));
124 }
125 newHost = duplicate;
126 } else {
127 log.error(sm.getString("mapper.duplicateHost", name,
128 duplicate.getRealHostName()));
129
130
131 return;
132 }
133 }
134 List<MappedHost> newAliases = new ArrayList<>(aliases.length);
135 for (String alias : aliases) {
136 alias = renameWildcardHost(alias);
137 MappedHost newAlias = new MappedHost(alias, newHost);
138 if (addHostAliasImpl(newAlias)) {
139 newAliases.add(newAlias);
140 }
141 }
142 newHost.addAliases(newAliases);
143 }
144
145
146
151 public synchronized void removeHost(String name) {
152 name = renameWildcardHost(name);
153
154 MappedHost host = exactFind(hosts, name);
155 if (host == null || host.isAlias()) {
156 return;
157 }
158 MappedHost[] newHosts = hosts.clone();
159
160 int j = 0;
161 for (int i = 0; i < newHosts.length; i++) {
162 if (newHosts[i].getRealHost() != host) {
163 newHosts[j++] = newHosts[i];
164 }
165 }
166 hosts = Arrays.copyOf(newHosts, j);
167 }
168
169
174 public synchronized void addHostAlias(String name, String alias) {
175 MappedHost realHost = exactFind(hosts, name);
176 if (realHost == null) {
177
178
179 return;
180 }
181 alias = renameWildcardHost(alias);
182 MappedHost newAlias = new MappedHost(alias, realHost);
183 if (addHostAliasImpl(newAlias)) {
184 realHost.addAlias(newAlias);
185 }
186 }
187
188 private synchronized boolean addHostAliasImpl(MappedHost newAlias) {
189 MappedHost[] newHosts = new MappedHost[hosts.length + 1];
190 if (insertMap(hosts, newHosts, newAlias)) {
191 hosts = newHosts;
192 if (newAlias.name.equals(defaultHostName)) {
193 defaultHost = newAlias;
194 }
195 if (log.isDebugEnabled()) {
196 log.debug(sm.getString("mapper.addHostAlias.success",
197 newAlias.name, newAlias.getRealHostName()));
198 }
199 return true;
200 } else {
201 MappedHost duplicate = hosts[find(hosts, newAlias.name)];
202 if (duplicate.getRealHost() == newAlias.getRealHost()) {
203
204
205
206 if (log.isDebugEnabled()) {
207 log.debug(sm.getString("mapper.addHostAlias.sameHost",
208 newAlias.name, newAlias.getRealHostName()));
209 }
210 return false;
211 }
212 log.error(sm.getString("mapper.duplicateHostAlias", newAlias.name,
213 newAlias.getRealHostName(), duplicate.getRealHostName()));
214 return false;
215 }
216 }
217
218
222 public synchronized void removeHostAlias(String alias) {
223 alias = renameWildcardHost(alias);
224
225 MappedHost hostMapping = exactFind(hosts, alias);
226 if (hostMapping == null || !hostMapping.isAlias()) {
227 return;
228 }
229 MappedHost[] newHosts = new MappedHost[hosts.length - 1];
230 if (removeMap(hosts, newHosts, alias)) {
231 hosts = newHosts;
232 hostMapping.getRealHost().removeAlias(hostMapping);
233 }
234
235 }
236
237
241 private void updateContextList(MappedHost realHost,
242 ContextList newContextList) {
243
244 realHost.contextList = newContextList;
245 for (MappedHost alias : realHost.getAliases()) {
246 alias.contextList = newContextList;
247 }
248 }
249
250
262 public void addContextVersion(String hostName, Host host, String path,
263 String version, Context context, String[] welcomeResources,
264 WebResourceRoot resources, Collection<WrapperMappingInfo> wrappers) {
265
266 hostName = renameWildcardHost(hostName);
267
268 MappedHost mappedHost = exactFind(hosts, hostName);
269 if (mappedHost == null) {
270 addHost(hostName, new String[0], host);
271 mappedHost = exactFind(hosts, hostName);
272 if (mappedHost == null) {
273 log.error(sm.getString("mapper.addContext.noHost", hostName));
274 return;
275 }
276 }
277 if (mappedHost.isAlias()) {
278 log.error(sm.getString("mapper.addContext.hostIsAlias", hostName));
279 return;
280 }
281 int slashCount = slashCount(path);
282 synchronized (mappedHost) {
283 ContextVersion newContextVersion = new ContextVersion(version,
284 path, slashCount, context, resources, welcomeResources);
285 if (wrappers != null) {
286 addWrappers(newContextVersion, wrappers);
287 }
288
289 ContextList contextList = mappedHost.contextList;
290 MappedContext mappedContext = exactFind(contextList.contexts, path);
291 if (mappedContext == null) {
292 mappedContext = new MappedContext(path, newContextVersion);
293 ContextList newContextList = contextList.addContext(
294 mappedContext, slashCount);
295 if (newContextList != null) {
296 updateContextList(mappedHost, newContextList);
297 contextObjectToContextVersionMap.put(context, newContextVersion);
298 }
299 } else {
300 ContextVersion[] contextVersions = mappedContext.versions;
301 ContextVersion[] newContextVersions = new ContextVersion[contextVersions.length + 1];
302 if (insertMap(contextVersions, newContextVersions,
303 newContextVersion)) {
304 mappedContext.versions = newContextVersions;
305 contextObjectToContextVersionMap.put(context, newContextVersion);
306 } else {
307
308
309 int pos = find(contextVersions, version);
310 if (pos >= 0 && contextVersions[pos].name.equals(version)) {
311 contextVersions[pos] = newContextVersion;
312 contextObjectToContextVersionMap.put(context, newContextVersion);
313 }
314 }
315 }
316 }
317
318 }
319
320
321
329 public void removeContextVersion(Context ctxt, String hostName,
330 String path, String version) {
331
332 hostName = renameWildcardHost(hostName);
333 contextObjectToContextVersionMap.remove(ctxt);
334
335 MappedHost host = exactFind(hosts, hostName);
336 if (host == null || host.isAlias()) {
337 return;
338 }
339
340 synchronized (host) {
341 ContextList contextList = host.contextList;
342 MappedContext context = exactFind(contextList.contexts, path);
343 if (context == null) {
344 return;
345 }
346
347 ContextVersion[] contextVersions = context.versions;
348 ContextVersion[] newContextVersions =
349 new ContextVersion[contextVersions.length - 1];
350 if (removeMap(contextVersions, newContextVersions, version)) {
351 if (newContextVersions.length == 0) {
352
353 ContextList newContextList = contextList.removeContext(path);
354 if (newContextList != null) {
355 updateContextList(host, newContextList);
356 }
357 } else {
358 context.versions = newContextVersions;
359 }
360 }
361 }
362 }
363
364
365
374 public void pauseContextVersion(Context ctxt, String hostName,
375 String contextPath, String version) {
376 hostName = renameWildcardHost(hostName);
377 ContextVersion contextVersion = findContextVersion(hostName,
378 contextPath, version, true);
379 if (contextVersion == null || !ctxt.equals(contextVersion.object)) {
380 return;
381 }
382 contextVersion.markPaused();
383 }
384
385
386 private ContextVersion findContextVersion(String hostName,
387 String contextPath, String version, boolean silent) {
388 MappedHost host = exactFind(hosts, hostName);
389 if (host == null || host.isAlias()) {
390 if (!silent) {
391 log.error(sm.getString("mapper.findContext.noHostOrAlias", hostName));
392 }
393 return null;
394 }
395 MappedContext context = exactFind(host.contextList.contexts,
396 contextPath);
397 if (context == null) {
398 if (!silent) {
399 log.error(sm.getString("mapper.findContext.noContext", contextPath));
400 }
401 return null;
402 }
403 ContextVersion contextVersion = exactFind(context.versions, version);
404 if (contextVersion == null) {
405 if (!silent) {
406 log.error(sm.getString("mapper.findContext.noContextVersion", contextPath, version));
407 }
408 return null;
409 }
410 return contextVersion;
411 }
412
413
414 public void addWrapper(String hostName, String contextPath, String version,
415 String path, Wrapper wrapper, boolean jspWildCard,
416 boolean resourceOnly) {
417 hostName = renameWildcardHost(hostName);
418 ContextVersion contextVersion = findContextVersion(hostName,
419 contextPath, version, false);
420 if (contextVersion == null) {
421 return;
422 }
423 addWrapper(contextVersion, path, wrapper, jspWildCard, resourceOnly);
424 }
425
426 public void addWrappers(String hostName, String contextPath,
427 String version, Collection<WrapperMappingInfo> wrappers) {
428 hostName = renameWildcardHost(hostName);
429 ContextVersion contextVersion = findContextVersion(hostName,
430 contextPath, version, false);
431 if (contextVersion == null) {
432 return;
433 }
434 addWrappers(contextVersion, wrappers);
435 }
436
437
443 private void addWrappers(ContextVersion contextVersion,
444 Collection<WrapperMappingInfo> wrappers) {
445 for (WrapperMappingInfo wrapper : wrappers) {
446 addWrapper(contextVersion, wrapper.getMapping(),
447 wrapper.getWrapper(), wrapper.isJspWildCard(),
448 wrapper.isResourceOnly());
449 }
450 }
451
452
463 protected void addWrapper(ContextVersion context, String path,
464 Wrapper wrapper, boolean jspWildCard, boolean resourceOnly) {
465
466 synchronized (context) {
467 if (path.endsWith(")) {
468
469 String name = path.substring(0, path.length() - 2);
470 MappedWrapper newWrapper = new MappedWrapper(name, wrapper,
471 jspWildCard, resourceOnly);
472 MappedWrapper[] oldWrappers = context.wildcardWrappers;
473 MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1];
474 if (insertMap(oldWrappers, newWrappers, newWrapper)) {
475 context.wildcardWrappers = newWrappers;
476 int slashCount = slashCount(newWrapper.name);
477 if (slashCount > context.nesting) {
478 context.nesting = slashCount;
479 }
480 }
481 } else if (path.startsWith("*.")) {
482
483 String name = path.substring(2);
484 MappedWrapper newWrapper = new MappedWrapper(name, wrapper,
485 jspWildCard, resourceOnly);
486 MappedWrapper[] oldWrappers = context.extensionWrappers;
487 MappedWrapper[] newWrappers =
488 new MappedWrapper[oldWrappers.length + 1];
489 if (insertMap(oldWrappers, newWrappers, newWrapper)) {
490 context.extensionWrappers = newWrappers;
491 }
492 } else if (path.equals("/")) {
493
494 MappedWrapper newWrapper = new MappedWrapper("", wrapper,
495 jspWildCard, resourceOnly);
496 context.defaultWrapper = newWrapper;
497 } else {
498
499 final String name;
500 if (path.length() == 0) {
501
502
503 name = "/";
504 } else {
505 name = path;
506 }
507 MappedWrapper newWrapper = new MappedWrapper(name, wrapper,
508 jspWildCard, resourceOnly);
509 MappedWrapper[] oldWrappers = context.exactWrappers;
510 MappedWrapper[] newWrappers = new MappedWrapper[oldWrappers.length + 1];
511 if (insertMap(oldWrappers, newWrappers, newWrapper)) {
512 context.exactWrappers = newWrappers;
513 }
514 }
515 }
516 }
517
518
519 /**
520 * Remove a wrapper from an existing context.
521 *
522 * @param hostName Virtual host name this wrapper belongs to
523 * @param contextPath Context path this wrapper belongs to
524 * @param version Context version this wrapper belongs to
525 * @param path Wrapper mapping
526 */
527 public void removeWrapper(String hostName, String contextPath,
528 String version, String path) {
529 hostName = renameWildcardHost(hostName);
530 ContextVersion contextVersion = findContextVersion(hostName,
531 contextPath, version, true);
532 if (contextVersion == null || contextVersion.isPaused()) {
533 return;
534 }
535 removeWrapper(contextVersion, path);
536 }
537
538 protected void removeWrapper(ContextVersion context, String path) {
539
540 if (log.isDebugEnabled()) {
541 log.debug(sm.getString("mapper.removeWrapper", context.name, path));
542 }
543
544 synchronized (context) {
545 if (path.endsWith(")) {
546
547 String name = path.substring(0, path.length() - 2);
548 MappedWrapper[] oldWrappers = context.wildcardWrappers;
549 if (oldWrappers.length == 0) {
550 return;
551 }
552 MappedWrapper[] newWrappers =
553 new MappedWrapper[oldWrappers.length - 1];
554 if (removeMap(oldWrappers, newWrappers, name)) {
555
556 context.nesting = 0;
557 for (int i = 0; i < newWrappers.length; i++) {
558 int slashCount = slashCount(newWrappers[i].name);
559 if (slashCount > context.nesting) {
560 context.nesting = slashCount;
561 }
562 }
563 context.wildcardWrappers = newWrappers;
564 }
565 } else if (path.startsWith("*.")) {
566
567 String name = path.substring(2);
568 MappedWrapper[] oldWrappers = context.extensionWrappers;
569 if (oldWrappers.length == 0) {
570 return;
571 }
572 MappedWrapper[] newWrappers =
573 new MappedWrapper[oldWrappers.length - 1];
574 if (removeMap(oldWrappers, newWrappers, name)) {
575 context.extensionWrappers = newWrappers;
576 }
577 } else if (path.equals("/")) {
578
579 context.defaultWrapper = null;
580 } else {
581
582 String name;
583 if (path.length() == 0) {
584
585
586 name = "/";
587 } else {
588 name = path;
589 }
590 MappedWrapper[] oldWrappers = context.exactWrappers;
591 if (oldWrappers.length == 0) {
592 return;
593 }
594 MappedWrapper[] newWrappers =
595 new MappedWrapper[oldWrappers.length - 1];
596 if (removeMap(oldWrappers, newWrappers, name)) {
597 context.exactWrappers = newWrappers;
598 }
599 }
600 }
601 }
602
603
604 /**
605 * Add a welcome file to the given context.
606 *
607 * @param hostName The host where the given context can be found
608 * @param contextPath The path of the given context
609 * @param version The version of the given context
610 * @param welcomeFile The welcome file to add
611 */
612 public void addWelcomeFile(String hostName, String contextPath, String version,
613 String welcomeFile) {
614 hostName = renameWildcardHost(hostName);
615 ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
616 if (contextVersion == null) {
617 return;
618 }
619 int len = contextVersion.welcomeResources.length + 1;
620 String[] newWelcomeResources = new String[len];
621 System.arraycopy(contextVersion.welcomeResources, 0, newWelcomeResources, 0, len - 1);
622 newWelcomeResources[len - 1] = welcomeFile;
623 contextVersion.welcomeResources = newWelcomeResources;
624 }
625
626
627
635 public void removeWelcomeFile(String hostName, String contextPath,
636 String version, String welcomeFile) {
637 hostName = renameWildcardHost(hostName);
638 ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
639 if (contextVersion == null || contextVersion.isPaused()) {
640 return;
641 }
642 int match = -1;
643 for (int i = 0; i < contextVersion.welcomeResources.length; i++) {
644 if (welcomeFile.equals(contextVersion.welcomeResources[i])) {
645 match = i;
646 break;
647 }
648 }
649 if (match > -1) {
650 int len = contextVersion.welcomeResources.length - 1;
651 String[] newWelcomeResources = new String[len];
652 System.arraycopy(contextVersion.welcomeResources, 0, newWelcomeResources, 0, match);
653 if (match < len) {
654 System.arraycopy(contextVersion.welcomeResources, match + 1,
655 newWelcomeResources, match, len - match);
656 }
657 contextVersion.welcomeResources = newWelcomeResources;
658 }
659 }
660
661
662
669 public void clearWelcomeFiles(String hostName, String contextPath, String version) {
670 hostName = renameWildcardHost(hostName);
671 ContextVersion contextVersion = findContextVersion(hostName, contextPath, version, false);
672 if (contextVersion == null) {
673 return;
674 }
675 contextVersion.welcomeResources = new String[0];
676 }
677
678
679
690 public void map(MessageBytes host, MessageBytes uri, String version,
691 MappingData mappingData) throws IOException {
692
693 if (host.isNull()) {
694 String defaultHostName = this.defaultHostName;
695 if (defaultHostName == null) {
696 return;
697 }
698 host.getCharChunk().append(defaultHostName);
699 }
700 host.toChars();
701 uri.toChars();
702 internalMap(host.getCharChunk(), uri.getCharChunk(), version, mappingData);
703 }
704
705
706
717 public void map(Context context, MessageBytes uri,
718 MappingData mappingData) throws IOException {
719
720 ContextVersion contextVersion =
721 contextObjectToContextVersionMap.get(context);
722 uri.toChars();
723 CharChunk uricc = uri.getCharChunk();
724 uricc.setLimit(-1);
725 internalMapWrapper(contextVersion, uricc, mappingData);
726 }
727
728
729
730
731
735 @SuppressWarnings("deprecation")
736 private final void internalMap(CharChunk host, CharChunk uri,
737 String version, MappingData mappingData) throws IOException {
738
739 if (mappingData.host != null) {
740
741
742
743
744 throw new AssertionError();
745 }
746
747
748 MappedHost[] hosts = this.hosts;
749 MappedHost mappedHost = exactFindIgnoreCase(hosts, host);
750 if (mappedHost == null) {
751
752
753 int firstDot = host.indexOf('.');
754 if (firstDot > -1) {
755 int offset = host.getOffset();
756 try {
757 host.setOffset(firstDot + offset);
758 mappedHost = exactFindIgnoreCase(hosts, host);
759 } finally {
760
761 host.setOffset(offset);
762 }
763 }
764 if (mappedHost == null) {
765 mappedHost = defaultHost;
766 if (mappedHost == null) {
767 return;
768 }
769 }
770 }
771 mappingData.host = mappedHost.object;
772
773 if (uri.isNull()) {
774
775 return;
776 }
777
778 uri.setLimit(-1);
779
780
781 ContextList contextList = mappedHost.contextList;
782 MappedContext[] contexts = contextList.contexts;
783 int pos = find(contexts, uri);
784 if (pos == -1) {
785 return;
786 }
787
788 int lastSlash = -1;
789 int uriEnd = uri.getEnd();
790 int length = -1;
791 boolean found = false;
792 MappedContext context = null;
793 while (pos >= 0) {
794 context = contexts[pos];
795 if (uri.startsWith(context.name)) {
796 length = context.name.length();
797 if (uri.getLength() == length) {
798 found = true;
799 break;
800 } else if (uri.startsWithIgnoreCase("/", length)) {
801 found = true;
802 break;
803 }
804 }
805 if (lastSlash == -1) {
806 lastSlash = nthSlash(uri, contextList.nesting + 1);
807 } else {
808 lastSlash = lastSlash(uri);
809 }
810 uri.setEnd(lastSlash);
811 pos = find(contexts, uri);
812 }
813 uri.setEnd(uriEnd);
814
815 if (!found) {
816 if (contexts[0].name.equals("")) {
817 context = contexts[0];
818 } else {
819 context = null;
820 }
821 }
822 if (context == null) {
823 return;
824 }
825
826 mappingData.contextPath.setString(context.name);
827
828 ContextVersion contextVersion = null;
829 ContextVersion[] contextVersions = context.versions;
830 final int versionCount = contextVersions.length;
831 if (versionCount > 1) {
832 Context[] contextObjects = new Context[contextVersions.length];
833 for (int i = 0; i < contextObjects.length; i++) {
834 contextObjects[i] = contextVersions[i].object;
835 }
836 mappingData.contexts = contextObjects;
837 if (version != null) {
838 contextVersion = exactFind(contextVersions, version);
839 }
840 }
841 if (contextVersion == null) {
842
843
844 contextVersion = contextVersions[versionCount - 1];
845 }
846 mappingData.context = contextVersion.object;
847 mappingData.contextSlashCount = contextVersion.slashCount;
848
849
850 if (!contextVersion.isPaused()) {
851 internalMapWrapper(contextVersion, uri, mappingData);
852 }
853
854 }
855
856
857
862 private final void internalMapWrapper(ContextVersion contextVersion,
863 CharChunk path,
864 MappingData mappingData) throws IOException {
865
866 int pathOffset = path.getOffset();
867 int pathEnd = path.getEnd();
868 boolean noServletPath = false;
869
870 int length = contextVersion.path.length();
871 if (length == (pathEnd - pathOffset)) {
872 noServletPath = true;
873 }
874 int servletPath = pathOffset + length;
875 path.setOffset(servletPath);
876
877
878 MappedWrapper[] exactWrappers = contextVersion.exactWrappers;
879 internalMapExactWrapper(exactWrappers, path, mappingData);
880
881
882 boolean checkJspWelcomeFiles = false;
883 MappedWrapper[] wildcardWrappers = contextVersion.wildcardWrappers;
884 if (mappingData.wrapper == null) {
885 internalMapWildcardWrapper(wildcardWrappers, contextVersion.nesting,
886 path, mappingData);
887 if (mappingData.wrapper != null && mappingData.jspWildCard) {
888 char[] buf = path.getBuffer();
889 if (buf[pathEnd - 1] == '/') {
890
898 mappingData.wrapper = null;
899 checkJspWelcomeFiles = true;
900 } else {
901
902 mappingData.wrapperPath.setChars(buf, path.getStart(),
903 path.getLength());
904 mappingData.pathInfo.recycle();
905 }
906 }
907 }
908
909 if(mappingData.wrapper == null && noServletPath &&
910 contextVersion.object.getMapperContextRootRedirectEnabled()) {
911
912 path.append('/');
913 pathEnd = path.getEnd();
914 mappingData.redirectPath.setChars
915 (path.getBuffer(), pathOffset, pathEnd - pathOffset);
916 path.setEnd(pathEnd - 1);
917 return;
918 }
919
920
921 MappedWrapper[] extensionWrappers = contextVersion.extensionWrappers;
922 if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
923 internalMapExtensionWrapper(extensionWrappers, path, mappingData,
924 true);
925 }
926
927
928 if (mappingData.wrapper == null) {
929 boolean checkWelcomeFiles = checkJspWelcomeFiles;
930 if (!checkWelcomeFiles) {
931 char[] buf = path.getBuffer();
932 checkWelcomeFiles = (buf[pathEnd - 1] == '/');
933 }
934 if (checkWelcomeFiles) {
935 for (int i = 0; (i < contextVersion.welcomeResources.length)
936 && (mappingData.wrapper == null); i++) {
937 path.setOffset(pathOffset);
938 path.setEnd(pathEnd);
939 path.append(contextVersion.welcomeResources[i], 0,
940 contextVersion.welcomeResources[i].length());
941 path.setOffset(servletPath);
942
943
944 internalMapExactWrapper(exactWrappers, path, mappingData);
945
946
947 if (mappingData.wrapper == null) {
948 internalMapWildcardWrapper
949 (wildcardWrappers, contextVersion.nesting,
950 path, mappingData);
951 }
952
953
954
955 if (mappingData.wrapper == null
956 && contextVersion.resources != null) {
957 String pathStr = path.toString();
958 WebResource file =
959 contextVersion.resources.getResource(pathStr);
960 if (file != null && file.isFile()) {
961 internalMapExtensionWrapper(extensionWrappers, path,
962 mappingData, true);
963 if (mappingData.wrapper == null
964 && contextVersion.defaultWrapper != null) {
965 mappingData.wrapper =
966 contextVersion.defaultWrapper.object;
967 mappingData.requestPath.setChars
968 (path.getBuffer(), path.getStart(),
969 path.getLength());
970 mappingData.wrapperPath.setChars
971 (path.getBuffer(), path.getStart(),
972 path.getLength());
973 mappingData.requestPath.setString(pathStr);
974 mappingData.wrapperPath.setString(pathStr);
975 }
976 }
977 }
978 }
979
980 path.setOffset(servletPath);
981 path.setEnd(pathEnd);
982 }
983
984 }
985
986
993 if (mappingData.wrapper == null) {
994 boolean checkWelcomeFiles = checkJspWelcomeFiles;
995 if (!checkWelcomeFiles) {
996 char[] buf = path.getBuffer();
997 checkWelcomeFiles = (buf[pathEnd - 1] == '/');
998 }
999 if (checkWelcomeFiles) {
1000 for (int i = 0; (i < contextVersion.welcomeResources.length)
1001 && (mappingData.wrapper == null); i++) {
1002 path.setOffset(pathOffset);
1003 path.setEnd(pathEnd);
1004 path.append(contextVersion.welcomeResources[i], 0,
1005 contextVersion.welcomeResources[i].length());
1006 path.setOffset(servletPath);
1007 internalMapExtensionWrapper(extensionWrappers, path,
1008 mappingData, false);
1009 }
1010
1011 path.setOffset(servletPath);
1012 path.setEnd(pathEnd);
1013 }
1014 }
1015
1016
1017
1018 if (mappingData.wrapper == null && !checkJspWelcomeFiles) {
1019 if (contextVersion.defaultWrapper != null) {
1020 mappingData.wrapper = contextVersion.defaultWrapper.object;
1021 mappingData.requestPath.setChars
1022 (path.getBuffer(), path.getStart(), path.getLength());
1023 mappingData.wrapperPath.setChars
1024 (path.getBuffer(), path.getStart(), path.getLength());
1025 mappingData.matchType = MappingMatch.DEFAULT;
1026 }
1027
1028 char[] buf = path.getBuffer();
1029 if (contextVersion.resources != null && buf[pathEnd -1 ] != '/') {
1030 String pathStr = path.toString();
1031
1032
1033 if (contextVersion.object.getMapperDirectoryRedirectEnabled()) {
1034 WebResource file;
1035
1036 if (pathStr.length() == 0) {
1037 file = contextVersion.resources.getResource("/");
1038 } else {
1039 file = contextVersion.resources.getResource(pathStr);
1040 }
1041 if (file != null && file.isDirectory()) {
1042
1043
1044
1045 path.setOffset(pathOffset);
1046 path.append('/');
1047 mappingData.redirectPath.setChars
1048 (path.getBuffer(), path.getStart(), path.getLength());
1049 } else {
1050 mappingData.requestPath.setString(pathStr);
1051 mappingData.wrapperPath.setString(pathStr);
1052 }
1053 } else {
1054 mappingData.requestPath.setString(pathStr);
1055 mappingData.wrapperPath.setString(pathStr);
1056 }
1057 }
1058 }
1059
1060 path.setOffset(pathOffset);
1061 path.setEnd(pathEnd);
1062 }
1063
1064
1065
1068 @SuppressWarnings("deprecation")
1069 private final void internalMapExactWrapper
1070 (MappedWrapper[] wrappers, CharChunk path, MappingData mappingData) {
1071 MappedWrapper wrapper = exactFind(wrappers, path);
1072 if (wrapper != null) {
1073 mappingData.requestPath.setString(wrapper.name);
1074 mappingData.wrapper = wrapper.object;
1075 if (path.equals("/")) {
1076
1077 mappingData.pathInfo.setString("/");
1078 mappingData.wrapperPath.setString("");
1079
1080 mappingData.contextPath.setString("");
1081 mappingData.matchType = MappingMatch.CONTEXT_ROOT;
1082 } else {
1083 mappingData.wrapperPath.setString(wrapper.name);
1084 mappingData.matchType = MappingMatch.EXACT;
1085 }
1086 }
1087 }
1088
1089
1090
1093 private final void internalMapWildcardWrapper
1094 (MappedWrapper[] wrappers, int nesting, CharChunk path,
1095 MappingData mappingData) {
1096
1097 int pathEnd = path.getEnd();
1098
1099 int lastSlash = -1;
1100 int length = -1;
1101 int pos = find(wrappers, path);
1102 if (pos != -1) {
1103 boolean found = false;
1104 while (pos >= 0) {
1105 if (path.startsWith(wrappers[pos].name)) {
1106 length = wrappers[pos].name.length();
1107 if (path.getLength() == length) {
1108 found = true;
1109 break;
1110 } else if (path.startsWithIgnoreCase("/", length)) {
1111 found = true;
1112 break;
1113 }
1114 }
1115 if (lastSlash == -1) {
1116 lastSlash = nthSlash(path, nesting + 1);
1117 } else {
1118 lastSlash = lastSlash(path);
1119 }
1120 path.setEnd(lastSlash);
1121 pos = find(wrappers, path);
1122 }
1123 path.setEnd(pathEnd);
1124 if (found) {
1125 mappingData.wrapperPath.setString(wrappers[pos].name);
1126 if (path.getLength() > length) {
1127 mappingData.pathInfo.setChars
1128 (path.getBuffer(),
1129 path.getOffset() + length,
1130 path.getLength() - length);
1131 }
1132 mappingData.requestPath.setChars
1133 (path.getBuffer(), path.getOffset(), path.getLength());
1134 mappingData.wrapper = wrappers[pos].object;
1135 mappingData.jspWildCard = wrappers[pos].jspWildCard;
1136 mappingData.matchType = MappingMatch.PATH;
1137 }
1138 }
1139 }
1140
1141
1142
1150 private final void internalMapExtensionWrapper(MappedWrapper[] wrappers,
1151 CharChunk path, MappingData mappingData, boolean resourceExpected) {
1152 char[] buf = path.getBuffer();
1153 int pathEnd = path.getEnd();
1154 int servletPath = path.getOffset();
1155 int slash = -1;
1156 for (int i = pathEnd - 1; i >= servletPath; i--) {
1157 if (buf[i] == '/') {
1158 slash = i;
1159 break;
1160 }
1161 }
1162 if (slash >= 0) {
1163 int period = -1;
1164 for (int i = pathEnd - 1; i > slash; i--) {
1165 if (buf[i] == '.') {
1166 period = i;
1167 break;
1168 }
1169 }
1170 if (period >= 0) {
1171 path.setOffset(period + 1);
1172 path.setEnd(pathEnd);
1173 MappedWrapper wrapper = exactFind(wrappers, path);
1174 if (wrapper != null
1175 && (resourceExpected || !wrapper.resourceOnly)) {
1176 mappingData.wrapperPath.setChars(buf, servletPath, pathEnd
1177 - servletPath);
1178 mappingData.requestPath.setChars(buf, servletPath, pathEnd
1179 - servletPath);
1180 mappingData.wrapper = wrapper.object;
1181 mappingData.matchType = MappingMatch.EXTENSION;
1182 }
1183 path.setOffset(servletPath);
1184 path.setEnd(pathEnd);
1185 }
1186 }
1187 }
1188
1189
1190
1195 private static final <T> int find(MapElement<T>[] map, CharChunk name) {
1196 return find(map, name, name.getStart(), name.getEnd());
1197 }
1198
1199
1200
1205 private static final <T> int find(MapElement<T>[] map, CharChunk name,
1206 int start, int end) {
1207
1208 int a = 0;
1209 int b = map.length - 1;
1210
1211
1212 if (b == -1) {
1213 return -1;
1214 }
1215
1216 if (compare(name, start, end, map[0].name) < 0 ) {
1217 return -1;
1218 }
1219 if (b == 0) {
1220 return 0;
1221 }
1222
1223 int i = 0;
1224 while (true) {
1225 i = (b + a) >>> 1;
1226 int result = compare(name, start, end, map[i].name);
1227 if (result == 1) {
1228 a = i;
1229 } else if (result == 0) {
1230 return i;
1231 } else {
1232 b = i;
1233 }
1234 if ((b - a) == 1) {
1235 int result2 = compare(name, start, end, map[b].name);
1236 if (result2 < 0) {
1237 return a;
1238 } else {
1239 return b;
1240 }
1241 }
1242 }
1243
1244 }
1245
1246
1251 private static final <T> int findIgnoreCase(MapElement<T>[] map, CharChunk name) {
1252 return findIgnoreCase(map, name, name.getStart(), name.getEnd());
1253 }
1254
1255
1256
1261 private static final <T> int findIgnoreCase(MapElement<T>[] map, CharChunk name,
1262 int start, int end) {
1263
1264 int a = 0;
1265 int b = map.length - 1;
1266
1267
1268 if (b == -1) {
1269 return -1;
1270 }
1271 if (compareIgnoreCase(name, start, end, map[0].name) < 0 ) {
1272 return -1;
1273 }
1274 if (b == 0) {
1275 return 0;
1276 }
1277
1278 int i = 0;
1279 while (true) {
1280 i = (b + a) >>> 1;
1281 int result = compareIgnoreCase(name, start, end, map[i].name);
1282 if (result == 1) {
1283 a = i;
1284 } else if (result == 0) {
1285 return i;
1286 } else {
1287 b = i;
1288 }
1289 if ((b - a) == 1) {
1290 int result2 = compareIgnoreCase(name, start, end, map[b].name);
1291 if (result2 < 0) {
1292 return a;
1293 } else {
1294 return b;
1295 }
1296 }
1297 }
1298
1299 }
1300
1301
1302
1308 private static final <T> int find(MapElement<T>[] map, String name) {
1309
1310 int a = 0;
1311 int b = map.length - 1;
1312
1313
1314 if (b == -1) {
1315 return -1;
1316 }
1317
1318 if (name.compareTo(map[0].name) < 0) {
1319 return -1;
1320 }
1321 if (b == 0) {
1322 return 0;
1323 }
1324
1325 int i = 0;
1326 while (true) {
1327 i = (b + a) >>> 1;
1328 int result = name.compareTo(map[i].name);
1329 if (result > 0) {
1330 a = i;
1331 } else if (result == 0) {
1332 return i;
1333 } else {
1334 b = i;
1335 }
1336 if ((b - a) == 1) {
1337 int result2 = name.compareTo(map[b].name);
1338 if (result2 < 0) {
1339 return a;
1340 } else {
1341 return b;
1342 }
1343 }
1344 }
1345
1346 }
1347
1348
1349
1355 private static final <T, E extends MapElement<T>> E exactFind(E[] map,
1356 String name) {
1357 int pos = find(map, name);
1358 if (pos >= 0) {
1359 E result = map[pos];
1360 if (name.equals(result.name)) {
1361 return result;
1362 }
1363 }
1364 return null;
1365 }
1366
1367
1372 private static final <T, E extends MapElement<T>> E exactFind(E[] map,
1373 CharChunk name) {
1374 int pos = find(map, name);
1375 if (pos >= 0) {
1376 E result = map[pos];
1377 if (name.equals(result.name)) {
1378 return result;
1379 }
1380 }
1381 return null;
1382 }
1383
1384
1390 private static final <T, E extends MapElement<T>> E exactFindIgnoreCase(
1391 E[] map, CharChunk name) {
1392 int pos = findIgnoreCase(map, name);
1393 if (pos >= 0) {
1394 E result = map[pos];
1395 if (name.equalsIgnoreCase(result.name)) {
1396 return result;
1397 }
1398 }
1399 return null;
1400 }
1401
1402
1403
1407 private static final int compare(CharChunk name, int start, int end,
1408 String compareTo) {
1409 int result = 0;
1410 char[] c = name.getBuffer();
1411 int len = compareTo.length();
1412 if ((end - start) < len) {
1413 len = end - start;
1414 }
1415 for (int i = 0; (i < len) && (result == 0); i++) {
1416 if (c[i + start] > compareTo.charAt(i)) {
1417 result = 1;
1418 } else if (c[i + start] < compareTo.charAt(i)) {
1419 result = -1;
1420 }
1421 }
1422 if (result == 0) {
1423 if (compareTo.length() > (end - start)) {
1424 result = -1;
1425 } else if (compareTo.length() < (end - start)) {
1426 result = 1;
1427 }
1428 }
1429 return result;
1430 }
1431
1432
1433
1437 private static final int compareIgnoreCase(CharChunk name, int start, int end,
1438 String compareTo) {
1439 int result = 0;
1440 char[] c = name.getBuffer();
1441 int len = compareTo.length();
1442 if ((end - start) < len) {
1443 len = end - start;
1444 }
1445 for (int i = 0; (i < len) && (result == 0); i++) {
1446 if (Ascii.toLower(c[i + start]) > Ascii.toLower(compareTo.charAt(i))) {
1447 result = 1;
1448 } else if (Ascii.toLower(c[i + start]) < Ascii.toLower(compareTo.charAt(i))) {
1449 result = -1;
1450 }
1451 }
1452 if (result == 0) {
1453 if (compareTo.length() > (end - start)) {
1454 result = -1;
1455 } else if (compareTo.length() < (end - start)) {
1456 result = 1;
1457 }
1458 }
1459 return result;
1460 }
1461
1462
1463
1466 private static final int lastSlash(CharChunk name) {
1467 char[] c = name.getBuffer();
1468 int end = name.getEnd();
1469 int start = name.getStart();
1470 int pos = end;
1471
1472 while (pos > start) {
1473 if (c[--pos] == '/') {
1474 break;
1475 }
1476 }
1477
1478 return pos;
1479 }
1480
1481
1482
1485 private static final int nthSlash(CharChunk name, int n) {
1486 char[] c = name.getBuffer();
1487 int end = name.getEnd();
1488 int start = name.getStart();
1489 int pos = start;
1490 int count = 0;
1491
1492 while (pos < end) {
1493 if ((c[pos++] == '/') && ((++count) == n)) {
1494 pos--;
1495 break;
1496 }
1497 }
1498
1499 return pos;
1500 }
1501
1502
1503
1506 private static final int slashCount(String name) {
1507 int pos = -1;
1508 int count = 0;
1509 while ((pos = name.indexOf('/', pos + 1)) != -1) {
1510 count++;
1511 }
1512 return count;
1513 }
1514
1515
1516
1520 private static final <T> boolean insertMap
1521 (MapElement<T>[] oldMap, MapElement<T>[] newMap, MapElement<T> newElement) {
1522 int pos = find(oldMap, newElement.name);
1523 if ((pos != -1) && (newElement.name.equals(oldMap[pos].name))) {
1524 return false;
1525 }
1526 System.arraycopy(oldMap, 0, newMap, 0, pos + 1);
1527 newMap[pos + 1] = newElement;
1528 System.arraycopy
1529 (oldMap, pos + 1, newMap, pos + 2, oldMap.length - pos - 1);
1530 return true;
1531 }
1532
1533
1534
1537 private static final <T> boolean removeMap
1538 (MapElement<T>[] oldMap, MapElement<T>[] newMap, String name) {
1539 int pos = find(oldMap, name);
1540 if ((pos != -1) && (name.equals(oldMap[pos].name))) {
1541 System.arraycopy(oldMap, 0, newMap, 0, pos);
1542 System.arraycopy(oldMap, pos + 1, newMap, pos,
1543 oldMap.length - pos - 1);
1544 return true;
1545 }
1546 return false;
1547 }
1548
1549
1550
1557 private static String renameWildcardHost(String hostName) {
1558 if (hostName != null && hostName.startsWith("*.")) {
1559 return hostName.substring(1);
1560 } else {
1561 return hostName;
1562 }
1563 }
1564
1565
1566
1567
1568
1569 protected abstract static class MapElement<T> {
1570
1571 public final String name;
1572 public final T object;
1573
1574 public MapElement(String name, T object) {
1575 this.name = name;
1576 this.object = object;
1577 }
1578 }
1579
1580
1581
1582
1583
1584 protected static final class MappedHost extends MapElement<Host> {
1585
1586 public volatile ContextList contextList;
1587
1588
1591 private final MappedHost realHost;
1592
1593
1598 private final List<MappedHost> aliases;
1599
1600
1606 public MappedHost(String name, Host host) {
1607 super(name, host);
1608 realHost = this;
1609 contextList = new ContextList();
1610 aliases = new CopyOnWriteArrayList<>();
1611 }
1612
1613
1619 public MappedHost(String alias, MappedHost realHost) {
1620 super(alias, realHost.object);
1621 this.realHost = realHost;
1622 this.contextList = realHost.contextList;
1623 this.aliases = null;
1624 }
1625
1626 public boolean isAlias() {
1627 return realHost != this;
1628 }
1629
1630 public MappedHost getRealHost() {
1631 return realHost;
1632 }
1633
1634 public String getRealHostName() {
1635 return realHost.name;
1636 }
1637
1638 public Collection<MappedHost> getAliases() {
1639 return aliases;
1640 }
1641
1642 public void addAlias(MappedHost alias) {
1643 aliases.add(alias);
1644 }
1645
1646 public void addAliases(Collection<? extends MappedHost> c) {
1647 aliases.addAll(c);
1648 }
1649
1650 public void removeAlias(MappedHost alias) {
1651 aliases.remove(alias);
1652 }
1653 }
1654
1655
1656
1657
1658
1659 protected static final class ContextList {
1660
1661 public final MappedContext[] contexts;
1662 public final int nesting;
1663
1664 public ContextList() {
1665 this(new MappedContext[0], 0);
1666 }
1667
1668 private ContextList(MappedContext[] contexts, int nesting) {
1669 this.contexts = contexts;
1670 this.nesting = nesting;
1671 }
1672
1673 public ContextList addContext(MappedContext mappedContext,
1674 int slashCount) {
1675 MappedContext[] newContexts = new MappedContext[contexts.length + 1];
1676 if (insertMap(contexts, newContexts, mappedContext)) {
1677 return new ContextList(newContexts, Math.max(nesting,
1678 slashCount));
1679 }
1680 return null;
1681 }
1682
1683 public ContextList removeContext(String path) {
1684 MappedContext[] newContexts = new MappedContext[contexts.length - 1];
1685 if (removeMap(contexts, newContexts, path)) {
1686 int newNesting = 0;
1687 for (MappedContext context : newContexts) {
1688 newNesting = Math.max(newNesting, slashCount(context.name));
1689 }
1690 return new ContextList(newContexts, newNesting);
1691 }
1692 return null;
1693 }
1694 }
1695
1696
1697
1698
1699
1700 protected static final class MappedContext extends MapElement<Void> {
1701 public volatile ContextVersion[] versions;
1702
1703 public MappedContext(String name, ContextVersion firstVersion) {
1704 super(name, null);
1705 this.versions = new ContextVersion[] { firstVersion };
1706 }
1707 }
1708
1709 protected static final class ContextVersion extends MapElement<Context> {
1710 public final String path;
1711 public final int slashCount;
1712 public final WebResourceRoot resources;
1713 public String[] welcomeResources;
1714 public MappedWrapper defaultWrapper = null;
1715 public MappedWrapper[] exactWrappers = new MappedWrapper[0];
1716 public MappedWrapper[] wildcardWrappers = new MappedWrapper[0];
1717 public MappedWrapper[] extensionWrappers = new MappedWrapper[0];
1718 public int nesting = 0;
1719 private volatile boolean paused;
1720
1721 public ContextVersion(String version, String path, int slashCount,
1722 Context context, WebResourceRoot resources,
1723 String[] welcomeResources) {
1724 super(version, context);
1725 this.path = path;
1726 this.slashCount = slashCount;
1727 this.resources = resources;
1728 this.welcomeResources = welcomeResources;
1729 }
1730
1731 public boolean isPaused() {
1732 return paused;
1733 }
1734
1735 public void markPaused() {
1736 paused = true;
1737 }
1738 }
1739
1740
1741
1742
1743 protected static class MappedWrapper extends MapElement<Wrapper> {
1744
1745 public final boolean jspWildCard;
1746 public final boolean resourceOnly;
1747
1748 public MappedWrapper(String name, Wrapper wrapper, boolean jspWildCard,
1749 boolean resourceOnly) {
1750 super(name, wrapper);
1751 this.jspWildCard = jspWildCard;
1752 this.resourceOnly = resourceOnly;
1753 }
1754 }
1755 }
1756