1 17 18 19 package org.apache.catalina.servlets; 20 21 22 import java.io.BufferedInputStream ; 23 import java.io.ByteArrayInputStream ; 24 import java.io.ByteArrayOutputStream ; 25 import java.io.File ; 26 import java.io.FileInputStream ; 27 import java.io.IOException ; 28 import java.io.InputStream ; 29 import java.io.InputStreamReader ; 30 import java.io.OutputStreamWriter ; 31 import java.io.PrintWriter ; 32 import java.io.RandomAccessFile ; 33 import java.io.Reader ; 34 import java.io.StringReader ; 35 import java.io.StringWriter ; 36 import java.util.ArrayList ; 37 import java.util.Iterator ; 38 import java.util.StringTokenizer ; 39 40 import javax.naming.InitialContext ; 41 import javax.naming.NameClassPair ; 42 import javax.naming.NamingEnumeration ; 43 import javax.naming.NamingException ; 44 import javax.naming.directory.DirContext ; 45 import javax.servlet.ServletException ; 46 import javax.servlet.ServletOutputStream ; 47 import javax.servlet.UnavailableException ; 48 import javax.servlet.http.HttpServlet ; 49 import javax.servlet.http.HttpServletRequest ; 50 import javax.servlet.http.HttpServletResponse ; 51 import javax.xml.transform.Source ; 52 import javax.xml.transform.Transformer ; 53 import javax.xml.transform.TransformerException ; 54 import javax.xml.transform.TransformerFactory ; 55 import javax.xml.transform.stream.StreamResult ; 56 import javax.xml.transform.stream.StreamSource ; 57 58 import org.apache.catalina.Globals; 59 import org.apache.catalina.util.ServerInfo; 60 import org.apache.catalina.util.StringManager; 61 import org.apache.catalina.util.URLEncoder; 62 import org.apache.naming.resources.CacheEntry; 63 import org.apache.naming.resources.ProxyDirContext; 64 import org.apache.naming.resources.Resource; 65 import org.apache.naming.resources.ResourceAttributes; 66 67 68 76 77 public class DefaultServlet 78 extends HttpServlet { 79 80 81 83 84 87 protected int debug = 0; 88 89 90 93 protected int input = 2048; 94 95 96 99 protected boolean listings = false; 100 101 102 105 protected boolean readOnly = true; 106 107 108 111 protected int output = 2048; 112 113 114 117 protected static URLEncoder urlEncoder; 118 119 120 123 protected String localXsltFile = null; 124 125 126 129 protected String globalXsltFile = null; 130 131 132 135 protected String readmeFile = null; 136 137 138 141 protected ProxyDirContext resources = null; 142 143 144 148 protected String fileEncoding = null; 149 150 151 154 protected int sendfileSize = 48 * 1024; 155 156 157 160 protected static ArrayList FULL = new ArrayList (); 161 162 163 165 166 169 static { 170 urlEncoder = new URLEncoder(); 171 urlEncoder.addSafeCharacter('-'); 172 urlEncoder.addSafeCharacter('_'); 173 urlEncoder.addSafeCharacter('.'); 174 urlEncoder.addSafeCharacter('*'); 175 urlEncoder.addSafeCharacter('/'); 176 } 177 178 179 182 protected static final String mimeSeparation = "CATALINA_MIME_BOUNDARY"; 183 184 185 188 protected static final String RESOURCES_JNDI_NAME = "java:/comp/Resources"; 189 190 191 194 protected static StringManager sm = 195 StringManager.getManager(Constants.Package); 196 197 198 201 protected static final int BUFFER_SIZE = 4096; 202 203 204 206 207 210 public void destroy() { 211 } 212 213 214 217 public void init() throws ServletException { 218 219 if (getServletConfig().getInitParameter("debug") != null) 220 debug = Integer.parseInt(getServletConfig().getInitParameter("debug")); 221 222 if (getServletConfig().getInitParameter("input") != null) 223 input = Integer.parseInt(getServletConfig().getInitParameter("input")); 224 225 if (getServletConfig().getInitParameter("output") != null) 226 output = Integer.parseInt(getServletConfig().getInitParameter("output")); 227 228 listings = Boolean.parseBoolean(getServletConfig().getInitParameter("listings")); 229 230 if (getServletConfig().getInitParameter("readonly") != null) 231 readOnly = Boolean.parseBoolean(getServletConfig().getInitParameter("readonly")); 232 233 if (getServletConfig().getInitParameter("sendfileSize") != null) 234 sendfileSize = 235 Integer.parseInt(getServletConfig().getInitParameter("sendfileSize")) * 1024; 236 237 fileEncoding = getServletConfig().getInitParameter("fileEncoding"); 238 239 globalXsltFile = getServletConfig().getInitParameter("globalXsltFile"); 240 localXsltFile = getServletConfig().getInitParameter("localXsltFile"); 241 readmeFile = getServletConfig().getInitParameter("readmeFile"); 242 243 if (input < 256) 245 input = 256; 246 if (output < 256) 247 output = 256; 248 249 if (debug > 0) { 250 log("DefaultServlet.init: input buffer size=" + input + 251 ", output buffer size=" + output); 252 } 253 254 resources = (ProxyDirContext) getServletContext() 256 .getAttribute(Globals.RESOURCES_ATTR); 257 if (resources == null) { 258 try { 259 resources = 260 (ProxyDirContext) new InitialContext () 261 .lookup(RESOURCES_JNDI_NAME); 262 } catch (NamingException e) { 263 throw new ServletException ("No resources", e); 265 } 266 } 267 268 if (resources == null) { 269 throw new UnavailableException ("No resources"); 270 } 271 272 } 273 274 275 277 278 283 protected String getRelativePath(HttpServletRequest request) { 284 285 if (request.getAttribute(Globals.INCLUDE_REQUEST_URI_ATTR) != null) { 287 String result = (String ) request.getAttribute( 288 Globals.INCLUDE_PATH_INFO_ATTR); 289 if (result == null) 290 result = (String ) request.getAttribute( 291 Globals.INCLUDE_SERVLET_PATH_ATTR); 292 if ((result == null) || (result.equals(""))) 293 result = "/"; 294 return (result); 295 } 296 297 String result = request.getPathInfo(); 299 if (result == null) { 300 result = request.getServletPath(); 301 } 302 if ((result == null) || (result.equals(""))) { 303 result = "/"; 304 } 305 return (result); 306 307 } 308 309 310 319 protected void doGet(HttpServletRequest request, 320 HttpServletResponse response) 321 throws IOException , ServletException { 322 323 serveResource(request, response, true); 325 326 } 327 328 329 338 protected void doHead(HttpServletRequest request, 339 HttpServletResponse response) 340 throws IOException , ServletException { 341 342 serveResource(request, response, false); 344 345 } 346 347 348 357 protected void doPost(HttpServletRequest request, 358 HttpServletResponse response) 359 throws IOException , ServletException { 360 doGet(request, response); 361 } 362 363 364 373 protected void doPut(HttpServletRequest req, HttpServletResponse resp) 374 throws ServletException , IOException { 375 376 if (readOnly) { 377 resp.sendError(HttpServletResponse.SC_FORBIDDEN); 378 return; 379 } 380 381 String path = getRelativePath(req); 382 383 boolean exists = true; 384 try { 385 resources.lookup(path); 386 } catch (NamingException e) { 387 exists = false; 388 } 389 390 boolean result = true; 391 392 File contentFile = null; 394 395 Range range = parseContentRange(req, resp); 396 397 InputStream resourceInputStream = null; 398 399 if (range != null) { 404 contentFile = executePartialPut(req, range, path); 405 resourceInputStream = new FileInputStream (contentFile); 406 } else { 407 resourceInputStream = req.getInputStream(); 408 } 409 410 try { 411 Resource newResource = new Resource(resourceInputStream); 412 if (exists) { 414 resources.rebind(path, newResource); 415 } else { 416 resources.bind(path, newResource); 417 } 418 } catch(NamingException e) { 419 result = false; 420 } 421 422 if (result) { 423 if (exists) { 424 resp.setStatus(HttpServletResponse.SC_NO_CONTENT); 425 } else { 426 resp.setStatus(HttpServletResponse.SC_CREATED); 427 } 428 } else { 429 resp.sendError(HttpServletResponse.SC_CONFLICT); 430 } 431 432 } 433 434 435 440 protected File executePartialPut(HttpServletRequest req, Range range, 441 String path) 442 throws IOException { 443 444 File tempDir = (File ) getServletContext().getAttribute 448 ("javax.servlet.context.tempdir"); 449 String convertedResourcePath = path.replace('/', '.'); 451 File contentFile = new File (tempDir, convertedResourcePath); 452 if (contentFile.createNewFile()) { 453 contentFile.deleteOnExit(); 455 } 456 457 RandomAccessFile randAccessContentFile = 458 new RandomAccessFile (contentFile, "rw"); 459 460 Resource oldResource = null; 461 try { 462 Object obj = resources.lookup(path); 463 if (obj instanceof Resource) 464 oldResource = (Resource) obj; 465 } catch (NamingException e) { 466 ; 467 } 468 469 if (oldResource != null) { 471 BufferedInputStream bufOldRevStream = 472 new BufferedInputStream (oldResource.streamContent(), 473 BUFFER_SIZE); 474 475 int numBytesRead; 476 byte[] copyBuffer = new byte[BUFFER_SIZE]; 477 while ((numBytesRead = bufOldRevStream.read(copyBuffer)) != -1) { 478 randAccessContentFile.write(copyBuffer, 0, numBytesRead); 479 } 480 481 bufOldRevStream.close(); 482 } 483 484 randAccessContentFile.setLength(range.length); 485 486 randAccessContentFile.seek(range.start); 488 int numBytesRead; 489 byte[] transferBuffer = new byte[BUFFER_SIZE]; 490 BufferedInputStream requestBufInStream = 491 new BufferedInputStream (req.getInputStream(), BUFFER_SIZE); 492 while ((numBytesRead = requestBufInStream.read(transferBuffer)) != -1) { 493 randAccessContentFile.write(transferBuffer, 0, numBytesRead); 494 } 495 randAccessContentFile.close(); 496 requestBufInStream.close(); 497 498 return contentFile; 499 500 } 501 502 503 512 protected void doDelete(HttpServletRequest req, HttpServletResponse resp) 513 throws ServletException , IOException { 514 515 if (readOnly) { 516 resp.sendError(HttpServletResponse.SC_FORBIDDEN); 517 return; 518 } 519 520 String path = getRelativePath(req); 521 522 boolean exists = true; 523 try { 524 resources.lookup(path); 525 } catch (NamingException e) { 526 exists = false; 527 } 528 529 if (exists) { 530 boolean result = true; 531 try { 532 resources.unbind(path); 533 } catch (NamingException e) { 534 result = false; 535 } 536 if (result) { 537 resp.setStatus(HttpServletResponse.SC_NO_CONTENT); 538 } else { 539 resp.sendError(HttpServletResponse.SC_METHOD_NOT_ALLOWED); 540 } 541 } else { 542 resp.sendError(HttpServletResponse.SC_NOT_FOUND); 543 } 544 545 } 546 547 548 559 protected boolean checkIfHeaders(HttpServletRequest request, 560 HttpServletResponse response, 561 ResourceAttributes resourceAttributes) 562 throws IOException { 563 564 return checkIfMatch(request, response, resourceAttributes) 565 && checkIfModifiedSince(request, response, resourceAttributes) 566 && checkIfNoneMatch(request, response, resourceAttributes) 567 && checkIfUnmodifiedSince(request, response, resourceAttributes); 568 569 } 570 571 572 577 protected String getETag(ResourceAttributes resourceAttributes) { 578 String result = null; 579 if ((result = resourceAttributes.getETag(true)) != null) { 580 return result; 581 } else if ((result = resourceAttributes.getETag()) != null) { 582 return result; 583 } else { 584 return "W/\"" + resourceAttributes.getContentLength() + "-" 585 + resourceAttributes.getLastModified() + "\""; 586 } 587 } 588 589 590 595 protected String rewriteUrl(String path) { 596 return urlEncoder.encode( path ); 597 } 598 599 600 603 protected void displaySize(StringBuffer buf, int filesize) { 604 605 int leftside = filesize / 1024; 606 int rightside = (filesize % 1024) / 103; if (leftside == 0 && rightside == 0 && filesize != 0) 609 rightside = 1; 610 buf.append(leftside).append(".").append(rightside); 611 buf.append(" KB"); 612 613 } 614 615 616 626 protected void serveResource(HttpServletRequest request, 627 HttpServletResponse response, 628 boolean content) 629 throws IOException , ServletException { 630 631 String path = getRelativePath(request); 633 if (debug > 0) { 634 if (content) 635 log("DefaultServlet.serveResource: Serving resource '" + 636 path + "' headers and data"); 637 else 638 log("DefaultServlet.serveResource: Serving resource '" + 639 path + "' headers only"); 640 } 641 642 CacheEntry cacheEntry = resources.lookupCache(path); 643 644 if (!cacheEntry.exists) { 645 String requestUri = (String ) request.getAttribute( 648 Globals.INCLUDE_REQUEST_URI_ATTR); 649 if (requestUri == null) { 650 requestUri = request.getRequestURI(); 651 } else { 652 response.getWriter().write( 657 sm.getString("defaultServlet.missingResource", 658 requestUri)); 659 } 660 661 response.sendError(HttpServletResponse.SC_NOT_FOUND, 662 requestUri); 663 return; 664 } 665 666 if (cacheEntry.context == null) { 669 if (path.endsWith("/") || (path.endsWith("\\"))) { 670 String requestUri = (String ) request.getAttribute( 673 Globals.INCLUDE_REQUEST_URI_ATTR); 674 if (requestUri == null) { 675 requestUri = request.getRequestURI(); 676 } 677 response.sendError(HttpServletResponse.SC_NOT_FOUND, 678 requestUri); 679 return; 680 } 681 } 682 683 if (cacheEntry.context == null) { 686 687 boolean included = 689 (request.getAttribute(Globals.INCLUDE_CONTEXT_PATH_ATTR) != null); 690 if (!included 691 && !checkIfHeaders(request, response, cacheEntry.attributes)) { 692 return; 693 } 694 695 } 696 697 String contentType = cacheEntry.attributes.getMimeType(); 699 if (contentType == null) { 700 contentType = getServletContext().getMimeType(cacheEntry.name); 701 cacheEntry.attributes.setMimeType(contentType); 702 } 703 704 ArrayList ranges = null; 705 long contentLength = -1L; 706 707 if (cacheEntry.context != null) { 708 709 if (!listings) { 712 response.sendError(HttpServletResponse.SC_NOT_FOUND, 713 request.getRequestURI()); 714 return; 715 } 716 contentType = "text/html;charset=UTF-8"; 717 718 } else { 719 720 722 ranges = parseRange(request, response, cacheEntry.attributes); 723 724 response.setHeader("ETag", getETag(cacheEntry.attributes)); 726 727 response.setHeader("Last-Modified", 729 cacheEntry.attributes.getLastModifiedHttp()); 730 731 contentLength = cacheEntry.attributes.getContentLength(); 733 if (contentLength == 0L) { 736 content = false; 737 } 738 739 } 740 741 ServletOutputStream ostream = null; 742 PrintWriter writer = null; 743 744 if (content) { 745 746 748 try { 749 ostream = response.getOutputStream(); 750 } catch (IllegalStateException e) { 751 if ( (contentType == null) 754 || (contentType.startsWith("text")) 755 || (contentType.endsWith("xml")) ) { 756 writer = response.getWriter(); 757 } else { 758 throw e; 759 } 760 } 761 762 } 763 764 if ( (cacheEntry.context != null) 765 || ( ((ranges == null) || (ranges.isEmpty())) 766 && (request.getHeader("Range") == null) ) 767 || (ranges == FULL) ) { 768 769 if (contentType != null) { 771 if (debug > 0) 772 log("DefaultServlet.serveFile: contentType='" + 773 contentType + "'"); 774 response.setContentType(contentType); 775 } 776 if ((cacheEntry.resource != null) && (contentLength >= 0)) { 777 if (debug > 0) 778 log("DefaultServlet.serveFile: contentLength=" + 779 contentLength); 780 if (contentLength < Integer.MAX_VALUE) { 781 response.setContentLength((int) contentLength); 782 } else { 783 response.setHeader("content-length", "" + contentLength); 785 } 786 } 787 788 InputStream renderResult = null; 789 if (cacheEntry.context != null) { 790 791 if (content) { 792 renderResult = 794 render(request.getContextPath(), cacheEntry); 795 } 796 797 } 798 799 if (content) { 801 try { 802 response.setBufferSize(output); 803 } catch (IllegalStateException e) { 804 } 806 if (ostream != null) { 807 if (!checkSendfile(request, response, cacheEntry, contentLength, null)) 808 copy(cacheEntry, renderResult, ostream); 809 } else { 810 copy(cacheEntry, renderResult, writer); 811 } 812 } 813 814 } else { 815 816 if ((ranges == null) || (ranges.isEmpty())) 817 return; 818 819 821 response.setStatus(HttpServletResponse.SC_PARTIAL_CONTENT); 822 823 if (ranges.size() == 1) { 824 825 Range range = (Range) ranges.get(0); 826 response.addHeader("Content-Range", "bytes " 827 + range.start 828 + "-" + range.end + "/" 829 + range.length); 830 long length = range.end - range.start + 1; 831 if (length < Integer.MAX_VALUE) { 832 response.setContentLength((int) length); 833 } else { 834 response.setHeader("content-length", "" + length); 836 } 837 838 if (contentType != null) { 839 if (debug > 0) 840 log("DefaultServlet.serveFile: contentType='" + 841 contentType + "'"); 842 response.setContentType(contentType); 843 } 844 845 if (content) { 846 try { 847 response.setBufferSize(output); 848 } catch (IllegalStateException e) { 849 } 851 if (ostream != null) { 852 if (!checkSendfile(request, response, cacheEntry, range.end - range.start + 1, range)) 853 copy(cacheEntry, ostream, range); 854 } else { 855 copy(cacheEntry, writer, range); 856 } 857 } 858 859 } else { 860 861 response.setContentType("multipart/byteranges; boundary=" 862 + mimeSeparation); 863 864 if (content) { 865 try { 866 response.setBufferSize(output); 867 } catch (IllegalStateException e) { 868 } 870 if (ostream != null) { 871 copy(cacheEntry, ostream, ranges.iterator(), 872 contentType); 873 } else { 874 copy(cacheEntry, writer, ranges.iterator(), 875 contentType); 876 } 877 } 878 879 } 880 881 } 882 883 } 884 885 886 893 protected Range parseContentRange(HttpServletRequest request, 894 HttpServletResponse response) 895 throws IOException { 896 897 String rangeHeader = request.getHeader("Content-Range"); 899 900 if (rangeHeader == null) 901 return null; 902 903 if (!rangeHeader.startsWith("bytes")) { 905 response.sendError(HttpServletResponse.SC_BAD_REQUEST); 906 return null; 907 } 908 909 rangeHeader = rangeHeader.substring(6).trim(); 910 911 int dashPos = rangeHeader.indexOf('-'); 912 int slashPos = rangeHeader.indexOf('/'); 913 914 if (dashPos == -1) { 915 response.sendError(HttpServletResponse.SC_BAD_REQUEST); 916 return null; 917 } 918 919 if (slashPos == -1) { 920 response.sendError(HttpServletResponse.SC_BAD_REQUEST); 921 return null; 922 } 923 924 Range range = new Range(); 925 926 try { 927 range.start = Long.parseLong(rangeHeader.substring(0, dashPos)); 928 range.end = 929 Long.parseLong(rangeHeader.substring(dashPos + 1, slashPos)); 930 range.length = Long.parseLong 931 (rangeHeader.substring(slashPos + 1, rangeHeader.length())); 932 } catch (NumberFormatException e) { 933 response.sendError(HttpServletResponse.SC_BAD_REQUEST); 934 return null; 935 } 936 937 if (!range.validate()) { 938 response.sendError(HttpServletResponse.SC_BAD_REQUEST); 939 return null; 940 } 941 942 return range; 943 944 } 945 946 947 954 protected ArrayList parseRange(HttpServletRequest request, 955 HttpServletResponse response, 956 ResourceAttributes resourceAttributes) 957 throws IOException { 958 959 String headerValue = request.getHeader("If-Range"); 961 962 if (headerValue != null) { 963 964 long headerValueTime = (-1L); 965 try { 966 headerValueTime = request.getDateHeader("If-Range"); 967 } catch (IllegalArgumentException e) { 968 ; 969 } 970 971 String eTag = getETag(resourceAttributes); 972 long lastModified = resourceAttributes.getLastModified(); 973 974 if (headerValueTime == (-1L)) { 975 976 if (!eTag.equals(headerValue.trim())) 979 return FULL; 980 981 } else { 982 983 if (lastModified > (headerValueTime + 1000)) 987 return FULL; 988 989 } 990 991 } 992 993 long fileLength = resourceAttributes.getContentLength(); 994 995 if (fileLength == 0) 996 return null; 997 998 String rangeHeader = request.getHeader("Range"); 1000 1001 if (rangeHeader == null) 1002 return null; 1003 if (!rangeHeader.startsWith("bytes")) { 1006 response.addHeader("Content-Range", "bytes */" + fileLength); 1007 response.sendError 1008 (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 1009 return null; 1010 } 1011 1012 rangeHeader = rangeHeader.substring(6); 1013 1014 ArrayList result = new ArrayList (); 1017 StringTokenizer commaTokenizer = new StringTokenizer (rangeHeader, ","); 1018 1019 while (commaTokenizer.hasMoreTokens()) { 1021 String rangeDefinition = commaTokenizer.nextToken().trim(); 1022 1023 Range currentRange = new Range(); 1024 currentRange.length = fileLength; 1025 1026 int dashPos = rangeDefinition.indexOf('-'); 1027 1028 if (dashPos == -1) { 1029 response.addHeader("Content-Range", "bytes */" + fileLength); 1030 response.sendError 1031 (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 1032 return null; 1033 } 1034 1035 if (dashPos == 0) { 1036 1037 try { 1038 long offset = Long.parseLong(rangeDefinition); 1039 currentRange.start = fileLength + offset; 1040 currentRange.end = fileLength - 1; 1041 } catch (NumberFormatException e) { 1042 response.addHeader("Content-Range", 1043 "bytes */" + fileLength); 1044 response.sendError 1045 (HttpServletResponse 1046 .SC_REQUESTED_RANGE_NOT_SATISFIABLE); 1047 return null; 1048 } 1049 1050 } else { 1051 1052 try { 1053 currentRange.start = Long.parseLong 1054 (rangeDefinition.substring(0, dashPos)); 1055 if (dashPos < rangeDefinition.length() - 1) 1056 currentRange.end = Long.parseLong 1057 (rangeDefinition.substring 1058 (dashPos + 1, rangeDefinition.length())); 1059 else 1060 currentRange.end = fileLength - 1; 1061 } catch (NumberFormatException e) { 1062 response.addHeader("Content-Range", 1063 "bytes */" + fileLength); 1064 response.sendError 1065 (HttpServletResponse 1066 .SC_REQUESTED_RANGE_NOT_SATISFIABLE); 1067 return null; 1068 } 1069 1070 } 1071 1072 if (!currentRange.validate()) { 1073 response.addHeader("Content-Range", "bytes */" + fileLength); 1074 response.sendError 1075 (HttpServletResponse.SC_REQUESTED_RANGE_NOT_SATISFIABLE); 1076 return null; 1077 } 1078 1079 result.add(currentRange); 1080 } 1081 1082 return result; 1083 } 1084 1085 1086 1087 1090 protected InputStream render(String contextPath, CacheEntry cacheEntry) 1091 throws IOException , ServletException { 1092 1093 InputStream xsltInputStream = 1094 findXsltInputStream(cacheEntry.context); 1095 1096 if (xsltInputStream==null) { 1097 return renderHtml(contextPath, cacheEntry); 1098 } else { 1099 return renderXml(contextPath, cacheEntry, xsltInputStream); 1100 } 1101 1102 } 1103 1104 1111 protected InputStream renderXml(String contextPath, 1112 CacheEntry cacheEntry, 1113 InputStream xsltInputStream) 1114 throws IOException , ServletException { 1115 1116 StringBuffer sb = new StringBuffer (); 1117 1118 sb.append("<?xml version=\"1.0\"?>"); 1119 sb.append("<listing "); 1120 sb.append(" contextPath='"); 1121 sb.append(contextPath); 1122 sb.append("'"); 1123 sb.append(" directory='"); 1124 sb.append(cacheEntry.name); 1125 sb.append("' "); 1126 sb.append(" hasParent='").append(!cacheEntry.name.equals("/")); 1127 sb.append("'>"); 1128 1129 sb.append("<entries>"); 1130 1131 try { 1132 1133 NamingEnumeration enumeration = resources.list(cacheEntry.name); 1135 1136 String rewrittenContextPath = rewriteUrl(contextPath); 1138 1139 while (enumeration.hasMoreElements()) { 1140 1141 NameClassPair ncPair = (NameClassPair ) enumeration.nextElement(); 1142 String resourceName = ncPair.getName(); 1143 String trimmed = resourceName; 1144 if (trimmed.equalsIgnoreCase("WEB-INF") || 1145 trimmed.equalsIgnoreCase("META-INF") || 1146 trimmed.equalsIgnoreCase(localXsltFile)) 1147 continue; 1148 1149 CacheEntry childCacheEntry = 1150 resources.lookupCache(cacheEntry.name + resourceName); 1151 if (!childCacheEntry.exists) { 1152 continue; 1153 } 1154 1155 sb.append("<entry"); 1156 sb.append(" type='") 1157 .append((childCacheEntry.context != null)?"dir":"file") 1158 .append("'"); 1159 sb.append(" urlPath='") 1160 .append(rewrittenContextPath) 1161 .append(rewriteUrl(cacheEntry.name + resourceName)) 1162 .append((childCacheEntry.context != null)?"/":"") 1163 .append("'"); 1164 if (childCacheEntry.resource != null) { 1165 sb.append(" size='") 1166 .append(renderSize(childCacheEntry.attributes.getContentLength())) 1167 .append("'"); 1168 } 1169 sb.append(" date='") 1170 .append(childCacheEntry.attributes.getLastModifiedHttp()) 1171 .append("'"); 1172 1173 sb.append(">"); 1174 sb.append(trimmed); 1175 if (childCacheEntry.context != null) 1176 sb.append("/"); 1177 sb.append("</entry>"); 1178 1179 } 1180 1181 } catch (NamingException e) { 1182 throw new ServletException ("Error accessing resource", e); 1184 } 1185 1186 sb.append("</entries>"); 1187 1188 String readme = getReadme(cacheEntry.context); 1189 1190 if (readme!=null) { 1191 sb.append("<readme><![CDATA["); 1192 sb.append(readme); 1193 sb.append("]]></readme>"); 1194 } 1195 1196 1197 sb.append("</listing>"); 1198 1199 1200 try { 1201 TransformerFactory tFactory = TransformerFactory.newInstance(); 1202 Source xmlSource = new StreamSource (new StringReader (sb.toString())); 1203 Source xslSource = new StreamSource (xsltInputStream); 1204 Transformer transformer = tFactory.newTransformer(xslSource); 1205 1206 ByteArrayOutputStream stream = new ByteArrayOutputStream (); 1207 OutputStreamWriter osWriter = new OutputStreamWriter (stream, "UTF8"); 1208 StreamResult out = new StreamResult (osWriter); 1209 transformer.transform(xmlSource, out); 1210 osWriter.flush(); 1211 return (new ByteArrayInputStream (stream.toByteArray())); 1212 } catch (TransformerException e) { 1213 throw new ServletException ("XSL transformer error", e); 1214 } 1215 } 1216 1217 1224 protected InputStream renderHtml(String contextPath, CacheEntry cacheEntry) 1225 throws IOException , ServletException { 1226 1227 String name = cacheEntry.name; 1228 1229 int trim = name.length(); 1231 if (!name.endsWith("/")) 1232 trim += 1; 1233 if (name.equals("/")) 1234 trim = 1; 1235 1236 ByteArrayOutputStream stream = new ByteArrayOutputStream (); 1238 OutputStreamWriter osWriter = new OutputStreamWriter (stream, "UTF8"); 1239 PrintWriter writer = new PrintWriter (osWriter); 1240 1241 StringBuffer sb = new StringBuffer (); 1242 1243 String rewrittenContextPath = rewriteUrl(contextPath); 1245 1246 sb.append("<html>\r\n"); 1248 sb.append("<head>\r\n"); 1249 sb.append("<title>"); 1250 sb.append(sm.getString("directory.title", name)); 1251 sb.append("</title>\r\n"); 1252 sb.append("<STYLE><!--"); 1253 sb.append(org.apache.catalina.util.TomcatCSS.TOMCAT_CSS); 1254 sb.append("--></STYLE> "); 1255 sb.append("</head>\r\n"); 1256 sb.append("<body>"); 1257 sb.append("<h1>"); 1258 sb.append(sm.getString("directory.title", name)); 1259 1260 String parentDirectory = name; 1262 if (parentDirectory.endsWith("/")) { 1263 parentDirectory = 1264 parentDirectory.substring(0, parentDirectory.length() - 1); 1265 } 1266 int slash = parentDirectory.lastIndexOf('/'); 1267 if (slash >= 0) { 1268 String parent = name.substring(0, slash); 1269 sb.append(" - <a HREF=\""); 1270 sb.append(rewrittenContextPath); 1271 if (parent.equals("")) 1272 parent = "/"; 1273 sb.append(rewriteUrl(parent)); 1274 if (!parent.endsWith("/")) 1275 sb.append("/"); 1276 sb.append("\">"); 1277 sb.append("<b>"); 1278 sb.append(sm.getString("directory.parent", parent)); 1279 sb.append("</b>"); 1280 sb.append("</a>"); 1281 } 1282 1283 sb.append("</h1>"); 1284 sb.append("<HR size=\"1\" noshade=\"noshade\">"); 1285 1286 sb.append("<table width=\"100%\" cellspacing=\"0\"" + 1287 " cellpadding=\"5\" align=\"center\">\r\n"); 1288 1289 sb.append("<tr>\r\n"); 1291 sb.append("<td align=\"left\"><font size=\"+1\"><strong>"); 1292 sb.append(sm.getString("directory.filename")); 1293 sb.append("</strong></font></td>\r\n"); 1294 sb.append("<td align=\"center\"><font size=\"+1\"><strong>"); 1295 sb.append(sm.getString("directory.size")); 1296 sb.append("</strong></font></td>\r\n"); 1297 sb.append("<td align=\"right\"><font size=\"+1\"><strong>"); 1298 sb.append(sm.getString("directory.lastModified")); 1299 sb.append("</strong></font></td>\r\n"); 1300 sb.append("</tr>"); 1301 1302 try { 1303 1304 NamingEnumeration enumeration = resources.list(cacheEntry.name); 1306 boolean shade = false; 1307 while (enumeration.hasMoreElements()) { 1308 1309 NameClassPair ncPair = (NameClassPair ) enumeration.nextElement(); 1310 String resourceName = ncPair.getName(); 1311 String trimmed = resourceName; 1312 if (trimmed.equalsIgnoreCase("WEB-INF") || 1313 trimmed.equalsIgnoreCase("META-INF")) 1314 continue; 1315 1316 CacheEntry childCacheEntry = 1317 resources.lookupCache(cacheEntry.name + resourceName); 1318 if (!childCacheEntry.exists) { 1319 continue; 1320 } 1321 1322 sb.append("<tr"); 1323 if (shade) 1324 sb.append(" bgcolor=\"#eeeeee\""); 1325 sb.append(">\r\n"); 1326 shade = !shade; 1327 1328 sb.append("<td align=\"left\"> \r\n"); 1329 sb.append("<a HREF=\""); 1330 sb.append(rewrittenContextPath); 1331 resourceName = rewriteUrl(name + resourceName); 1332 sb.append(resourceName); 1333 if (childCacheEntry.context != null) 1334 sb.append("/"); 1335 sb.append("\"><tt>"); 1336 sb.append(trimmed); 1337 if (childCacheEntry.context != null) 1338 sb.append("/"); 1339 sb.append("</tt></a></td>\r\n"); 1340 1341 sb.append("<td align=\"right\"><tt>"); 1342 if (childCacheEntry.context != null) 1343 sb.append(" "); 1344 else 1345 sb.append(renderSize(childCacheEntry.attributes.getContentLength())); 1346 sb.append("</tt></td>\r\n"); 1347 1348 sb.append("<td align=\"right\"><tt>"); 1349 sb.append(childCacheEntry.attributes.getLastModifiedHttp()); 1350 sb.append("</tt></td>\r\n"); 1351 1352 sb.append("</tr>\r\n"); 1353 } 1354 1355 } catch (NamingException e) { 1356 throw new ServletException ("Error accessing resource", e); 1358 } 1359 1360 sb.append("</table>\r\n"); 1362 1363 sb.append("<HR size=\"1\" noshade=\"noshade\">"); 1364 1365 String readme = getReadme(cacheEntry.context); 1366 if (readme!=null) { 1367 sb.append(readme); 1368 sb.append("<HR size=\"1\" noshade=\"noshade\">"); 1369 } 1370 1371 sb.append("<h3>").append(ServerInfo.getServerInfo()).append("</h3>"); 1372 sb.append("</body>\r\n"); 1373 sb.append("</html>\r\n"); 1374 1375 writer.write(sb.toString()); 1377 writer.flush(); 1378 return (new ByteArrayInputStream (stream.toByteArray())); 1379 1380 } 1381 1382 1383 1388 protected String renderSize(long size) { 1389 1390 long leftSide = size / 1024; 1391 long rightSide = (size % 1024) / 103; if ((leftSide == 0) && (rightSide == 0) && (size > 0)) 1393 rightSide = 1; 1394 1395 return ("" + leftSide + "." + rightSide + " kb"); 1396 1397 } 1398 1399 1400 1403 protected String getReadme(DirContext directory) 1404 throws IOException , ServletException { 1405 1406 if (readmeFile != null) { 1407 try { 1408 Object obj = directory.lookup(readmeFile); 1409 if ((obj != null) && (obj instanceof Resource)) { 1410 StringWriter buffer = new StringWriter (); 1411 InputStream is = ((Resource) obj).streamContent(); 1412 copyRange(new InputStreamReader (is), 1413 new PrintWriter (buffer)); 1414 return buffer.toString(); 1415 } 1416 } catch (NamingException e) { 1417 throw new ServletException ("Error opening readme resource", e); 1418 } 1419 } 1420 1421 return null; 1422 } 1423 1424 1425 1428 protected InputStream findXsltInputStream(DirContext directory) 1429 throws IOException , ServletException { 1430 1431 if (localXsltFile != null) { 1432 try { 1433 Object obj = directory.lookup(localXsltFile); 1434 if ((obj != null) && (obj instanceof Resource)) { 1435 InputStream is = ((Resource) obj).streamContent(); 1436 if (is != null) 1437 return is; 1438 } 1439 } catch (NamingException e) { 1440 throw new ServletException ("Error opening XSLT resource", e); 1441 } 1442 } 1443 1444 1447 if (globalXsltFile!=null) { 1448 FileInputStream fis = null; 1449 1450 try { 1451 File f = new File (globalXsltFile); 1452 if (f.exists()){ 1453 fis =new FileInputStream (f); 1454 byte b[] = new byte[(int)f.length()]; 1455 fis.read(b); 1456 return new ByteArrayInputStream (b); 1457 } 1458 } finally { 1459 if (fis!=null) 1460 fis.close(); 1461 } 1462 } 1463 1464 return null; 1465 1466 } 1467 1468 1469 1471 1472 1475 protected boolean checkSendfile(HttpServletRequest request, 1476 HttpServletResponse response, 1477 CacheEntry entry, 1478 long length, Range range) { 1479 if ((sendfileSize > 0) 1480 && (entry.resource != null) 1481 && ((length > sendfileSize) || (entry.resource.getContent() == null)) 1482 && (entry.attributes.getCanonicalPath() != null) 1483 && (Boolean.TRUE == request.getAttribute("org.apache.tomcat.sendfile.support")) 1484 && (request.getClass().getName().equals("org.apache.catalina.connector.RequestFacade")) 1485 && (response.getClass().getName().equals("org.apache.catalina.connector.ResponseFacade"))) { 1486 request.setAttribute("org.apache.tomcat.sendfile.filename", entry.attributes.getCanonicalPath()); 1487 if (range == null) { 1488 request.setAttribute("org.apache.tomcat.sendfile.start", new Long (0L)); 1489 request.setAttribute("org.apache.tomcat.sendfile.end", new Long (length)); 1490 } else { 1491 request.setAttribute("org.apache.tomcat.sendfile.start", new Long (range.start)); 1492 request.setAttribute("org.apache.tomcat.sendfile.end", new Long (range.end + 1)); 1493 } 1494 request.setAttribute("org.apache.tomcat.sendfile.token", this); 1495 return true; 1496 } else { 1497 return false; 1498 } 1499 } 1500 1501 1502 1512 protected boolean checkIfMatch(HttpServletRequest request, 1513 HttpServletResponse response, 1514 ResourceAttributes resourceAttributes) 1515 throws IOException { 1516 1517 String eTag = getETag(resourceAttributes); 1518 String headerValue = request.getHeader("If-Match"); 1519 if (headerValue != null) { 1520 if (headerValue.indexOf('*') == -1) { 1521 1522 StringTokenizer commaTokenizer = new StringTokenizer 1523 (headerValue, ","); 1524 boolean conditionSatisfied = false; 1525 1526 while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) { 1527 String currentToken = commaTokenizer.nextToken(); 1528 if (currentToken.trim().equals(eTag)) 1529 conditionSatisfied = true; 1530 } 1531 1532 if (!conditionSatisfied) { 1535 response.sendError 1536 (HttpServletResponse.SC_PRECONDITION_FAILED); 1537 return false; 1538 } 1539 1540 } 1541 } 1542 return true; 1543 1544 } 1545 1546 1547 1557 protected boolean checkIfModifiedSince(HttpServletRequest request, 1558 HttpServletResponse response, 1559 ResourceAttributes resourceAttributes) 1560 throws IOException { 1561 try { 1562 long headerValue = request.getDateHeader("If-Modified-Since"); 1563 long lastModified = resourceAttributes.getLastModified(); 1564 if (headerValue != -1) { 1565 1566 if ((request.getHeader("If-None-Match") == null) 1569 && (lastModified <= headerValue + 1000)) { 1570 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); 1573 return false; 1574 } 1575 } 1576 } catch (IllegalArgumentException illegalArgument) { 1577 return true; 1578 } 1579 return true; 1580 1581 } 1582 1583 1584 1594 protected boolean checkIfNoneMatch(HttpServletRequest request, 1595 HttpServletResponse response, 1596 ResourceAttributes resourceAttributes) 1597 throws IOException { 1598 1599 String eTag = getETag(resourceAttributes); 1600 String headerValue = request.getHeader("If-None-Match"); 1601 if (headerValue != null) { 1602 1603 boolean conditionSatisfied = false; 1604 1605 if (!headerValue.equals("*")) { 1606 1607 StringTokenizer commaTokenizer = 1608 new StringTokenizer (headerValue, ","); 1609 1610 while (!conditionSatisfied && commaTokenizer.hasMoreTokens()) { 1611 String currentToken = commaTokenizer.nextToken(); 1612 if (currentToken.trim().equals(eTag)) 1613 conditionSatisfied = true; 1614 } 1615 1616 } else { 1617 conditionSatisfied = true; 1618 } 1619 1620 if (conditionSatisfied) { 1621 1622 if ( ("GET".equals(request.getMethod())) 1627 || ("HEAD".equals(request.getMethod())) ) { 1628 response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); 1629 return false; 1630 } else { 1631 response.sendError 1632 (HttpServletResponse.SC_PRECONDITION_FAILED); 1633 return false; 1634 } 1635 } 1636 } 1637 return true; 1638 1639 } 1640 1641 1642 1652 protected boolean checkIfUnmodifiedSince(HttpServletRequest request, 1653 HttpServletResponse response, 1654 ResourceAttributes resourceAttributes) 1655 throws IOException { 1656 try { 1657 long lastModified = resourceAttributes.getLastModified(); 1658 long headerValue = request.getDateHeader("If-Unmodified-Since"); 1659 if (headerValue != -1) { 1660 if ( lastModified > (headerValue + 1000)) { 1661 response.sendError(HttpServletResponse.SC_PRECONDITION_FAILED); 1664 return false; 1665 } 1666 } 1667 } catch(IllegalArgumentException illegalArgument) { 1668 return true; 1669 } 1670 return true; 1671 1672 } 1673 1674 1675 1685 protected void copy(CacheEntry cacheEntry, InputStream is, 1686 ServletOutputStream ostream) 1687 throws IOException { 1688 1689 IOException exception = null; 1690 InputStream resourceInputStream = null; 1691 1692 if (cacheEntry.resource != null) { 1695 byte buffer[] = cacheEntry.resource.getContent(); 1696 if (buffer != null) { 1697 ostream.write(buffer, 0, buffer.length); 1698 return; 1699 } 1700 resourceInputStream = cacheEntry.resource.streamContent(); 1701 } else { 1702 resourceInputStream = is; 1703 } 1704 1705 InputStream istream = new BufferedInputStream 1706 (resourceInputStream, input); 1707 1708 exception = copyRange(istream, ostream); 1710 1711 istream.close(); 1713 1714 if (exception != null) 1716 throw exception; 1717 1718 } 1719 1720 1721 1731 protected void copy(CacheEntry cacheEntry, InputStream is, PrintWriter writer) 1732 throws IOException { 1733 1734 IOException exception = null; 1735 1736 InputStream resourceInputStream = null; 1737 if (cacheEntry.resource != null) { 1738 resourceInputStream = cacheEntry.resource.streamContent(); 1739 } else { 1740 resourceInputStream = is; 1741 } 1742 1743 Reader reader; 1744 if (fileEncoding == null) { 1745 reader = new InputStreamReader (resourceInputStream); 1746 } else { 1747 reader = new InputStreamReader (resourceInputStream, 1748 fileEncoding); 1749 } 1750 1751 exception = copyRange(reader, writer); 1753 1754 reader.close(); 1756 1757 if (exception != null) 1759 throw exception; 1760 1761 } 1762 1763 1764 1774 protected void copy(CacheEntry cacheEntry, ServletOutputStream ostream, 1775 Range range) 1776 throws IOException { 1777 1778 IOException exception = null; 1779 1780 InputStream resourceInputStream = cacheEntry.resource.streamContent(); 1781 InputStream istream = 1782 new BufferedInputStream (resourceInputStream, input); 1783 exception = copyRange(istream, ostream, range.start, range.end); 1784 1785 istream.close(); 1787 1788 if (exception != null) 1790 throw exception; 1791 1792 } 1793 1794 1795 1805 protected void copy(CacheEntry cacheEntry, PrintWriter writer, 1806 Range range) 1807 throws IOException { 1808 1809 IOException exception = null; 1810 1811 InputStream resourceInputStream = cacheEntry.resource.streamContent(); 1812 1813 Reader reader; 1814 if (fileEncoding == null) { 1815 reader = new InputStreamReader (resourceInputStream); 1816 } else { 1817 reader = new InputStreamReader (resourceInputStream, 1818 fileEncoding); 1819 } 1820 1821 exception = copyRange(reader, writer, range.start, range.end); 1822 1823 reader.close(); 1825 1826 if (exception != null) 1828 throw exception; 1829 1830 } 1831 1832 1833 1844 protected void copy(CacheEntry cacheEntry, ServletOutputStream ostream, 1845 Iterator ranges, String contentType) 1846 throws IOException { 1847 1848 IOException exception = null; 1849 1850 while ( (exception == null) && (ranges.hasNext()) ) { 1851 1852 InputStream resourceInputStream = cacheEntry.resource.streamContent(); 1853 InputStream istream = 1854 new BufferedInputStream (resourceInputStream, input); 1855 1856 Range currentRange = (Range) ranges.next(); 1857 1858 ostream.println(); 1860 ostream.println("--" + mimeSeparation); 1861 if (contentType != null) 1862 ostream.println("Content-Type: " + contentType); 1863 ostream.println("Content-Range: bytes " + currentRange.start 1864 + "-" + currentRange.end + "/" 1865 + currentRange.length); 1866 ostream.println(); 1867 1868 exception = copyRange(istream, ostream, currentRange.start, 1870 currentRange.end); 1871 1872 istream.close(); 1873 1874 } 1875 1876 ostream.println(); 1877 ostream.print("--" + mimeSeparation + "--"); 1878 1879 if (exception != null) 1881 throw exception; 1882 1883 } 1884 1885 1886 1897 protected void copy(CacheEntry cacheEntry, PrintWriter writer, 1898 Iterator ranges, String contentType) 1899 throws IOException { 1900 1901 IOException exception = null; 1902 1903 while ( (exception == null) && (ranges.hasNext()) ) { 1904 1905 InputStream resourceInputStream = cacheEntry.resource.streamContent(); 1906 1907 Reader reader; 1908 if (fileEncoding == null) { 1909 reader = new InputStreamReader (resourceInputStream); 1910 } else { 1911 reader = new InputStreamReader (resourceInputStream, 1912 fileEncoding); 1913 } 1914 1915 Range currentRange = (Range) ranges.next(); 1916 1917 writer.println(); 1919 writer.println("--" + mimeSeparation); 1920 if (contentType != null) 1921 writer.println("Content-Type: " + contentType); 1922 writer.println("Content-Range: bytes " + currentRange.start 1923 + "-" + currentRange.end + "/" 1924 + currentRange.length); 1925 writer.println(); 1926 1927 exception = copyRange(reader, writer, currentRange.start, 1929 currentRange.end); 1930 1931 reader.close(); 1932 1933 } 1934 1935 writer.println(); 1936 writer.print("--" + mimeSeparation + "--"); 1937 1938 if (exception != null) 1940 throw exception; 1941 1942 } 1943 1944 1945 1954 protected IOException copyRange(InputStream istream, 1955 ServletOutputStream ostream) { 1956 1957 IOException exception = null; 1959 byte buffer[] = new byte[input]; 1960 int len = buffer.length; 1961 while (true) { 1962 try { 1963 len = istream.read(buffer); 1964 if (len == -1) 1965 break; 1966 ostream.write(buffer, 0, len); 1967 } catch (IOException e) { 1968 exception = e; 1969 len = -1; 1970 break; 1971 } 1972 } 1973 return exception; 1974 1975 } 1976 1977 1978 1987 protected IOException copyRange(Reader reader, PrintWriter writer) { 1988 1989 IOException exception = null; 1991 char buffer[] = new char[input]; 1992 int len = buffer.length; 1993 while (true) { 1994 try { 1995 len = reader.read(buffer); 1996 if (len == -1) 1997 break; 1998 writer.write(buffer, 0, len); 1999 } catch (IOException e) { 2000 exception = e; 2001 len = -1; 2002 break; 2003 } 2004 } 2005 return exception; 2006 2007 } 2008 2009 2010 2021 protected IOException copyRange(InputStream istream, 2022 ServletOutputStream ostream, 2023 long start, long end) { 2024 2025 if (debug > 10) 2026 log("Serving bytes:" + start + "-" + end); 2027 2028 try { 2029 istream.skip(start); 2030 } catch (IOException e) { 2031 return e; 2032 } 2033 2034 IOException exception = null; 2035 long bytesToRead = end - start + 1; 2036 2037 byte buffer[] = new byte[input]; 2038 int len = buffer.length; 2039 while ( (bytesToRead > 0) && (len >= buffer.length)) { 2040 try { 2041 len = istream.read(buffer); 2042 if (bytesToRead >= len) { 2043 ostream.write(buffer, 0, len); 2044 bytesToRead -= len; 2045 } else { 2046 ostream.write(buffer, 0, (int) bytesToRead); 2047 bytesToRead = 0; 2048 } 2049 } catch (IOException e) { 2050 exception = e; 2051 len = -1; 2052 } 2053 if (len < buffer.length) 2054 break; 2055 } 2056 2057 return exception; 2058 2059 } 2060 2061 2062 2073 protected IOException copyRange(Reader reader, PrintWriter writer, 2074 long start, long end) { 2075 2076 try { 2077 reader.skip(start); 2078 } catch (IOException e) { 2079 return e; 2080 } 2081 2082 IOException exception = null; 2083 long bytesToRead = end - start + 1; 2084 2085 char buffer[] = new char[input]; 2086 int len = buffer.length; 2087 while ( (bytesToRead > 0) && (len >= buffer.length)) { 2088 try { 2089 len = reader.read(buffer); 2090 if (bytesToRead >= len) { 2091 writer.write(buffer, 0, len); 2092 bytesToRead -= len; 2093 } else { 2094 writer.write(buffer, 0, (int) bytesToRead); 2095 bytesToRead = 0; 2096 } 2097 } catch (IOException e) { 2098 exception = e; 2099 len = -1; 2100 } 2101 if (len < buffer.length) 2102 break; 2103 } 2104 2105 return exception; 2106 2107 } 2108 2109 2110 2111 2113 2114 protected class Range { 2115 2116 public long start; 2117 public long end; 2118 public long length; 2119 2120 2123 public boolean validate() { 2124 if (end >= length) 2125 end = length - 1; 2126 return ( (start >= 0) && (end >= 0) && (start <= end) 2127 && (length > 0) ); 2128 } 2129 2130 public void recycle() { 2131 start = 0; 2132 end = 0; 2133 length = 0; 2134 } 2135 2136 } 2137 2138 2139} 2140 | Popular Tags |