001: /**
002: * Licensed to the Apache Software Foundation (ASF) under one or more
003: * contributor license agreements. See the NOTICE file distributed with
004: * this work for additional information regarding copyright ownership.
005: * The ASF licenses this file to You under the Apache License, Version 2.0
006: * (the "License"); you may not use this file except in compliance with
007: * the License. You may obtain a copy of the License at
008: *
009: * http://www.apache.org/licenses/LICENSE-2.0
010: *
011: * Unless required by applicable law or agreed to in writing, software
012: * distributed under the License is distributed on an "AS IS" BASIS,
013: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
014: * See the License for the specific language governing permissions and
015: * limitations under the License.
016: */package org.apache.openejb.webadmin.httpd;
017:
018: import java.io.ByteArrayOutputStream;
019: import java.io.DataOutput;
020: import java.io.DataOutputStream;
021: import java.io.IOException;
022: import java.io.InputStream;
023: import java.io.OutputStream;
024: import java.io.PrintWriter;
025: import java.net.URL;
026: import java.net.URLConnection;
027: import java.util.HashMap;
028: import java.util.Iterator;
029: import java.util.Map;
030: import java.util.Properties;
031: import java.util.StringTokenizer;
032:
033: import org.apache.openejb.webadmin.HttpRequest;
034: import org.apache.openejb.webadmin.HttpResponse;
035: import org.apache.openejb.webadmin.HttpSession;
036: import org.apache.openejb.util.JarUtils;
037: import org.apache.openejb.util.OpenEjbVersion;
038:
039: /** This class takes care of HTTP Responses. It sends data back to the browser.
040: * @author <a href="mailto:david.blevins@visi.com">David Blevins</a>
041: * @author <a href="mailto:tim_urberg@yahoo.com">Tim Urberg</a>
042: */
043: public class HttpResponseImpl implements HttpResponse {
044:
045: /** Response string */
046: private String responseString = "OK";
047:
048: /** Code */
049: private int code = 200;
050:
051: /** Response headers */
052: private HashMap headers;
053:
054: /** Response body */
055: private byte[] body = new byte[0];
056:
057: /** the writer for the response */
058: private transient PrintWriter writer;
059: /** the raw body */
060: private transient ByteArrayOutputStream baos;
061:
062: /** the HTTP version */
063: public static final String HTTP_VERSION = "HTTP/1.1";
064: /** a line feed character */
065: public static final String CRLF = "\r\n";
066: /** a space character */
067: public static final String SP = " ";
068: /** a colon and space */
069: public static final String CSP = ": ";
070: /** the server to send data from */
071: public static String server;
072:
073: private HttpRequestImpl request;
074: private URLConnection content;
075:
076: protected void setRequest(HttpRequestImpl request) {
077: this .request = request;
078: }
079:
080: /** sets a header to be sent back to the browser
081: * @param name the name of the header
082: * @param value the value of the header
083: */
084: public void setHeader(String name, String value) {
085: headers.put(name, value);
086: }
087:
088: /** Gets a header based on the name passed in
089: * @param name The name of the header
090: * @return the value of the header
091: */
092: public String getHeader(String name) {
093: return (String) headers.get(name);
094: }
095:
096: /** Gets the PrintWriter to send data to the browser
097: * @return the PrintWriter to send data to the browser
098: */
099: public PrintWriter getPrintWriter() {
100: return writer;
101: }
102:
103: /** gets the OutputStream to send data to the browser
104: * @return the OutputStream to send data to the browser
105: */
106: public OutputStream getOutputStream() {
107: return baos;
108: }
109:
110: /** sets the HTTP response code to be sent to the browser. These codes are:
111: *
112: * OPTIONS = 0
113: * GET = 1
114: * HEAD = 2
115: * POST = 3
116: * PUT = 4
117: * DELETE = 5
118: * TRACE = 6
119: * CONNECT = 7
120: * UNSUPPORTED = 8
121: * @param code the code to be sent to the browser
122: */
123: public void setCode(int code) {
124: this .code = code;
125: }
126:
127: /** gets the HTTP response code
128: * @return the HTTP response code
129: */
130: public int getCode() {
131: return code;
132: }
133:
134: /** sets the content type to be sent back to the browser
135: * @param type the type to be sent to the browser (i.e. "text/html")
136: */
137: public void setContentType(String type) {
138: setHeader("Content-Type", type);
139: }
140:
141: /** gets the content type that will be sent to the browser
142: * @return the content type (i.e. "text/html")
143: */
144: public String getContentType() {
145: return getHeader("Content-Type");
146: }
147:
148: /** Sets the response string to be sent to the browser
149: * @param responseString the response string
150: */
151: public void setResponseString(String responseString) {
152: this .responseString = responseString;
153: }
154:
155: /** Sets the response string to be sent to the browser
156: * @return the response string
157: */
158: public String getResponseString() {
159: return responseString;
160: }
161:
162: /** resets the data to be sent to the browser */
163: public void reset() {
164: initBody();
165: }
166:
167: /** resets the data to be sent to the browser with the response code and response
168: * string
169: * @param code the code to be sent to the browser
170: * @param responseString the response string to be sent to the browser
171: */
172: public void reset(int code, String responseString) {
173: setCode(code);
174: setResponseString(responseString);
175: initBody();
176: }
177:
178: /*------------------------------------------------------------*/
179: /* Methods for writing out a response */
180: /*------------------------------------------------------------*/
181: /** creates a new instance of HttpResponseImpl with default values */
182: protected HttpResponseImpl() {
183: this (200, "OK", "text/html");
184: }
185:
186: /** Creates a new HttpResponseImpl with user provided parameters
187: * @param code the HTTP Response code, see <a href="http://www.ietf.org/rfc/rfc2616.txt">http://www.ietf.org/rfc/rfc2616.txt</a>
188: * for these codes
189: * @param responseString the response string to be sent back
190: * @param contentType the content type to be sent back
191: */
192: protected HttpResponseImpl(int code, String responseString,
193: String contentType) {
194: this .responseString = responseString;
195: this .headers = new HashMap();
196: this .code = code;
197:
198: // Default headers
199: setHeader("Server", getServerName());
200: setHeader("Connection", "close");
201: setHeader("Content-Type", contentType);
202:
203: // create the body.
204: initBody();
205: }
206:
207: /** Takes care of sending the response line, headers and body
208: *
209: * HTTP/1.1 200 OK
210: * Server: Netscape-Enterprise/3.6 SP3
211: * Date: Thu, 07 Jun 2001 17:30:42 GMT
212: * Content-Type: text/html
213: * Connection: close
214: * @param output the output to send the response to
215: * @throws IOException if an exception is thrown
216: */
217: protected void writeMessage(OutputStream output) throws IOException {
218: DataOutput out = new DataOutputStream(output);
219: DataOutput log = new DataOutputStream(System.out);
220: //System.out.println("\nRESPONSE");
221: closeMessage();
222: //writeResponseLine(log);
223: // writeHeaders(log);
224: // writeBody(log);
225: writeResponseLine(out);
226: writeHeaders(out);
227: writeBody(out);
228: }
229:
230: /** initalizes the body */
231: private void initBody() {
232: baos = new ByteArrayOutputStream();
233: writer = new PrintWriter(baos);
234: }
235:
236: /** Creates a string version of the response similar to:
237: *
238: * HTTP/1.1 200 OK
239: * @return the string value of this HttpResponseImpl
240: */
241: public String toString() {
242: StringBuffer buf = new StringBuffer(40);
243:
244: buf.append(HTTP_VERSION);
245: buf.append(SP);
246: buf.append(code + "");
247: buf.append(SP);
248: buf.append(responseString);
249:
250: return buf.toString();
251: }
252:
253: /** closes the message sent to the browser
254: * @throws IOException if an exception is thrown
255: */
256: private void closeMessage() throws IOException {
257: setContentLengthHeader();
258: setCookieHeader();
259: }
260:
261: private void setContentLengthHeader() {
262: if (content == null) {
263: writer.flush();
264: writer.close();
265: body = baos.toByteArray();
266: setHeader("Content-Length", body.length + "");
267: } else {
268: setHeader("Content-Length", content.getContentLength() + "");
269: }
270: }
271:
272: private void setCookieHeader() {
273: if (request == null || request.getSession() == null)
274: return;
275:
276: HttpSession session = request.getSession(false);
277:
278: if (session == null)
279: return;
280:
281: StringBuffer cookie = new StringBuffer();
282: cookie.append(HttpRequestImpl.EJBSESSIONID);
283: cookie.append('=');
284: cookie.append(session.getId());
285: cookie.append("; Path=/");
286:
287: headers.put(HttpRequest.HEADER_SET_COOKIE, cookie.toString());
288: }
289:
290: /** Writes a response line similar to this:
291: *
292: * HTTP/1.1 200 OK
293: *
294: * to the browser
295: * @param out the output stream to write the response line to
296: * @throws IOException if an exception is thrown
297: */
298: private void writeResponseLine(DataOutput out) throws IOException {
299: out.writeBytes(HTTP_VERSION);
300: out.writeBytes(SP);
301: out.writeBytes(code + "");
302: out.writeBytes(SP);
303: out.writeBytes(responseString);
304: out.writeBytes(CRLF);
305: }
306:
307: /** writes the headers out to the browser
308: * @param out the output stream to be sent to the browser
309: * @throws IOException if an exception is thrown
310: */
311: private void writeHeaders(DataOutput out) throws IOException {
312: Iterator it = headers.entrySet().iterator();
313:
314: while (it.hasNext()) {
315: Map.Entry entry = (Map.Entry) it.next();
316: out.writeBytes("" + entry.getKey());
317: out.writeBytes(CSP);
318: out.writeBytes("" + entry.getValue());
319: out.writeBytes(CRLF);
320: }
321: }
322:
323: /** writes the body out to the browser
324: * @param out the output stream that writes to the browser
325: * @throws IOException if an exception is thrown
326: */
327: private void writeBody(DataOutput out) throws IOException {
328: out.writeBytes(CRLF);
329: if (content == null) {
330: out.write(body);
331: } else {
332: InputStream in = content.getInputStream();
333: byte buf[] = new byte[1024];
334: for (int i = 0; (i = in.read(buf)) != -1; out.write(buf, 0,
335: i))
336: ;
337: }
338: }
339:
340: /** gets the name of the server being used
341: * @return the name of the server
342: */
343: public String getServerName() {
344: if (server == null) {
345: String version = "???";
346: String os = "(unknown os)";
347:
348: OpenEjbVersion openejbInfo = OpenEjbVersion.get();
349:
350: version = openejbInfo.getVersion();
351: os = System.getProperty("os.name") + "/"
352: + System.getProperty("os.version") + " ("
353: + System.getProperty("os.arch") + ")";
354:
355: server = "OpenEJB/" + version + " " + os;
356: }
357: return server;
358: }
359:
360: /** This could be improved at some day in the future
361: * to also include a stack trace of the exceptions
362: * @param message the error message to be sent
363: * @return the HttpResponseImpl that this error belongs to
364: */
365: protected static HttpResponseImpl createError(String message) {
366: return createError(message, null);
367: }
368:
369: /** creates an error with user defined variables
370: * @param message the message of the error
371: * @param t a Throwable to print a stack trace to
372: * @return the HttpResponseImpl that this error belongs to
373: */
374: protected static HttpResponseImpl createError(String message,
375: Throwable t) {
376: HttpResponseImpl res = new HttpResponseImpl(500,
377: "Internal Server Error", "text/html");
378: java.io.PrintWriter body = res.getPrintWriter();
379:
380: body.println("<html>");
381: body.println("<body>");
382: body.println("<h3>Internal Server Error</h3>");
383: body.println("<br><br>");
384:
385: if (message != null) {
386: StringTokenizer msg = new StringTokenizer(message, "\n\r");
387:
388: while (msg.hasMoreTokens()) {
389: body.print(msg.nextToken());
390: body.println("<br>");
391: }
392: }
393:
394: if (t != null) {
395: try {
396: body.println("<br><br>");
397: body.println("Stack Trace:<br>");
398: ByteArrayOutputStream baos = new ByteArrayOutputStream();
399: PrintWriter writer = new PrintWriter(baos);
400: t.printStackTrace(writer);
401: writer.flush();
402: writer.close();
403: message = new String(baos.toByteArray());
404: StringTokenizer msg = new StringTokenizer(message,
405: "\n\r");
406:
407: while (msg.hasMoreTokens()) {
408: body.print(msg.nextToken());
409: body.println("<br>");
410: }
411: } catch (Exception e) {
412: }
413: }
414:
415: body.println("</body>");
416: body.println("</html>");
417:
418: return res;
419: }
420:
421: /** Creates a forbidden response to be sent to the browser using IP authentication
422: * @param ip the ip that is forbidden
423: * @return the HttpResponseImpl that this error belongs to
424: */
425: protected static HttpResponseImpl createForbidden(String ip) {
426: HttpResponseImpl res = new HttpResponseImpl(403, "Forbidden",
427: "text/html");
428: java.io.PrintWriter body = res.getPrintWriter();
429:
430: body.println("<html>");
431: body.println("<body>");
432: body.println("<h3>Forbidden</h3>");
433: body.println("<br><br>");
434: // Add more text here
435: // IP not allowed, etc.
436: body
437: .println("IP address: "
438: + ip
439: + " is not registered on this server, please contact your system administrator.");
440: body.println("</body>");
441: body.println("</html>");
442:
443: return res;
444: }
445:
446: /** writes this object out to a file
447: * @param out the ObjectOutputStream to write to
448: * @throws IOException if an exception is thrown
449: */
450: private void writeObject(java.io.ObjectOutputStream out)
451: throws IOException {
452: /** Response string */
453: out.writeObject(responseString);
454:
455: /** Code */
456: out.writeInt(code);
457:
458: /** Response headers */
459: out.writeObject(headers);
460:
461: /** Response body */
462: writer.flush();
463: body = baos.toByteArray();
464: //System.out.println("[] body "+body.length );
465: out.writeObject(body);
466: }
467:
468: /** Reads in a serilized HttpResponseImpl object from a file
469: * @param in the input to read the object from
470: * @throws IOException if an exception is thrown
471: * @throws ClassNotFoundException if an exception is thrown
472: */
473: private void readObject(java.io.ObjectInputStream in)
474: throws IOException, ClassNotFoundException {
475: /** Response string */
476: this .responseString = (String) in.readObject();
477:
478: /** Code */
479: this .code = in.readInt();
480:
481: /** Response headers */
482: this .headers = (HashMap) in.readObject();
483:
484: /** Response body */
485: body = (byte[]) in.readObject();
486: //System.out.println("[] body "+body.length );
487: baos = new ByteArrayOutputStream();
488: baos.write(body);
489: writer = new PrintWriter(baos);
490:
491: }
492:
493: /**
494: * @param content The content to set.
495: */
496: public void setContent(URLConnection content) {
497: this.content = content;
498: }
499:
500: }
|