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.wicket.protocol.http.servlet;
018:
019: import java.io.IOException;
020: import java.io.InputStream;
021: import java.io.UnsupportedEncodingException;
022: import java.util.Iterator;
023: import java.util.List;
024: import java.util.Map;
025:
026: import javax.servlet.http.HttpServletRequest;
027:
028: import org.apache.wicket.WicketRuntimeException;
029: import org.apache.wicket.protocol.http.IMultipartWebRequest;
030: import org.apache.wicket.util.lang.Bytes;
031: import org.apache.wicket.util.upload.DiskFileItemFactory;
032: import org.apache.wicket.util.upload.FileItem;
033: import org.apache.wicket.util.upload.FileUploadException;
034: import org.apache.wicket.util.upload.ServletFileUpload;
035: import org.apache.wicket.util.upload.ServletRequestContext;
036: import org.apache.wicket.util.value.ValueMap;
037:
038: /**
039: * Servlet specific WebRequest subclass for multipart content uploads.
040: *
041: * @author Jonathan Locke
042: * @author Eelco Hillenius
043: * @author Cameron Braid
044: * @author Ate Douma
045: * @author Igor Vaynberg (ivaynberg)
046: */
047: public class MultipartServletWebRequest extends ServletWebRequest
048: implements IMultipartWebRequest {
049: /** Map of file items. */
050: private final ValueMap files = new ValueMap();
051:
052: /** Map of parameters. */
053: private final ValueMap parameters = new ValueMap();
054:
055: /**
056: * total bytes uploaded (downloaded from server's pov) so far. used for
057: * upload notifications
058: */
059: private int bytesUploaded;
060:
061: /** content length cache, used for upload notifications */
062: private int totalBytes;
063:
064: /**
065: * Constructor
066: *
067: * @param maxSize
068: * the maximum size allowed for this request
069: * @param request
070: * the servlet request
071: * @throws FileUploadException
072: * Thrown if something goes wrong with upload
073: */
074: public MultipartServletWebRequest(HttpServletRequest request,
075: Bytes maxSize) throws FileUploadException {
076: super (request);
077:
078: if (maxSize == null) {
079: throw new IllegalArgumentException(
080: "argument maxSize must be not null");
081: }
082:
083: // Check that request is multipart
084: final boolean isMultipart = ServletFileUpload
085: .isMultipartContent(request);
086: if (!isMultipart) {
087: throw new IllegalStateException(
088: "ServletRequest does not contain multipart content");
089: }
090:
091: DiskFileItemFactory factory = new DiskFileItemFactory();
092:
093: // Configure the factory here, if desired.
094: ServletFileUpload upload = new ServletFileUpload(factory);
095:
096: // The encoding that will be used to decode the string parameters
097: // It should NOT be null at this point, but it may be
098: // if the older Servlet API 2.2 is used
099: String encoding = request.getCharacterEncoding();
100:
101: // set encoding specifically when we found it
102: if (encoding != null) {
103: upload.setHeaderEncoding(encoding);
104: }
105:
106: upload.setSizeMax(maxSize.bytes());
107:
108: final List items;
109:
110: if (wantUploadProgressUpdates()) {
111: ServletRequestContext ctx = new ServletRequestContext(
112: request) {
113: public InputStream getInputStream() throws IOException {
114: return new CountingInputStream(super
115: .getInputStream());
116: }
117: };
118: totalBytes = request.getContentLength();
119:
120: onUploadStarted(totalBytes);
121: items = upload.parseRequest(ctx);
122: onUploadCompleted();
123:
124: } else {
125: items = upload.parseRequest(request);
126: }
127:
128: // Loop through items
129: for (Iterator i = items.iterator(); i.hasNext();) {
130: // Get next item
131: final FileItem item = (FileItem) i.next();
132:
133: // If item is a form field
134: if (item.isFormField()) {
135: // Set parameter value
136: final String value;
137: if (encoding != null) {
138: try {
139: value = item.getString(encoding);
140: } catch (UnsupportedEncodingException e) {
141: throw new WicketRuntimeException(e);
142: }
143: } else {
144: value = item.getString();
145: }
146:
147: addParameter(item.getFieldName(), value);
148: } else {
149: // Add to file list
150: files.put(item.getFieldName(), item);
151: }
152: }
153: }
154:
155: /**
156: * Adds a parameter to the parameters value map
157: *
158: * @param name
159: * parameter name
160: * @param value
161: * parameter value
162: */
163: private void addParameter(final String name, final String value) {
164: final String[] currVal = (String[]) parameters.get(name);
165:
166: String[] newVal = null;
167:
168: if (currVal != null) {
169: newVal = new String[currVal.length + 1];
170: System.arraycopy(currVal, 0, newVal, 0, currVal.length);
171: newVal[currVal.length] = value;
172: } else {
173: newVal = new String[] { value };
174:
175: }
176:
177: parameters.put(name, newVal);
178: }
179:
180: /**
181: * @return Returns the files.
182: */
183: public Map getFiles() {
184: return files;
185: }
186:
187: /**
188: * Gets the file that was uploaded using the given field name.
189: *
190: * @param fieldName
191: * the field name that was used for the upload
192: * @return the upload with the given field name
193: */
194: public FileItem getFile(final String fieldName) {
195: return (FileItem) files.get(fieldName);
196: }
197:
198: /**
199: * @see org.apache.wicket.protocol.http.WebRequest#getParameter(java.lang.String)
200: */
201: public String getParameter(final String key) {
202: String[] val = (String[]) parameters.get(key);
203: return (val == null) ? null : val[0];
204: }
205:
206: /**
207: * @see org.apache.wicket.protocol.http.WebRequest#getParameterMap()
208: */
209: public Map getParameterMap() {
210: return parameters;
211: }
212:
213: /**
214: * @see org.apache.wicket.protocol.http.WebRequest#getParameters(java.lang.String)
215: */
216: public String[] getParameters(final String key) {
217: return (String[]) parameters.get(key);
218: }
219:
220: /**
221: * Subclasses that want to receive upload notifiactions should return true
222: *
223: * @return true if upload status update event should be invoked
224: */
225: protected boolean wantUploadProgressUpdates() {
226: return false;
227: }
228:
229: /**
230: * Upload start callback
231: *
232: * @param totalBytes
233: */
234: protected void onUploadStarted(int totalBytes) {
235:
236: }
237:
238: /**
239: * Upload status update callback
240: *
241: * @param bytesUploaded
242: * @param total
243: */
244: protected void onUploadUpdate(int bytesUploaded, int total) {
245:
246: }
247:
248: /**
249: * Upload completed callback
250: */
251: protected void onUploadCompleted() {
252:
253: }
254:
255: /**
256: * An {@link InputStream} that updates total number of bytes read
257: *
258: * @author Igor Vaynberg (ivaynberg)
259: */
260: private class CountingInputStream extends InputStream {
261:
262: private final InputStream in;
263:
264: /**
265: * Constructs a new CountingInputStream.
266: *
267: * @param in
268: * InputStream to delegate to
269: */
270: public CountingInputStream(InputStream in) {
271: this .in = in;
272: }
273:
274: /**
275: * @see java.io.InputStream#read()
276: */
277: public int read() throws IOException {
278: int read = in.read();
279: bytesUploaded += (read < 0) ? 0 : 1;
280: onUploadUpdate(bytesUploaded, totalBytes);
281: return read;
282: }
283:
284: /**
285: * @see java.io.InputStream#read(byte[])
286: */
287: public int read(byte[] b) throws IOException {
288: int read = in.read(b);
289: bytesUploaded += (read < 0) ? 0 : read;
290: onUploadUpdate(bytesUploaded, totalBytes);
291: return read;
292: }
293:
294: /**
295: * @see java.io.InputStream#read(byte[], int, int)
296: */
297: public int read(byte[] b, int off, int len) throws IOException {
298: int read = in.read(b, off, len);
299: bytesUploaded += (read < 0) ? 0 : read;
300: onUploadUpdate(bytesUploaded, totalBytes);
301: return read;
302: }
303:
304: }
305:
306: }
|