// $Id: JUploadApplet.java 750 2009-05-06 14:36:50Z etienne_sf $ // // jupload - A file upload applet. // Copyright 2007 The JUpload Team // // Created: ? // Creator: William JinHua Kwong // Last modified: $Date: 2009-05-06 16:36:50 +0200 (mer., 06 mai 2009) $ // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 2 of the License, or (at your option) any later // version. This program is distributed in the hope that it will be useful, but // WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or // FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more // details. You should have received a copy of the GNU General Public License // along with this program; if not, write to the Free Software Foundation, Inc., // 675 Mass Ave, Cambridge, MA 02139, USA. package wjhk.jupload2.context; import java.awt.Cursor; import java.awt.Frame; import java.io.InputStream; import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Method; import java.text.MessageFormat; import java.util.ArrayList; import java.util.Date; import java.util.List; import java.util.Locale; import java.util.Properties; import java.util.Vector; import javax.swing.JApplet; import javax.swing.JOptionPane; import javax.swing.RootPaneContainer; import javax.swing.SwingUtilities; import wjhk.jupload2.exception.JUploadException; import wjhk.jupload2.gui.JUploadPanel; import wjhk.jupload2.gui.JUploadPanelImpl; import wjhk.jupload2.gui.JUploadTextArea; import wjhk.jupload2.policies.UploadPolicy; import wjhk.jupload2.policies.UploadPolicyFactory; import wjhk.jupload2.upload.FileUploadManagerThread; /** * The Jupload Context. One such context is created at run time. It can be the * Applet, or the 'main' class, depending on the launch type.
* It contains the call to the creation of the * {@link wjhk.jupload2.gui.JUploadPanel}, which contains the real code, and * some technical stuff that depend on the technical context (mainly applet or * stand alone application).
* The functional control of JUpload is done by using {@link UploadPolicy}. This * class should not be changed, in order to remain compatible with next JUpload * releases.
*
* Technical note: This class should be abstract. But it is used by the * build.xml file, to load the version. So all methods of the * {@link JUploadContext} interface are implemented. Those who actually can't be * coded here, just generate a UnsupportedOperationException exception. * * @author etienne_sf * @version $Revision: 750 $ */ public class DefaultJUploadContext implements JUploadContext { /** * The final that contains the SVN properties. These properties are * generated during compilation, by the build.xml ant file. */ private final static String SVN_PROPERTIES_FILENAME = "/conf/svn.properties"; /** * Used as default value when calling getProperty, to identify missing * properties, and have a correct behavior in this case. */ private final static String DEFAULT_PROP_UNKNOWN = "Unknown"; /** * The properties, created at build time, by the build.xml ant file. Or a * dummy property set, with 'unknown' values. */ Properties svnProperties = getSvnProperties(); /** * The frame that contains the application. Mainly used to attached modal * dialog. */ Frame frame = null; /** * variable to hold reference to JavascriptHandler object */ JavascriptHandler jsHandler = null; /** * the mime type list, coming from: http://www.mimetype.org/ Thanks to them! */ Properties mimeTypesProperties = null; /** * The current upload policy. This class is responsible for the call to the * UploadPolicyFactory. */ UploadPolicy uploadPolicy = null; /** * The JUploadPanel, which actually contains all the applet components. */ JUploadPanel jUploadPanel = null; /** * The log messages should go there ... */ JUploadTextArea logWindow = null; /** * This class represent the Callback method. It is then possible to run the * {@link JUploadContext#registerUnload(Object, String)} method to register * new callback methods. These callback methods are executed when the applet * or the application closes, by calling the * {@link JUploadContext#runUnload()} method. */ static class Callback { private String method; private Object object; Callback(Object object, String method) { this.object = object; this.method = method; } void invoke() throws IllegalArgumentException, IllegalAccessException, InvocationTargetException, SecurityException { Object args[] = {}; Method methods[] = this.object.getClass().getMethods(); for (int i = 0; i < methods.length; i++) { if (methods[i].getName().equals(this.method)) { methods[i].invoke(this.object, args); } } } /** * @return the method */ public String getMethod() { return method; } /** * @return the object */ public Object getObject() { return object; } } /** * All registered callbacks. * * @see Callback */ List unloadCallbacks = new ArrayList(20); /** * Reaction on the start of the applet: creation of each specific item of * the GUI, and the upload policy.
* This method needs that the initialization of the called is finished. For * instance, {@link JUploadContextApplet} needs to have set theApplet, to be * able to properly execute some method calls that are in the init() method. * So we can not do this initialization in the constructor of * DefaultJUploadContext. * * @param frame * The frame that contains the application. Mainly used to * attached modal dialog. * @param rootPaneContainer * The mother window (JApplet, JFrame...), which contains the * rootPaneContainer. Used to set the {@link JUploadPanel} in it. */ public void init(Frame frame, RootPaneContainer rootPaneContainer) { try { this.frame = frame; // The standard thread name is: thread // applet-wjhk.jupload2.JUploadApplet.class // Too long ! :-) Thread.currentThread().setName( rootPaneContainer.getClass().getName()); // The logWindow must exist before the uploadPolicy creation. But it // needs the uploadPolicy, to know the logging parameters. We'll set // the uploadPolicy just after. this.logWindow = new JUploadTextArea(20, 20, this.uploadPolicy); // Now we can create the upload policy: the logWindow exists. this.uploadPolicy = UploadPolicyFactory.getUploadPolicy(this); this.uploadPolicy.displayDebug( "After UploadPolicyFactory.getUploadPolicy(this)", 80); // We set the uploadPolicy to the logWindow. The logThread starts, // and it register its unload method, to be called when the JUpload // finishes. this.uploadPolicy.displayDebug( "Before this.logWindow.setUploadPolicy(this.uploadPolicy)", 80); this.logWindow.setUploadPolicy(this.uploadPolicy); // getMainPanel().setLayout(new BorderLayout()); this.uploadPolicy .displayDebug( "Before new JUploadPanelImpl(this.logWindow,this.uploadPolicy)", 80); this.jUploadPanel = new JUploadPanelImpl(this.logWindow, this.uploadPolicy); // getMainPanel().add(this.jUploadPanel, BorderLayout.CENTER); this.uploadPolicy .displayDebug( "Before rootPaneContainer.setContentPane(this.jUploadPanel);", 80); rootPaneContainer.setContentPane(this.jUploadPanel.getJComponent()); // We start the jsHandler thread, that allows javascript to send // upload command to the applet. this.uploadPolicy .displayDebug( "Before new JavascriptHandler(this.uploadPolicy, this.jUploadPanel)", 80); this.jsHandler = new JavascriptHandler(this.uploadPolicy, this.jUploadPanel); this.jsHandler.start(); // Then we register the unload method, that'll be called like all // unload callbacks when the applet is stopped. registerUnload(this, "unload"); } catch (final Exception e) { System.out.println(e.getMessage()); e.printStackTrace(); // TODO Translate this sentence JOptionPane.showMessageDialog(null, "Error during applet initialization!\nHave a look in your Java console (" + e.getClass().getName() + ")", "Error", JOptionPane.ERROR_MESSAGE); } this.uploadPolicy.displayDebug("Before new Properties();", 80); this.mimeTypesProperties = new Properties(); final String mimetypePropertiesFilename = "/conf/mimetypes.properties"; try { InputStream isProperties = this.getClass().getResourceAsStream( mimetypePropertiesFilename); this.mimeTypesProperties.load(isProperties); isProperties.close(); this.uploadPolicy.displayDebug("Mime types list loaded Ok (" + mimetypePropertiesFilename + ")", 50); } catch (Exception e) { this.uploadPolicy .displayWarn("Unable to load the mime types list (" + mimetypePropertiesFilename + "): " + e.getClass().getName() + " (" + e.getMessage() + ")"); } this.uploadPolicy.displayDebug("End of DefaultJUploadContext.init()", 80); } /** * This method is called when the applet is unloaded (actually, when it is * stopped). it is registered as a callback in the * {@link #init(Frame, RootPaneContainer)}, here above. */ public void unload() { if (this.jsHandler != null && this.jsHandler.isAlive()) { this.jsHandler.interrupt(); this.jsHandler = null; } } /** {@inheritDoc} */ public String getDetailedVersionMessage() { String version = getVersion(); String svnRevision = getSvnRevision(); boolean gotSvnRevision = !svnRevision.equals(DEFAULT_PROP_UNKNOWN); int buildNumber = getBuildNumber(); boolean gotBuildNumber = buildNumber > 0; String buildDate = getBuildDate(); boolean gotBuildDate = !buildDate.equals(DEFAULT_PROP_UNKNOWN); StringBuffer sb = new StringBuffer(); sb.append(version); if (gotSvnRevision || gotBuildNumber) { sb.append(" ["); String space = ""; if (gotSvnRevision) { sb.append("SVN-Rev: "); sb.append(svnRevision); space = " "; } if (gotBuildNumber) { sb.append(space); sb.append("build "); sb.append(buildNumber); } sb.append("]"); } if (gotBuildDate) { sb.append(" - "); sb.append(buildDate); } return sb.toString(); } /** {@inheritDoc} */ public String getVersion() { return getProperty("jupload.version", DEFAULT_PROP_UNKNOWN); } /** {@inheritDoc} */ public String getSvnRevision() { String svnRevision = getProperty("jupload.svn.revision", DEFAULT_PROP_UNKNOWN); if (svnRevision.startsWith("{") || svnRevision.startsWith("${")) { // This particular case should not happen with standard maven build. // But it occurs when launching after an eclipse automatic build. // Returning DEFAULT_PROP_UNKNOWN in this case, make the applet // behave like if it was built by maven. return DEFAULT_PROP_UNKNOWN; } else { return svnRevision; } } /** {@inheritDoc} */ public String getLastModified() { return getProperty("jupload.lastSrcDirModificationDate", DEFAULT_PROP_UNKNOWN); } /** {@inheritDoc} */ public String getBuildDate() { String timestamp = getProperty("jupload.buildTimestamp", DEFAULT_PROP_UNKNOWN); if (timestamp.equals(DEFAULT_PROP_UNKNOWN)) { return DEFAULT_PROP_UNKNOWN; } else { Locale locale = Locale.getDefault(); if (this.uploadPolicy != null) { locale = this.uploadPolicy.getLocale(); } MessageFormat msgFormat = new MessageFormat("{0,date,medium}", locale); try { Object[] args = { new Date(Long.parseLong(timestamp)) }; return msgFormat.format(args); } catch (NumberFormatException e) { // uploadPolicy is null at startup. // TODO Better handling logging, here System.out.println("[WARN] The timestamp can not be read (" + timestamp + "). Will return '" + DEFAULT_PROP_UNKNOWN + "'."); return DEFAULT_PROP_UNKNOWN; } } } /** {@inheritDoc} */ public int getBuildNumber() { String valuePropBuildNumber = getProperty("jupload.buildNumber", "-1"); try { return Integer.parseInt(valuePropBuildNumber); } catch (java.lang.NumberFormatException e) { System.out.println("[WARN] " + e.getClass().getName() + " when getting the buildNumber, while parsing '" + valuePropBuildNumber + "'). Will return -1"); return -1; } } /** * Try to get a property from the loaded properties. If the property is not * available, the default value is returned. * * @param propertyName * @param defaultValue * @return */ private String getProperty(String propertyName, String defaultValue) { String value = null; try { value = this.svnProperties.getProperty(propertyName); } catch (Exception e) { System.out.println("[WARN] " + e.getClass().getName() + " when getting the " + propertyName + " property (" + e.getMessage() + "). Will return '" + value + "'"); } return (value == null) ? defaultValue : value; } /** {@inheritDoc} */ public JUploadTextArea getLogWindow() { return this.logWindow; } /** {@inheritDoc} */ public String getMimeType(String fileExtension) { String mimeType = this.mimeTypesProperties.getProperty(fileExtension .toLowerCase()); return (mimeType == null) ? "application/octet-stream" : mimeType; } /** {@inheritDoc} */ public JUploadPanel getUploadPanel() { return this.jUploadPanel; } /** * Retrieves the current upload policy. The JUploadContext is responsible * for storing the UploadPolicy associated with the current instance. * * @return the current upload policy of this instance. * @throws JUploadException */ public UploadPolicy getUploadPolicy() throws JUploadException { return this.uploadPolicy; } // ///////////////////////////////////////////////////////////////////////////////////////////////////////: // //////////////// FUNCTIONS INTENDED TO BE CALLED BY JAVASCRIPT FUNCTIONS // ////////////////////////////: // ///////////////////////////////////////////////////////////////////////////////////////////////////////: /** * This allow runtime modifications of properties, from javascript. * Currently, this can only be used after full initialization. This method * only calls the UploadPolicy.setProperty method.
* Ex: document.jupload.setProperty(prop, value); * * @param prop * The property name that must be set. * @param value * The value of this property. */ public void setProperty(String prop, String value) { // FIXME setProperty should use jsHandler class PropertySetter implements Runnable { String prop; String value; PropertySetter(String prop, String value) { this.prop = prop; this.value = value; } public void run() { try { // We'll wait up to 2s until the applet initialized (we need // an // upload policy). // FIXME should be done in a separate thread (block the // browser) for (int i = 0; i < 20 && uploadPolicy == null; i += 1) { this.wait(100); } if (uploadPolicy == null) { System.out .println("uploadPolicy is null. Impossible to set " + prop + " to " + value); } else { // FIXME There should be a boolean: initialized, to // indicate when property may be set. uploadPolicy.setProperty(prop, value); } } catch (Exception e) { uploadPolicy.displayErr(e); } } } try { SwingUtilities.invokeLater(new PropertySetter(prop, value)); } catch (Exception e) { if (this.uploadPolicy != null) { this.uploadPolicy.displayErr(e); } else { System.out.println(e.getClass().getName() + ": " + e.getMessage()); } } } /** {@inheritDoc} */ public String startUpload() { return this.jsHandler.doCommand(JavascriptHandler.COMMAND_START_UPLOAD); } /** * Call to {@link UploadPolicy#displayErr(Exception)} * * @param err * The error text to be displayed. */ public void displayErr(String err) { this.uploadPolicy.displayErr(err); } /** * Call to {@link UploadPolicy#displayInfo(String)} * * @param info * The info text to display */ public void displayInfo(String info) { this.uploadPolicy.displayInfo(info); } /** * Call to {@link UploadPolicy#displayWarn(String)} * * @param warn * The error text to be displayed. */ public void displayWarn(String warn) { this.uploadPolicy.displayWarn(warn); } /** * Call to {@link UploadPolicy#displayDebug(String, int)} * * @param debug * The debug message. * @param minDebugLevel * The minimum level that debug level should have, to display * this message. Values can go from 0 to 100. */ public void displayDebug(String debug, int minDebugLevel) { this.uploadPolicy.displayDebug(debug, minDebugLevel); } // ///////////////////////////////////////////////////////////////////////// // ////////////////////// Helper functions // ///////////////////////////////////////////////////////////////////////// /** * Helper function, to get the Revision number, if available. The applet * must be built from the build.xml ant file. * * @return The svn properties */ public static Properties getSvnProperties() { Properties properties = new Properties(); Boolean bPropertiesLoaded = false; // Let's try to load the properties file. // The upload policy is not created yet: we can not use its display // methods to trace what is happening here. try { InputStream isProperties = Class.forName( "wjhk.jupload2.JUploadApplet").getResourceAsStream( SVN_PROPERTIES_FILENAME); properties.load(isProperties); isProperties.close(); bPropertiesLoaded = true; } catch (Exception e) { // An error occurred when reading the file. The applet was // probably not built with the build.xml ant file. // We'll create a fake property list. See below. // We can not output to the uploadPolicy display method, as the // upload policy is not created yet. We output to the system output. // Consequence: if this doesn't work during build, you'll see an // error during the build: the generated file name will contain the // following error message. System.out.println(e.getClass().getName() + " in DefaultJUploadContext.getSvnProperties() (" + e.getMessage() + ")"); } // If we could not read the property file. The applet was probably not // built with the build.xml ant file, we create a fake property list. if (!bPropertiesLoaded) { properties.setProperty("buildDate", "Unknown build date (please use the build.xml ant script)"); properties .setProperty("lastSrcDirModificationDate", "Unknown last modification date (please use the build.xml ant script)"); properties.setProperty("revision", "Unknown revision (please use the build.xml ant script)"); } return properties; } /** {@inheritDoc} */ public void registerUnload(Object object, String method) { // We insert each item at the beginning, so that the callbacks are // called in the reverse order of the order in which they were // registered. // For instance: the removal of the log file is the first one to be // registered ... and must be the last one to be executed. this.unloadCallbacks.add(0, new Callback(object, method)); } /** {@inheritDoc} */ public synchronized void runUnload() { // If an upload is runing on, we have to stop it. FileUploadManagerThread fileUploadManagerThread = this.getUploadPanel() .getFileUploadManagerThread(); if (fileUploadManagerThread != null) { fileUploadManagerThread.stopUpload(); } // Then we call all unload callback. for (Callback callback : this.unloadCallbacks) { try { callback.invoke(); } catch (Exception e) { System.out.println(e.getClass().getName() + " while calling the callback: " + callback.getObject().getClass().getName() + "." + callback.getMethod()); e.printStackTrace(); } } this.unloadCallbacks.clear(); } /** * Displays the debug information for the current parameter. */ void displayDebugParameterValue(String key, String value) { if (this.uploadPolicy != null && this.uploadPolicy.getDebugLevel() >= 80) { this.uploadPolicy.displayDebug("Parameter '" + key + "' loaded. Value: " + value, 80); } } /** {@inheritDoc} */ public int parseInt(String value, int def) { int ret = def; // Then, parse it as an integer. try { ret = Integer.parseInt(value); } catch (NumberFormatException e) { ret = def; if (this.uploadPolicy != null) { this.uploadPolicy.displayWarn("Invalid int value: " + value + ", using default value: " + def); } } return ret; } /** {@inheritDoc} */ public float parseFloat(String value, float def) { float ret = def; // Then, parse it as an integer. try { ret = Float.parseFloat(value); } catch (NumberFormatException e) { ret = def; if (this.uploadPolicy != null) { this.uploadPolicy.displayWarn("Invalid float value: " + value + ", using default value: " + def); } } return ret; } /** {@inheritDoc} */ public long parseLong(String value, long def) { long ret = def; // Then, parse it as an integer. try { ret = Long.parseLong(value); } catch (NumberFormatException e) { ret = def; if (this.uploadPolicy != null) { this.uploadPolicy.displayWarn("Invalid long value: " + value + ", using default value: " + def); } } return ret; } /** {@inheritDoc} */ public boolean parseBoolean(String value, boolean def) { // Then, parse it as a boolean. if (value.toUpperCase().equals("FALSE")) { return false; } else if (value.toUpperCase().equals("TRUE")) { return true; } else { if (this.uploadPolicy != null) { this.uploadPolicy.displayWarn("Invalid boolean value: " + value + ", using default value: " + def); } return def; } } /** * @return The cursor that was active before the call to this method * @see JUploadContext#setCursor(Cursor) */ public Cursor setWaitCursor() { return setCursor(new Cursor(Cursor.WAIT_CURSOR)); } /** * Just throws a UnsupportedOperationException exception. * * @param url * @param success */ public void displayURL(String url, boolean success) { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @return Not used */ public JApplet getApplet() { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** @see JUploadContext#getFrame() */ public Frame getFrame() { return this.frame; } /** * Just throws a UnsupportedOperationException exception. * * @return Not used. */ public Cursor getCursor() { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @param key * @param def * @return Not used */ public String getParameter(String key, String def) { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @param key * @param def * @return Not used */ public int getParameter(String key, int def) { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @param key * @param def * @return Not used */ public float getParameter(String key, float def) { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @param key * @param def * @return Not used */ public long getParameter(String key, long def) { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @param key * @param def * @return Not used */ public boolean getParameter(String key, boolean def) { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @param url * @return Not used * @throws JUploadException */ public String normalizeURL(String url) throws JUploadException { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @param headers */ public void readCookieFromNavigator(Vector headers) { throw new UnsupportedOperationException( "DefaultJUploadContext.readCookieFromNavigator()"); } /** * Just throws a UnsupportedOperationException exception. * * @param headers */ public void readUserAgentFromNavigator(Vector headers) { throw new UnsupportedOperationException( "DefaultJUploadContext.readUserAgentFromNavigator()"); } /** * Just throws a UnsupportedOperationException exception. * * @param cursor * @return Not used */ public Cursor setCursor(Cursor cursor) { throw new UnsupportedOperationException( "DefaultJUploadContext.setCursor()"); } /** * Just throws a UnsupportedOperationException exception. * * @param status */ public void showStatus(String status) { throw new UnsupportedOperationException( "DefaultJUploadContext.showStatus()"); } }