From: Joonas Lehtinen Date: Wed, 18 Jul 2007 12:59:08 +0000 (+0000) Subject: Making the gwt adapter even more simple - removed variable map alltogether X-Git-Tag: 6.7.0.beta1~6161 X-Git-Url: https://source.dussan.org/?a=commitdiff_plain;h=9f42a2734f19444d49402f465260f81dba23cf15;p=vaadin-framework.git Making the gwt adapter even more simple - removed variable map alltogether svn changeset:1882/svn branch:trunk --- diff --git a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java index e1c490f4de..06824e5541 100755 --- a/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java +++ b/src/com/itmill/toolkit/terminal/gwt/client/ApplicationConnection.java @@ -185,8 +185,8 @@ public class ApplicationConnection implements EntryPoint { } private void addVariableToQueue(String paintableId, String variableName, - String encodedValue, boolean immediate) { - String id = paintableId + "_" + variableName; + String encodedValue, boolean immediate, char type) { + String id = paintableId + "_" + variableName + "_" + type; for (int i = 0; i < pendingVariables.size(); i += 2) if ((pendingVariables.get(i)).equals(id)) { pendingVariables.remove(i); @@ -202,37 +202,35 @@ public class ApplicationConnection implements EntryPoint { public void sendPendingVariableChanges() { StringBuffer req = new StringBuffer(); + req.append("changes="); for (int i = 0; i < pendingVariables.size(); i++) { - req.append(pendingVariables.get(i++)); - req.append("="); + if (i>0) req.append("\u0001"); req.append(pendingVariables.get(i)); - req.append("&"); } pendingVariables.clear(); makeUidlRequest(req.toString()); } - private String escapeString(String value) { - // TODO - return value; - } + private static native String escapeString(String value) /*-{ + return encodeURIComponent(value); + }-*/; public void updateVariable(String paintableId, String variableName, String newValue, boolean immediate) { addVariableToQueue(paintableId, variableName, escapeString(newValue), - immediate); + immediate, 's'); } public void updateVariable(String paintableId, String variableName, int newValue, boolean immediate) { - addVariableToQueue(paintableId, variableName, "" + newValue, immediate); + addVariableToQueue(paintableId, variableName, "" + newValue, immediate, 'i'); } public void updateVariable(String paintableId, String variableName, boolean newValue, boolean immediate) { addVariableToQueue(paintableId, variableName, newValue ? "true" - : "false", immediate); + : "false", immediate, 'b'); } public void updateVariable(String paintableId, String variableName, @@ -243,8 +241,8 @@ public class ApplicationConnection implements EntryPoint { buf.append(","); buf.append(escapeString(values[i].toString())); } - addVariableToQueue("array:" + paintableId, variableName, - buf.toString(), immediate); + addVariableToQueue(paintableId, variableName, + buf.toString(), immediate, 'a'); } public WidgetFactory getWidgetFactory() { diff --git a/src/com/itmill/toolkit/terminal/gwt/server/AjaxHttpUploadStream.java b/src/com/itmill/toolkit/terminal/gwt/server/AjaxHttpUploadStream.java deleted file mode 100644 index e431d5126e..0000000000 --- a/src/com/itmill/toolkit/terminal/gwt/server/AjaxHttpUploadStream.java +++ /dev/null @@ -1,114 +0,0 @@ -/* ************************************************************************* - - IT Mill Toolkit - - Development of Browser User Interfaces Made Easy - - Copyright (C) 2000-2006 IT Mill Ltd - - ************************************************************************* - - This product is distributed under commercial license that can be found - from the product package on license.pdf. Use of this product might - require purchasing a commercial license from IT Mill Ltd. For guidelines - on usage, see licensing-guidelines.html - - ************************************************************************* - - For more information, contact: - - IT Mill Ltd phone: +358 2 4802 7180 - Ruukinkatu 2-4 fax: +358 2 4802 7181 - 20540, Turku email: info@itmill.com - Finland company www: www.itmill.com - - Primary source for information and releases: www.itmill.com - - ********************************************************************** */ - -package com.itmill.toolkit.terminal.gwt.server; - -import java.io.InputStream; - -/** - * AjaxAdapter implementation of the UploadStream interface. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 5.0 - */ -public class AjaxHttpUploadStream implements - com.itmill.toolkit.terminal.UploadStream { - - /** - * Holds value of property variableName. - */ - private String streamName; - - private String contentName; - - private String contentType; - - /** - * Holds value of property variableValue. - */ - private InputStream stream; - - /** - * Creates a new instance of UploadStreamImpl. - * - * @param name - * the name of the stream. - * @param stream - * the input stream. - * @param contentName - * the name of the content. - * @param contentType - * the type of the content. - */ - public AjaxHttpUploadStream(String name, InputStream stream, - String contentName, String contentType) { - this.streamName = name; - this.stream = stream; - this.contentName = contentName; - this.contentType = contentType; - } - - /** - * Gets the name of the stream. - * - * @return the name of the stream. - */ - public String getStreamName() { - return this.streamName; - } - - /** - * Gets the input stream. - * - * @return the Input stream. - */ - public InputStream getStream() { - return this.stream; - } - - /** - * Gets the input stream content type. - * - * @return the content type of the input stream. - */ - public String getContentType() { - return this.contentType; - } - - /** - * Gets the stream content name. Stream content name usually differs from - * the actual stream name. It is used to identify the content of the stream. - * - * @return the Name of the stream content. - */ - public String getContentName() { - return this.contentName; - } -} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/AjaxVariableMap.java b/src/com/itmill/toolkit/terminal/gwt/server/AjaxVariableMap.java deleted file mode 100644 index 54c0013361..0000000000 --- a/src/com/itmill/toolkit/terminal/gwt/server/AjaxVariableMap.java +++ /dev/null @@ -1,803 +0,0 @@ -/* ************************************************************************* - - IT Mill Toolkit - - Development of Browser User Interfaces Made Easy - - Copyright (C) 2000-2006 IT Mill Ltd - - ************************************************************************* - - This product is distributed under commercial license that can be found - from the product package on license.pdf. Use of this product might - require purchasing a commercial license from IT Mill Ltd. For guidelines - on usage, see licensing-guidelines.html - - ************************************************************************* - - For more information, contact: - - IT Mill Ltd phone: +358 2 4802 7180 - Ruukinkatu 2-4 fax: +358 2 4802 7181 - 20540, Turku email: info@itmill.com - Finland company www: www.itmill.com - - Primary source for information and releases: www.itmill.com - - ********************************************************************** */ - -package com.itmill.toolkit.terminal.gwt.server; - -import java.io.IOException; -import java.lang.ref.WeakReference; -import java.util.ArrayList; -import java.util.Arrays; -import java.util.Enumeration; -import java.util.HashMap; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedList; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.StringTokenizer; -import java.util.WeakHashMap; - -import javax.servlet.http.HttpServletRequest; - -import com.itmill.toolkit.terminal.SystemError; -import com.itmill.toolkit.terminal.Terminal; -import com.itmill.toolkit.terminal.UploadStream; -import com.itmill.toolkit.terminal.VariableOwner; - -/** - * Variable map for ajax applications. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 5.0 - */ -public class AjaxVariableMap { - - // Id <-> (Owner,Name) mapping - private Map idToNameMap = new HashMap(); - - private Map idToTypeMap = new HashMap(); - - private Map idToOwnerMap = new HashMap(); - - private Map idToValueMap = new HashMap(); - - private Map ownerToNameToIdMap = new WeakHashMap(); - - private Object mapLock = new Object(); - - // Id generator - private long lastId = 0; - - /** - * Converts the string to a supported class. - * - * @param type - * @param value - * @return - * @throws java.lang.ClassCastException - * if the code has attempted to cast an object to a subclass of - * which it is not an instance - */ - private static Object convert(Class type, String value) - throws java.lang.ClassCastException { - try { - - // Boolean typed variables - if (type.equals(Boolean.class)) - return new Boolean(!(value.equals("") || value.equals("false"))); - - // Integer typed variables - if (type.equals(Integer.class)) - return new Integer(value.trim()); - - // String typed variables - if (type.equals(String.class)) - return value; - - throw new ClassCastException("Unsupported type: " + type.getName()); - } catch (NumberFormatException e) { - return null; - } - } - - /** - * Registers a new variable. - * - * @param name - * the Variable name. - * @param type - * @param value - * @param owner - * the Listener for variable changes. - * @return id to assigned for this variable. - */ - public String registerVariable(String name, Class type, Object value, - VariableOwner owner) { - - // Checks that the type of the class is supported - if (!(type.equals(Boolean.class) || type.equals(Integer.class) - || type.equals(String.class) || type.equals(String[].class) || type - .equals(UploadStream.class))) - throw new SystemError("Unsupported variable type: " - + type.getClass()); - - synchronized (mapLock) { - - // Checks if the variable is already mapped - HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); - if (nameToIdMap == null) { - nameToIdMap = new HashMap(); - ownerToNameToIdMap.put(owner, nameToIdMap); - } - String id = (String) nameToIdMap.get(name); - - if (id == null) { - // Generates new id and register it - -// ---------- -// TODO This HACK is only included for testing GWT integration -//Original id = "v" + String.valueOf(++lastId); - Object pid = ApplicationManager.paintableIdMap.get(owner); - id = pid + "_"+name; -// ---------- - - - nameToIdMap.put(name, id); - idToOwnerMap.put(id, new WeakReference(owner)); - idToNameMap.put(id, name); - idToTypeMap.put(id, type); - } - - idToValueMap.put(id, value); - - return id; - } - } - - /** - * Unregisters the variable. - * - * @param name - * the Variable name. - * @param owner - * the Listener for variable changes. - */ - public void unregisterVariable(String name, VariableOwner owner) { - - synchronized (mapLock) { - - // Get the id - HashMap nameToIdMap = (HashMap) ownerToNameToIdMap.get(owner); - if (nameToIdMap == null) - return; - String id = (String) nameToIdMap.get(name); - if (id != null) - return; - - // Remove all the mappings - nameToIdMap.remove(name); - if (nameToIdMap.isEmpty()) - ownerToNameToIdMap.remove(owner); - idToNameMap.remove(id); - idToTypeMap.remove(id); - idToValueMap.remove(id); - idToOwnerMap.remove(id); - - } - } - - /** - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 3.0 - */ - private class ParameterContainer { - - /** - * Constructs the mapping: listener to set of listened parameter names. - */ - private HashMap parameters = new HashMap(); - - /** - * Parameter values. - */ - private HashMap values = new HashMap(); - - /** - * Multipart parser used for parsing the request. - */ - private ServletMultipartRequest parser = null; - - /** - * Name - Value mapping of parameters that are not variables. - */ - private HashMap nonVariables = new HashMap(); - - /** - * Creates a new parameter container and parse the parameters from the - * request using GET, POST and POST/MULTIPART parsing - * - * @param req - * the Http request to handle. - * @throws IOException - * if the writing failed due to input/output error. - */ - public ParameterContainer(HttpServletRequest req) throws IOException { - // Parse GET / POST parameters - for (Enumeration e = req.getParameterNames(); e.hasMoreElements();) { - String paramName = (String) e.nextElement(); - String[] paramValues = req.getParameterValues(paramName); - addParam(paramName, paramValues); - } - - // Parse multipart variables - try { - parser = new ServletMultipartRequest(req, - MultipartRequest.MAX_READ_BYTES); - } catch (IllegalArgumentException ignored) { - parser = null; - } - - if (parser != null) { - for (Enumeration e = parser.getFileParameterNames(); e - .hasMoreElements();) { - String paramName = (String) e.nextElement(); - addParam(paramName, null); - } - for (Enumeration e = parser.getParameterNames(); e - .hasMoreElements();) { - String paramName = (String) e.nextElement(); - Enumeration val = parser.getURLParameters(paramName); - - // Create a linked list from enumeration to calculate - // elements - LinkedList l = new LinkedList(); - while (val.hasMoreElements()) - l.addLast(val.nextElement()); - - // String array event constructor - String[] s = new String[l.size()]; - Iterator i = l.iterator(); - for (int j = 0; j < s.length; j++) - s[j] = (String) i.next(); - - addParam(paramName, s); - } - } - - } - - /** - * Adds the parameter to container. - * - * @param name - * the Parameter name. - * @param value - * the Parameter value. - */ - private void addParam(String name, String[] value) { - - // Support name="set:name=value" value="ignored" notation - if (name.startsWith("set:")) { - int equalsIndex = name.indexOf('='); - value[0] = name.substring(equalsIndex + 1, name.length()); - name = name.substring(4, equalsIndex); - String[] curVal = (String[]) values.get(name); - if (curVal != null) { - String[] newVal = new String[1 + curVal.length]; - newVal[curVal.length] = value[0]; - for (int i = 0; i < curVal.length; i++) - newVal[i] = curVal[i]; - value = newVal; - - // Special case - if the set:-method is used for - // declaring array of length 2, where either of the - // following conditions are true: - // - the both items are the same - // - the both items have the same length and - // - the items only differ on last character - // - second last character is '.' - // - last char of one string is 'x' and other is 'y' - // Browser is unporposely modifying the name. - if (value.length == 2 - && value[0].length() == value[1].length()) { - boolean same = true; - for (int i = 0; i < value[0].length() - 1 && same; i++) - if (value[0].charAt(i) != value[1].charAt(i)) - same = false; - if (same - && ((value[0].charAt(value[0].length() - 1) == 'x' && value[1] - .charAt(value[1].length() - 1) == 'y') || (value[0] - .charAt(value[0].length() - 1) == 'y' && value[1] - .charAt(value[1].length() - 1) == 'x'))) { - value = new String[] { value[0].substring(0, - value[1].length() - 2) }; - } else if (same && value[0].equals(value[1])) - value = new String[] { value[0] }; - } - - // Special case - if the set:-method is used for - // declaring array of length 3, where all of the - // following conditions are true: - // - two last items have the same length - // - the first item is 2 chars shorter - // - the longer items only differ on last character - // - the shortest item is a prefix of the longer ones - // - second last character of longer ones is '.' - // - last char of one long string is 'x' and other is 'y' - // Browser is unporposely modifying the name. (Mozilla, - // Firefox, ..) - if (value.length == 3 - && value[1].length() == value[2].length() - && value[0].length() + 2 == value[1].length()) { - boolean same = true; - for (int i = 0; i < value[1].length() - 1 && same; i++) - if (value[2].charAt(i) != value[1].charAt(i)) - same = false; - for (int i = 0; i < value[0].length() && same; i++) - if (value[0].charAt(i) != value[1].charAt(i)) - same = false; - if (same - && (value[2].charAt(value[2].length() - 1) == 'x' && value[1] - .charAt(value[1].length() - 1) == 'y') - || (value[2].charAt(value[2].length() - 1) == 'y' && value[1] - .charAt(value[1].length() - 1) == 'x')) { - value = new String[] { value[0] }; - } - } - - } - } - - // Support for setting arrays in format - // set-array:name=value1,value2,value3,... - else if (name.startsWith("set-array:")) { - int equalsIndex = name.indexOf('='); - if (equalsIndex < 0) - return; - - StringTokenizer commalist = new StringTokenizer(name - .substring(equalsIndex + 1), ","); - name = name.substring(10, equalsIndex); - String[] curVal = (String[]) values.get(name); - ArrayList elems = new ArrayList(); - - // Add old values if present. - if (curVal != null) { - for (int i = 0; i < curVal.length; i++) - elems.add(curVal[i]); - } - while (commalist.hasMoreTokens()) { - String token = commalist.nextToken(); - if (token != null && token.length() > 0) - elems.add(token); - } - value = new String[elems.size()]; - for (int i = 0; i < value.length; i++) - value[i] = (String) elems.get(i); - - } - - // Support name="array:name" value="val1,val2,val3" notation - // All the empty elements are ignored - else if (name.startsWith("array:")) { - - name = name.substring(6); - StringTokenizer commalist = new StringTokenizer(value[0], ","); - String[] curVal = (String[]) values.get(name); - ArrayList elems = new ArrayList(); - - // Add old values if present. - if (curVal != null) { - for (int i = 0; i < curVal.length; i++) - elems.add(curVal[i]); - } - while (commalist.hasMoreTokens()) { - String token = commalist.nextToken(); - if (token != null && token.length() > 0) - elems.add(token); - } - value = new String[elems.size()]; - for (int i = 0; i < value.length; i++) - value[i] = (String) elems.get(i); - } - - // Support declaring variables with name="declare:name" - else if (name.startsWith("declare:")) { - name = name.substring(8); - value = (String[]) values.get(name); - if (value == null) - value = new String[0]; - } - - // Gets the owner - WeakReference ref = (WeakReference) idToOwnerMap.get(name); - VariableOwner owner = null; - if (ref != null) - owner = (VariableOwner) ref.get(); - - // Adds the parameter to mapping only if they have owners - if (owner != null) { - Set p = (Set) parameters.get(owner); - if (p == null) - parameters.put(owner, p = new HashSet()); - p.add(name); - if (value != null) - values.put(name, value); - } - - // If the owner can not be found - else { - - // If parameter has been mapped before, remove the old owner - // mapping - if (ref != null) { - - // The owner has been destroyed, so we remove the mappings - idToNameMap.remove(name); - idToOwnerMap.remove(name); - idToTypeMap.remove(name); - idToValueMap.remove(name); - } - - // Adds the parameter to set of non-variables - nonVariables.put(name, value); - } - - } - - /** - * Gets the set of all parameters connected to given variable owner. - * - * @param owner - * the Listener for variable changes. - * @return the set of all the parameters. - */ - public Set getParameters(VariableOwner owner) { - if (owner == null) - return null; - return (Set) parameters.get(owner); - } - - /** - * Gets the set of all variable owners owning parameters in this - * request. - * - * @return the set of all varaible owners. - */ - public Set getOwners() { - return parameters.keySet(); - } - - /** - * Gets the value of a parameter. - * - * @param parameterName - * the name of the parameter. - * @return the value of the parameter. - */ - public String[] getValue(String parameterName) { - return (String[]) values.get(parameterName); - } - - /** - * Gets the servlet multipart parser. - * - * @return the parser. - */ - public ServletMultipartRequest getParser() { - return parser; - } - - /** - * Gets the name - value[] mapping of non variable parameters. - * - * @return the mapping of non variable parameters. - */ - public Map getNonVariables() { - return nonVariables; - } - } - - /** - * Handles all variable changes in this request. - * - * @param req - * the Http request to handle. - * @param errorListener - * the listeners If the list is non null, only the listed - * listeners are served. Otherwise all the listeners are served. - * @return Name to Value[] mapping of unhandled variables. - * @throws IOException - * if the writing failed due to input/output error. - */ - public Map handleVariables(HttpServletRequest req, - Terminal.ErrorListener errorListener) throws IOException { - - // Gets the parameters - ParameterContainer parcon = new ParameterContainer(req); - - // Sorts listeners to dependency order - List listeners = getDependencySortedListenerList(parcon.getOwners()); - - // Handles all parameters for all listeners - while (!listeners.isEmpty()) { - VariableOwner listener = (VariableOwner) listeners.remove(0); - boolean changed = false; // Has any of this owners variabes - // changed - // Handles all parameters for listener - Set params = parcon.getParameters(listener); - if (params != null) { // Name value mapping - Map variables = new HashMap(); - for (Iterator pi = params.iterator(); pi.hasNext();) { - // Gets the name of the parameter - String param = (String) pi.next(); - // Extracts more information about the parameter - String varName = (String) idToNameMap.get(param); - Class varType = (Class) idToTypeMap.get(param); - Object varOldValue = idToValueMap.get(param); - if (varName == null || varType == null) - // TODO Remove this? - System.err - .println("VariableMap: No variable found for parameter " - + param - + " (" - + varName - + "," - + listener + ")"); - else { - - ServletMultipartRequest parser = parcon.getParser(); - - // Uploads events - if (varType.equals(UploadStream.class)) { - if (parser != null - && parser.getFileParameter(param, - MultipartRequest.FILENAME) != null) { - String filename = (String) parser - .getFileParameter(param, - MultipartRequest.FILENAME); - String contentType = (String) parser - .getFileParameter(param, - MultipartRequest.CONTENT_TYPE); - UploadStream upload = new AjaxHttpUploadStream( - varName, parser.getFileContents(param), - filename, contentType); - variables.put(varName, upload); - changed = true; - } - } - - // Normal variable change events - else { - // First try to parse the event without multipart - String[] values = parcon.getValue(param); - if (values != null) { - - if (varType.equals(String[].class)) { - variables.put(varName, values); - changed |= (!Arrays.equals(values, - (String[]) varOldValue)); - } else { - try { - if (values.length == 1) { - Object val = convert(varType, - values[0]); - variables.put(varName, val); - changed |= ((val == null && varOldValue != null) || (val != null && !val - .equals(varOldValue))); - } else if (values.length == 0 - && varType - .equals(Boolean.class)) { - Object val = new Boolean(false); - variables.put(varName, val); - changed |= (!val - .equals(varOldValue)); - } else { - // TODO Remove this? - System.err - .println("Empty variable '" - + varName - + "' of type " - + varType - .toString()); - } - - } catch (java.lang.ClassCastException e) { - // TODO Remove this? - System.err - .println("WebVariableMap conversion exception"); - e.printStackTrace(System.err); - errorListener - .terminalError(new TerminalErrorImpl( - e)); - } - } - } - } - } - } - - // Do the valuechange if the listener is enabled - if (listener.isEnabled() && changed) { - try { - listener.changeVariables(req, variables); - } catch (Throwable t) { - // Notify the error listener - errorListener.terminalError(new VariableOwnerErrorImpl( - listener, t)); - } - } - } - } - - return parcon.getNonVariables(); - } - - /** - * Implementation of VariableOwner.Error interface. - */ - public class TerminalErrorImpl implements Terminal.ErrorEvent { - private Throwable throwable; - - /** - * - * @param throwable - */ - private TerminalErrorImpl(Throwable throwable) { - this.throwable = throwable; - } - - /** - * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() - */ - public Throwable getThrowable() { - return this.throwable; - } - - } - - /** - * Implementation of VariableOwner.Error interface. - */ - public class VariableOwnerErrorImpl extends TerminalErrorImpl implements - VariableOwner.ErrorEvent { - - private VariableOwner owner; - - /** - * - * @param owner - * the Listener for variable changes. - * @param throwable - */ - private VariableOwnerErrorImpl(VariableOwner owner, Throwable throwable) { - super(throwable); - this.owner = owner; - } - - /** - * @see com.itmill.toolkit.terminal.VariableOwner.ErrorEvent#getVariableOwner() - */ - public VariableOwner getVariableOwner() { - return this.owner; - } - - } - - /** - * Resolves the VariableOwners needed from the request and sort them to - * assure that the dependencies are met (as well as possible). - * - * @param listeners - * @return List of variable list changers, that are needed for handling all - * the variables in the request - */ - private List getDependencySortedListenerList(Set listeners) { - - LinkedList resultNormal = new LinkedList(); - LinkedList resultImmediate = new LinkedList(); - - // Go trough the listeners and either add them to result or resolve - // their dependencies - HashMap deepdeps = new HashMap(); - LinkedList unresolved = new LinkedList(); - for (Iterator li = listeners.iterator(); li.hasNext();) { - - VariableOwner listener = (VariableOwner) li.next(); - if (listener != null) { - Set dependencies = listener.getDirectDependencies(); - - // The listeners with no dependencies are added to the front of - // the - // list directly - if (dependencies == null || dependencies.isEmpty()) { - if (listener.isImmediate()) - resultImmediate.addFirst(listener); - else - resultNormal.addFirst(listener); - } - - // Resolve deep dependencies for the listeners with dependencies - // (the listeners will be added to the end of results in correct - // dependency order later). Also the dependencies of all the - // depended listeners are resolved. - else if (deepdeps.get(listener) == null) { - - // Set the fifo for unresolved parents to contain only the - // listener to be resolved - unresolved.clear(); - unresolved.add(listener); - - // Resolve dependencies - HashSet tmpdeepdeps = new HashSet(); - while (!unresolved.isEmpty()) { - - VariableOwner l = (VariableOwner) unresolved - .removeFirst(); - if (!tmpdeepdeps.contains(l)) { - tmpdeepdeps.add(l); - if (deepdeps.containsKey(l)) { - tmpdeepdeps.addAll((Set) deepdeps.get(l)); - } else { - Set deps = l.getDirectDependencies(); - if (deps != null && !deps.isEmpty()) - for (Iterator di = deps.iterator(); di - .hasNext();) { - Object d = di.next(); - if (d != null - && !tmpdeepdeps.contains(d)) - unresolved.addLast(d); - } - } - } - } - - tmpdeepdeps.remove(listener); - deepdeps.put(listener, tmpdeepdeps); - } - } - } - - // Adds the listeners with dependencies in sane order to the result - for (Iterator li = deepdeps.keySet().iterator(); li.hasNext();) { - VariableOwner l = (VariableOwner) li.next(); - boolean immediate = l.isImmediate(); - - // Adds each listener after the last depended listener already in - // the list - int index = -1; - for (Iterator di = ((Set) deepdeps.get(l)).iterator(); di.hasNext();) { - int k; - Object depended = di.next(); - if (immediate) { - k = resultImmediate.lastIndexOf(depended); - } else { - k = resultNormal.lastIndexOf(depended); - } - if (k > index) - index = k; - } - if (immediate) { - resultImmediate.add(index + 1, l); - } else { - resultNormal.add(index + 1, l); - } - } - - // Appends immediate listeners to normal listeners - // This way the normal handlers are always called before - // immediate ones - resultNormal.addAll(resultImmediate); - return resultNormal; - } -} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationManager.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationManager.java deleted file mode 100644 index 3a3c27af53..0000000000 --- a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationManager.java +++ /dev/null @@ -1,929 +0,0 @@ -/* ************************************************************************* - - IT Mill Toolkit - - Development of Browser User Interfaces Made Easy - - Copyright (C) 2000-2006 IT Mill Ltd - - ************************************************************************* - - This product is distributed under commercial license that can be found - from the product package on license.pdf. Use of this product might - require purchasing a commercial license from IT Mill Ltd. For guidelines - on usage, see licensing-guidelines.html - - ************************************************************************* - - For more information, contact: - - IT Mill Ltd phone: +358 2 4802 7180 - Ruukinkatu 2-4 fax: +358 2 4802 7181 - 20540, Turku email: info@itmill.com - Finland company www: www.itmill.com - - Primary source for information and releases: www.itmill.com - - ********************************************************************** */ - -package com.itmill.toolkit.terminal.gwt.server; - -import java.io.BufferedWriter; -import java.io.FileInputStream; -import java.io.IOException; -import java.io.InputStream; -import java.io.InputStreamReader; -import java.io.OutputStream; -import java.io.OutputStreamWriter; -import java.io.PrintWriter; -import java.text.DateFormatSymbols; -import java.text.SimpleDateFormat; -import java.util.ArrayList; -import java.util.Calendar; -import java.util.Collections; -import java.util.Comparator; -import java.util.GregorianCalendar; -import java.util.HashSet; -import java.util.Iterator; -import java.util.LinkedHashSet; -import java.util.List; -import java.util.Locale; -import java.util.Map; -import java.util.Set; -import java.util.WeakHashMap; - -import javax.servlet.ServletException; -import javax.servlet.ServletOutputStream; -import javax.servlet.http.HttpServlet; -import javax.servlet.http.HttpServletRequest; -import javax.servlet.http.HttpServletResponse; - -import com.itmill.toolkit.Application; -import com.itmill.toolkit.Application.WindowAttachEvent; -import com.itmill.toolkit.Application.WindowDetachEvent; -import com.itmill.toolkit.terminal.DownloadStream; -import com.itmill.toolkit.terminal.PaintTarget; -import com.itmill.toolkit.terminal.Paintable; -import com.itmill.toolkit.terminal.URIHandler; -import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent; -import com.itmill.toolkit.ui.Component; -import com.itmill.toolkit.ui.FrameWindow; -import com.itmill.toolkit.ui.Window; - -/** - * Application manager processes changes and paints for single application - * instance. - * - * @author IT Mill Ltd. - * @version - * @VERSION@ - * @since 5.0 - */ -public class ApplicationManager implements - Paintable.RepaintRequestListener, Application.WindowAttachListener, - Application.WindowDetachListener { - - private static String GET_PARAM_REPAINT_ALL = "repaintAll"; - - private static int DEFAULT_BUFFER_SIZE = 32 * 1024; - - private static int MAX_BUFFER_SIZE = 64 * 1024; - - private WeakHashMap applicationToVariableMapMap = new WeakHashMap(); - - private HashSet dirtyPaintabletSet = new HashSet(); - - - // TODO THIS TEMPORARY HACK IS ONLY HERE TO MAKE GWT DEVEL EASIER - static WeakHashMap paintableIdMap = new WeakHashMap(); - - private int idSequence = 0; - - private Application application; - - private Set removedWindows = new HashSet(); - - private JsonPaintTarget paintTarget; - - private List locales; - - private int pendingLocalesIndex; - - private ApplicationServlet applicationServlet; - - public ApplicationManager(Application application, ApplicationServlet applicationServlet) { - this.application = application; - this.applicationServlet = applicationServlet; - requireLocale(application.getLocale().toString()); - } - - /** - * - * @return - */ - private AjaxVariableMap getVariableMap() { - AjaxVariableMap vm = (AjaxVariableMap) applicationToVariableMapMap - .get(application); - if (vm == null) { - vm = new AjaxVariableMap(); - applicationToVariableMapMap.put(application, vm); - } - return vm; - } - - /** - * - * - */ - public void takeControl() { - application.addListener((Application.WindowAttachListener) this); - application.addListener((Application.WindowDetachListener) this); - - } - - /** - * - * - */ - public void releaseControl() { - application.removeListener((Application.WindowAttachListener) this); - application.removeListener((Application.WindowDetachListener) this); - } - - - public void handleUidlRequest(HttpServletRequest request, - HttpServletResponse response) throws IOException { - - // repaint requested or sesssion has timed out and new one is created - boolean repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null) - || request.getSession().isNew(); - - OutputStream out = response.getOutputStream(); - PrintWriter outWriter = new PrintWriter(new BufferedWriter( - new OutputStreamWriter(out, "UTF-8"))); - - outWriter.print(")/*{"); // some dirt to prevent cross site scripting vulnerabilities - - try { - - // Is this a download request from application - DownloadStream download = null; - - // The rest of the process is synchronized with the application - // in order to guarantee that no parallel variable handling is - // made - synchronized (application) { - - // Change all variables based on request parameters - Map unhandledParameters = getVariableMap().handleVariables( - request, application); - - // Handles the URI if the application is still running - if (application.isRunning()) - download = handleURI(application, request, response); - - // If this is not a download request - if (download == null) { - - // Finds the window within the application - Window window = null; - if (application.isRunning()) - window = getApplicationWindow(request, application); - - // Handles the unhandled parameters if the application is - // still running - if (window != null && unhandledParameters != null - && !unhandledParameters.isEmpty()) - window.handleParameters(unhandledParameters); - - // Removes application if it has stopped - if (!application.isRunning()) { - endApplication(request, response, application); - return; - } - - // Returns if no window found - if (window == null) - return; - - // Sets the response type - response.setContentType("application/json; charset=UTF-8"); - outWriter.print("\"changes\":["); - - paintTarget = new JsonPaintTarget(getVariableMap(), - this, outWriter); - - // Paints components - Set paintables; - if (repaintAll) { - paintables = new LinkedHashSet(); - paintables.add(window); - - // Reset sent locales - locales = null; - requireLocale(application.getLocale().toString()); - - // Adds all non-native windows - for (Iterator i = window.getApplication().getWindows() - .iterator(); i.hasNext();) { - Window w = (Window) i.next(); - if (!"native".equals(w.getStyle()) && w != window) - paintables.add(w); - } - } else - paintables = getDirtyComponents(); - if (paintables != null) { - - // Creates "working copy" of the current state. - List currentPaintables = new ArrayList(paintables); - - // Sorts the paintable so that parent windows - // are always painted before child windows - Collections.sort(currentPaintables, new Comparator() { - - public int compare(Object o1, Object o2) { - - // If first argumement is now window - // the second is "smaller" if it is. - if (!(o1 instanceof Window)) { - return (o2 instanceof Window) ? 1 : 0; - } - - // Now, if second is not window the - // first is smaller. - if (!(o2 instanceof Window)) { - return -1; - } - - // Both are windows. - String n1 = ((Window) o1).getName(); - String n2 = ((Window) o2).getName(); - if (o1 instanceof FrameWindow) { - if (((FrameWindow) o1).getFrameset() - .getFrame(n2) != null) { - return -1; - } else if (!(o2 instanceof FrameWindow)) { - return -1; - } - } - if (o2 instanceof FrameWindow) { - if (((FrameWindow) o2).getFrameset() - .getFrame(n1) != null) { - return 1; - } else if (!(o1 instanceof FrameWindow)) { - return 1; - } - } - - return 0; - } - }); - - for (Iterator i = currentPaintables.iterator(); i - .hasNext();) { - Paintable p = (Paintable) i.next(); - - // TODO CLEAN - if (p instanceof Window) { - Window w = (Window) p; - if (w.getTerminal() == null) - w.setTerminal(application.getMainWindow() - .getTerminal()); - } - - paintTarget.startTag("change"); - paintTarget.addAttribute("format", "uidl"); - String pid = getPaintableId(p); - paintTarget.addAttribute("pid", pid); - - // Track paints to identify empty paints - paintTarget.setTrackPaints(true); - p.paint(paintTarget); - - // If no paints add attribute empty - if (paintTarget.getNumberOfPaints() <= 0) { - paintTarget.addAttribute("visible", false); - } - paintTarget.endTag("change"); - paintablePainted(p); - } - } - - - paintTarget.close(); - outWriter.print("]"); // close changes - - - // Render the removed windows - // TODO refactor commented area to send some meta instructions to close window -// Set removed = new HashSet(getRemovedWindows()); -// if (removed.size() > 0) { -// for (Iterator i = removed.iterator(); i.hasNext();) { -// Window w = (Window) i.next(); -// paintTarget.startTag("change"); -// paintTarget.addAttribute("format", "uidl"); -// String pid = getPaintableId(w); -// paintTarget.addAttribute("pid", pid); -// paintTarget.addAttribute("windowname", w.getName()); -// paintTarget.addAttribute("visible", false); -// paintTarget.endTag("change"); -// removedWindowNotified(w); -// -// } -// } - - - - outWriter.print(", \"meta\" : {"); - boolean metaOpen = false; - - - // .. or initializion (first uidl-request) - if(application.ajaxInit()) { - outWriter.print("\"appInit\":true"); - } - // add meta instruction for client to set focus if it is set - Paintable f = (Paintable) application.consumeFocus(); - if(f != null) { - if(metaOpen) - outWriter.append(","); - outWriter.write("\"focus\":\""+ getPaintableId(f) +"\""); - } - - outWriter.print("}, \"resources\" : {"); - - // Precache custom layouts - // TODO Does not support theme-get param or different themes in different windows -> Allways preload layouts with the theme specified by the applications - String themeName = application.getTheme() != null ? application.getTheme() : ApplicationServlet.DEFAULT_THEME; - // TODO We should only precache the layouts that are not cached already - int resourceIndex = 0; - for (Iterator i=paintTarget.getPreCachedResources().iterator(); i.hasNext();) { - String resource = (String) i.next(); - InputStream is = null; - try { - is = applicationServlet.getServletContext().getResourceAsStream(ApplicationServlet.THEME_DIRECTORY_PATH + themeName + "/" + resource); - } catch (Exception e) { - Log.info(e.getMessage()); - } - if (is != null) { - - outWriter.print((resourceIndex++ > 0 ? ", " : "") + "\""+resource + "\" : "); - StringBuffer layout = new StringBuffer(); - - try { - InputStreamReader r = new InputStreamReader(is); - char[] buffer = new char[20000]; - int charsRead = 0; - while ((charsRead = r.read(buffer)) > 0) - layout.append(buffer, 0, charsRead); - r.close(); - } catch (java.io.IOException e) { - Log.info("Resource transfer failed: " + request.getRequestURI() - + ". (" + e.getMessage() + ")"); - } - outWriter.print("\"" + JsonPaintTarget.escapeJSON(layout.toString()) + "\""); - } - } - outWriter.print("}"); - - - /* ----------------------------- - * Sending Locale sensitive date - * ----------------------------- - */ - - // Store JVM default locale for later restoration - // (we'll have to change the default locale for a while) - Locale jvmDefault = Locale.getDefault(); - - // Send locale informations to client - outWriter.print(", \"locales\":["); - for(;pendingLocalesIndex < locales.size(); pendingLocalesIndex++) { - - Locale l = generateLocale((String) locales.get(pendingLocalesIndex)); - // Locale name - outWriter.print("{\"name\":\"" + l.toString() + "\","); - - /* - * Month names (both short and full) - */ - DateFormatSymbols dfs = new DateFormatSymbols(l); - String[] short_months = dfs.getShortMonths(); - String[] months = dfs.getMonths(); - outWriter.print("\"smn\":[\"" + // ShortMonthNames - short_months[0] + "\",\"" + - short_months[1] + "\",\"" + - short_months[2] + "\",\"" + - short_months[3] + "\",\"" + - short_months[4] + "\",\"" + - short_months[5] + "\",\"" + - short_months[6] + "\",\"" + - short_months[7] + "\",\"" + - short_months[8] + "\",\"" + - short_months[9] + "\",\"" + - short_months[10] + "\",\"" + - short_months[11] + "\"" + - "],"); - outWriter.print("\"mn\":[\"" + // MonthNames - months[0] + "\",\"" + - months[1] + "\",\"" + - months[2] + "\",\"" + - months[3] + "\",\"" + - months[4] + "\",\"" + - months[5] + "\",\"" + - months[6] + "\",\"" + - months[7] + "\",\"" + - months[8] + "\",\"" + - months[9] + "\",\"" + - months[10] + "\",\"" + - months[11] + "\"" + - "],"); - - /* - * Weekday names (both short and full) - */ - String[] short_days = dfs.getShortWeekdays(); - String[] days = dfs.getWeekdays(); - outWriter.print("\"sdn\":[\"" + // ShortDayNames - short_days[1] + "\",\"" + - short_days[2] + "\",\"" + - short_days[3] + "\",\"" + - short_days[4] + "\",\"" + - short_days[5] + "\",\"" + - short_days[6] + "\",\"" + - short_days[7] + "\"" + - "],"); - outWriter.print("\"dn\":[\"" + // DayNames - days[1] + "\",\"" + - days[2] + "\",\"" + - days[3] + "\",\"" + - days[4] + "\",\"" + - days[5] + "\",\"" + - days[6] + "\",\"" + - days[7] + "\"" + - "],"); - - /* - * First day of week (0 = sunday, 1 = monday) - */ - Calendar cal = new GregorianCalendar(l); - outWriter.print("\"fdow\":" + (cal.getFirstDayOfWeek() - 1) + ","); - - /* - * Date formatting (MM/DD/YYYY etc.) - */ - // Force our locale as JVM default for a while (SimpleDateFormat uses JVM default) - Locale.setDefault(l); - String df = new SimpleDateFormat().toPattern(); - int timeStart = df.indexOf("H"); - if(timeStart < 0) - timeStart = df.indexOf("h"); - int ampm_first = df.indexOf("a"); - // E.g. in Korean locale AM/PM is before h:mm - // TODO should take that into consideration on client-side as well, now always h:mm a - if(ampm_first > 0 && ampm_first < timeStart) - timeStart = ampm_first; - String dateformat = df.substring(0, timeStart-1); - - outWriter.print("\"df\":\"" + dateformat.trim() + "\","); - - /* - * Time formatting (24 or 12 hour clock and AM/PM suffixes) - */ - String timeformat = df.substring(timeStart, df.length()); // Doesn't return second or milliseconds - // We use timeformat to determine 12/24-hour clock - boolean twelve_hour_clock = timeformat.contains("a"); - // TODO there are other possibilities as well, like 'h' in french (ignore them, too complicated) - String hour_min_delimiter = timeformat.contains(".")? "." : ":"; - //outWriter.print("\"tf\":\"" + timeformat + "\","); - outWriter.print("\"thc\":" + twelve_hour_clock + ","); - outWriter.print("\"hmd\":\"" + hour_min_delimiter + "\""); - if(twelve_hour_clock) { - String[] ampm = dfs.getAmPmStrings(); - outWriter.print(",\"ampm\":[\""+ampm[0]+"\",\""+ampm[1]+"\"]"); - } - outWriter.print("}"); - if(pendingLocalesIndex < locales.size()-1) - outWriter.print(","); - } - outWriter.print("]"); // Close locales - - // Restore JVM default locale - Locale.setDefault(jvmDefault); - - outWriter.flush(); - outWriter.close(); - out.flush(); - } else { - - // For download request, transfer the downloaded data - handleDownload(download, request, response); - } - } - - out.flush(); - out.close(); - - } catch (Throwable e) { - // Writes the error report to client - OutputStreamWriter w = new OutputStreamWriter(out); - PrintWriter err = new PrintWriter(w); - err - .write("Application Internal Error"); - err.write("

