001: /*
002: * Copyright 2005 Joe Walker
003: *
004: * Licensed under the Apache License, Version 2.0 (the "License");
005: * you may not use this file except in compliance with the License.
006: * You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016: package org.directwebremoting.dwrp;
017:
018: import java.io.IOException;
019: import java.io.PrintWriter;
020:
021: import javax.servlet.http.HttpServletResponse;
022:
023: import org.apache.commons.logging.LogFactory;
024: import org.apache.commons.logging.Log;
025: import org.directwebremoting.extend.Alarm;
026: import org.directwebremoting.extend.ConverterManager;
027: import org.directwebremoting.extend.EnginePrivate;
028: import org.directwebremoting.extend.ScriptConduit;
029: import org.directwebremoting.impl.BasicAlarm;
030: import org.directwebremoting.util.DebuggingPrintWriter;
031:
032: /**
033: * A ScriptConduit that works with the parent Marshaller.
034: * In some ways this is nasty because it has access to essentially private parts
035: * of PollHandler, however there is nowhere sensible to store them
036: * within that class, so this is a hacky simplification.
037: * @author Joe Walker [joe at getahead dot ltd dot uk]
038: */
039: public abstract class BaseScriptConduit extends ScriptConduit {
040: /**
041: * Simple ctor
042: * @param response Used to flush output
043: * @param batchId The id of the batch that we are responding to
044: * @param converterManager How we convert objects to script
045: * @throws IOException If stream actions fail
046: */
047: public BaseScriptConduit(HttpServletResponse response,
048: String batchId, ConverterManager converterManager,
049: boolean jsonOutput) throws IOException {
050: super (RANK_SLOW);
051:
052: this .response = response;
053: this .batchId = batchId;
054: this .converterManager = converterManager;
055: this .jsonOutput = jsonOutput;
056:
057: response.setContentType(getOutboundMimeType());
058: out = response.getWriter();
059:
060: if (debugScriptOutput && log.isDebugEnabled()) {
061: // This might be considered evil - altering the program flow
062: // depending on the log status, however DebuggingPrintWriter is
063: // very thin and only about debugging
064: DebuggingPrintWriter dpw = new DebuggingPrintWriter("", out);
065: dpw.setPrefix("out(" + hashCode() + "): ");
066:
067: out = dpw;
068: }
069:
070: beginStream();
071: }
072:
073: /**
074: * What mime type should we send to the browser for this data?
075: * @return A mime-type
076: */
077: protected abstract String getOutboundMimeType();
078:
079: /**
080: * Called when we are initially setting up the stream. This does not send
081: * any data to the client, just sets it up for data.
082: * <p>This method is always called exactly once in the lifetime of a
083: * conduit.
084: */
085: protected abstract void beginStream();
086:
087: /**
088: * Called when we are shutting the stream down.
089: * <p>This method is always called exactly once in the lifetime of a
090: * conduit, just before the stream is closed.
091: */
092: protected abstract void endStream();
093:
094: /**
095: * A poll has finished, get the client to call us back
096: * @param timetoNextPoll How long before we tell the browser to come back?
097: * @throws IOException When we fail to call endStream()
098: */
099: public void close(int timetoNextPoll) throws IOException {
100: try {
101: EnginePrivate.remoteHandleCallback(this , batchId, "0",
102: timetoNextPoll);
103: } catch (Exception ex) {
104: EnginePrivate.remoteHandleException(this , batchId, "0", ex);
105: log.warn("--Erroring: batchId[" + batchId + "] message["
106: + ex.toString() + ']', ex);
107: }
108:
109: endStream();
110: }
111:
112: /**
113: * Ensure that output we have done is written to the client
114: * @return true/false depending on the write status
115: */
116: protected boolean flush() {
117: out.flush();
118:
119: // I'm not totally sure if this is the right thing to do.
120: // A PrintWriter that encounters an error never recovers so maybe
121: // we could be more robust by using a lower level object and
122: // working out what to do if something goes wrong. Annoyingly
123: // PrintWriter also throws the original exception away.
124: if (out.checkError()) {
125: log.debug("Error writing to stream");
126: // throw new IOException("Error writing to stream");
127: return false;
128: }
129:
130: try {
131: response.flushBuffer();
132: return true;
133: } catch (IOException ex) {
134: // This is likely to be because the user has gone away. Maybe
135: // we should do something clever like remove the script session?
136: log.debug("Error writing to HTTP response:" + ex);
137: alarm.raiseAlarm();
138: return false;
139: }
140: }
141:
142: /**
143: * Do we debug all the scripts that we output?
144: * @param debugScriptOutput true to debug all of the output scripts (verbose)
145: */
146: public void setDebugScriptOutput(boolean debugScriptOutput) {
147: this .debugScriptOutput = debugScriptOutput;
148: }
149:
150: /**
151: * @return The Alarm that goes off if something is badly broken
152: */
153: public Alarm getErrorAlarm() {
154: return alarm;
155: }
156:
157: /**
158: * Do we debug all the scripts that we output?
159: */
160: protected boolean debugScriptOutput = false;
161:
162: /**
163: * Are we outputting in JSON mode?
164: */
165: protected boolean jsonOutput = false;
166:
167: /**
168: * How we convert parameters
169: */
170: protected ConverterManager converterManager = null;
171:
172: /**
173: * Used to flush data to the output stream
174: */
175: protected final HttpServletResponse response;
176:
177: /**
178: * The PrintWriter to send output to, and that we should synchronize against
179: */
180: protected PrintWriter out;
181:
182: /**
183: * What is the ID of the request that we are responding to?
184: */
185: protected final String batchId;
186:
187: /**
188: * An Alarm that goes off if something is badly broken
189: */
190: protected BasicAlarm alarm = new BasicAlarm();
191:
192: /**
193: * The log stream
194: */
195: private static final Log log = LogFactory
196: .getLog(BaseScriptConduit.class);
197: }
|