001: // Copyright (C) 1998-2001 by Jason Hunter <jhunter_AT_acm_DOT_org>.
002: // All rights reserved. Use of this class is limited.
003: // Please see the LICENSE for more information.
004:
005: package com.oreilly.servlet;
006:
007: import java.io.*;
008: import java.lang.reflect.*;
009: import java.net.*;
010: import java.util.*;
011: import javax.servlet.*;
012: import javax.servlet.http.*;
013:
014: //import javax.servlet.jsp.*;
015:
016: /**
017: * A collection of static utility methods useful to servlets.
018: * Some methods require Servlet API 2.2.
019: *
020: * @author <b>Jason Hunter</b>, Copyright © 1998-2000
021: * @version 1.5, 2001/02/11, added getResource() ".." check
022: * @version 1.4, 2000/09/27, finalized getResource() behavior
023: * @version 1.3, 2000/08/15, improved getStackTraceAsString() to take Throwable
024: * @version 1.2, 2000/03/10, added getResource() method
025: * @version 1.1, 2000/02/13, added returnURL() methods
026: * @version 1.0, 1098/09/18
027: */
028: public class ServletUtils {
029:
030: /**
031: * Sends the contents of the specified file to the output stream
032: *
033: * @param filename the file to send
034: * @param out the output stream to write the file
035: * @exception FileNotFoundException if the file does not exist
036: * @exception IOException if an I/O error occurs
037: */
038: public static void returnFile(String filename, OutputStream out)
039: throws FileNotFoundException, IOException {
040: // A FileInputStream is for bytes
041: FileInputStream fis = null;
042: try {
043: fis = new FileInputStream(filename);
044: byte[] buf = new byte[4 * 1024]; // 4K buffer
045: int bytesRead;
046: while ((bytesRead = fis.read(buf)) != -1) {
047: out.write(buf, 0, bytesRead);
048: }
049: } finally {
050: if (fis != null)
051: fis.close();
052: }
053: }
054:
055: /**
056: * Sends the contents of the specified URL to the output stream
057: *
058: * @param URL whose contents are to be sent
059: * @param out the output stream to write the contents
060: * @exception IOException if an I/O error occurs
061: */
062: public static void returnURL(URL url, OutputStream out)
063: throws IOException {
064: InputStream in = url.openStream();
065: byte[] buf = new byte[4 * 1024]; // 4K buffer
066: int bytesRead;
067: while ((bytesRead = in.read(buf)) != -1) {
068: out.write(buf, 0, bytesRead);
069: }
070: }
071:
072: /**
073: * Sends the contents of the specified URL to the Writer (commonly either a
074: * PrintWriter or JspWriter)
075: *
076: * @param URL whose contents are to be sent
077: * @param out the Writer to write the contents
078: * @exception IOException if an I/O error occurs
079: */
080: public static void returnURL(URL url, Writer out)
081: throws IOException {
082: // Determine the URL's content encoding
083: URLConnection con = url.openConnection();
084: con.connect();
085: String encoding = con.getContentEncoding();
086:
087: // Construct a Reader appropriate for that encoding
088: BufferedReader in = null;
089: if (encoding == null) {
090: in = new BufferedReader(new InputStreamReader(url
091: .openStream()));
092: } else {
093: in = new BufferedReader(new InputStreamReader(url
094: .openStream(), encoding));
095: }
096: char[] buf = new char[4 * 1024]; // 4Kchar buffer
097: int charsRead;
098: while ((charsRead = in.read(buf)) != -1) {
099: out.write(buf, 0, charsRead);
100: }
101: }
102:
103: /**
104: * Gets an exception's stack trace as a String
105: *
106: * @param e the exception
107: * @return the stack trace of the exception
108: */
109: public static String getStackTraceAsString(Throwable t) {
110: ByteArrayOutputStream bytes = new ByteArrayOutputStream();
111: PrintWriter writer = new PrintWriter(bytes, true);
112: t.printStackTrace(writer);
113: return bytes.toString();
114: }
115:
116: /**
117: * Gets a reference to the named servlet, attempting to load it
118: * through an HTTP request if necessary. Returns null if there's a problem.
119: * This method behaves similarly to <tt>ServletContext.getServlet()</tt>
120: * except, while that method may return null if the
121: * named servlet wasn't already loaded, this method tries to load
122: * the servlet using a dummy HTTP request. Only loads HTTP servlets.
123: *
124: * @param name the name of the servlet
125: * @param req the servlet request
126: * @param context the servlet context
127: * @return the named servlet, or null if there was a problem
128: */
129: public static Servlet getServlet(String name, ServletRequest req,
130: ServletContext context) {
131: try {
132: // Try getting the servlet the old fashioned way
133: Servlet servlet = context.getServlet(name);
134: if (servlet != null)
135: return servlet;
136:
137: // If getServlet() returned null, we have to load it ourselves.
138: // Do this by making an HTTP GET request to the servlet.
139: // Use a raw socket connection so we can set a timeout.
140: Socket socket = new Socket(req.getServerName(), req
141: .getServerPort());
142: socket.setSoTimeout(4000); // wait up to 4 secs for a response
143: PrintWriter out = new PrintWriter(socket.getOutputStream(),
144: true);
145: out.println("GET /servlet/" + name + " HTTP/1.0"); // the request
146: out.println();
147: try {
148: socket.getInputStream().read(); // Even one byte means its loaded
149: } catch (InterruptedIOException e) { /* timeout: ignore, hope for best */
150: }
151: out.close();
152:
153: // Try getting the servlet again.
154: return context.getServlet(name);
155: } catch (Exception e) {
156: // If there's any problem, return null.
157: return null;
158: }
159: }
160:
161: /**
162: * Gets a reference to the given resource within the given context,
163: * making sure not to serve the contents of WEB-INF, META-INF, or to
164: * display .jsp file source.
165: * Throws an IOException if the resource can't be read.
166: *
167: * @param context the context containing the resource
168: * @param resource the resource to be read
169: * @return a URL reference to the resource
170: * @exception IOException if there's any problem accessing the resource
171: */
172: public static URL getResource(ServletContext context,
173: String resource) throws IOException {
174: // Short-circuit if resource is null
175: if (resource == null) {
176: throw new FileNotFoundException(
177: "Requested resource was null (passed in null)");
178: }
179:
180: if (resource.endsWith("/") || resource.endsWith("\\")
181: || resource.endsWith(".")) {
182: throw new MalformedURLException(
183: "Path may not end with a slash or dot");
184: }
185:
186: if (resource.indexOf("..") != -1) {
187: throw new MalformedURLException(
188: "Path may not contain double dots");
189: }
190:
191: String upperResource = resource.toUpperCase();
192: if (upperResource.startsWith("/WEB-INF")
193: || upperResource.startsWith("/META-INF")) {
194: throw new MalformedURLException(
195: "Path may not begin with /WEB-INF or /META-INF");
196: }
197:
198: if (upperResource.endsWith(".JSP")) {
199: throw new MalformedURLException(
200: "Path may not end with .jsp");
201: }
202:
203: // Convert the resource to a URL
204: URL url = context.getResource(resource);
205: if (url == null) {
206: throw new FileNotFoundException(
207: "Requested resource was null (" + resource + ")");
208: }
209:
210: return url;
211: }
212: }
|