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: */
017: package org.apache.cocoon.components.jsp;
018:
019: import org.apache.avalon.framework.logger.AbstractLogEnabled;
020: import org.apache.avalon.framework.parameters.ParameterException;
021: import org.apache.avalon.framework.parameters.Parameters;
022: import org.apache.avalon.framework.parameters.Parameterizable;
023: import org.apache.avalon.framework.thread.ThreadSafe;
024:
025: import javax.servlet.RequestDispatcher;
026: import javax.servlet.ServletContext;
027: import javax.servlet.ServletException;
028: import javax.servlet.http.Cookie;
029: import javax.servlet.http.HttpServletRequest;
030: import javax.servlet.http.HttpServletResponse;
031: import java.io.ByteArrayOutputStream;
032: import java.io.IOException;
033: import java.util.Locale;
034:
035: /**
036: * Allows WLS JSP to be used as a generator.
037: *
038: * This implementation includes via ServletContext.getNamedDispatcher() the
039: * jsp-response. This a WLS-specific implementation.
040: * This code contain WLS 5.1 specific classes, and uses WLS internal classes.
041: *
042: * @author <a href="mailto:dims@yahoo.com">Davanum Srinivas</a>
043: * @author <a href="mailto:bh22351@i-one.at">Bernhard Huber</a>
044: * @version CVS $Id: JSPEngineImplWLS.java 433543 2006-08-22 06:22:54Z crossley $
045: */
046: public class JSPEngineImplWLS extends AbstractLogEnabled implements
047: JSPEngine, Parameterizable, ThreadSafe {
048:
049: /** The Servlet Include Path */
050: public static final String INC_SERVLET_PATH = "javax.servlet.include.servlet_path";
051:
052: /** config-parameter name for specifying the jsp servlet-name.
053: ie. servlet-name
054: */
055: public static final String CONFIG_SERVLET_NAME = "servlet-name";
056: /** default value of CONFIG_SERVLET_NAME.
057: ie. *jsp, this is the WLS JSP servlet default name
058: */
059: public static final String DEFAULT_SERVLET_NAME = "*.jsp";
060: /** the configured name of the jsp servlet
061: */
062:
063: String servletName = DEFAULT_SERVLET_NAME;
064:
065: /**
066: * parameterize
067: * @param params Parameters
068: * @exception ParameterException
069: */
070: public void parameterize(Parameters params)
071: throws ParameterException {
072: this .servletName = params.getParameter(CONFIG_SERVLET_NAME,
073: DEFAULT_SERVLET_NAME);
074: }
075:
076: /**
077: * execute the JSP and return the output
078: *
079: * @param url
080: * @param servletRequest
081: * @param servletResponse
082: * @param servletContext
083: * @exception IOException
084: * @exception ServletException
085: * @exception Exception
086: */
087: public byte[] executeJSP(String url,
088: HttpServletRequest servletRequest,
089: HttpServletResponse servletResponse,
090: ServletContext servletContext) throws IOException,
091: ServletException, Exception {
092:
093: byte[] bytes = null;
094:
095: HttpServletRequest request = servletRequest;
096: String inc_servlet_path_was = (String) servletRequest
097: .getAttribute(INC_SERVLET_PATH);
098: request.setAttribute(INC_SERVLET_PATH, url);
099: MyWLSResponse response = new MyWLSResponse(
100: servletResponse,
101: (weblogic.servlet.internal.ServletContextImpl) servletContext);
102:
103: // dispatch to the named servlet
104: RequestDispatcher rd = servletContext
105: .getNamedDispatcher(servletName);
106: if (rd != null) {
107: rd.include(request, response);
108: response.flushBuffer();
109:
110: if (getLogger().isDebugEnabled()) {
111: getLogger()
112: .debug(
113: "JSP response: "
114: + response
115: .getResponseContentAsString());
116: }
117:
118: bytes = response.getResponseContentAsByteArray();
119:
120: if (inc_servlet_path_was != null) {
121: servletRequest.setAttribute(INC_SERVLET_PATH,
122: inc_servlet_path_was);
123: }
124: } else {
125: getLogger().error(
126: "Specify a correct " + CONFIG_SERVLET_NAME + " "
127: + servletName);
128: }
129:
130: return bytes;
131: }
132:
133: /** WLS jsp servlet hack.
134: <p>
135: Here WLS specific classes are used.
136: </p>
137: <p>
138: The weblogic.servlet.JSPServlet, and
139: weblogic.servlet.internal.RequesDispatcherImpl expects
140: objects weblogic.servlet.internal.ServletOutputStreamImpl,
141: and weblogic.servlet.internal.ServletResponseImpl.
142: Thus we have to use <i>exactly</i> these classes!
143: </p>
144: */
145: static class MyWLSResponse extends
146: weblogic.servlet.internal.ServletResponseImpl {
147: /* the cocoon2 response. Let's use this response to forward headers
148: , cookies, etc generated inside the jsp-response
149: */
150: HttpServletResponse response;
151:
152: ByteArrayOutputStream baos;
153: weblogic.servlet.internal.ServletOutputStreamImpl wlsOutputStream;
154:
155: public MyWLSResponse(
156: HttpServletResponse response,
157: weblogic.servlet.internal.ServletContextImpl servlet_context) {
158:
159: super (servlet_context);
160: this .response = response;
161:
162: baos = new ByteArrayOutputStream();
163:
164: wlsOutputStream = new weblogic.servlet.internal.ServletOutputStreamImpl(
165: baos);
166: this .setOutputStream(wlsOutputStream);
167: wlsOutputStream.setImpl(this );
168: }
169:
170: /** flush response content.
171: */
172: public void flushBuffer() throws IOException {
173: super .flushBuffer();
174: baos.flush();
175: }
176:
177: /** return response as byte array.
178: <p>Note: http-headers are skipped. More exactly all chars until first
179: '<?xml', or '\r\n\r\n< sequence. This may be a bit heuristic.
180: </p>
181: <p>Note: we are expecting the xml prolog, without the xml prolog http
182: -headers are passed further, and the xml parser will surly complain!
183: </p>
184: */
185: public byte[] getResponseContentAsByteArray() {
186: byte[] baos_arr = baos.toByteArray();
187:
188: int baos_arr_length = baos_arr.length;
189: int i = 0;
190: boolean matched = false;
191: final int I_MAX = 8192; // check only header
192:
193: final byte MATCH_0d = (byte) '\r';
194: final byte MATCH_0a = (byte) '\n';
195:
196: final byte MATCH_FIRST = (byte) '<';
197: final byte MATCH_SECOND = (byte) '?';
198: final byte MATCH_THIRD = (byte) 'x';
199: final byte MATCH_FOURTH = (byte) 'm';
200: final byte MATCH_FIFTH = (byte) 'l';
201:
202: final int MATCH_COUNT = 5;
203:
204: while (i + MATCH_COUNT < baos_arr_length && i < I_MAX
205: && !matched) {
206:
207: matched = (baos_arr[i] == MATCH_FIRST
208: && baos_arr[i + 1] == MATCH_SECOND
209: && baos_arr[i + 2] == MATCH_THIRD
210: && baos_arr[i + 3] == MATCH_FOURTH && baos_arr[i + 4] == MATCH_FIFTH);
211: if (matched)
212: break;
213:
214: matched = (baos_arr[i] == MATCH_0d
215: && baos_arr[i + 1] == MATCH_0a
216: && baos_arr[i + 2] == MATCH_0d
217: && baos_arr[i + 3] == MATCH_0a && baos_arr[i + 4] == MATCH_FIRST);
218: if (matched) {
219: i += 4; // skip leading \r\n\r\n, too
220: break;
221: }
222: i += 2;
223: }
224: if (matched && i > 0) {
225: int baos_arr_new_length = baos_arr_length - i;
226:
227: byte[] new_baos_arr = new byte[baos_arr_new_length];
228: System.arraycopy(baos_arr, i, new_baos_arr, 0,
229: baos_arr_new_length);
230: baos_arr = new_baos_arr;
231: }
232: return baos_arr;
233: }
234:
235: public String getResponseContentAsString() {
236: String s = new String(getResponseContentAsByteArray());
237: return s;
238: }
239:
240: // following methods forwarding from jsp-repsonse to cocoon2-repsonse
241:
242: public String getCharacterEncoding() {
243: return this .response.getCharacterEncoding();
244: }
245:
246: public Locale getLocale() {
247: return this .response.getLocale();
248: }
249:
250: public void addCookie(Cookie cookie) {
251: response.addCookie(cookie);
252: }
253:
254: public boolean containsHeader(String s) {
255: return response.containsHeader(s);
256: }
257:
258: /** @deprecated use encodeURL(String url) instead. */
259: public String encodeUrl(String s) {
260: return response.encodeUrl(s);
261: }
262:
263: public String encodeURL(String s) {
264: return response.encodeURL(s);
265: }
266:
267: /** @deprecated use encodeRedirectURL(String url) instead. */
268: public String encodeRedirectUrl(String s) {
269: return response.encodeRedirectUrl(s);
270: }
271:
272: public String encodeRedirectURL(String s) {
273: return response.encodeRedirectURL(s);
274: }
275:
276: public void sendError(int i, String s) throws IOException {
277: response.sendError(i, s);
278: }
279:
280: public void sendError(int i) throws IOException {
281: response.sendError(i);
282: }
283:
284: public void sendRedirect(String s) throws IOException {
285: response.sendRedirect(s);
286: }
287:
288: public void setDateHeader(String s, long l) {
289: response.setDateHeader(s, l);
290: }
291:
292: public void addDateHeader(String s, long l) {
293: response.addDateHeader(s, l);
294: }
295:
296: public void setHeader(String s, String s1) {
297: response.setHeader(s, s1);
298: }
299:
300: public void addHeader(String s, String s1) {
301: response.addHeader(s, s1);
302: }
303:
304: public void setIntHeader(String s, int i) {
305: response.setIntHeader(s, i);
306: }
307:
308: public void addIntHeader(String s, int i) {
309: response.addIntHeader(s, i);
310: }
311:
312: public void setStatus(int i) {
313: response.setStatus(i);
314: }
315:
316: /** @deprecated use sendError(int, String) instead */
317: public void setStatus(int i, String s) {
318: response.setStatus(i, s);
319: }
320: }
321: }
|