KickJava   Java API By Example, From Geeks To Geeks.

Java > Open Source Codes > org > wings > SFileChooser


1 /*
2  * $Id: SFileChooser.java,v 1.10 2005/04/08 15:05:18 blueshift Exp $
3  * Copyright 2000,2005 wingS development team.
4  *
5  * This file is part of wingS (http://www.j-wings.org).
6  *
7  * wingS is free software; you can redistribute it and/or modify
8  * it under the terms of the GNU Lesser General Public License
9  * as published by the Free Software Foundation; either version 2.1
10  * of the License, or (at your option) any later version.
11  *
12  * Please see COPYING for the complete licence.
13  */

14 package org.wings;
15
16 import org.apache.commons.logging.Log;
17 import org.apache.commons.logging.LogFactory;
18 import org.wings.plaf.FileChooserCG;
19
20 import javax.servlet.http.HttpUtils JavaDoc;
21 import java.io.File JavaDoc;
22 import java.io.FilterOutputStream JavaDoc;
23 import java.io.IOException JavaDoc;
24 import java.util.Hashtable JavaDoc;
25
26 /**
27  * A filechooser shows a textfield with a browse-button to enter a file.
28  * The file is uploaded via HTTP and made accessible to the WingS application.
29  * <p/>
30  * <p>The uploaded file is stored temporarily in the filesystem of the
31  * server with a unique name, so that uploaded files with the same
32  * filename do not clash. You can access this internal name with
33  * the {@link #getFileDir()} and {@link #getFileId()} methods.
34  * The user provided filename can be queried with the
35  * {@link #getFileName()} method.
36  * <p/>
37  * Since the file is stored temporarily in the filesystem, you should
38  * {@link File#delete()} it, when you are done with it.
39  * However, if you don't delete the file yourself, it is eventually
40  * being removed by the Java garbage collector, if you haven't renamed it
41  * (see {@link #getFile()}).
42  * <p/>
43  * <p>The form, you add this SFileChooser to, needs to have the encoding type
44  * <code>multipart/form-data</code> set
45  * (form.{@link SForm#setEncodingType(String) setEncodingType("multipart/form-data")}).
46  * <p/>
47  * <p>You can limit the size of files to be uploaded, so it is hard to make
48  * a denial-of-service (harddisk, bandwidth) attack from outside to your
49  * server. You can modify the maximum content length to be posted in
50  * {@link org.wings.session.Session#setMaxContentLength(int)}. This is
51  * 64 kByte by default, so you might want to change this in your application.
52  * <p/>
53  * <p/>
54  * The SFileChooser notifies the form if something has gone
55  * wrong with uploading a file.
56  * <p/>
57  * <b>Szenario</b>
58  * Files that are too big to be uploaded are blocked
59  * very early in the upload-process (if you are curious: this is done in
60  * {@link org.wings.session.MultipartRequest}).
61  * At that time, only a partial input is
62  * read, the rest is discarded to thwart denial of service attacks. Since we
63  * read only part of the input, we cannot make sure, that <em>all</em>
64  * parameters are gathered from the input, thus we cannot just deliver the
65  * events contained, since they might be incomplete. However, the file
66  * chooser needs to be informed, that something went wrong as to present
67  * an error message to the user. So in that case, only <em>one</em> event
68  * is delivered to the enclosing <em>form</em>, that contains this
69  * SFileChooser.
70  * <p/>
71  * <p>Note, that in this case, this will <em>not</em> trigger the action
72  * listener that you might have added to the submit-button.
73  * This means, that you <em>always</em> should add your action listener
74  * to the {@link SForm} ({@link SForm#addActionListener(java.awt.event.ActionListener)}),
75  * <em>not</em> the submit button.
76  *
77  * @author <a HREF="mailto:HEngels@mercatis.de">Holger Engels</a>
78  * @author <a HREF="mailto:H.Zeller@acm.org">Henner Zeller</a>
79  * @version $Revision: 1.10 $
80  */