" + e.toString() + "

\n");
-			e.printStackTrace(new PrintWriter(err));
-			err.write("\n
"); - err.close(); - } finally { - - } - - } - - /** - * Gets the existing application or create a new one. Get a window within an - * application based on the requested URI. - * - * @param request - * the HTTP Request. - * @param application - * the Application to query for window. - * @return Window mathing the given URI or null if not found. - * @throws ServletException - * if an exception has occurred that interferes with the - * servlet's normal operation. - */ - private Window getApplicationWindow(HttpServletRequest request, - Application application) throws ServletException { - - Window window = null; - - // Find the window where the request is handled - String path = request.getPathInfo(); - - // Main window as the URI is empty - if (path == null || path.length() == 0 || path.equals("/")) - window = application.getMainWindow(); - - // Try to search by window name - else { - String windowName = null; - if (path.charAt(0) == '/') - path = path.substring(1); - int index = path.indexOf('/'); - if (index < 0) { - windowName = path; - path = ""; - } else { - windowName = path.substring(0, index); - path = path.substring(index + 1); - } - window = application.getWindow(windowName); - - // By default, we use main window - if (window == null) - window = application.getMainWindow(); - } - - return window; - } - - /** - * Handles the requested URI. An application can add handlers to do special - * processing, when a certain URI is requested. The handlers are invoked - * before any windows URIs are processed and if a DownloadStream is returned - * it is sent to the client. - * - * @param application - * the Application owning the URI. - * @param request - * the HTTP request instance. - * @param response - * the HTTP response to write to. - * @return boolean true if the request was handled and - * further processing should be suppressed, otherwise - * false. - * @see com.itmill.toolkit.terminal.URIHandler - */ - private DownloadStream handleURI(Application application, - HttpServletRequest request, HttpServletResponse response) { - - String uri = request.getPathInfo(); - - // If no URI is available - if (uri == null || uri.length() == 0 || uri.equals("/")) - return null; - - // Remove the leading / - while (uri.startsWith("/") && uri.length() > 0) - uri = uri.substring(1); - - // Handle the uri - DownloadStream stream = null; - try { - stream = application.handleURI(application.getURL(), uri); - } catch (Throwable t) { - application.terminalError(new URIHandlerErrorImpl(application, t)); - } - - return stream; - } - - /** - * Handles the requested URI. An application can add handlers to do special - * processing, when a certain URI is requested. The handlers are invoked - * before any windows URIs are processed and if a DownloadStream is returned - * it is sent to the client. - * - * @param stream - * the downloadable stream. - * - * @param request - * the HTTP request instance. - * @param response - * the HTTP response to write to. - * - * @see com.itmill.toolkit.terminal.URIHandler - */ - private void handleDownload(DownloadStream stream, - HttpServletRequest request, HttpServletResponse response) { - - // Download from given stream - InputStream data = stream.getStream(); - if (data != null) { - - // Sets content type - response.setContentType(stream.getContentType()); - - // Sets cache headers - long cacheTime = stream.getCacheTime(); - if (cacheTime <= 0) { - response.setHeader("Cache-Control", "no-cache"); - response.setHeader("Pragma", "no-cache"); - response.setDateHeader("Expires", 0); - } else { - response.setHeader("Cache-Control", "max-age=" + cacheTime - / 1000); - response.setDateHeader("Expires", System.currentTimeMillis() - + cacheTime); - response.setHeader("Pragma", "cache"); // Required to apply - // caching in some - // Tomcats - } - - // Copy download stream parameters directly - // to HTTP headers. - Iterator i = stream.getParameterNames(); - if (i != null) { - while (i.hasNext()) { - String param = (String) i.next(); - response.setHeader((String) param, stream - .getParameter(param)); - } - } - - int bufferSize = stream.getBufferSize(); - if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) - bufferSize = DEFAULT_BUFFER_SIZE; - byte[] buffer = new byte[bufferSize]; - int bytesRead = 0; - - try { - OutputStream out = response.getOutputStream(); - - while ((bytesRead = data.read(buffer)) > 0) { - out.write(buffer, 0, bytesRead); - out.flush(); - } - out.close(); - } catch (IOException ignored) { - } - - } - - } - - /** - * Ends the Application. - * - * @param request - * the HTTP request instance. - * @param response - * the HTTP response to write to. - * @param application - * the Application to end. - * @throws IOException - * if the writing failed due to input/output error. - */ - private void endApplication(HttpServletRequest request, - HttpServletResponse response, Application application) - throws IOException { - - String logoutUrl = application.getLogoutURL(); - if (logoutUrl == null) - logoutUrl = application.getURL().toString(); - // clients JS app is still running, send a special xml file to - // tell client that application is quit and where to point browser now - // Set the response type - response.setContentType("application/xml; charset=UTF-8"); - ServletOutputStream out = response.getOutputStream(); - out.println(""); - out.println(""); - } - - /** - * Gets the Paintable Id. - * - * @param paintable - * @return the paintable Id. - */ - public synchronized String getPaintableId(Paintable paintable) { - - String id = (String) paintableIdMap.get(paintable); - if (id == null) { - id = "PID" + Integer.toString(idSequence++); - paintableIdMap.put(paintable, id); - } - - return id; - } - - /** - * - * @return - */ - public synchronized Set getDirtyComponents() { - - // Remove unnecessary repaints from the list - Object[] paintables = dirtyPaintabletSet.toArray(); - for (int i = 0; i < paintables.length; i++) { - if (paintables[i] instanceof Component) { - Component c = (Component) paintables[i]; - - // Check if any of the parents of c already exist in the list - Component p = c.getParent(); - while (p != null) { - if (dirtyPaintabletSet.contains(p)) { - - // Remove component c from the dirty paintables as its - // parent is also dirty - dirtyPaintabletSet.remove(c); - p = null; - } else - p = p.getParent(); - } - } - } - - return Collections.unmodifiableSet(dirtyPaintabletSet); - } - - /** - * Clears the Dirty Components. - * - */ - public synchronized void clearDirtyComponents() { - dirtyPaintabletSet.clear(); - } - - /** - * @see com.itmill.toolkit.terminal.Paintable.RepaintRequestListener#repaintRequested(com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent) - */ - public void repaintRequested(RepaintRequestEvent event) { - Paintable p = event.getPaintable(); - dirtyPaintabletSet.add(p); - - // For FrameWindows we mark all frames (windows) dirty - if (p instanceof FrameWindow) { - FrameWindow fw = (FrameWindow) p; - repaintFrameset(fw.getFrameset()); - } - } - - /** - * Recursively request repaint for all frames in frameset. - * - * @param fs - * the Framewindow.Frameset. - */ - private void repaintFrameset(FrameWindow.Frameset fs) { - List frames = fs.getFrames(); - for (Iterator i = frames.iterator(); i.hasNext();) { - FrameWindow.Frame f = (FrameWindow.Frame) i.next(); - if (f instanceof FrameWindow.Frameset) { - repaintFrameset((FrameWindow.Frameset) f); - } else { - Window w = f.getWindow(); - if (w != null) { - w.requestRepaint(); - } - } - } - } - - /** - * - * @param p - */ - public void paintablePainted(Paintable p) { - dirtyPaintabletSet.remove(p); - p.requestRepaintRequests(); - } - - /** - * - * @param paintable - * @return - */ - public boolean isDirty(Paintable paintable) { - return (dirtyPaintabletSet.contains(paintable)); - } - - /** - * @see com.itmill.toolkit.Application.WindowAttachListener#windowAttached(com.itmill.toolkit.Application.WindowAttachEvent) - */ - public void windowAttached(WindowAttachEvent event) { - event.getWindow().addListener(this); - dirtyPaintabletSet.add(event.getWindow()); - } - - /** - * @see com.itmill.toolkit.Application.WindowDetachListener#windowDetached(com.itmill.toolkit.Application.WindowDetachEvent) - */ - public void windowDetached(WindowDetachEvent event) { - event.getWindow().removeListener(this); - // Notify client of the close operation - removedWindows.add(event.getWindow()); - } - - /** - * - * @return - */ - public synchronized Set getRemovedWindows() { - return Collections.unmodifiableSet(removedWindows); - - } - - /** - * - * @param w - */ - private void removedWindowNotified(Window w) { - this.removedWindows.remove(w); - } - - /** - * Implementation of URIHandler.ErrorEvent interface. - */ - public class URIHandlerErrorImpl implements URIHandler.ErrorEvent { - - private URIHandler owner; - - private Throwable throwable; - - /** - * - * @param owner - * @param throwable - */ - private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) { - this.owner = owner; - this.throwable = throwable; - } - - /** - * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() - */ - public Throwable getThrowable() { - return this.throwable; - } - - /** - * @see com.itmill.toolkit.terminal.URIHandler.ErrorEvent#getURIHandler() - */ - public URIHandler getURIHandler() { - return this.owner; - } - } - - public void requireLocale(String value) { - if(locales == null) { - locales = new ArrayList(); - locales.add(application.getLocale().toString()); - pendingLocalesIndex = 0; - } - if(!locales.contains(value)) - locales.add(value); - } - - private Locale generateLocale(String value) { - String[] temp = value.split("_"); - if(temp.length == 1) - return new Locale(temp[0]); - else if(temp.length == 2) - return new Locale(temp[0], temp[1]); - else - return new Locale(temp[0], temp[1], temp[2]); - } -} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java index 5ba33b7778..324cd6cc12 100644 --- a/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java +++ b/src/com/itmill/toolkit/terminal/gwt/server/ApplicationServlet.java @@ -1242,14 +1242,14 @@ public class ApplicationServlet extends HttpServlet { * @param application * @return AJAX Application Manager */ - private ApplicationManager getApplicationManager(Application application) { - ApplicationManager mgr = (ApplicationManager) applicationToAjaxAppMgrMap + private CommunicationManager getApplicationManager(Application application) { + CommunicationManager mgr = (CommunicationManager) applicationToAjaxAppMgrMap .get(application); // This application is going from Web to AJAX mode, create new manager if (mgr == null) { // Creates new manager - mgr = new ApplicationManager(application, this); + mgr = new CommunicationManager(application, this); applicationToAjaxAppMgrMap.put(application, mgr); // Manager takes control over the application diff --git a/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java new file mode 100644 index 0000000000..b2bcc68ee5 --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/CommunicationManager.java @@ -0,0 +1,1053 @@ +/* ************************************************************************* + + IT Mill Toolkit + + Development of Browser User Interfaces Made Easy + + Copyright (C) 2000-2006 IT Mill Ltd + + ************************************************************************* + + This product is distributed under commercial license that can be found + from the product package on license.pdf. Use of this product might + require purchasing a commercial license from IT Mill Ltd. For guidelines + on usage, see licensing-guidelines.html + + ************************************************************************* + + For more information, contact: + + IT Mill Ltd phone: +358 2 4802 7180 + Ruukinkatu 2-4 fax: +358 2 4802 7181 + 20540, Turku email: info@itmill.com + Finland company www: www.itmill.com + + Primary source for information and releases: www.itmill.com + + ********************************************************************** */ + +package com.itmill.toolkit.terminal.gwt.server; + +import java.io.BufferedWriter; +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; +import java.io.InputStreamReader; +import java.io.OutputStream; +import java.io.OutputStreamWriter; +import java.io.PrintWriter; +import java.text.DateFormatSymbols; +import java.text.SimpleDateFormat; +import java.util.ArrayList; +import java.util.Calendar; +import java.util.Collection; +import java.util.Collections; +import java.util.Comparator; +import java.util.GregorianCalendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.LinkedHashSet; +import java.util.LinkedList; +import java.util.List; +import java.util.Locale; +import java.util.Map; +import java.util.Set; +import java.util.WeakHashMap; + +import javax.servlet.ServletException; +import javax.servlet.ServletOutputStream; +import javax.servlet.http.HttpServlet; +import javax.servlet.http.HttpServletRequest; +import javax.servlet.http.HttpServletResponse; + +import com.itmill.toolkit.Application; +import com.itmill.toolkit.Application.WindowAttachEvent; +import com.itmill.toolkit.Application.WindowDetachEvent; +import com.itmill.toolkit.terminal.DownloadStream; +import com.itmill.toolkit.terminal.PaintTarget; +import com.itmill.toolkit.terminal.Paintable; +import com.itmill.toolkit.terminal.URIHandler; +import com.itmill.toolkit.terminal.VariableOwner; +import com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent; +import com.itmill.toolkit.ui.Component; +import com.itmill.toolkit.ui.FrameWindow; +import com.itmill.toolkit.ui.Window; + +/** + * Application manager processes changes and paints for single application + * instance. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 5.0 + */ +public class CommunicationManager implements Paintable.RepaintRequestListener, + Application.WindowAttachListener, Application.WindowDetachListener { + + private static String GET_PARAM_REPAINT_ALL = "repaintAll"; + + private static int DEFAULT_BUFFER_SIZE = 32 * 1024; + + private static int MAX_BUFFER_SIZE = 64 * 1024; + + private WeakHashMap applicationToVariableMapMap = new WeakHashMap(); + + private HashSet dirtyPaintabletSet = new HashSet(); + + private WeakHashMap paintableIdMap = new WeakHashMap(); + + private WeakHashMap idPaintableMap = new WeakHashMap(); + + private int idSequence = 0; + + private Application application; + + private Set removedWindows = new HashSet(); + + private JsonPaintTarget paintTarget; + + private List locales; + + private int pendingLocalesIndex; + + private ApplicationServlet applicationServlet; + + public CommunicationManager(Application application, + ApplicationServlet applicationServlet) { + this.application = application; + this.applicationServlet = applicationServlet; + requireLocale(application.getLocale().toString()); + } + + /** + * + * + */ + public void takeControl() { + application.addListener((Application.WindowAttachListener) this); + application.addListener((Application.WindowDetachListener) this); + + } + + /** + * + * + */ + public void releaseControl() { + application.removeListener((Application.WindowAttachListener) this); + application.removeListener((Application.WindowDetachListener) this); + } + + public void handleUidlRequest(HttpServletRequest request, + HttpServletResponse response) throws IOException { + + // repaint requested or sesssion has timed out and new one is created + boolean repaintAll = (request.getParameter(GET_PARAM_REPAINT_ALL) != null) + || request.getSession().isNew(); + + OutputStream out = response.getOutputStream(); + PrintWriter outWriter = new PrintWriter(new BufferedWriter( + new OutputStreamWriter(out, "UTF-8"))); + + // TODO Move dirt elsewhere + outWriter.print(")/*{"); // some dirt to prevent cross site scripting + // vulnerabilities + + try { + + // Is this a download request from application + DownloadStream download = null; + + // The rest of the process is synchronized with the application + // in order to guarantee that no parallel variable handling is + // made + synchronized (application) { + + // Change all variables based on request parameters + Map unhandledParameters = handleVariables(request, application); + + // Handles the URI if the application is still running + if (application.isRunning()) + download = handleURI(application, request, response); + + // If this is not a download request + if (download == null) { + + // Finds the window within the application + Window window = null; + if (application.isRunning()) + window = getApplicationWindow(request, application); + + // Handles the unhandled parameters if the application is + // still running + if (window != null && unhandledParameters != null + && !unhandledParameters.isEmpty()) + window.handleParameters(unhandledParameters); + + // Removes application if it has stopped + if (!application.isRunning()) { + endApplication(request, response, application); + return; + } + + // Returns if no window found + if (window == null) + return; + + // Sets the response type + response.setContentType("application/json; charset=UTF-8"); + outWriter.print("\"changes\":["); + + paintTarget = new JsonPaintTarget(this, outWriter); + + // Paints components + Set paintables; + if (repaintAll) { + paintables = new LinkedHashSet(); + paintables.add(window); + + // Reset sent locales + locales = null; + requireLocale(application.getLocale().toString()); + + // Adds all non-native windows + for (Iterator i = window.getApplication().getWindows() + .iterator(); i.hasNext();) { + Window w = (Window) i.next(); + if (!"native".equals(w.getStyle()) && w != window) + paintables.add(w); + } + } else + paintables = getDirtyComponents(); + if (paintables != null) { + + // Creates "working copy" of the current state. + List currentPaintables = new ArrayList(paintables); + + // Sorts the paintable so that parent windows + // are always painted before child windows + Collections.sort(currentPaintables, new Comparator() { + + public int compare(Object o1, Object o2) { + + // If first argumement is now window + // the second is "smaller" if it is. + if (!(o1 instanceof Window)) { + return (o2 instanceof Window) ? 1 : 0; + } + + // Now, if second is not window the + // first is smaller. + if (!(o2 instanceof Window)) { + return -1; + } + + // Both are windows. + String n1 = ((Window) o1).getName(); + String n2 = ((Window) o2).getName(); + if (o1 instanceof FrameWindow) { + if (((FrameWindow) o1).getFrameset() + .getFrame(n2) != null) { + return -1; + } else if (!(o2 instanceof FrameWindow)) { + return -1; + } + } + if (o2 instanceof FrameWindow) { + if (((FrameWindow) o2).getFrameset() + .getFrame(n1) != null) { + return 1; + } else if (!(o1 instanceof FrameWindow)) { + return 1; + } + } + + return 0; + } + }); + + for (Iterator i = currentPaintables.iterator(); i + .hasNext();) { + Paintable p = (Paintable) i.next(); + + // TODO CLEAN + if (p instanceof Window) { + Window w = (Window) p; + if (w.getTerminal() == null) + w.setTerminal(application.getMainWindow() + .getTerminal()); + } + + paintTarget.startTag("change"); + paintTarget.addAttribute("format", "uidl"); + String pid = getPaintableId(p); + paintTarget.addAttribute("pid", pid); + + // Track paints to identify empty paints + paintTarget.setTrackPaints(true); + p.paint(paintTarget); + + // If no paints add attribute empty + if (paintTarget.getNumberOfPaints() <= 0) { + paintTarget.addAttribute("visible", false); + } + paintTarget.endTag("change"); + paintablePainted(p); + } + } + + paintTarget.close(); + outWriter.print("]"); // close changes + + outWriter.print(", \"meta\" : {"); + boolean metaOpen = false; + + // .. or initializion (first uidl-request) + if (application.ajaxInit()) { + outWriter.print("\"appInit\":true"); + } + // add meta instruction for client to set focus if it is set + Paintable f = (Paintable) application.consumeFocus(); + if (f != null) { + if (metaOpen) + outWriter.append(","); + outWriter.write("\"focus\":\"" + getPaintableId(f) + + "\""); + } + + outWriter.print("}, \"resources\" : {"); + + // Precache custom layouts + // TODO Does not support theme-get param or different themes + // in different windows -> Allways preload layouts with the + // theme specified by the applications + String themeName = application.getTheme() != null ? application + .getTheme() + : ApplicationServlet.DEFAULT_THEME; + // TODO We should only precache the layouts that are not + // cached already + int resourceIndex = 0; + for (Iterator i = paintTarget.getPreCachedResources() + .iterator(); i.hasNext();) { + String resource = (String) i.next(); + InputStream is = null; + try { + is = applicationServlet + .getServletContext() + .getResourceAsStream( + ApplicationServlet.THEME_DIRECTORY_PATH + + themeName + + "/" + + resource); + } catch (Exception e) { + Log.info(e.getMessage()); + } + if (is != null) { + + outWriter.print((resourceIndex++ > 0 ? ", " : "") + + "\"" + resource + "\" : "); + StringBuffer layout = new StringBuffer(); + + try { + InputStreamReader r = new InputStreamReader(is); + char[] buffer = new char[20000]; + int charsRead = 0; + while ((charsRead = r.read(buffer)) > 0) + layout.append(buffer, 0, charsRead); + r.close(); + } catch (java.io.IOException e) { + Log.info("Resource transfer failed: " + + request.getRequestURI() + ". (" + + e.getMessage() + ")"); + } + outWriter.print("\"" + + JsonPaintTarget.escapeJSON(layout + .toString()) + "\""); + } + } + outWriter.print("}"); + + printLocaleDeclarations(outWriter); + + outWriter.flush(); + outWriter.close(); + out.flush(); + } else { + + // For download request, transfer the downloaded data + handleDownload(download, request, response); + } + } + + out.flush(); + out.close(); + + } catch (Throwable e) { + // Writes the error report to client + OutputStreamWriter w = new OutputStreamWriter(out); + PrintWriter err = new PrintWriter(w); + err + .write("Application Internal Error"); + err.write("

" + e.toString() + "

\n");
+			e.printStackTrace(new PrintWriter(err));
+			err.write("\n
"); + err.close(); + } finally { + + } + + } + + private Map handleVariables(HttpServletRequest request, + Application application2) { + + Map params = new HashMap(request.getParameterMap()); + String changes = (String) ((params.get("changes") instanceof String[]) ? ((String[]) params + .get("changes"))[0] + : params.get("changes")); + params.remove("changes"); + if (changes != null) { + String[] ca = changes.split("\u0001"); + System.out.println("Changes = " + changes); + for (int i = 0; i < ca.length; i++) { + String[] vid = ca[i].split("_"); + VariableOwner owner = (VariableOwner) idPaintableMap + .get(vid[0]); + if (owner != null) { + Map m; + if (i + 2 >= ca.length + || !vid[0].equals(ca[i + 2].split("_")[0])) + m = new SingleValueMap(vid[1], convertVariableValue( + vid[2].charAt(0), ca[++i])); + else { + m = new HashMap(); + m.put(vid[1], convertVariableValue(vid[2].charAt(0), + ca[++i])); + } + while (i + 1 < ca.length + && vid[0].equals(ca[i + 1].split("_")[0])) { + vid = ca[++i].split("_"); + m.put(vid[1], convertVariableValue(vid[2].charAt(0), + ca[++i])); + } + owner.changeVariables(request, m); + } + } + } + + return params; + } + + private Object convertVariableValue(char variableType, String strValue) { + Object val = null; + System.out.println("converting " + strValue + " of type " + + variableType); + switch (variableType) { + case 'a': + val = strValue.split(","); + break; + case 's': + val = strValue; + break; + case 'i': + val = Integer.valueOf(strValue); + break; + case 'b': + val = Boolean.valueOf(strValue); + break; + } + + System.out.println("result: " + val + " of type " + (val == null ? "-" : val.getClass())); + return val; + } + + private void printLocaleDeclarations(PrintWriter outWriter) { + /* + * ----------------------------- Sending Locale sensitive date + * ----------------------------- + */ + + // Store JVM default locale for later restoration + // (we'll have to change the default locale for a while) + Locale jvmDefault = Locale.getDefault(); + + // Send locale informations to client + outWriter.print(", \"locales\":["); + for (; pendingLocalesIndex < locales.size(); pendingLocalesIndex++) { + + Locale l = generateLocale((String) locales.get(pendingLocalesIndex)); + // Locale name + outWriter.print("{\"name\":\"" + l.toString() + "\","); + + /* + * Month names (both short and full) + */ + DateFormatSymbols dfs = new DateFormatSymbols(l); + String[] short_months = dfs.getShortMonths(); + String[] months = dfs.getMonths(); + outWriter.print("\"smn\":[\"" + + // ShortMonthNames + short_months[0] + "\",\"" + short_months[1] + "\",\"" + + short_months[2] + "\",\"" + short_months[3] + "\",\"" + + short_months[4] + "\",\"" + short_months[5] + "\",\"" + + short_months[6] + "\",\"" + short_months[7] + "\",\"" + + short_months[8] + "\",\"" + short_months[9] + "\",\"" + + short_months[10] + "\",\"" + short_months[11] + "\"" + + "],"); + outWriter.print("\"mn\":[\"" + + // MonthNames + months[0] + "\",\"" + months[1] + "\",\"" + months[2] + + "\",\"" + months[3] + "\",\"" + months[4] + "\",\"" + + months[5] + "\",\"" + months[6] + "\",\"" + months[7] + + "\",\"" + months[8] + "\",\"" + months[9] + "\",\"" + + months[10] + "\",\"" + months[11] + "\"" + "],"); + + /* + * Weekday names (both short and full) + */ + String[] short_days = dfs.getShortWeekdays(); + String[] days = dfs.getWeekdays(); + outWriter.print("\"sdn\":[\"" + + // ShortDayNames + short_days[1] + "\",\"" + short_days[2] + "\",\"" + + short_days[3] + "\",\"" + short_days[4] + "\",\"" + + short_days[5] + "\",\"" + short_days[6] + "\",\"" + + short_days[7] + "\"" + "],"); + outWriter.print("\"dn\":[\"" + + // DayNames + days[1] + "\",\"" + days[2] + "\",\"" + days[3] + "\",\"" + + days[4] + "\",\"" + days[5] + "\",\"" + days[6] + "\",\"" + + days[7] + "\"" + "],"); + + /* + * First day of week (0 = sunday, 1 = monday) + */ + Calendar cal = new GregorianCalendar(l); + outWriter.print("\"fdow\":" + (cal.getFirstDayOfWeek() - 1) + ","); + + /* + * Date formatting (MM/DD/YYYY etc.) + */ + // Force our locale as JVM default for a while (SimpleDateFormat + // uses JVM default) + Locale.setDefault(l); + String df = new SimpleDateFormat().toPattern(); + int timeStart = df.indexOf("H"); + if (timeStart < 0) + timeStart = df.indexOf("h"); + int ampm_first = df.indexOf("a"); + // E.g. in Korean locale AM/PM is before h:mm + // TODO should take that into consideration on client-side as well, + // now always h:mm a + if (ampm_first > 0 && ampm_first < timeStart) + timeStart = ampm_first; + String dateformat = df.substring(0, timeStart - 1); + + outWriter.print("\"df\":\"" + dateformat.trim() + "\","); + + /* + * Time formatting (24 or 12 hour clock and AM/PM suffixes) + */ + String timeformat = df.substring(timeStart, df.length()); // Doesn't + // return + // second + // or + // milliseconds + // We use timeformat to determine 12/24-hour clock + boolean twelve_hour_clock = timeformat.contains("a"); + // TODO there are other possibilities as well, like 'h' in french + // (ignore them, too complicated) + String hour_min_delimiter = timeformat.contains(".") ? "." : ":"; + // outWriter.print("\"tf\":\"" + timeformat + "\","); + outWriter.print("\"thc\":" + twelve_hour_clock + ","); + outWriter.print("\"hmd\":\"" + hour_min_delimiter + "\""); + if (twelve_hour_clock) { + String[] ampm = dfs.getAmPmStrings(); + outWriter.print(",\"ampm\":[\"" + ampm[0] + "\",\"" + ampm[1] + + "\"]"); + } + outWriter.print("}"); + if (pendingLocalesIndex < locales.size() - 1) + outWriter.print(","); + } + outWriter.print("]"); // Close locales + + // Restore JVM default locale + Locale.setDefault(jvmDefault); + } + + /** + * Gets the existing application or create a new one. Get a window within an + * application based on the requested URI. + * + * @param request + * the HTTP Request. + * @param application + * the Application to query for window. + * @return Window mathing the given URI or null if not found. + * @throws ServletException + * if an exception has occurred that interferes with the + * servlet's normal operation. + */ + private Window getApplicationWindow(HttpServletRequest request, + Application application) throws ServletException { + + Window window = null; + + // Find the window where the request is handled + String path = request.getPathInfo(); + + // Main window as the URI is empty + if (path == null || path.length() == 0 || path.equals("/")) + window = application.getMainWindow(); + + // Try to search by window name + else { + String windowName = null; + if (path.charAt(0) == '/') + path = path.substring(1); + int index = path.indexOf('/'); + if (index < 0) { + windowName = path; + path = ""; + } else { + windowName = path.substring(0, index); + path = path.substring(index + 1); + } + window = application.getWindow(windowName); + + // By default, we use main window + if (window == null) + window = application.getMainWindow(); + } + + return window; + } + + /** + * Handles the requested URI. An application can add handlers to do special + * processing, when a certain URI is requested. The handlers are invoked + * before any windows URIs are processed and if a DownloadStream is returned + * it is sent to the client. + * + * @param application + * the Application owning the URI. + * @param request + * the HTTP request instance. + * @param response + * the HTTP response to write to. + * @return boolean true if the request was handled and + * further processing should be suppressed, otherwise + * false. + * @see com.itmill.toolkit.terminal.URIHandler + */ + private DownloadStream handleURI(Application application, + HttpServletRequest request, HttpServletResponse response) { + + String uri = request.getPathInfo(); + + // If no URI is available + if (uri == null || uri.length() == 0 || uri.equals("/")) + return null; + + // Remove the leading / + while (uri.startsWith("/") && uri.length() > 0) + uri = uri.substring(1); + + // Handle the uri + DownloadStream stream = null; + try { + stream = application.handleURI(application.getURL(), uri); + } catch (Throwable t) { + application.terminalError(new URIHandlerErrorImpl(application, t)); + } + + return stream; + } + + /** + * Handles the requested URI. An application can add handlers to do special + * processing, when a certain URI is requested. The handlers are invoked + * before any windows URIs are processed and if a DownloadStream is returned + * it is sent to the client. + * + * @param stream + * the downloadable stream. + * + * @param request + * the HTTP request instance. + * @param response + * the HTTP response to write to. + * + * @see com.itmill.toolkit.terminal.URIHandler + */ + private void handleDownload(DownloadStream stream, + HttpServletRequest request, HttpServletResponse response) { + + // Download from given stream + InputStream data = stream.getStream(); + if (data != null) { + + // Sets content type + response.setContentType(stream.getContentType()); + + // Sets cache headers + long cacheTime = stream.getCacheTime(); + if (cacheTime <= 0) { + response.setHeader("Cache-Control", "no-cache"); + response.setHeader("Pragma", "no-cache"); + response.setDateHeader("Expires", 0); + } else { + response.setHeader("Cache-Control", "max-age=" + cacheTime + / 1000); + response.setDateHeader("Expires", System.currentTimeMillis() + + cacheTime); + response.setHeader("Pragma", "cache"); // Required to apply + // caching in some + // Tomcats + } + + // Copy download stream parameters directly + // to HTTP headers. + Iterator i = stream.getParameterNames(); + if (i != null) { + while (i.hasNext()) { + String param = (String) i.next(); + response.setHeader((String) param, stream + .getParameter(param)); + } + } + + int bufferSize = stream.getBufferSize(); + if (bufferSize <= 0 || bufferSize > MAX_BUFFER_SIZE) + bufferSize = DEFAULT_BUFFER_SIZE; + byte[] buffer = new byte[bufferSize]; + int bytesRead = 0; + + try { + OutputStream out = response.getOutputStream(); + + while ((bytesRead = data.read(buffer)) > 0) { + out.write(buffer, 0, bytesRead); + out.flush(); + } + out.close(); + } catch (IOException ignored) { + } + + } + + } + + /** + * Ends the Application. + * + * @param request + * the HTTP request instance. + * @param response + * the HTTP response to write to. + * @param application + * the Application to end. + * @throws IOException + * if the writing failed due to input/output error. + */ + private void endApplication(HttpServletRequest request, + HttpServletResponse response, Application application) + throws IOException { + + String logoutUrl = application.getLogoutURL(); + if (logoutUrl == null) + logoutUrl = application.getURL().toString(); + // clients JS app is still running, send a special xml file to + // tell client that application is quit and where to point browser now + // Set the response type + response.setContentType("application/xml; charset=UTF-8"); + ServletOutputStream out = response.getOutputStream(); + out.println(""); + out.println(""); + } + + /** + * Gets the Paintable Id. + * + * @param paintable + * @return the paintable Id. + */ + public synchronized String getPaintableId(Paintable paintable) { + + String id = (String) paintableIdMap.get(paintable); + if (id == null) { + id = "PID" + Integer.toString(idSequence++); + paintableIdMap.put(paintable, id); + idPaintableMap.put(id, paintable); + } + + return id; + } + + /** + * + * @return + */ + public synchronized Set getDirtyComponents() { + + // Remove unnecessary repaints from the list + Object[] paintables = dirtyPaintabletSet.toArray(); + for (int i = 0; i < paintables.length; i++) { + if (paintables[i] instanceof Component) { + Component c = (Component) paintables[i]; + + // Check if any of the parents of c already exist in the list + Component p = c.getParent(); + while (p != null) { + if (dirtyPaintabletSet.contains(p)) { + + // Remove component c from the dirty paintables as its + // parent is also dirty + dirtyPaintabletSet.remove(c); + p = null; + } else + p = p.getParent(); + } + } + } + + return Collections.unmodifiableSet(dirtyPaintabletSet); + } + + /** + * Clears the Dirty Components. + * + */ + public synchronized void clearDirtyComponents() { + dirtyPaintabletSet.clear(); + } + + /** + * @see com.itmill.toolkit.terminal.Paintable.RepaintRequestListener#repaintRequested(com.itmill.toolkit.terminal.Paintable.RepaintRequestEvent) + */ + public void repaintRequested(RepaintRequestEvent event) { + Paintable p = event.getPaintable(); + dirtyPaintabletSet.add(p); + + // For FrameWindows we mark all frames (windows) dirty + if (p instanceof FrameWindow) { + FrameWindow fw = (FrameWindow) p; + repaintFrameset(fw.getFrameset()); + } + } + + /** + * Recursively request repaint for all frames in frameset. + * + * @param fs + * the Framewindow.Frameset. + */ + private void repaintFrameset(FrameWindow.Frameset fs) { + List frames = fs.getFrames(); + for (Iterator i = frames.iterator(); i.hasNext();) { + FrameWindow.Frame f = (FrameWindow.Frame) i.next(); + if (f instanceof FrameWindow.Frameset) { + repaintFrameset((FrameWindow.Frameset) f); + } else { + Window w = f.getWindow(); + if (w != null) { + w.requestRepaint(); + } + } + } + } + + /** + * + * @param p + */ + public void paintablePainted(Paintable p) { + dirtyPaintabletSet.remove(p); + p.requestRepaintRequests(); + } + + /** + * + * @param paintable + * @return + */ + public boolean isDirty(Paintable paintable) { + return (dirtyPaintabletSet.contains(paintable)); + } + + /** + * @see com.itmill.toolkit.Application.WindowAttachListener#windowAttached(com.itmill.toolkit.Application.WindowAttachEvent) + */ + public void windowAttached(WindowAttachEvent event) { + event.getWindow().addListener(this); + dirtyPaintabletSet.add(event.getWindow()); + } + + /** + * @see com.itmill.toolkit.Application.WindowDetachListener#windowDetached(com.itmill.toolkit.Application.WindowDetachEvent) + */ + public void windowDetached(WindowDetachEvent event) { + event.getWindow().removeListener(this); + // Notify client of the close operation + removedWindows.add(event.getWindow()); + } + + /** + * + * @return + */ + public synchronized Set getRemovedWindows() { + return Collections.unmodifiableSet(removedWindows); + + } + + /** + * + * @param w + */ + private void removedWindowNotified(Window w) { + this.removedWindows.remove(w); + } + + private final class SingleValueMap implements Map { + private final String name; + + private final Object value; + + private SingleValueMap(String name, Object value) { + this.name = name; + this.value = value; + } + + public void clear() { + throw new UnsupportedOperationException(); + } + + public boolean containsKey(Object key) { + if (name == null) + return key == null; + return name.equals(key); + } + + public boolean containsValue(Object v) { + if (value == null) + return v == null; + return value.equals(v); + } + + public Set entrySet() { + Set s = new HashSet(); + s.add(new Map.Entry() { + + public Object getKey() { + return name; + } + + public Object getValue() { + return value; + } + + public Object setValue(Object value) { + throw new UnsupportedOperationException(); + } + }); + return s; + } + + public Object get(Object key) { + if (!name.equals(key)) + return null; + return value; + } + + public boolean isEmpty() { + return false; + } + + public Set keySet() { + Set s = new HashSet(); + s.add(name); + return s; + } + + public Object put(Object key, Object value) { + throw new UnsupportedOperationException(); + } + + public void putAll(Map t) { + throw new UnsupportedOperationException(); + } + + public Object remove(Object key) { + throw new UnsupportedOperationException(); + } + + public int size() { + return 1; + } + + public Collection values() { + LinkedList s = new LinkedList(); + s.add(value); + return s; + + } + } + + /** + * Implementation of URIHandler.ErrorEvent interface. + */ + public class URIHandlerErrorImpl implements URIHandler.ErrorEvent { + + private URIHandler owner; + + private Throwable throwable; + + /** + * + * @param owner + * @param throwable + */ + private URIHandlerErrorImpl(URIHandler owner, Throwable throwable) { + this.owner = owner; + this.throwable = throwable; + } + + /** + * @see com.itmill.toolkit.terminal.Terminal.ErrorEvent#getThrowable() + */ + public Throwable getThrowable() { + return this.throwable; + } + + /** + * @see com.itmill.toolkit.terminal.URIHandler.ErrorEvent#getURIHandler() + */ + public URIHandler getURIHandler() { + return this.owner; + } + } + + public void requireLocale(String value) { + if (locales == null) { + locales = new ArrayList(); + locales.add(application.getLocale().toString()); + pendingLocalesIndex = 0; + } + if (!locales.contains(value)) + locales.add(value); + } + + private Locale generateLocale(String value) { + String[] temp = value.split("_"); + if (temp.length == 1) + return new Locale(temp[0]); + else if (temp.length == 2) + return new Locale(temp[0], temp[1]); + else + return new Locale(temp[0], temp[1], temp[2]); + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/HttpUploadStream.java b/src/com/itmill/toolkit/terminal/gwt/server/HttpUploadStream.java new file mode 100644 index 0000000000..d6ee9b9bac --- /dev/null +++ b/src/com/itmill/toolkit/terminal/gwt/server/HttpUploadStream.java @@ -0,0 +1,114 @@ +/* ************************************************************************* + + IT Mill Toolkit + + Development of Browser User Interfaces Made Easy + + Copyright (C) 2000-2006 IT Mill Ltd + + ************************************************************************* + + This product is distributed under commercial license that can be found + from the product package on license.pdf. Use of this product might + require purchasing a commercial license from IT Mill Ltd. For guidelines + on usage, see licensing-guidelines.html + + ************************************************************************* + + For more information, contact: + + IT Mill Ltd phone: +358 2 4802 7180 + Ruukinkatu 2-4 fax: +358 2 4802 7181 + 20540, Turku email: info@itmill.com + Finland company www: www.itmill.com + + Primary source for information and releases: www.itmill.com + + ********************************************************************** */ + +package com.itmill.toolkit.terminal.gwt.server; + +import java.io.InputStream; + +/** + * AjaxAdapter implementation of the UploadStream interface. + * + * @author IT Mill Ltd. + * @version + * @VERSION@ + * @since 5.0 + */ +public class HttpUploadStream implements + com.itmill.toolkit.terminal.UploadStream { + + /** + * Holds value of property variableName. + */ + private String streamName; + + private String contentName; + + private String contentType; + + /** + * Holds value of property variableValue. + */ + private InputStream stream; + + /** + * Creates a new instance of UploadStreamImpl. + * + * @param name + * the name of the stream. + * @param stream + * the input stream. + * @param contentName + * the name of the content. + * @param contentType + * the type of the content. + */ + public HttpUploadStream(String name, InputStream stream, + String contentName, String contentType) { + this.streamName = name; + this.stream = stream; + this.contentName = contentName; + this.contentType = contentType; + } + + /** + * Gets the name of the stream. + * + * @return the name of the stream. + */ + public String getStreamName() { + return this.streamName; + } + + /** + * Gets the input stream. + * + * @return the Input stream. + */ + public InputStream getStream() { + return this.stream; + } + + /** + * Gets the input stream content type. + * + * @return the content type of the input stream. + */ + public String getContentType() { + return this.contentType; + } + + /** + * Gets the stream content name. Stream content name usually differs from + * the actual stream name. It is used to identify the content of the stream. + * + * @return the Name of the stream content. + */ + public String getContentName() { + return this.contentName; + } +} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/JsonPaintTarget.java b/src/com/itmill/toolkit/terminal/gwt/server/JsonPaintTarget.java index 6e4662e557..8095141895 100644 --- a/src/com/itmill/toolkit/terminal/gwt/server/JsonPaintTarget.java +++ b/src/com/itmill/toolkit/terminal/gwt/server/JsonPaintTarget.java @@ -78,11 +78,9 @@ public class JsonPaintTarget implements PaintTarget { private PrintWriter uidlBuffer; - private AjaxVariableMap variableMap; - private boolean closed = false; - private ApplicationManager manager; + private CommunicationManager manager; private boolean trackPaints = false; @@ -106,13 +104,11 @@ public class JsonPaintTarget implements PaintTarget { * @throws PaintException * if the paint operation failed. */ - public JsonPaintTarget(AjaxVariableMap variableMap, - ApplicationManager manager, PrintWriter outWriter) + public JsonPaintTarget( + CommunicationManager manager, PrintWriter outWriter) throws PaintException { this.manager = manager; - // Sets the variable map - this.variableMap = variableMap; // Sets the target for UIDL writing this.uidlBuffer = outWriter; @@ -561,10 +557,7 @@ public class JsonPaintTarget implements PaintTarget { */ public void addUploadStreamVariable(VariableOwner owner, String name) throws PaintException { - String code = variableMap.registerVariable(name, UploadStream.class, - null, owner); startTag("uploadstream"); - addAttribute(UIDL_ARG_ID, code); addAttribute(UIDL_ARG_NAME, name); endTag("uploadstream"); } @@ -750,8 +743,6 @@ public class JsonPaintTarget implements PaintTarget { Vector attr = new Vector(); - private HashMap childTagCounters = new HashMap(); - StringBuffer data = new StringBuffer(); public boolean childrenArrayOpen = false; @@ -891,21 +882,18 @@ public class JsonPaintTarget implements PaintTarget { } abstract class Variable { - String code; String name; public abstract String getJsonPresentation(); } - + class BooleanVariable extends Variable { boolean value; public BooleanVariable(VariableOwner owner, String name, boolean v) { value = v; this.name = name; - code = variableMap.registerVariable(name, Boolean.class, - new Boolean(value), owner); } public String getJsonPresentation() { @@ -920,8 +908,6 @@ public class JsonPaintTarget implements PaintTarget { public StringVariable(VariableOwner owner, String name, String v) { value = v; this.name = name; - code = variableMap.registerVariable(name, String.class, value, - owner); } public String getJsonPresentation() { @@ -936,8 +922,6 @@ public class JsonPaintTarget implements PaintTarget { public IntVariable(VariableOwner owner, String name, int v) { value = v; this.name = name; - code = variableMap.registerVariable(name, Integer.class, - new Integer(value), owner); } public String getJsonPresentation() { @@ -951,8 +935,6 @@ public class JsonPaintTarget implements PaintTarget { public ArrayVariable(VariableOwner owner, String name, String[] v) { value = v; this.name = name; - code = variableMap.registerVariable(name, String[].class, value, - owner); } public String getJsonPresentation() { diff --git a/src/com/itmill/toolkit/terminal/gwt/server/Log.java b/src/com/itmill/toolkit/terminal/gwt/server/Log.java index 7a84341543..27bd738c62 100644 --- a/src/com/itmill/toolkit/terminal/gwt/server/Log.java +++ b/src/com/itmill/toolkit/terminal/gwt/server/Log.java @@ -55,9 +55,9 @@ package com.itmill.toolkit.terminal.gwt.server; * @author IT Mill Ltd. * @version * @VERSION@ - * @since 3.0 + * @since 5.0 */ -public class Log { +class Log { private static boolean useStdOut = true; diff --git a/src/com/itmill/toolkit/terminal/gwt/server/MultipartRequest.java b/src/com/itmill/toolkit/terminal/gwt/server/MultipartRequest.java deleted file mode 100644 index ae22f5bbbb..0000000000 --- a/src/com/itmill/toolkit/terminal/gwt/server/MultipartRequest.java +++ /dev/null @@ -1,1332 +0,0 @@ -/* ************************************************************************* - - IT Mill Toolkit - - Development of Browser User Interfaces Made Easy - - Copyright (C) 2000-2006 IT Mill Ltd - - ************************************************************************* - - This product is distributed under commercial license that can be found - from the product package on license.pdf. Use of this product might - require purchasing a commercial license from IT Mill Ltd. For guidelines - on usage, see licensing-guidelines.html - - ************************************************************************* - - For more information, contact: - - IT Mill Ltd phone: +358 2 4802 7180 - Ruukinkatu 2-4 fax: +358 2 4802 7181 - 20540, Turku email: info@itmill.com - Finland company www: www.itmill.com - - Primary source for information and releases: www.itmill.com - - ********************************************************************** */ - -package com.itmill.toolkit.terminal.gwt.server; - -import java.util.Hashtable; -import java.io.BufferedOutputStream; -import java.io.BufferedInputStream; -import java.io.OutputStream; -import java.io.InputStream; -import java.io.PrintWriter; -import java.io.ByteArrayOutputStream; -import java.io.ByteArrayInputStream; -import java.io.FileOutputStream; -import java.io.UnsupportedEncodingException; -import java.io.IOException; -import java.util.Enumeration; -import java.util.Vector; -import java.io.File; - -/** - * A Multipart form data parser. Parses an input stream and writes out any files - * found, making available a hashtable of other url parameters. As of version - * 1.17 the files can be saved to memory, and optionally written to a database, - * etc. - * - *
- *
- * Copyright (C)2001 Jason Pell.
- * - *
- * 
- * This library is free software; you can redistribute it and/or modify it under
- * the terms of the GNU Lesser General Public License as published by the Free
- * Software Foundation; either version 2.1 of the License, or (at your option)
- * any later version. 
- * This library 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 Lesser General Public License for more - * details.
- * You should have received a copy of the GNU Lesser General Public License - * along with this library; if not, write to the Free Software Foundation, Inc., - * 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
- * Email: jasonpell@hotmail.com Url: http://www.geocities.com/jasonpell - * - *
- * - * @author Jason Pell - * - * @version 1.18 Fixed some serious bugs. A new method readAndWrite(InputStream - * in, OutputStream out) which now does the generic processing in - * common for readAndWriteFile and readFile. The differences are that - * now the two extra bytes at the end of a file upload are processed - * once, instead of after each line. Also if an empty file is - * encountered, an outputstream is opened, but then deleted if no data - * written to it. The getCharArray method has been - * removed. Replaced by the new String(bytes, encoding) method using a - * specific encoding (Defaults to ISO-8859-1) to ensure that extended - * characters are supported. All strings are processed using this - * encoding. The addition of static methods setEncoding(String) and - * getEncoding method to allow the use of - * MultipartRequest with a specific encoding type. All - * instances of MultipartRequest will utilise the static charEncoding - * variable value, that the setEncoding method can be - * used to set. Started to introduce support for multiple file uploads - * with the same form field name, but not completed for v1.18. - * 26/06/2001 - * - * @version 1.17 A few _very_ minor fixes. Plus a cool new feature added. The - * ability to save files into memory. Thanks to Mark Latham for the - * idea and some of the code. 11/04/2001 - * @version 1.16 Added support for multiple parameter values. Also fixed - * getCharArray(...) method to support parameters with non-english - * ascii values (ascii above 127). Thanks to Stefan Schmidt & Michael - * Elvers for this. (No fix yet for reported problems with Tomcat 3.2 - * or a single extra byte appended to uploads of certain files). By - * 1.17 hopefully will have a resolution for the second problem. - * 14/03/2001 - * @version 1.15 A new parameter added, intMaxReadBytes, to allow arbitrary - * length files. Released under the LGPL (Lesser General Public - * License). 03/02/2001 - * @version 1.14 Fix for IE problem with filename being empty. This is because - * IE includes a default Content-Type even when no file is uploaded. - * 16/02/2001 - * @version 1.13 If an upload directory is not specified, then all file contents - * are sent into oblivion, but the rest of the parsing works as normal. - * @version 1.12 Fix, was allowing zero length files. Will not even create the - * output file until there is something to write. getFile(String) now - * returns null, if a zero length file was specified. 06/11/2000 - * @version 1.11 Fix, in case Content-type is not specified. - * @version 1.1 Removed dependence on Servlets. Now passes in a generic - * InputStream instead. "Borrowed" readLine from Tomcat 3.1 - * ServletInputStream class, so we can remove some of the dependencies - * on ServletInputStream. Fixed bug where a empty INPUT TYPE="FILE" - * value, would cause an exception. - * @version 1.0 Initial Release. - */ - -public class MultipartRequest { - /** - * Defines Character Encoding method here. - */ - private String charEncoding = "UTF-8"; - - // If not null, send debugging out here. - private PrintWriter debug = null; - - private Hashtable htParameters = null; - - private Hashtable htFiles = null; - - private String strBoundary = null; - - // If this Directory spec remains null, writing of files will be disabled... - private File fileOutPutDirectory = null; - - private boolean loadIntoMemory = false; - - private long intContentLength = -1; - - private long intTotalRead = -1; - - /** - * Prevent a denial of service by defining this, will never read more data. - * If Content-Length is specified to be more than this, will throw an - * exception. - * - * This limits the maximum number of bytes to the value of an int, which is - * 2 Gigabytes. - */ - public static final int MAX_READ_BYTES = Integer.MAX_VALUE; - - /** - * Defines the number of bytes to read per readLine call. 128K - */ - public static final int READ_LINE_BLOCK = 1024 * 128; - - /** - * Store a read from the input stream here. Global so we do not keep - * creating new arrays each read. - */ - private byte[] blockOfBytes = null; - - /** - * Type constant for File FILENAME. - */ - public static final int FILENAME = 0; - - /** - * Type constant for the File CONTENT_TYPE. - */ - public static final int CONTENT_TYPE = 1; - - /** - * Type constant for the File SIZE. - */ - public static final int SIZE = 2; - - /** - * Type constant for the File CONTENTS. - * - * Note: Only used for file upload to memory. - */ - public static final int CONTENTS = 3; - - /** - * This method should be called on the MultipartRequest - * itself, not on any instances of MultipartRequest, because - * this sets up the encoding for all instances of multipartrequest. You can - * set the encoding to null, in which case the default encoding will be - * applied. The default encoding if this method is not called has been set - * to ISO-8859-1, which seems to offer the best hope of support for - * international characters, such as german "Umlaut" characters. - * - *

- * Warning: In multithreaded environments it is the responsibility - * of the implementer to make sure that this method is not called while - * another instance is being constructed. When an instance of - * MultipartRequest is constructed, it parses the input data, and uses the - * result of getEncoding method to convert between bytes and - * strings. If setEncoding method is called by another - * thread, while the private parse method is executing, the - * method will utilise this new encoding, which may cause serious problems. - *

- */ - public void setEncoding(String enc) throws UnsupportedEncodingException { - if (enc == null || enc.trim() == "") - charEncoding = System.getProperty("file.encoding"); - else { - // This will test the encoding for validity. - new String(new byte[] { '\n' }, enc); - - charEncoding = enc; - } - } - - /** - * Gets the current encoding method. - * - * @return the encoding method. - */ - public String getEncoding() { - return charEncoding; - } - - /** - * Constructor. - * - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - public MultipartRequest(String strContentTypeText, int intContentLength, - InputStream in, String strSaveDirectory) - throws IllegalArgumentException, IOException { - this(null, strContentTypeText, intContentLength, in, strSaveDirectory, - MAX_READ_BYTES); - } - - /** - * Constructor. - * - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - public MultipartRequest(String strContentTypeText, int intContentLength, - InputStream in, String strSaveDirectory, int intMaxReadBytes) - throws IllegalArgumentException, IOException { - this(null, strContentTypeText, intContentLength, in, strSaveDirectory, - intMaxReadBytes); - } - - /** - * Constructor. - * - * @param debug - * A PrintWriter that can be used for debugging. - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - * @deprecated Replaced by MultipartRequest(PrintWriter, String, int, - * InputStream, int) You can specify - * MultipartRequest.MAX_READ_BYTES for the intMaxReadBytes - * parameter - */ - public MultipartRequest(PrintWriter debug, String strContentTypeText, - int intContentLength, InputStream in, String strSaveDirectory) - throws IllegalArgumentException, IOException { - this(debug, strContentTypeText, intContentLength, in, strSaveDirectory, - MAX_READ_BYTES); - - } - - /** - * Constructor - load into memory constructor - * - * @param debug - * A PrintWriter that can be used for debugging. - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - public MultipartRequest(PrintWriter debug, String strContentTypeText, - int intContentLength, InputStream in, int intMaxReadBytes) - throws IllegalArgumentException, IOException { - this.loadIntoMemory = true; - - // Now initialise the object, which will actually call the parse method - // to parse multipart stream. - init(debug, strContentTypeText, intContentLength, in, intMaxReadBytes); - } - - /** - * Constructor. - * - * @param debug - * A PrintWriter that can be used for debugging. - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - public MultipartRequest(PrintWriter debug, String strContentTypeText, - int intContentLength, InputStream in, String strSaveDirectory, - int intMaxReadBytes) throws IllegalArgumentException, IOException { - // IF strSaveDirectory == NULL, then we should ignore any files - // uploaded. - if (strSaveDirectory != null) { - fileOutPutDirectory = new File(strSaveDirectory); - if (!fileOutPutDirectory.exists()) - throw new IOException("Directory [" + strSaveDirectory - + "] is invalid."); - else if (!fileOutPutDirectory.canWrite()) - throw new IOException("Directory [" + strSaveDirectory - + "] is readonly."); - } - - // Now initialise the object, which will actually call the parse method - // to parse multipart stream. - init(debug, strContentTypeText, intContentLength, in, intMaxReadBytes); - } - - /** - * Initialise the parser. - * - * @param debug - * A PrintWriter that can be used for debugging. - * @param strContentTypeText - * the "Content-Type" HTTP header value. - * @param intContentLength - * the "Content-Length" HTTP header value. - * @param in - * the InputStream to read and parse. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @exception IllegalArgumentException - * If the strContentTypeText does not contain a Content-Type - * of "multipart/form-data" or the boundary is not found. - * @exception IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - * - * @see #MAX_READ_BYTES - */ - private void init(PrintWriter debug, String strContentTypeText, - int intContentLength, InputStream in, int intMaxReadBytes) - throws IllegalArgumentException, IOException { - // saves reference to debug stream for later. - this.debug = debug; - - if (strContentTypeText != null - && strContentTypeText.startsWith("multipart/form-data") - && strContentTypeText.indexOf("boundary=") != -1) - strBoundary = strContentTypeText.substring( - strContentTypeText.indexOf("boundary=") - + "boundary=".length()).trim(); - else { - // - debug("ContentType = " + strContentTypeText); - throw new IllegalArgumentException("Invalid Content Type."); - } - - this.intContentLength = intContentLength; - // FIX: 115 - if (intContentLength > intMaxReadBytes) - throw new IOException("Content Length Error (" + intContentLength - + " > " + intMaxReadBytes + ")"); - - // Instantiate the hashtable... - htParameters = new Hashtable(); - htFiles = new Hashtable(); - blockOfBytes = new byte[READ_LINE_BLOCK]; - - // Now parse the data. - parse(new BufferedInputStream(in)); - - // No need for this once parse is complete. - this.blockOfBytes = null; - this.debug = null; - this.strBoundary = null; - } - - /** - * Gets the value of the strName URLParameter. If more than one value for a - * particular Parameter, will return the first. If an error occurs will - * return null. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return the value of the URL Parameter. - */ - public String getURLParameter(String strName) { - Object value = htParameters.get(strName); - if (value instanceof Vector) - return (String) ((Vector) value).firstElement(); - else - return (String) htParameters.get(strName); - } - - /** - * Gets an enumeration of all values for the strName parameter. Even if a - * single value for, will always return an enumeration, although it may - * actually be empty if no value was encountered for strName or it is an - * invalid parameter name. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return the enumeration of all values. - */ - public Enumeration getURLParameters(String strName) { - Object value = htParameters.get(strName); - if (value instanceof Vector) - return ((Vector) value).elements(); - else { - Vector vector = new Vector(); - if (value != null) - vector.addElement(value); - return vector.elements(); - } - } - - /** - * Gets the enumeration of all URL Parameters for the current HTTP Request. - * - * @return the enumeration of URl Parameters. - */ - public Enumeration getParameterNames() { - return htParameters.keys(); - } - - /** - * Gets the enumeration of all INPUT TYPE=FILE parameter NAMES as - * encountered during the upload. - * - * @return - */ - public Enumeration getFileParameterNames() { - return htFiles.keys(); - } - - /** - * Returns the Content-Type of a file. - * - * @see #getFileParameter(java.lang.String, int) - */ - public String getContentType(String strName) { - // Can cast null, it will be ignored. - return (String) getFileParameter(strName, CONTENT_TYPE); - } - - /** - * If files were uploaded into memory, this method will retrieve the - * contents of the file as a InputStream. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return the contents of the file as a InputStream, or null if not file - * uploaded, or file uploaded to file system directory. - * @see #getFileParameter(java.lang.String, int) - */ - public InputStream getFileContents(String strName) { - Object obj = getFileParameter(strName, CONTENTS); - if (obj != null) - return new ByteArrayInputStream((byte[]) obj); - else - return null; - } - - /** - * Returns a File reference to the uploaded file. This reference is to the - * files uploaded location, and allows you to read/move/delete the file. - * This method is only of use, if files were uploaded to the file system. - * Will return null if uploaded to memory, in which case you should use - * getFileContents(strName) instead. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return a null file reference if a call to getFileSize(strName) returns - * zero or files were uploaded to memory. - * @see #getFileSize(java.lang.String) - * @see #getFileContents(java.lang.String) - * @see #getFileSystemName(java.lang.String) - */ - public File getFile(String strName) { - String filename = getFileSystemName(strName); - // Fix: If fileOutPutDirectory is null, then we are ignoring any file - // contents, so we must return null. - if (filename != null && getFileSize(strName) > 0 - && fileOutPutDirectory != null) - return new File(fileOutPutDirectory, filename); - else - return null; - } - - /** - * Gets the file system basename of an uploaded file. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return null if strName not found. - * - * @see #getFileParameter(java.lang.String, int) - */ - public String getFileSystemName(String strName) { - // Can cast null, it will be ignored. - return (String) getFileParameter(strName, FILENAME); - } - - /** - * Returns the File Size of a uploaded file. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @return -1 if file size not defined. - * - * @see #getFileParameter(java.lang.String, int) - */ - public long getFileSize(String strName) { - Object obj = getFileParameter(strName, SIZE); - if (obj != null) - return ((Long) obj).longValue(); - else - return (long) -1; - } - - /** - * Access an attribute of a file upload parameter record. - *

- * The getFileSystemName(String strName),getFileSize(String - * strName),getContentType(String strName), getContents(String strName) - * methods all use this method passing in a different type argument. - *

- * - *

- * Note: This class has been changed to provide for future - * functionality where you will be able to access all files uploaded, even - * if they are uploaded using the same form field name. At this point - * however, only the first file uploaded via a form field name is - * accessible. - *

- * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * - * @param type - * What attribute you want from the File Parameter. The following - * types are supported: MultipartRequest.FILENAME, - * MultipartRequest.CONTENT_TYPE, MultipartRequest.SIZE, - * MultipartRequest.CONTENTS - * - * @see #getContentType(java.lang.String) - * @see #getFileSize(java.lang.String) - * @see #getFileContents(java.lang.String) - * @see #getFileSystemName(java.lang.String) - */ - public Object getFileParameter(String strName, int type) { - Object[] objArray = null; - Object value = htFiles.get(strName); - if (value instanceof Vector) - objArray = (Object[]) ((Vector) value).firstElement(); - else - objArray = (Object[]) htFiles.get(strName); - - // Now ensure valid value. - if (objArray != null && type >= FILENAME && type <= CONTENTS) - return objArray[type]; - else - return null; - } - - /** - * This is the main parse method. - * - * @param in - * the InputStream to read and parse. - * @throws IOException - * If the intContentLength is higher than MAX_READ_BYTES or - * strSaveDirectory is invalid or cannot be written to. - */ - private void parse(InputStream in) throws IOException { - String strContentType = null; - String strName = null; - String strFilename = null; - String strLine = null; - int read = -1; - - // First run through, check that the first line is a boundary, otherwise - // throw a exception as format incorrect. - read = readLine(in, blockOfBytes); - strLine = read > 0 ? new String(blockOfBytes, 0, read, charEncoding) - : null; - - // Must be boundary at top of loop, otherwise we have finished. - if (strLine == null || strLine.indexOf(this.strBoundary) == -1) - // Just exit. Exception would be overkill - return; - // throw new IOException("Invalid Form Data, no boundary encountered."); - - // At the top of loop, we assume that the Content-Disposition line is - // next, otherwise we are at the end. - while (true) { - // Get Content-Disposition line. - read = readLine(in, blockOfBytes); - if (read <= 0) - break; // Nothing to do. - else { - strLine = new String(blockOfBytes, 0, read, charEncoding); - - // Mac IE4 adds extra line after last boundary - 1.21 - if (strLine == null || strLine.length() == 0 - || strLine.trim().length() == 0) - break; - - strName = trimQuotes(getValue("name", strLine)); - // If this is not null, it indicates that we are processing a - // filename. - strFilename = trimQuotes(getValue("filename", strLine)); - // Now if not null, strip it of any directory information. - - if (strFilename != null) { - // Fix: did not check whether filename was empty string - // indicating FILE contents were not passed. - if (strFilename.length() > 0) { - // Need to get the content type. - read = readLine(in, blockOfBytes); - strLine = read > 0 ? new String(blockOfBytes, 0, read, - charEncoding) : null; - - strContentType = "application/octet-stream"; - // Fix 1.11: If not null AND strLine.length() is long - // enough. - if (strLine != null - && strLine.length() > "Content-Type: ".length()) - strContentType = strLine.substring("Content-Type: " - .length());// Changed 1.13 - } else { - // FIX 1.14: IE problem with empty filename. - read = readLine(in, blockOfBytes); - strLine = read > 0 ? new String(blockOfBytes, 0, read, - charEncoding) : null; - - if (strLine != null - && strLine.startsWith("Content-Type:")) - readLine(in, blockOfBytes); - } - } - - // Ignore next line, as it should be blank. - readLine(in, blockOfBytes); - - // No filename specified at all. - if (strFilename == null) { - String param = readParameter(in); - addParameter(strName, param); - } else { - if (strFilename.length() > 0) { - long filesize = -1; - // Will remain null for read onto file system uploads. - byte[] contentsOfFile = null; - - // Get the BASENAME version of strFilename. - strFilename = getBasename(strFilename); - - // Are we loading files into memory instead of the - // filesystem? - if (loadIntoMemory) { - contentsOfFile = readFile(in); - if (contentsOfFile != null) - filesize = contentsOfFile.length; - } else - // Read the file onto file system. - filesize = readAndWriteFile(in, strFilename); - - // Fix 1.18 for multiple FILE parameter values. - if (filesize > 0) - addFileParameter(strName, new Object[] { - strFilename, strContentType, - new Long(filesize), contentsOfFile }); - else - // Zero length file. - addFileParameter(strName, new Object[] { - strFilename, null, new Long(0), null }); - } else // Fix: FILE INPUT TYPE, but no file passed as - // input... - { - addFileParameter(strName, new Object[] { null, null, - null, null }); - readLine(in, blockOfBytes); - } - } - } - }// while - } - - /** - * So we can put the logic for supporting multiple parameters with the same - * form field name in the one location. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @param value - * the form field value. - */ - private void addParameter(String strName, String value) { - // Fix NPE in case of null name - if (strName == null) - return; - - // Fix 1.16: for multiple parameter values. - Object objParms = htParameters.get(strName); - - // Adds an new entry to the param vector. - if (objParms instanceof Vector) - ((Vector) objParms).addElement(value); - else if (objParms instanceof String)// There is only one entry, so we - // create a vector! - { - Vector vecParms = new Vector(); - vecParms.addElement(objParms); - vecParms.addElement(value); - - htParameters.put(strName, vecParms); - } else - // first entry for strName. - htParameters.put(strName, value); - } - - /** - * So we can put the logic for supporting multiple files with the same form - * field name in the one location. - * - * Assumes that this method will never be called with a null fileObj or - * strFilename. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @param fileObj - */ - private void addFileParameter(String strName, Object[] fileObj) { - Object objParms = htFiles.get(strName); - - // Add an new entry to the param vector. - if (objParms instanceof Vector) - ((Vector) objParms).addElement(fileObj); - else if (objParms instanceof Object[])// There is only one entry, so - // we create a vector! - { - Vector vecParms = new Vector(); - vecParms.addElement(objParms); - vecParms.addElement(fileObj); - - htFiles.put(strName, vecParms); - } else - // first entry for strName. - htFiles.put(strName, fileObj); - } - - /** - * Reads the parameters, assume already passed Content-Disposition and blank - * line. - * - * @param in - * the InputStream to read and parse. - * @return the value read in. - * @throws IOException - * if an error occurs writing the file. - */ - private String readParameter(InputStream in) throws IOException { - StringBuffer buf = new StringBuffer(); - int read = -1; - - String line = null; - while (true) { - read = readLine(in, blockOfBytes); - if (read < 0) - throw new IOException("Stream ended prematurely."); - - // Change v1.18: Only instantiate string once for performance - // reasons. - line = new String(blockOfBytes, 0, read, charEncoding); - if (read < blockOfBytes.length - && line.indexOf(this.strBoundary) != -1) - break; // Boundary found, we need to finish up. - else - buf.append(line); - } - - if (buf.length() > 0) - buf.setLength(getLengthMinusEnding(buf)); - return buf.toString(); - } - - /** - * Read from in, write to out, minus last two line ending bytes. - * - * @param in - * the InputStream to read and parse. - * @param out - * the OutputStream. - * @throws IOException - * if an error occurs writing the file. - */ - private long readAndWrite(InputStream in, OutputStream out) - throws IOException { - long fileSize = 0; - int read = -1; - - // This variable will be assigned the bytes actually read. - byte[] secondLineOfBytes = new byte[blockOfBytes.length]; - // So we do not have to keep creating the second array. - int sizeOfSecondArray = 0; - - while (true) { - read = readLine(in, blockOfBytes); - if (read < 0) - throw new IOException("Stream ended prematurely."); - - // Found boundary. - if (read < blockOfBytes.length - && new String(blockOfBytes, 0, read, charEncoding) - .indexOf(this.strBoundary) != -1) { - // Writes the line, minus any line ending bytes. - // The secondLineOfBytes will NEVER BE NON-NULL if out==null, so - // there is no need to included this in the test - if (sizeOfSecondArray != 0) { - // Only used once, so declare here. - int actualLength = getLengthMinusEnding(secondLineOfBytes, - sizeOfSecondArray); - if (actualLength > 0 && out != null) { - out.write(secondLineOfBytes, 0, actualLength); - // Updates the file size. - fileSize += actualLength; - } - } - break; - } else { - // Writes the out previous line. - // The sizeOfSecondArray will NEVER BE ZERO if out==null, so - // there is no need to included this in the test - if (sizeOfSecondArray != 0) { - out.write(secondLineOfBytes, 0, sizeOfSecondArray); - // Updates the file size. - fileSize += sizeOfSecondArray; - } - - // out will always be null, so there is no need to reset - // sizeOfSecondArray to zero each time. - if (out != null) { - // Copy the read bytes into the array. - System.arraycopy(blockOfBytes, 0, secondLineOfBytes, 0, - read); - // That is how many bytes to read from the secondLineOfBytes - sizeOfSecondArray = read; - } - } - } - - // Returns the number of bytes written to outstream. - return fileSize; - } - - /** - * Reads a Multipart section that is a file type. Assumes that the - * Content-Disposition/Content-Type and blank line have already been - * processed. So we read until we hit a boundary, then close file and - * return. - * - * @param in - * the InputStream to read and parse. - * @param strFilename - * the FileSystemName of the file. - * @return the number of bytes read. - * @throws IOException - * if an error occurs writing the file. - */ - private long readAndWriteFile(InputStream in, String strFilename) - throws IOException { - // Stores a reference to this, as we may need to delete it later. - File outFile = new File(fileOutPutDirectory, strFilename); - - BufferedOutputStream out = null; - // Do not bother opening a OutputStream, if we cannot even write the - // file. - if (fileOutPutDirectory != null) - out = new BufferedOutputStream(new FileOutputStream(outFile)); - - long count = readAndWrite(in, out); - // Count would NOT be larger than zero if out was null. - if (count > 0) { - out.flush(); - out.close(); - } else { - out.close(); - // Deletes the file as empty. We should be able to delete it, if we - // can open it! - outFile.delete(); - } - return count; - } - - /** - * If the fileOutPutDirectory wasn't specified, just read the file to - * memory. - * - * @param in - * the InputStream to read and parse. - * @return contents of file, from which you can garner the size as well. - * @throws IOException - * if the writing failed due to input/output error. - */ - private byte[] readFile(InputStream in) throws IOException { - // In this case, we do not need to worry about a outputdirectory. - ByteArrayOutputStream out = new ByteArrayOutputStream(); - - long count = readAndWrite(in, out); - // Count would NOT be larger than zero if out was null. - if (count > 0) { - // Return contents of file to parse method for inclusion in htFiles - // object. - return out.toByteArray(); - } else - return null; - } - - /** - * Gets the length of the line minus line ending. - * - * @param byteLine - * @param endOfArray - * This is because in many cases the byteLine will have garbage - * data at the end, so we act as though the actual end of the - * array is this parameter. If you want to process the complete - * byteLine, specify byteLine.length as the endOfArray parameter. - * @return the length. - */ - private static final int getLengthMinusEnding(byte byteLine[], - int endOfArray) { - if (byteLine == null) - return 0; - - if (endOfArray >= 2 && byteLine[endOfArray - 2] == '\r' - && byteLine[endOfArray - 1] == '\n') - return endOfArray - 2; - else if (endOfArray >= 1 && byteLine[endOfArray - 1] == '\n' - || byteLine[endOfArray - 1] == '\r') - return endOfArray - 1; - else - return endOfArray; - } - - /** - * - * @param buf - * @return - */ - private static final int getLengthMinusEnding(StringBuffer buf) { - if (buf.length() >= 2 && buf.charAt(buf.length() - 2) == '\r' - && buf.charAt(buf.length() - 1) == '\n') - return buf.length() - 2; - else if (buf.length() >= 1 && buf.charAt(buf.length() - 1) == '\n' - || buf.charAt(buf.length() - 1) == '\r') - return buf.length() - 1; - else - return buf.length(); - } - - /** - * Reads at most READ_BLOCK blocks of data, or a single line whichever is - * smaller. Returns -1, if nothing to read, or we have reached the specified - * content-length. - * - * Assumes that bytToBeRead.length indicates the block size to read. - * - * @param in - * the InputStream to read and parse. - * @param bytesToBeRead - * the bytes to be read. - * @return -1 if stream has ended, before a newline encountered (should - * never happen) OR we have read past the Content-Length specified. - * (Should also not happen). Otherwise return the number of - * characters read. You can test whether the number returned is less - * than bytesToBeRead.length, which indicates that we have read the - * last line of a file or parameter or a border line, or some other - * formatting stuff. - * @throws IOException - * if the writing failed due to input/output error. - */ - private int readLine(InputStream in, byte[] bytesToBeRead) - throws IOException { - // Ensure that there is still stuff to read... - if (intTotalRead >= intContentLength) - return -1; - - // Get the length of what we are wanting to read. - int length = bytesToBeRead.length; - - // End of content, but some servers (apparently) may not realise this - // and end the InputStream, so - // we cover ourselves this way. - if (length > (intContentLength - intTotalRead)) - length = (int) (intContentLength - intTotalRead); // So we only - // read the data - // that is left. - - int result = readLine(in, bytesToBeRead, 0, length); - // Only if we get actually read something, otherwise something weird has - // happened, such as the end of stream. - if (result > 0) - intTotalRead += result; - - return result; - } - - /** - * This needs to support the possibility of a / or a \ separator. - * - * @param strFilename - * the FileSystemName of the file. - * @return the strFilename after removing all characters before the last - * occurence of / or \. - */ - private static final String getBasename(String strFilename) { - if (strFilename == null) - return strFilename; - - int intIndex = strFilename.lastIndexOf("/"); - if (intIndex == -1 || strFilename.lastIndexOf("\\") > intIndex) - intIndex = strFilename.lastIndexOf("\\"); - - if (intIndex != -1) - return strFilename.substring(intIndex + 1); - else - return strFilename; - } - - /** - * Trims any quotes from the start and end of a string. - * - * @param strItem - * @return the trimmed string. - */ - private static final String trimQuotes(String strItem) { - // Saves having to go any further.... - if (strItem == null || strItem.indexOf("\"") == -1) - return strItem; - - // Gets the rid of any whitespace.. - strItem = strItem.trim(); - - if (strItem.charAt(0) == '\"') - strItem = strItem.substring(1); - - if (strItem.charAt(strItem.length() - 1) == '\"') - strItem = strItem.substring(0, strItem.length() - 1); - - return strItem; - } - - /** - * Format of string name=value; name=value; If not found, will return null. - * - * @param strName - * the form field name, used to upload the file. This identifies - * the formfield location in the storage facility. - * @param strToDecode - * - */ - private static final String getValue(String strName, String strToDecode) { - strName = strName + "="; - - int startIndexOf = 0; - while (startIndexOf < strToDecode.length()) { - int indexOf = strToDecode.indexOf(strName, startIndexOf); - // Ensure either first name, or a space or ; precedes it. - if (indexOf != -1) { - if (indexOf == 0 - || Character.isWhitespace(strToDecode - .charAt(indexOf - 1)) - || strToDecode.charAt(indexOf - 1) == ';') { - int endIndexOf = strToDecode.indexOf(";", indexOf - + strName.length()); - if (endIndexOf == -1) // May return an empty string... - return strToDecode - .substring(indexOf + strName.length()); - else - return strToDecode.substring( - indexOf + strName.length(), endIndexOf); - } else - startIndexOf = indexOf + strName.length(); - } else - return null; - } - return null; - } - - /** - * Tomcat's ServletInputStream.readLine(byte[],int,int) Slightly Modified - * to utilise in.read()
- * Reads the input stream, one line at a time. Starting at an offset, reads - * bytes into an array, until it reads a certain number of bytes or reaches - * a newline character, which it reads into the array as well. - * - *

- * This method does not returns -1 if it reaches the end of - * the input stream before reading the maximum number of bytes, it returns - * -1, if no bytes read. - * - * @param in - * the InputStream to read and parse. - * @param b - * an array of bytes into which data is read. - * - * @param off - * an integer specifying the character at which this method - * begins reading. - * - * @param len - * an integer specifying the maximum number of bytes to read. - * - * @return an integer specifying the actual number of bytes read, or -1 if - * the end of the stream is reached. - * - * @throws IOException - * if an input or output exception has occurred - * - * - * Note: We have a problem with Tomcat reporting an erroneous number of - * bytes, so we need to check this. This is the method where we get an - * infinite loop, but only with binary files. - */ - private int readLine(InputStream in, byte[] b, int off, int len) - throws IOException { - if (len <= 0) - return 0; - - int count = 0, c; - - while ((c = in.read()) != -1) { - b[off++] = (byte) c; - count++; - if (c == '\n' || count == len) - break; - } - - return count > 0 ? count : -1; - } - - /** - * Prints the given debugging message. - * - * @param x - * the message to print. - */ - protected void debug(String x) { - if (debug != null) { - debug.println(x); - debug.flush(); - } - } - - /** - * Gets the Html Table.For debugging. - */ - public String getHtmlTable() { - StringBuffer sbReturn = new StringBuffer(); - - sbReturn.append("

Parameters

"); - sbReturn - .append("\n"); - for (Enumeration e = getParameterNames(); e.hasMoreElements();) { - String strName = (String) e.nextElement(); - sbReturn.append("\n" + ""); - - sbReturn.append(""); - } - sbReturn.append("
NameValue
" + strName + ""); - for (Enumeration f = getURLParameters(strName); f.hasMoreElements();) { - String value = (String) f.nextElement(); - sbReturn.append(""); - } - sbReturn.append("
" + value + "
"); - - sbReturn.append("

File Parameters

"); - - sbReturn - .append("\n"); - for (Enumeration e = getFileParameterNames(); e.hasMoreElements();) { - String strName = (String) e.nextElement(); - - sbReturn - .append("\n" - + "" - + ""); - - if (loadIntoMemory) - sbReturn.append(""); - else - sbReturn.append(""); - - sbReturn - .append("" - + "" + ""); - } - sbReturn.append("
NameFilenamePathContent TypeSize
" - + strName - + "" - + (getFileSystemName(strName) != null ? getFileSystemName(strName) - : "") + "" - + (getFileSize(strName) > 0 ? "in memory" : "") - + "" - + (getFile(strName) != null ? getFile(strName) - .getAbsolutePath() : "") + "" - + (getContentType(strName) != null ? getContentType(strName) - : "") - + "" - + (getFileSize(strName) != -1 ? getFileSize(strName) - + "" - : "") + "
"); - - return sbReturn.toString(); - } -} diff --git a/src/com/itmill/toolkit/terminal/gwt/server/ServletMultipartRequest.java b/src/com/itmill/toolkit/terminal/gwt/server/ServletMultipartRequest.java deleted file mode 100644 index a0352e5944..0000000000 --- a/src/com/itmill/toolkit/terminal/gwt/server/ServletMultipartRequest.java +++ /dev/null @@ -1,139 +0,0 @@ -/* ************************************************************************* - - IT Mill Toolkit - - Development of Browser User Interfaces Made Easy - - Copyright (C) 2000-2006 IT Mill Ltd - - ************************************************************************* - - This product is distributed under commercial license that can be found - from the product package on license.pdf. Use of this product might - require purchasing a commercial license from IT Mill Ltd. For guidelines - on usage, see licensing-guidelines.html - - ************************************************************************* - - For more information, contact: - - IT Mill Ltd phone: +358 2 4802 7180 - Ruukinkatu 2-4 fax: +358 2 4802 7181 - 20540, Turku email: info@itmill.com - Finland company www: www.itmill.com - - Primary source for information and releases: www.itmill.com - - ********************************************************************** */ - -package com.itmill.toolkit.terminal.gwt.server; - -import java.io.IOException; - -import javax.servlet.http.HttpServletRequest; - -/** - * This class wraps the MultipartRequest class by Jason Pell for the Servlet - * environment. - * - * @author IT Mill Ltd - * @version - * @VERSION@ - * @since 3.0 - */ -public class ServletMultipartRequest extends MultipartRequest { - /** - * Constructor wrapper, unwraps the InputStream, content type and content - * length from the servlet request object. - * - * @param request - * the HttpServletRequest will be used to initialise the - * MultipartRequest super class. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * - * @throws IllegalArgumentException - * If the request.getContentType() does not contain a - * Content-Type of "multipart/form-data" or the boundary is not - * found. - * @throws IOException - * If the request.getContentLength() is higher than - * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be - * written to. - * - * @see MultipartRequest#MAX_READ_BYTES - */ - public ServletMultipartRequest(HttpServletRequest request, - String strSaveDirectory) throws IllegalArgumentException, - IOException { - super(null, request.getContentType(), request.getContentLength(), - request.getInputStream(), strSaveDirectory, - MultipartRequest.MAX_READ_BYTES); - } - - /** - * Constructor wrapper, unwraps the InputStream, content type and content - * lenght from the servlet request object. Also allow to explicitly set the - * max permissable length of the request. - * - * @param request - * the HttpServletRequest will be used to initialise the - * MultipartRequest super class. - * @param strSaveDirectory - * the temporary directory to save the file from where they can - * then be moved to wherever by the calling process. If you - * specify null for this parameter, then any files - * uploaded will be silently ignored. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @throws IllegalArgumentException - * If the request.getContentType() does not contain a - * Content-Type of "multipart/form-data" or the boundary is not - * found. - * @throws IOException - * If the request.getContentLength() is higher than - * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be - * written to. - * - * @see MultipartRequest#MAX_READ_BYTES - */ - public ServletMultipartRequest(HttpServletRequest request, - String strSaveDirectory, int intMaxReadBytes) - throws IllegalArgumentException, IOException { - super(null, request.getContentType(), request.getContentLength(), - request.getInputStream(), strSaveDirectory, intMaxReadBytes); - } - - /** - * Constructor wrapper for loading the request into memory rather than - * temp-file. - * - * @param request - * the HttpServletRequest will be used to initialise the - * MultipartRequest super class. - * @param intMaxReadBytes - * Overrides the MAX_BYTES_READ value, to allow arbitrarily long - * files. - * - * @throws IllegalArgumentException - * If the request.getContentType() does not contain a - * Content-Type of "multipart/form-data" or the boundary is not - * found. - * @throws IOException - * If the request.getContentLength() is higher than - * MAX_READ_BYTES or strSaveDirectory is invalid or cannot be - * written to. - * - * @see MultipartRequest#MAX_READ_BYTES - */ - public ServletMultipartRequest(HttpServletRequest request, - int intMaxReadBytes) throws IllegalArgumentException, IOException { - super(null, request.getContentType(), request.getContentLength(), - request.getInputStream(), intMaxReadBytes); - } -}