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.util.ArrayList;
019: import java.util.HashMap;
020: import java.util.Iterator;
021: import java.util.List;
022: import java.util.Map;
023:
024: import javax.servlet.http.HttpServletRequest;
025:
026: import org.apache.commons.logging.Log;
027: import org.apache.commons.logging.LogFactory;
028: import org.directwebremoting.extend.Call;
029: import org.directwebremoting.extend.Calls;
030: import org.directwebremoting.extend.FormField;
031: import org.directwebremoting.extend.InboundContext;
032: import org.directwebremoting.extend.ServerException;
033: import org.directwebremoting.util.LocalUtil;
034: import org.directwebremoting.util.Messages;
035:
036: /**
037: * A container for all the by-products of an HttpRequest parse
038: * @author Joe Walker [joe at getahead dot ltd dot uk]
039: */
040: public class Batch {
041: /**
042: * Parse an inbound request into a Calls object
043: * @param request The original browser's request
044: */
045: public Batch(HttpServletRequest request) throws ServerException {
046: get = "GET".equals(request.getMethod());
047:
048: ParseUtil parseUtil = new ParseUtil();
049: allParameters = parseUtil.parseRequest(request);
050:
051: parseParameters();
052: }
053:
054: /**
055: * Ctor for the Bayeux client which doesn't have requests and responses
056: * @param allParameters A set of name value pairs
057: * @throws SecurityException If the parameters can't be decoded
058: */
059: public Batch(Map<String, FormField> allParameters) {
060: this .allParameters = allParameters;
061: parseParameters();
062: }
063:
064: /**
065: * Fish out the important parameters
066: * @throws SecurityException If the parsing of input parameter fails
067: */
068: protected void parseParameters() {
069: Map<String, FormField> paramMap = new HashMap<String, FormField>(
070: allParameters);
071: calls = new Calls();
072:
073: // Work out how many calls are in this packet
074: String callStr = extractParameter(paramMap,
075: ProtocolConstants.INBOUND_CALL_COUNT);
076: int callCount;
077: try {
078: callCount = Integer.parseInt(callStr);
079: } catch (NumberFormatException ex) {
080: throw new SecurityException("Invalid Call Count");
081: }
082:
083: if (callCount > maxCallsPerBatch) {
084: throw new SecurityException("Too many calls in a batch");
085: }
086:
087: // Extract the ids, script names and method names
088: for (int callNum = 0; callNum < callCount; callNum++) {
089: Call call = new Call();
090: calls.addCall(call);
091:
092: InboundContext inctx = new InboundContext();
093: inboundContexts.add(inctx);
094:
095: String prefix = ProtocolConstants.INBOUND_CALLNUM_PREFIX
096: + callNum
097: + ProtocolConstants.INBOUND_CALLNUM_SUFFIX;
098:
099: // The special values
100: String callId = extractParameter(paramMap, prefix
101: + ProtocolConstants.INBOUND_KEY_ID);
102: call.setCallId(callId);
103: if (!LocalUtil.isLetterOrDigitOrUnderline(callId)) {
104: throw new SecurityException(
105: "Call IDs may only contain Java Identifiers");
106: }
107:
108: String scriptName = extractParameter(paramMap, prefix
109: + ProtocolConstants.INBOUND_KEY_SCRIPTNAME);
110: call.setScriptName(scriptName);
111: if (!LocalUtil.isLetterOrDigitOrUnderline(scriptName)) {
112: throw new SecurityException(
113: "Script names may only contain Java Identifiers");
114: }
115:
116: String methodName = extractParameter(paramMap, prefix
117: + ProtocolConstants.INBOUND_KEY_METHODNAME);
118: call.setMethodName(methodName);
119: if (!LocalUtil.isLetterOrDigitOrUnderline(methodName)) {
120: throw new SecurityException(
121: "Method names may only contain Java Identifiers");
122: }
123:
124: // Look for parameters to this method
125: for (Iterator<Map.Entry<String, FormField>> it = paramMap
126: .entrySet().iterator(); it.hasNext();) {
127: Map.Entry<String, FormField> entry = it.next();
128: String key = entry.getKey();
129:
130: if (key.startsWith(prefix)) {
131: FormField formField = entry.getValue();
132: if (formField.isFile()) {
133: inctx.createInboundVariable(callNum, key,
134: ProtocolConstants.TYPE_FILE, formField);
135: } else {
136: String[] split = ParseUtil
137: .splitInbound(formField.getString());
138:
139: String value = split[LocalUtil.INBOUND_INDEX_VALUE];
140: String type = split[LocalUtil.INBOUND_INDEX_TYPE];
141: inctx.createInboundVariable(callNum, key, type,
142: value);
143: }
144: it.remove();
145: }
146: }
147: }
148:
149: String batchId = extractParameter(paramMap,
150: ProtocolConstants.INBOUND_KEY_BATCHID);
151: calls.setBatchId(batchId);
152: if (!LocalUtil.isLetterOrDigitOrUnderline(batchId)) {
153: throw new SecurityException(
154: "Batch IDs may only contain Java Identifiers");
155: }
156:
157: httpSessionId = extractParameter(paramMap,
158: ProtocolConstants.INBOUND_KEY_HTTP_SESSIONID);
159: scriptSessionId = extractParameter(paramMap,
160: ProtocolConstants.INBOUND_KEY_SCRIPT_SESSIONID);
161: page = extractParameter(paramMap,
162: ProtocolConstants.INBOUND_KEY_PAGE);
163: windowName = extractParameter(allParameters,
164: ProtocolConstants.INBOUND_KEY_WINDOWNAME);
165:
166: for (Map.Entry<String, FormField> entry : paramMap.entrySet()) {
167: String key = entry.getKey();
168: FormField value = entry.getValue();
169: if (key.startsWith(ProtocolConstants.INBOUND_KEY_METADATA)) {
170: spareParameters
171: .put(
172: key
173: .substring(ProtocolConstants.INBOUND_KEY_METADATA
174: .length()), value);
175: }
176: }
177: }
178:
179: /**
180: * Extract a parameter and ensure it is in the request.
181: * This is needed to cope with Jetty continuations that are not real
182: * continuations.
183: * @param parameters The parameter list parsed out of the request
184: * @param paramName The name of the parameter sent
185: * @return The found value
186: */
187: protected String extractParameter(
188: Map<String, FormField> parameters, String paramName) {
189: FormField formField = parameters.remove(paramName);
190: if (formField == null) {
191: throw new IllegalArgumentException(Messages.getString(
192: "PollHandler.MissingParameter", paramName));
193: }
194:
195: return formField.getString();
196: }
197:
198: /**
199: * @return the inboundContexts
200: */
201: public List<InboundContext> getInboundContexts() {
202: return inboundContexts;
203: }
204:
205: /**
206: * @return the spareParameters
207: */
208: public Map<String, FormField> getSpareParameters() {
209: return spareParameters;
210: }
211:
212: /**
213: * @return the calls
214: */
215: public Calls getCalls() {
216: return calls;
217: }
218:
219: /**
220: * @return the httpSessionId
221: */
222: public String getHttpSessionId() {
223: return httpSessionId;
224: }
225:
226: /**
227: * @return the scriptSessionId
228: */
229: public String getScriptSessionId() {
230: return scriptSessionId;
231: }
232:
233: /**
234: * @return the page
235: */
236: public String getPage() {
237: return page;
238: }
239:
240: /**
241: * @return the window name
242: */
243: public String getWindowName() {
244: return windowName;
245: }
246:
247: /**
248: * Is this request from a GET?
249: * @return true if the request is a GET request
250: */
251: public boolean isGet() {
252: return get;
253: }
254:
255: /* (non-Javadoc)
256: * @see java.lang.Object#toString()
257: */
258: @Override
259: public String toString() {
260: return "Batch[page=" + page + ",scriptSessionId="
261: + scriptSessionId + "]";
262: }
263:
264: /**
265: * There is one inbound context to keep track of the conversions that are
266: * done for each call.
267: */
268: private List<InboundContext> inboundContexts = new ArrayList<InboundContext>();
269:
270: /**
271: * We don't want to allow too many calls in a batch
272: */
273: private int maxCallsPerBatch = 1000;
274:
275: /**
276: * The list of calls in the batch
277: */
278: private Calls calls;
279:
280: /**
281: * The unique ID sent to the browser in the session cookie
282: */
283: private String httpSessionId;
284:
285: /**
286: * The page that the request was sent from
287: */
288: private String page;
289:
290: /**
291: * Window name is used by reverse ajax to get around the 2 connection limit
292: */
293: private String windowName;
294:
295: /**
296: * The unique ID sent to the current page
297: */
298: private String scriptSessionId;
299:
300: /**
301: * Is it a GET request?
302: */
303: private boolean get;
304:
305: /**
306: * All the parameters sent by the browser
307: */
308: private Map<String, FormField> allParameters;
309:
310: /**
311: * The unused parameters
312: */
313: private Map<String, FormField> spareParameters = new HashMap<String, FormField>();
314:
315: /**
316: * The log stream
317: */
318: protected static final Log log = LogFactory.getLog(Batch.class);
319: }
|