81 public class SFileChooser
82         extends SComponent
83         implements LowLevelEventListener {
84     private final transient static Log log = LogFactory.getLog(SFileChooser.class);
85
86     /**
87      * maximum visible amount of characters in the file chooser.
88      */

89     protected int columns = 16;
90
91     protected String JavaDoc fileNameFilter = null;
92
93     protected Class JavaDoc filter = null;
94     protected String JavaDoc fileDir = null;
95     protected String JavaDoc fileName = null;
96     protected String JavaDoc fileId = null;
97     protected String JavaDoc fileType = null;
98
99     /**
100      * the temporary file created on upload. This file is automatically
101      * removed if and when it is not accessible anymore.
102      */

103     protected TempFile currentFile = null;
104
105     /**
106      * the temporary file created on upload. This file is automatically
107      * removed if and when it is not accessible anymore.
108      */

109     protected IOException JavaDoc exception = null;
110
111     /**
112      * Creates a new FileChooser.
113      */

114     public SFileChooser() {
115     }
116
117     /**
118      * Find the form, this FileChooser is embedded in.
119      */

120     protected final SForm getParentForm() {
121         SComponent parent = getParent();
122
123         while (parent != null && !(parent instanceof SForm)) {
124             parent = parent.getParent();
125         }
126
127         return (SForm) parent;
128     }
129
130     /**
131      * notifies the parent form, to fire action performed. This is necessary,
132      * if an exception in parsing a MultiPartRequest occurs, e.g. upload
133      * file is too big.
134      */

135     protected final void notifyParentForm() {
136         SForm form = getParentForm();
137
138         if (form != null) {
139             SForm.addArmedComponent(form);
140         }
141     }
142
143     /**
144      * Set the visible amount of columns in the textfield.
145      *
146      * @param c columns; '-1' sets the default that is browser dependent.
147      */

148     public void setColumns(int c) {
149         int oldColumns = columns;
150         columns = c;
151         if (columns != oldColumns)
152             reload();
153     }
154
155     /**
156      * returns the number of visible columns.
157      *
158      * @return number of visible columns.
159      */

160     public int getColumns() {
161         return columns;
162     }
163
164     /**
165      * Unlike the swing filechooser that allows to match certain file-suffices,
166      * this sets the <em>mimetype</em> to be accepted. This filter may be fully
167      * qualified like <code>text/html</code> or can contain
168      * a wildcard in the subtype like <code>text/ *</code>. Some browsers
169      * may as well accept a file-suffix wildcard as well.
170      * <p/>
171      * <p>In any case, you hould check the result, since you cannot assume,
172      * that the browser
173      * actually does filter. Worse, browsers may not guess the
174      * correct type so users cannot upload a file even if it has the correct
175      * type. So, bottomline, it is generally a good idea to let the file
176      * name filter untouched, unless you know bugs of the browser at the
177      * other end of the wire...
178      *
179      * @param mimeFilter the mime type to be filtered.
180      */

181     public void setFileNameFilter(String JavaDoc mimeFilter) {
182         fileNameFilter = mimeFilter;
183     }
184
185     /**
186      * returns the current filename filter. This is a mimetype-filter
187      *
188      * @return the current filename filter or 'null', if no filename filter
189      * is provided.
190      * @see #setFileNameFilter(String)
191      */

192     public String JavaDoc getFileNameFilter() {
193         return fileNameFilter;
194     }
195
196     /**
197      * Returns the filename, that has been given by the user in the
198      * upload text-field.
199      *
200      * @return the filename, given by the user.
201      * @throws IOException if something went wrong with the upload (most
202      * likely, the maximum allowed filesize is exceeded, see
203      * {@link org.wings.session.Session#setMaxContentLength(int)})
204      */

205     public String JavaDoc getFileName() throws IOException JavaDoc {
206         if (exception != null)
207             throw exception;
208
209         return fileName;
210     }
211
212     /**
213      * Returns the name of the system directory, the file has been stored
214      * temporarily in. You won't need this, unless you want to access the
215      * file directly. Don't store the value you receive here for use later,
216      * since the SFileChooser does its own garbage collecting of unused files.
217      *
218      * @return the pathname of the system directory, the file is stored in.
219      * @throws IOException if something went wrong with the upload (most
220      * likely, the maximum allowed filesize is exceeded, see
221      * {@link org.wings.session.Session#setMaxContentLength(int)})
222      */

223     public String JavaDoc getFileDir() throws IOException JavaDoc {
224         if (exception != null)
225             throw exception;
226
227         return fileDir;
228     }
229
230     /**
231      * Returns the internal ID of this file, that has been assigned at upload
232      * time. This ID is unique to prevent clashes with other uploaded files.
233      * You won't need this, unless you want to access the file directly. Don't
234      * store the value you receive here for later use, since the SFileChooser
235      * does its own garbage collecting of unused files.
236      *
237      * @return the internal, unique file id given to the uploaded file.
238      * @throws IOException if something went wrong with the upload (most
239      * likely, the maximum allowed filesize is exceeded, see
240      * {@link org.wings.session.Session#setMaxContentLength(int)})
241      */

242     public String JavaDoc getFileId() throws IOException JavaDoc {
243         if (exception != null)
244             throw exception;
245
246         return fileId;
247     }
248
249     /**
250      * Returns the mime type of this file, if known.
251      *
252      * @return the mime type of this file.
253      * @throws IOException if something went wrong with the upload (most
254      * likely, the maximum allowed filesize is exceeded, see
255      * {@link org.wings.session.Session#setMaxContentLength(int)})
256      */

257     public String JavaDoc getFileType() throws IOException JavaDoc {
258         if (exception != null)
259             throw exception;
260
261         return fileType;
262     }
263
264     /**
265      * pseudonym for {@link #getFile()} (see there).
266      *
267      * @return a File to access the content of the uploaded file.
268      * @throws IOException if something went wrong with the upload (most
269      * likely, the maximum allowed filesize is exceeded, see
270      * {@link org.wings.session.Session#setMaxContentLength(int)})
271      */

272     public File JavaDoc getSelectedFile() throws IOException JavaDoc {
273         return getFile();
274     }
275
276     /**
277      * resets this FileChooser (no file selected). It does not remove an
278      * upload filter!.
279      * reset() will <em>not</em> remove a previously selected file from
280      * the local tmp disk space, so as long as you have a reference to
281      * such a file, you can still access it. If you don't have a reference
282      * to the file, it will automatically be removed when the file object
283      * is garbage collected.
284      */

285     public void reset() {
286         currentFile = null;
287         fileId = null;
288         fileDir = null;
289         fileType = null;
290         fileName = null;
291         exception = null;
292     }
293
294     /**
295      * returns the file, that has been uploaded. Use this, to open and
296      * read from the file uploaded by the user. Don't use this method
297      * to query the actual filename given by the user, since this file
298      * wraps a system generated file with a different (unique) name.
299      * Use {@link #getFileName()} instead.
300      * <p/>
301      * <p>The file returned here
302      * will delete itself if you loose the reference to it and it is
303      * garbage collected to avoid filling up the filesystem (This doesn't
304      * mean, that you shouldn't be a good programmer and delete the
305      * file yourself, if you don't need it anymore :-).
306      * If you rename() the file to use it somewhere else,
307      * it is regarded not temporary anymore and thus will <em>not</em>
308      * be removed from the filesystem.
309      *
310      * @return a File to access the content of the uploaded file.
311      * @throws IOException if something went wrong with the upload (most
312      * likely, the maximum allowed filesize is exceeded, see
313      * {@link org.wings.session.Session#setMaxContentLength(int)})
314      */

315     public File JavaDoc getFile() throws IOException JavaDoc {
316         if (exception != null)
317             throw exception;
318
319         return currentFile;
320     }
321
322     /**
323      * An FilterOutputStream, that filters incoming files. You can use
324      * UploadFilters to inspect the stream or rewrite it to some own
325      * format.
326      *
327      * @param filter the Class that is instanciated to filter incoming
328      * files.
329      */

330     public void setUploadFilter(Class JavaDoc filter) {
331         if (!FilterOutputStream JavaDoc.class.isAssignableFrom(filter))
332             throw new IllegalArgumentException JavaDoc(filter.getName() + " is not a FilterOutputStream!");
333
334         UploadFilterManager.registerFilter(getLowLevelEventId(), filter);
335         this.filter = filter;
336     }
337
338     /**
339      * Returns the upload filter set in {@link #setUploadFilter(Class)}
340      */

341     public Class JavaDoc getUploadFilter() {
342         return filter;
343     }
344
345
346     public FilterOutputStream JavaDoc getUploadFilterInstance() {
347         return UploadFilterManager.getFilterInstance(getLowLevelEventId());
348     }
349
350     public void setCG(FileChooserCG cg) {
351         super.setCG(cg);
352     }
353
354     // -- Implementation of LowLevelEventListener
355
public void processLowLevelEvent(String JavaDoc action, String JavaDoc[] values) {
356         processKeyEvents(values);
357
358         exception = null;
359
360         String JavaDoc value = values[0];
361
362         if ("exception".equals(value)) {
363             exception = new IOException JavaDoc(values[1]);
364
365             notifyParentForm();
366         } else {
367             try {
368                 Hashtable JavaDoc params = HttpUtils.parseQueryString(value);
369                 String JavaDoc[] arr;
370                 arr = (String JavaDoc[]) params.get("dir");
371                 this.fileDir = (arr != null) ? arr[0] : null;
372                 arr = (String JavaDoc[]) params.get("name");
373                 this.fileName = (arr != null) ? arr[0] : null;
374                 arr = (String JavaDoc[]) params.get("id");
375                 this.fileId = (arr != null) ? arr[0] : null;
376                 arr = (String JavaDoc[]) params.get("type");
377                 this.fileType = (arr != null) ? arr[0] : null;
378                 if (fileDir != null && fileId != null) {
379                     currentFile = new TempFile(fileDir, fileId);
380                 }
381             } catch (Exception JavaDoc e) {
382                 log.fatal(null, e);
383             }
384         }
385     }
386
387     public void fireIntermediateEvents() {
388     }
389
390     /** @see LowLevelEventListener#isEpochCheckEnabled() */
391     private boolean epochCheckEnabled = true;
392
393     /** @see LowLevelEventListener#isEpochCheckEnabled() */
394     public boolean isEpochCheckEnabled() {
395         return epochCheckEnabled;
396     }
397
398     /** @see LowLevelEventListener#isEpochCheckEnabled() */
399     public void setEpochCheckEnabled(boolean epochCheckEnabled) {
400         this.epochCheckEnabled = epochCheckEnabled;
401     }
402
403     /**
404      * A temporary file. This file removes its representation in the
405      * filesysten, when there are no references to it (i.e. it is garbage
406      * collected)
407      */

408     private static class TempFile extends File JavaDoc {
409         private boolean isTemp;
410
411         public TempFile(String JavaDoc parent, String JavaDoc child) {
412             super(parent, child);
413             deleteOnExit();
414             isTemp = true;
415         }
416
417         /**
418          * when this file is renamed, then it is not temporary anymore,
419          * thus will not be removed on cleanup.
420          */

421         public boolean renameTo(File JavaDoc newfile) {
422             boolean success;
423             success = super.renameTo(newfile);
424             isTemp &= !success; // we are not temporary anymore on success.
425
return success;
426         }
427
428         /**
429          * removes the file in the filesystem, if it is still temporary.
430          */

431         private void cleanup() {
432             if (isTemp) {
433                 delete();
434             }
435         }
436
437         /**
438          * do a cleanup, if this temporary file is garbage collected.
439          */

440         protected void finalize() throws Throwable JavaDoc {
441             super.finalize();
442             if (isTemp) log.debug("garbage collect file " + getName());
443             cleanup();
444         }
445     }
446 }
447
448
449
Popular Tags