diff --git a/build/build.xml b/build/build.xml index 71e90697..c9451c10 100644 --- a/build/build.xml +++ b/build/build.xml @@ -8,7 +8,7 @@ - + diff --git a/lib/village.jar b/lib/village.jar deleted file mode 100644 index 74545253..00000000 Binary files a/lib/village.jar and /dev/null differ diff --git a/src/FESI/Data/ESLoader.java b/src/FESI/Data/ESLoader.java index 9460f320..a990b18b 100644 --- a/src/FESI/Data/ESLoader.java +++ b/src/FESI/Data/ESLoader.java @@ -432,11 +432,7 @@ public abstract class ESLoader extends ESObject { debugInfo = " rejected (not widening numbers)"; } // Handle String of length 1 as a Char, which can be converted to a number - } else if ((targetClass == Character.class || - targetClass == Integer.class || - targetClass == Long.class || - targetClass == Float.class || - targetClass == Double.class) + } else if (targetClass == Character.class && params[i] instanceof String) { if (((String) params[i]).length()==1) { accepted = true; // will require conversion of parameter diff --git a/src/FESI/Interpreter/ClassInfo.java b/src/FESI/Interpreter/ClassInfo.java index fe0a1204..e53eb838 100644 --- a/src/FESI/Interpreter/ClassInfo.java +++ b/src/FESI/Interpreter/ClassInfo.java @@ -74,11 +74,11 @@ public class ClassInfo { * @return the ClassInfo of cls, added to the cache if needed */ private static ClassInfo ensureClassInfo(Class cls) { - boolean debug = ESLoader.isDebugJavaAccess(); + // boolean debug = ESLoader.isDebugJavaAccess(); ClassInfo classInfo = (ClassInfo) allClassInfo.get(cls); if (classInfo == null) { - if (debug) System.out.println("** Class info for class '" + - cls + "' not found in cache, created"); + // if (debug) System.out.println("** Class info for class '" + + // cls + "' not found in cache, created"); classInfo = new ClassInfo(); allClassInfo.put(cls, classInfo); } @@ -94,7 +94,7 @@ public class ClassInfo { * @param cls The class for which we look for the property. * @return The PropertyDescriptor or null if not found or in case of error */ - synchronized public static PropertyDescriptor lookupBeanField(String fieldName, Class cls) { + public static PropertyDescriptor lookupBeanField(String fieldName, Class cls) { ClassInfo classInfo = ClassInfo.ensureClassInfo(cls); return classInfo.cachedBeanFieldLookup(fieldName, cls); } @@ -110,29 +110,29 @@ public class ClassInfo { * @return The PropertyDescriptor or null if not found or in case of error */ private PropertyDescriptor cachedBeanFieldLookup(String propertyName, Class cls) { - boolean debug = ESLoader.isDebugJavaAccess(); + // boolean debug = ESLoader.isDebugJavaAccess(); // Check that there is a bean properties cache, chech if the property was cached if (beanProperties != null) { - if (debug) System.out.println("** Bean properties for class '" + - cls + "' found in cache"); + // if (debug) System.out.println("** Bean properties for class '" + + // cls + "' found in cache"); PropertyDescriptor descriptor = (PropertyDescriptor) beanProperties.get(propertyName); if (descriptor!= null) { - if (debug) System.out.println("** property descriptor '" + propertyName + "' found in cache"); + // if (debug) System.out.println("** property descriptor '" + propertyName + "' found in cache"); return descriptor; } } // Not in cache - if (debug) System.out.println("** No property named '" + - propertyName + "' found in cache, lookup started"); + // if (debug) System.out.println("** No property named '" + + // propertyName + "' found in cache, lookup started"); // Do we have a cached BeanInfo ? create it if no if (beanInfo == null) { try { beanInfo = Introspector.getBeanInfo(cls); } catch (IntrospectionException e) { - if (debug) System.out.println(" ** Error getting beaninfo: " + e); + // if (debug) System.out.println(" ** Error getting beaninfo: " + e); return null; } } @@ -142,7 +142,7 @@ public class ClassInfo { PropertyDescriptor descriptor = null; // none found for (int i=0; i0) { - System.out.println("** Looking in " + interfaces.length + " interfaces"); - } + // if (debug && interfaces.length>0) { + // System.out.println("** Looking in " + interfaces.length + " interfaces"); + // } SEARCHININTERFACE: for (int ix=0; ix0) { - if (debug) System.out.println("** " + nmbMethods + " methods named: '" + functionName + "' + found, add to class cache"); + // if (debug) System.out.println("** " + nmbMethods + " methods named: '" + functionName + "' + found, add to class cache"); methods = new Method[nmbMethods]; methodVector.copyInto(methods); if (publicMethods==null) { @@ -435,7 +435,7 @@ public class ClassInfo { } publicMethods.put(functionName, methods); } else { - if (debug) System.out.println("** No method named '" + functionName + "' found"); + // if (debug) System.out.println("** No method named '" + functionName + "' found"); } return methods; } @@ -449,7 +449,7 @@ public class ClassInfo { * @param cls The class of the method being looked up * @return The method array or null if none found or in case of error */ - synchronized public static Method[] lookupBeanMethod(String functionName, Class cls) { + public static Method[] lookupBeanMethod(String functionName, Class cls) { ClassInfo classInfo = ClassInfo.ensureClassInfo(cls); return classInfo.cachedBeanMethodLookup(functionName, cls); } @@ -469,27 +469,27 @@ public class ClassInfo { * @return The list of methods or null in case of error or if none found. */ private Method [] cachedBeanMethodLookup(String functionName, Class cls) { - boolean debug = ESLoader.isDebugJavaAccess(); + // boolean debug = ESLoader.isDebugJavaAccess(); if (beanMethods != null) { - if (debug) System.out.println("** Method descriptor for bean '" + - cls + "' found in cache"); + // if (debug) System.out.println("** Method descriptor for bean '" + + // cls + "' found in cache"); Method [] methods = (Method []) beanMethods.get(functionName); if (methods!= null) { - if (debug) System.out.println("** " + methods.length + - " method(s) named '" + functionName + "' found in cache"); + // if (debug) System.out.println("** " + methods.length + + // " method(s) named '" + functionName + "' found in cache"); return methods; } - } + } // Not in cache, find if any matching the same name can be found - if (debug) System.out.println("** No method named '" + - functionName + "' found in bean cache, lookup started"); + // if (debug) System.out.println("** No method named '" + + // functionName + "' found in bean cache, lookup started"); // Do we have a cached BeanInfo ? create it if no if (beanInfo == null) { try { beanInfo = Introspector.getBeanInfo(cls); } catch (IntrospectionException e) { - if (debug) System.out.println(" ** Error getting beaninfo: " + e); + // if (debug) System.out.println(" ** Error getting beaninfo: " + e); return null; } } @@ -498,7 +498,7 @@ public class ClassInfo { Vector methodVector = new Vector(allDescriptors.length); for (int i=0; i0) { - if (debug) System.out.println("** " + nmbMethods + " methods named: '" - + functionName + "' + found, add to bean cache"); + // if (debug) System.out.println("** " + nmbMethods + " methods named: '" + // + functionName + "' + found, add to bean cache"); methods = new Method[nmbMethods]; methodVector.copyInto(methods); if (beanMethods==null) { @@ -556,8 +556,8 @@ public class ClassInfo { } beanMethods.put(functionName, methods); } else { - if (debug) System.out.println("** No bean method named: '" + - functionName + "' + found"); + // if (debug) System.out.println("** No bean method named: '" + + // functionName + "' + found"); } return methods; } diff --git a/src/helma/extensions/HelmaExtension.java b/src/helma/extensions/HelmaExtension.java index 5750e65c..9c02c7f5 100644 --- a/src/helma/extensions/HelmaExtension.java +++ b/src/helma/extensions/HelmaExtension.java @@ -32,6 +32,12 @@ public abstract class HelmaExtension { */ public abstract void applicationStopped (Application app); + /** + * called when an Application's properties are have been updated. + * note that this will be called at startup once *before* applicationStarted(). + */ + public abstract void applicationUpdated (Application app); + /** * called by the ScriptingEngine when it is initizalized. Throws a ConfigurationException * when this type of ScriptingEngine is not supported. New methods and prototypes can be diff --git a/src/helma/extensions/demo/DemoExtension.java b/src/helma/extensions/demo/DemoExtension.java index 0d1d9455..2a946e06 100644 --- a/src/helma/extensions/demo/DemoExtension.java +++ b/src/helma/extensions/demo/DemoExtension.java @@ -43,6 +43,10 @@ public class DemoExtension extends HelmaExtension { app.logEvent ("DemoExtension stopped on app " + app.getName () ); } + public void applicationUpdated (Application app) { + app.logEvent ("DemoExtension updated on app " + app.getName () ); + } + public HashMap initScripting (Application app, ScriptingEngine engine) throws ConfigurationException { if (!(engine instanceof FesiEngine)) throw new ConfigurationException ("scripting engine " + engine.toString () + " not supported in DemoExtension"); diff --git a/src/helma/framework/ResponseBean.java b/src/helma/framework/ResponseBean.java index 063f9f6f..b3803a9c 100644 --- a/src/helma/framework/ResponseBean.java +++ b/src/helma/framework/ResponseBean.java @@ -62,7 +62,7 @@ public class ResponseBean implements Serializable { res.writeBinary (what); } - public void debug (String message) { + public void debug (Object message) { res.debug (message); } diff --git a/src/helma/framework/ResponseTrans.java b/src/helma/framework/ResponseTrans.java index 898eb493..ea69a7cb 100644 --- a/src/helma/framework/ResponseTrans.java +++ b/src/helma/framework/ResponseTrans.java @@ -185,19 +185,15 @@ public final class ResponseTrans implements Externalizable { } /** - * Returns the number of characters written to the response buffer so far. + * Get the response buffer, creating it if it doesn't exist */ - public int getBufferLength() { + public StringBuffer getBuffer () { if (buffer == null) - return 0; - return buffer.length (); - } - - public void setBufferLength(int l) { - if (buffer != null) - buffer.setLength (l); + buffer = new StringBuffer (INITIAL_BUFFER_SIZE); + return buffer; } + /** * Append a string to the response unchanged. This is often called * at the end of a request to write out the whole page, so if buffer @@ -237,10 +233,13 @@ public final class ResponseTrans implements Externalizable { * that buffer exists and its length is larger than offset. str may be null, in which * case nothing happens. */ - public void debug (String str) { + public void debug (Object message) { if (debugBuffer == null) debugBuffer = new StringBuffer (); - debugBuffer.append ("

"+str+"

"); + String str = message == null ? "null" : message.toString (); + debugBuffer.append ("

"); + debugBuffer.append (str); + debugBuffer.append ("

"); } /** @@ -335,17 +334,25 @@ public final class ResponseTrans implements Externalizable { if (charset == null) charset = "ISO-8859-1"; boolean encodingError = false; + // only close if the response hasn't been closed yet if (response == null) { - if (buffer != null) { - if (debugBuffer != null) + // if debug buffer exists, append it to main buffer + if (debugBuffer != null) { + if (buffer == null) + buffer = debugBuffer; + else buffer.append (debugBuffer); + } + // get the buffer's bytes in the specified encoding + if (buffer != null) { try { response = buffer.toString ().getBytes (charset); } catch (UnsupportedEncodingException uee) { encodingError = true; response = buffer.toString ().getBytes (); } - buffer = null; // make sure this is done only once, even with more requsts attached + // make sure this is done only once, even with more requsts attached + buffer = null; } else { response = new byte[0]; } diff --git a/src/helma/framework/core/Application.java b/src/helma/framework/core/Application.java index 63d52d95..9edde03b 100644 --- a/src/helma/framework/core/Application.java +++ b/src/helma/framework/core/Application.java @@ -228,6 +228,11 @@ public final class Application implements IPathElement, Runnable { } } + // read the sessions if wanted + if ("true".equalsIgnoreCase (getProperty("persistentSessions"))) { + loadSessionData (null); + } + typemgr = new TypeManager (this); typemgr.createPrototypes (); // logEvent ("Started type manager for "+name); @@ -319,6 +324,11 @@ public final class Application implements IPathElement, Runnable { ext.applicationStopped (this); } + // store the sessions if wanted + if ("true".equalsIgnoreCase (getProperty("persistentSessions"))) { + storeSessionData (null); + } + // stop logs if they exist if (eventLog != null) { eventLog.close (); @@ -840,34 +850,31 @@ public final class Application implements IPathElement, Runnable { */ public String getNodeHref (Object elem, String actionName) { // Object root = getDataRoot (); - // check optional root prototype from app.properties String rootProto = props.getProperty ("rootPrototype"); - String divider = "/"; - StringBuffer b = new StringBuffer (); - Object parent = null; - int loopWatch = 0; + StringBuffer b = new StringBuffer (baseURI); - while (elem != null && (parent = getParentElement (elem)) != null && elem != rootObject) { - - if (rootProto != null && rootProto.equals (getPrototypeName (elem))) - break; - b.insert (0, divider); - b.insert (0, UrlEncoded.encode (getElementName (elem))); - // move down to the element's parent - elem = parent; - - if (loopWatch++ > 20) - break; - } + composeHref (elem, b, rootProto, 0); if (actionName != null) b.append (UrlEncoded.encode (actionName)); - return baseURI + b.toString (); + return b.toString (); } + private final void composeHref (Object elem, StringBuffer b, String rootProto, int pathCount) { + if (elem == null || pathCount > 20) + return; + if (rootProto != null && rootProto.equals (getPrototypeName (elem))) + return; + Object parent = getParentElement (elem); + if (parent == null) + return; + composeHref (getParentElement (elem), b, rootProto, pathCount++); + b.append (UrlEncoded.encode (getElementName (elem))); + b.append ("/"); + } /** * This method sets the base URL of this application which will be prepended to @@ -1112,9 +1119,8 @@ public final class Application implements IPathElement, Runnable { NodeHandle userhandle = session.userHandle; if (userhandle != null) { try { - String[] str = new String [1]; - str[0] = session.getSessionID (); - eval.invokeFunction (userhandle, "onLogout", str); + Object[] param = { session.getSessionID() }; + eval.invokeFunction (userhandle, "onLogout", param); } catch (Exception ignore) {} } destroySession(session); @@ -1143,7 +1149,7 @@ public final class Application implements IPathElement, Runnable { } // sleep until we have work to do - try { + try { worker.sleep (Math.min (cleanupSleep, scheduleSleep)); } catch (InterruptedException x) { logEvent ("Scheduler for "+name+" interrupted"); @@ -1256,6 +1262,14 @@ public final class Application implements IPathElement, Runnable { // if node manager exists, update it if (nmgr != null) nmgr.updateProperties (props); + // update extensions + Vector extensions = Server.getServer ().getExtensions (); + for (int i=0; i 50) throw new RuntimeException ("Recursive skin invocation suspected"); - if (parts == null) { + if (macros == null) { reval.res.writeCharArray (source, 0, sourceLength); reval.skinDepth--; return; @@ -113,14 +113,14 @@ public final class Skin { try { int written = 0; Map handlerCache = null; - if (parts.length > 3) { + if (macros.length > 3) { handlerCache = new HashMap(); } - for (int i=0; i written) - reval.res.writeCharArray (source, written, parts[i].start-written); - parts[i].render (reval, thisObject, paramObject, handlerCache); - written = parts[i].end; + for (int i=0; i written) + reval.res.writeCharArray (source, written, macros[i].start-written); + macros[i].render (reval, thisObject, paramObject, handlerCache); + written = macros[i].end; } if (written < sourceLength) reval.res.writeCharArray (source, written, sourceLength-written); @@ -133,9 +133,9 @@ public final class Skin { * Check if a certain macro is present in this skin. The macro name is in handler.name notation */ public boolean containsMacro (String macroname) { - for (int i=0; i=0; i--) { - Object pathelem = reval.requestPath.get (i); - if (handler.equals (app.getPrototypeName (pathelem))) { - handlerObject = pathelem; - break; - } - } */ + // eiter because thisObject == null or the right object wasn't found + // in the object's parent path. Check if a matching macro handler + // is registered with the response object (res.handlers). handlerObject = reval.res.getMacroHandlers().get (handler); } @@ -380,12 +389,14 @@ public final class Skin { String funcName = name+"_macro"; if (reval.scriptingEngine.hasFunction (handlerObject, funcName)) { + + StringBuffer buffer = reval.res.getBuffer(); // remember length of response buffer before calling macro - int bufLength = reval.res.getBufferLength (); + int bufLength = buffer.length(); // remember length of buffer with prefix written out int preLength = 0; if (prefix != null) { - reval.res.write (prefix); + buffer.append (prefix); preLength = prefix.length(); } @@ -396,24 +407,36 @@ public final class Skin { // parameters = new HashMap (); Object[] arguments = { parameters == null ? new HashMap () : - new HashMap (parameters) }; + new HashMap (parameters) + }; + + Object value = reval.scriptingEngine.invoke (handlerObject, funcName, arguments, false); - Object v = reval.scriptingEngine.invoke (handlerObject, funcName, arguments, false); // check if macro wrote out to response buffer - if (reval.res.getBufferLength () == bufLength + preLength) { - // function didn't write out anything itself + if (buffer.length () == bufLength + preLength) { + // function didn't write out anything itself. + // erase previously written prefix if (preLength > 0) - reval.res.setBufferLength (bufLength); - writeToResponse (v, reval.res, true); + buffer.setLength (bufLength); + // write out macro's return value + writeResponse (value, buffer, true); } else { - if (suffix != null) - reval.res.write (suffix); - writeToResponse (v, reval.res, false); + if (encoding != ENCODE_NONE) { + // if an encoding is specified, re-encode the macro's output + String output = buffer.substring (bufLength + preLength); + buffer.setLength (bufLength); + writeResponse (output, buffer, false); + } else { + // no re-encoding needed, just append suffix + if (suffix != null) + buffer.append (suffix); + } + writeResponse (value, buffer, false); } } else { // System.err.println ("Getting macro from property"); - Object v = reval.scriptingEngine.get (handlerObject, name); - writeToResponse (v, reval.res, true); + Object value = reval.scriptingEngine.get (handlerObject, name); + writeResponse (value, reval.res.getBuffer(), true); } } else { String msg = "[HopMacro unhandled: "+fullName+"]"; @@ -445,21 +468,21 @@ public final class Skin { value = reval.res.error; if (value == null) value = reval.res.get (name); - writeToResponse (value, reval.res, true); + writeResponse (value, reval.res.getBuffer(), true); } private void renderFromRequest (RequestEvaluator reval) { if (reval.req == null) return; Object value = reval.req.get (name); - writeToResponse (value, reval.res, true); + writeResponse (value, reval.res.getBuffer(), true); } private void renderFromSession (RequestEvaluator reval) { if (reval.session == null) return; Object value = reval.session.getCacheNode().getString (name); - writeToResponse (value, reval.res, true); + writeResponse (value, reval.res.getBuffer(), true); } private void renderFromParam (RequestEvaluator reval, Map paramObject) { @@ -467,14 +490,14 @@ public final class Skin { reval.res.write ("[HopMacro error: Skin requires a parameter object]"); else { Object value = paramObject.get (name); - writeToResponse (value, reval.res, true); + writeResponse (value, reval.res.getBuffer(), true); } } /** * Utility method for writing text out to the response object. */ - void writeToResponse (Object value, ResponseTrans res, boolean useDefault) { + void writeResponse (Object value, StringBuffer buffer, boolean useDefault) { String text; if (value == null) { if (useDefault) @@ -484,36 +507,39 @@ public final class Skin { } else { text = value.toString (); } - if (text == null || text.length() == 0) - return; - if (encoding != null) - text = encode (text, encoding); - res.write (prefix); - res.write (text); - res.write (suffix); - } - - /** - * Utility method for performing different kind of character - * encodings on the macro output. - */ - String encode (String text, String encoding) { - if ("html".equalsIgnoreCase (encoding)) - return HtmlEncoder.encode (text); - if ("xml".equalsIgnoreCase (encoding)) - return HtmlEncoder.encodeXml (text); - if ("form".equalsIgnoreCase (encoding)) - return HtmlEncoder.encodeFormValue (text); - if ("url".equalsIgnoreCase (encoding)) - return URLEncoder.encode (text); - return text; + if (text != null && text.length() > 0) { + if (prefix != null) + buffer.append (prefix); + switch (encoding) { + case ENCODE_NONE: + buffer.append (text); + break; + case ENCODE_HTML: + HtmlEncoder.encode (text, buffer); + break; + case ENCODE_XML: + HtmlEncoder.encodeXml (text, buffer); + break; + case ENCODE_FORM: + HtmlEncoder.encodeFormValue (text, buffer); + break; + case ENCODE_URL: + buffer.append (URLEncoder.encode (text)); + break; + case ENCODE_ALL: + HtmlEncoder.encodeAll (text, buffer); + break; + } + if (suffix != null) + buffer.append (suffix); + } } public String toString () { return "[HopMacro: "+fullName+"]"; } - + /** * Return the full name of the macro in handler.name notation diff --git a/src/helma/image/ImageGenerator.java b/src/helma/image/ImageGenerator.java index debf059a..3602c822 100644 --- a/src/helma/image/ImageGenerator.java +++ b/src/helma/image/ImageGenerator.java @@ -9,10 +9,10 @@ import java.net.URL; import java.net.MalformedURLException; /** - * This creates an invisible frame in order to be able to create images - * from Java. (Java needs a window context in order to user the Image class). + * Factory class for generating Image objects from various sources. + * */ - + public class ImageGenerator { public ImageGenerator () { @@ -20,10 +20,9 @@ public class ImageGenerator { } public ImageWrapper createPaintableImage (int w, int h) { - Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); + BufferedImage img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); Graphics g = img.getGraphics (); - ImageWrapper rimg = null; - rimg = new SunImageWrapper (img, g, w, h, this); + ImageWrapper rimg = new SunImageWrapper (img, g, w, h, this); return rimg; } @@ -31,13 +30,17 @@ public class ImageGenerator { ImageWrapper rimg = null; Image img1 = Toolkit.getDefaultToolkit ().createImage (src); ImageLoader loader = new ImageLoader (img1); - loader.load (); - int w = loader.getWidth (); - int h = loader.getHeight (); - Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); - Graphics g = img.getGraphics (); - g.drawImage (img1, 0, 0, null); - rimg = new SunImageWrapper (img, g, w, h, this); + try { + loader.getDimensions (); + int w = loader.getWidth (); + int h = loader.getHeight (); + Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); + Graphics g = img.getGraphics (); + g.drawImage (img1, 0, 0, null); + rimg = new SunImageWrapper (img, g, w, h, this); + } finally { + loader.done(); + } return rimg; } @@ -45,10 +48,14 @@ public class ImageGenerator { ImageWrapper rimg = null; Image img = Toolkit.getDefaultToolkit ().createImage (src); ImageLoader loader = new ImageLoader (img); - loader.load (); - int w = loader.getWidth (); - int h = loader.getHeight (); - rimg = new SunImageWrapper (img, null, w, h, this); + try { + loader.getDimensions (); + int w = loader.getWidth (); + int h = loader.getHeight (); + rimg = new SunImageWrapper (img, null, w, h, this); + } finally { + loader.done(); + } return rimg; } @@ -58,13 +65,17 @@ public class ImageGenerator { URL url = new URL (urlstring); Image img1 = Toolkit.getDefaultToolkit ().createImage (url); ImageLoader loader = new ImageLoader (img1); - loader.load (); - int w = loader.getWidth (); - int h = loader.getHeight (); - Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); - Graphics g = img.getGraphics (); - g.drawImage (img1, 0, 0, null); - rimg = new SunImageWrapper (img, g, w, h, this); + try { + loader.getDimensions (); + int w = loader.getWidth (); + int h = loader.getHeight (); + Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); + Graphics g = img.getGraphics (); + g.drawImage (img1, 0, 0, null); + rimg = new SunImageWrapper (img, g, w, h, this); + } finally { + loader.done(); + } return rimg; } @@ -73,13 +84,17 @@ public class ImageGenerator { FilteredImageSource fis = new FilteredImageSource (iw.getSource(), filter); Image img1 = Toolkit.getDefaultToolkit().createImage (fis); ImageLoader loader = new ImageLoader (img1); - loader.load (); - int w = loader.getWidth (); - int h = loader.getHeight (); - Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); - Graphics g = img.getGraphics (); - g.drawImage (img1, 0, 0, null); - rimg = new SunImageWrapper (img, g, w, h, this); + try { + loader.getDimensions (); + int w = loader.getWidth (); + int h = loader.getHeight (); + Image img = new BufferedImage (w, h, BufferedImage.TYPE_INT_RGB); + Graphics g = img.getGraphics (); + g.drawImage (img1, 0, 0, null); + rimg = new SunImageWrapper (img, g, w, h, this); + } finally { + loader.done(); + } return rimg; } @@ -88,7 +103,8 @@ public class ImageGenerator { Image img = null; img = Toolkit.getDefaultToolkit ().createImage (filename); ImageLoader loader = new ImageLoader (img); - loader.load (); + loader.getDimensions (); + loader.done(); return img; } @@ -96,7 +112,8 @@ public class ImageGenerator { Image img = null; img = Toolkit.getDefaultToolkit ().createImage (producer); ImageLoader loader = new ImageLoader (img); - loader.load (); + loader.getDimensions (); + loader.done(); return img; } @@ -104,26 +121,27 @@ public class ImageGenerator { Image img; int w, h; + boolean waiting; + boolean firstFrameLoaded; ImageLoader (Image img) { this.img = img; + waiting = true; + firstFrameLoaded = false; } - int getWidth () { - return w; - } - - int getHeight () { - return h; - } - - synchronized void load () { + synchronized void getDimensions () { w = img.getWidth(this); h = img.getHeight (this); - if (w == -1 || h == -1) try { - wait (30000); - } catch (InterruptedException x) { - return; + if (w == -1 || h == -1) { + try { + wait (45000); + } catch (InterruptedException x) { + waiting = false; + return; + } finally { + waiting = false; + } } // if width and height haven't been set, throw tantrum if (w == -1 || h == -1) { @@ -131,6 +149,18 @@ public class ImageGenerator { } } + synchronized void done () { + waiting = false; + notifyAll (); + } + + int getWidth () { + return w; + } + + int getHeight () { + return h; + } public synchronized boolean imageUpdate(Image img, int infoflags, @@ -138,24 +168,24 @@ public class ImageGenerator { int y, int width, int height) { - if (w == -1 && (infoflags & WIDTH) > 0) - w = width; - if (h == -1 && (infoflags & HEIGHT) > 0) - h = height; - if (h > -1 && w > -1 && (infoflags & ALLBITS) > 0) { - // we know all we want to know. notify waiting thread that - // the image is loaded and ready to be used. - notifyAll (); - return false; - } // check if there was an error - if ((infoflags & ERROR) > 0) { + if (!waiting || (infoflags & ERROR) > 0 || (infoflags & ABORT) > 0) { + // we either timed out or there was an error. notifyAll (); return false; } - // TODO: If image production was aborted, but no error was reported, - // we might want to start production again. For now, we just give up. - if ((infoflags & ABORT) > 0) { + if ((infoflags & WIDTH) > 0 || (infoflags & HEIGHT) > 0) { + if ((infoflags & WIDTH) > 0) + w = width; + if ((infoflags & HEIGHT) > 0) + h = height; + if (w > -1 && h > -1 && firstFrameLoaded) { + notifyAll (); + return false; + } + } + if ((infoflags & ALLBITS) > 0 || (infoflags & FRAMEBITS) > 0) { + firstFrameLoaded = true; notifyAll (); return false; } diff --git a/src/helma/image/ImageWrapper.java b/src/helma/image/ImageWrapper.java index beccd67d..b5531fa8 100644 --- a/src/helma/image/ImageWrapper.java +++ b/src/helma/image/ImageWrapper.java @@ -94,12 +94,16 @@ public abstract class ImageWrapper { } public void resize (int w, int h) { - // ImageFilter filter = new ReplicateScaleFilter (w, h); - // img = Toolkit.getDefaultToolkit ().createImage(new FilteredImageSource(img.getSource(), filter)); img = img.getScaledInstance (w, h, Image.SCALE_SMOOTH); width = w; height = h; } + + public void resizeFast (int w, int h) { + img = img.getScaledInstance (w, h, Image.SCALE_FAST); + width = w; + height = h; + } public abstract void reduceColors (int colors); diff --git a/src/helma/main/Server.java b/src/helma/main/Server.java index 1d8669e5..ff908b4e 100644 --- a/src/helma/main/Server.java +++ b/src/helma/main/Server.java @@ -25,7 +25,7 @@ import org.apache.xmlrpc.*; public class Server implements IPathElement, Runnable { - public static final String version = "1.2 RC2 2002/12/05"; + public static final String version = "1.2.2 (2003/02/04)"; public final long starttime; // if true we only accept RMI and XML-RPC connections from @@ -90,6 +90,9 @@ import org.apache.xmlrpc.*; // create new server instance server = new Server (args); + + // start the server main thread + server.start (); } /** @@ -137,8 +140,13 @@ import org.apache.xmlrpc.*; } catch (Exception portx) { usageError = true; } - } else + } else if (args[i].equals ("-i") && i+1 -1) { + read += r; + if (read == buffer.length) { + // grow input buffer + char[] newBuffer = new char[buffer.length*2]; + System.arraycopy (buffer, 0, newBuffer, 0, buffer.length); + buffer = newBuffer; + } + } + newprop.setStringValue (new String(buffer, 0, read)); + } + break; + case Types.CHAR: case Types.VARCHAR: case Types.OTHER: @@ -435,9 +454,9 @@ public final class Node implements INode, Serializable { Relation rel = getDbMapping ().getSubnodeRelation (); if (rel != null) { if (rel.usesPrimaryKey()) { - nmgr.evictKey (new DbKey (getDbMapping().getSubnodeMapping(), key)); + nmgr.evictNodeByKey (new DbKey (getDbMapping().getSubnodeMapping(), key)); } else { - nmgr.evictKey (new SyntheticKey (getKey(), key)); + nmgr.evictNodeByKey (new SyntheticKey (getKey(), key)); } } } @@ -493,7 +512,7 @@ public final class Node implements INode, Serializable { } else { anonymous = true; } - } else if (p.contains (this) > -1) { + } else if (!anonymous && p.contains (this) > -1) { anonymous = true; } } catch (Exception ignore) { @@ -873,13 +892,13 @@ public final class Node implements INode, Serializable { public INode createNode (String nm, int where) { checkWriteLock (); - boolean anon = false; - if (nm == null || "".equals (nm.trim ())) + boolean anon = false; + if (nm == null || "".equals (nm.trim ())) anon = true; Node n = new Node (nm, null, nmgr); if (anon) addNode (n, where); - else + else setNode (nm, n); return n; } @@ -917,7 +936,7 @@ public final class Node implements INode, Serializable { if (rel.otherType != null && rel.otherType.isRelational ()) return (IPathElement) nmgr.getNode (this, name, rel); else - return (IPathElement) getNode (name); + return (IPathElement) getNode (name); } return (IPathElement) getSubnode (name); } else { @@ -1748,11 +1767,20 @@ public final class Node implements INode, Serializable { if (propMap == null) return; try { - Property p = (Property) propMap.remove (propname.toLowerCase ()); + // if node is relational, leave a null property so that it is + // updated in the DB. Otherwise, remove the property. + Property p; + boolean relational = dbmap != null && dbmap.isRelational(); + if (relational) + p = (Property) propMap.get (propname.toLowerCase ()); + else + p = (Property) propMap.remove (propname.toLowerCase ()); if (p != null) { checkWriteLock (); if (p.getType() == Property.NODE) p.unregisterNode (); + if (relational) + p.setStringValue (null); // Server.throwNodeEvent (new NodeEvent (this, NodeEvent.PROPERTIES_CHANGED)); lastmodified = System.currentTimeMillis (); if (state == CLEAN) diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index bab0a103..c2740d5d 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -9,7 +9,6 @@ import helma.framework.core.Application; import java.sql.*; import java.io.*; import java.util.*; -import com.workingdogs.village.*; /** * The NodeManager is responsible for fetching Nodes from the internal or @@ -360,8 +359,8 @@ public final class NodeManager { } /** - * Remove a node from the node cache. If at a later time it is accessed again, it will be - * refetched from the database. + * Remove a node from the node cache. If at a later time it is accessed again, + * it will be refetched from the database. */ public void evictNode (Node node) { node.setState (INode.INVALID); @@ -369,7 +368,21 @@ public final class NodeManager { } /** - * Used when a key stops being valid for a node. + * Remove a node from the node cache. If at a later time it is accessed again, + * it will be refetched from the database. + */ + public void evictNodeByKey (Key key) { + Node n = (Node) cache.remove (key); + if (n != null) { + n.setState (INode.INVALID); + if (!(key instanceof DbKey)) + cache.remove (n.getKey ()); + } + } + + /** + * Used when a key stops being valid for a node. The cached node itself + * remains valid, if it is present in the cache by other keys. */ public void evictKey (Key key) { cache.remove (key); @@ -396,62 +409,129 @@ public final class NodeManager { db.saveNode (txn, node.getID (), node); } else { // app.logEvent ("inserting relational node: "+node.getID ()); - TableDataSet tds = null; + + DbColumn[] columns = dbm.getColumns (); + + StringBuffer b1 = dbm.getInsert (); + StringBuffer b2 = new StringBuffer (" ) VALUES ( ?"); + + String nameField = dbm.getNameField (); + String prototypeField = dbm.getPrototypeField (); + + for (int i=0; i 0) { - // mark the key value as clean so no try is made to update it - rec.markValueClean (dbm.getIDField ()); - rec.markForUpdate (); - tds.save (); - } + + stmt.executeUpdate (); + + } catch (Exception x) { + x.printStackTrace (); + throw x; } finally { - if (tds != null) try { - tds.close (); + if (stmt != null) try { + stmt.close (); } catch (Exception ignore) {} } + if (markMappingAsUpdated) dbm.notifyDataChange (); } @@ -579,14 +723,17 @@ public final class NodeManager { Statement st = null; try { Connection con = dbm.getConnection (); - st = con.createStatement (); - st.executeUpdate (new StringBuffer ("DELETE FROM ") + String str = new StringBuffer ("DELETE FROM ") .append(dbm.getTableName ()) .append(" WHERE ") .append(dbm.getIDField()) .append(" = ") .append(node.getID()) - .toString()); + .toString(); + st = con.createStatement (); + st.executeUpdate (str); + if (logSql) + app.logEvent ("### deleteNode: "+str); } finally { if (st != null) try { st.close (); @@ -1084,7 +1231,13 @@ public final class NodeManager { q.append ("WHERE "); q.append (idfield); q.append (" = "); - q.append (kstr); + if (dbm.needsQuotes (idfield)) { + q.append ("'"); + q.append (escape(kstr)); + q.append ("'"); + } else { + q.append (kstr); + } if (logSql) app.logEvent ("### getNodeByKey: "+q.toString()); diff --git a/src/helma/objectmodel/db/Property.java b/src/helma/objectmodel/db/Property.java index 01ff127e..d933c5b2 100644 --- a/src/helma/objectmodel/db/Property.java +++ b/src/helma/objectmodel/db/Property.java @@ -8,6 +8,7 @@ import java.util.*; import java.io.*; import java.text.*; import helma.objectmodel.*; +import java.sql.Timestamp; /** * A property implementation for Nodes stored inside a database. Basically @@ -19,13 +20,7 @@ public final class Property implements IProperty, Serializable, Cloneable { private String propname; private Node node; - private String svalue; - private boolean bvalue; - private long lvalue; - private double dvalue; - // protected String nvalueID; - private NodeHandle nhandle; - private Object jvalue; + private Object value; private int type; @@ -42,29 +37,31 @@ public final class Property implements IProperty, Serializable, Cloneable { case STRING: // try to convert from old format if (node.version < 7) - svalue = in.readUTF (); + value = in.readUTF (); else - svalue = (String) in.readObject (); + value = in.readObject (); break; case BOOLEAN: - bvalue = in.readBoolean (); + value = in.readBoolean () ? Boolean.TRUE : Boolean.FALSE; break; case INTEGER: + value = new Long (in.readLong ()); + break; case DATE: - lvalue = in.readLong (); + value = new Date (in.readLong ()); break; case FLOAT: - dvalue = in.readDouble (); + value = new Double (in.readDouble ()); break; case NODE: // try to convert from old format if (node.version > 4) - nhandle = (NodeHandle) in.readObject (); + value = (NodeHandle) in.readObject (); else - nhandle = new NodeHandle (new DbKey (null, in.readUTF ())); + value = new NodeHandle (new DbKey (null, in.readUTF ())); break; case JAVAOBJECT: - jvalue = in.readObject (); + value = in.readObject (); break; } } catch (ClassNotFoundException x) { @@ -78,26 +75,28 @@ public final class Property implements IProperty, Serializable, Cloneable { out.writeInt (type); switch (type) { case STRING: - out.writeObject (svalue); + out.writeObject (value); break; case BOOLEAN: - out.writeBoolean (bvalue); + out.writeBoolean (((Boolean) value).booleanValue()); break; case INTEGER: + out.writeLong (((Long) value).longValue()); + break; case DATE: - out.writeLong (lvalue); + out.writeLong (((Date) value).getTime()); break; case FLOAT: - out.writeDouble (dvalue); + out.writeDouble (((Double) value).doubleValue()); break; case NODE: - out.writeObject (nhandle); + out.writeObject (value); break; case JAVAOBJECT: - if (jvalue != null && !(jvalue instanceof Serializable)) + if (value != null && !(value instanceof Serializable)) out.writeObject (null); else - out.writeObject (jvalue); + out.writeObject (value); break; } } @@ -114,10 +113,10 @@ public final class Property implements IProperty, Serializable, Cloneable { dirty = true; } - public Property (String propname, Node node, Node value) { + public Property (String propname, Node node, Node valueNode) { this (propname, node); type = NODE; - nhandle = value == null ? null : value.getHandle (); + value = valueNode == null ? null : valueNode.getHandle (); dirty = true; } @@ -126,141 +125,115 @@ public final class Property implements IProperty, Serializable, Cloneable { } public Object getValue () { - switch (type) { - case STRING: - return svalue; - case BOOLEAN: - return new Boolean (bvalue); - case INTEGER: - return new Long (lvalue); - case FLOAT: - return new Double (dvalue); - case DATE: - return new Date (lvalue); - case NODE: - return null; - case JAVAOBJECT: - return jvalue; - } - return null; + return value; } - public void setStringValue (String value) { + public int getType () { + return type; + } + + public void setStringValue (String str) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = STRING; - this.svalue = value; + value = str; dirty = true; } - public void setIntegerValue (long value) { + public void setIntegerValue (long l) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = INTEGER; - this.lvalue = value; + value = new Long(l); dirty = true; } - public void setFloatValue (double value) { + public void setFloatValue (double d) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = FLOAT; - this.dvalue = value; + value = new Double(d); dirty = true; } - public void setDateValue (Date value) { + public void setDateValue (Date date) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = DATE; - this.lvalue = value == null ? 0 : value.getTime(); + value = date; dirty = true; } - public void setBooleanValue (boolean value) { + public void setBooleanValue (boolean bool) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; type = BOOLEAN; - this.bvalue = value; + value = bool ? Boolean.TRUE : Boolean.FALSE; dirty = true; } - public void setNodeValue (Node value) { + public void setNodeValue (Node node) { // value.checkWriteLock (); if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; // registerNode (value); type = NODE; - nhandle = value.getHandle (); + value = node == null ? null : node.getHandle (); dirty = true; } - public void setNodeHandle (NodeHandle value) { + public void setNodeHandle (NodeHandle handle) { if (type == NODE) unregisterNode (); - if (type == JAVAOBJECT) - this.jvalue = null; // registerNode (value); type = NODE; - nhandle = value; + value = handle; dirty = true; } public NodeHandle getNodeHandle () { - return nhandle; + if (type == NODE) + return (NodeHandle) value; + return null; } - + public void convertToNodeReference (DbMapping dbm) { - String id = getStringValue (); - if (id == null) - nhandle = null; - else - nhandle = new NodeHandle (new DbKey (dbm, id)); + if (value != null && !(value instanceof NodeHandle)) + value = new NodeHandle (new DbKey (dbm, value.toString ())); type = NODE; } - public void setJavaObjectValue (Object value) { + public void setJavaObjectValue (Object obj) { if (type == NODE) unregisterNode (); type = JAVAOBJECT; - this.jvalue = value; + value = obj; } /** - * tell a the value node that it is no longer used as a property. + * tell a the value node that it is no longer used as a property. * If this was the "main" property for the node, also remove all other references. */ protected void unregisterNode () { - Node nvalue = null; - if (nhandle != null) - nvalue = nhandle.getNode (node.nmgr); - + if (value == null || !(value instanceof NodeHandle)) + return; + NodeHandle nhandle = (NodeHandle) value; + Node nvalue = nhandle.getNode (node.nmgr); + DbMapping nvmap = null; Relation nvrel = null; if (node.dbmap != null) { nvmap = node.dbmap.getPropertyMapping (propname); nvrel = node.dbmap.getPropertyRelation (propname); } - + if (nvalue == null) return; - + nvalue.checkWriteLock (); // check if the property node is also a subnode // BUG: this doesn't work because properties for subnode/properties are never stored and therefore @@ -281,22 +254,20 @@ public final class Property implements IProperty, Serializable, Cloneable { public String getStringValue () { + if (value == null) + return null; switch (type) { case STRING: - return svalue; case BOOLEAN: - return bvalue ? "true" : "false"; - case DATE: - SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy HH:mm"); - return format.format (new Date (lvalue)); case INTEGER: - return Long.toString (lvalue); case FLOAT: - return Double.toString (dvalue); - case NODE: - return nhandle == null ? null : nhandle.getID (); case JAVAOBJECT: - return jvalue == null ? null : jvalue.toString (); + return value.toString (); + case DATE: + SimpleDateFormat format = new SimpleDateFormat ("dd.MM.yy hh:mm:ss"); + return format.format ((Date) value); + case NODE: + return ((NodeHandle) value).getID (); } return ""; } @@ -306,51 +277,66 @@ public final class Property implements IProperty, Serializable, Cloneable { } public long getIntegerValue () { - if (type == INTEGER) - return lvalue; - return 0; + if (type == INTEGER) + return ((Long) value).longValue (); + if (type == FLOAT) + return ((Double) value).longValue (); + if (type == BOOLEAN) + return ((Boolean) value).booleanValue() ? 1 : 0; + try { + return Long.parseLong (getStringValue()); + } catch (Exception x) { + return 0; + } } public double getFloatValue () { - if (type == FLOAT) - return dvalue; - return 0.0; + if (type == FLOAT) + return ((Double) value).doubleValue(); + if (type == INTEGER) + return ((Long) value).doubleValue (); + try { + return Double.parseDouble (getStringValue()); + } catch (Exception x) { + return 0.0; + } } public Date getDateValue () { - if (type == DATE) - return new Date (lvalue); + if (type == DATE) + return (Date) value; + return null; + } + + public Timestamp getTimestampValue () { + if (type == DATE && value != null) + return new Timestamp (((Date) value).getTime()); return null; } public boolean getBooleanValue () { - if (type == BOOLEAN) - return bvalue; + if (type == BOOLEAN) + return ((Boolean) value).booleanValue(); + if (type == INTEGER) + return !(0 == getIntegerValue()); return false; } public INode getNodeValue () { - - if (nhandle != null) { - Node n = nhandle.getNode (node.nmgr); - if (n != null) return n; + if (type == NODE && value != null) { + NodeHandle nhandle = (NodeHandle) value; + return nhandle.getNode (node.nmgr); } return null; } public Object getJavaObjectValue () { if (type == JAVAOBJECT) - return jvalue; + return value; return null; } - - public int getType () { - return type; - } - } - diff --git a/src/helma/objectmodel/db/WrappedNodeManager.java b/src/helma/objectmodel/db/WrappedNodeManager.java index 5a425bac..0499b821 100644 --- a/src/helma/objectmodel/db/WrappedNodeManager.java +++ b/src/helma/objectmodel/db/WrappedNodeManager.java @@ -110,6 +110,10 @@ import java.util.Vector; nmgr.evictNode (node); } + public void evictNodeByKey (Key key) { + nmgr.evictNodeByKey (key); + } + public void evictKey (Key key) { nmgr.evictKey (key); } diff --git a/src/helma/objectmodel/dom/XmlConverter.java b/src/helma/objectmodel/dom/XmlConverter.java index ba735de0..51c6472e 100644 --- a/src/helma/objectmodel/dom/XmlConverter.java +++ b/src/helma/objectmodel/dom/XmlConverter.java @@ -6,6 +6,7 @@ import java.util.*; import javax.xml.parsers.*; import org.w3c.dom.*; +import org.xml.sax.InputSource; import helma.objectmodel.*; import helma.util.SystemProperties; @@ -75,6 +76,15 @@ public class XmlConverter implements XmlConstants { } } + public INode convertFromString( String xml, INode helmaNode ) throws RuntimeException { + Document document = XmlUtil.parse (new InputSource (new StringReader (xml))); + if ( document!=null && document.getDocumentElement()!=null ) { + return convert( document.getDocumentElement(), helmaNode, new HashMap() ); + } else { + return helmaNode; + } + } + public INode convert( Element element, INode helmaNode, Map nodeCache ) { offset++; // previousNode is used to cache previous nodes with the same prototype @@ -82,17 +92,16 @@ public class XmlConverter implements XmlConstants { Object previousNode = null; if (DEBUG) debug("reading " + element.getNodeName() ); - helmaNode.setName( element.getNodeName() ); String prototype = props.getProperty(element.getNodeName()+"._prototype"); if ( prototype == null && !sparse ) prototype = "HopObject"; - // if we have a prototype (either explicit or implicit "hopobject"), + // if we have a prototype (either explicit or implicit "hopobject"), // set it on the Helma node and store it in the node cache. if ( prototype != null ) { + helmaNode.setName( element.getNodeName() ); helmaNode.setPrototype( prototype ); previousNode = nodeCache.put (prototype, helmaNode); } - // check attributes of the current element attributes(element, helmaNode, nodeCache); // check child nodes of the current element @@ -113,7 +122,7 @@ public class XmlConverter implements XmlConstants { private INode children( Element element, helma.objectmodel.INode helmaNode, Map nodeCache ) { NodeList list = element.getChildNodes(); int len = list.getLength(); - boolean nodeHasPrototype = helmaNode.getPrototype() != null; + boolean nodeIsInitialized = !nodeCache.isEmpty(); StringBuffer textcontent = new StringBuffer(); String domKey, helmaKey; for ( int i=0; i -1) { String prototype = helmaKey.substring (0, dot); INode node = (INode) nodeCache.get (prototype); - if (node != null) + if (node != null) { node.setString (helmaKey.substring(dot+1), attr.getNodeValue()); + } } else if (helmaNode.getPrototype() != null) { helmaNode.setString( helmaKey, attr.getNodeValue() ); } diff --git a/src/helma/objectmodel/dom/XmlUtil.java b/src/helma/objectmodel/dom/XmlUtil.java index e4c519ea..bd7bb63d 100644 --- a/src/helma/objectmodel/dom/XmlUtil.java +++ b/src/helma/objectmodel/dom/XmlUtil.java @@ -97,7 +97,8 @@ public class XmlUtil { int ct = childlist.getLength(); for ( int j=0; j1 ) { + converter = new XmlConverter (arguments[1].toString()); + } else { + converter = new XmlConverter (); + } + INode node = new helma.objectmodel.db.Node ( (String)null, (String)null, this.evaluator.engine.getApplication().getWrappedNodeManager() ); + INode result = converter.convertFromString (arguments[0].toString(),node); + return this.evaluator.engine.getNodeWrapper(result); + } catch ( NoClassDefFoundError e ) { + throw new EcmaScriptException("Can't load dom-capable xml parser."); + } catch ( RuntimeException f ) { + throw new EcmaScriptException(f.toString()); + } + } + } + } diff --git a/src/helma/servlet/AbstractServletClient.java b/src/helma/servlet/AbstractServletClient.java index c0dbc599..5ec01caf 100644 --- a/src/helma/servlet/AbstractServletClient.java +++ b/src/helma/servlet/AbstractServletClient.java @@ -49,6 +49,8 @@ public abstract class AbstractServletClient extends HttpServlet { uploadLimit = upstr == null ? 1024 : Integer.parseInt (upstr); // get cookie domain cookieDomain = init.getInitParameter ("cookieDomain"); + if (cookieDomain != null) + cookieDomain = cookieDomain.toLowerCase(); // get default encoding defaultEncoding = init.getInitParameter ("charset"); debug = ("true".equalsIgnoreCase (init.getInitParameter ("debug"))); @@ -73,7 +75,6 @@ public abstract class AbstractServletClient extends HttpServlet { protected void execute (HttpServletRequest request, HttpServletResponse response, byte method) { - Cookie[] cookies = request.getCookies(); RequestTrans reqtrans = new RequestTrans (method); // get app and path from original request path @@ -120,11 +121,12 @@ public abstract class AbstractServletClient extends HttpServlet { } // read cookies - if (cookies != null) { - for (int i=0; i < cookies.length;i++) try { + Cookie[] reqCookies = request.getCookies(); + if (reqCookies != null) { + for (int i=0; i < reqCookies.length;i++) try { // get Cookies - String nextKey = cookies[i].getName (); - String nextPart = cookies[i].getValue (); + String nextKey = reqCookies[i].getName (); + String nextPart = reqCookies[i].getValue (); if ("HopSession".equals (nextKey)) reqtrans.session = nextPart; else @@ -157,6 +159,14 @@ public abstract class AbstractServletClient extends HttpServlet { if (remotehost != null) reqtrans.set ("http_remotehost", remotehost); + // get the cookie domain to use for this response, if any. + String resCookieDomain = cookieDomain; + if (resCookieDomain != null) { + // check if cookieDomain is valid for this response. + // (note: cookieDomain is guaranteed to be lower case) + if (host != null && host.toLowerCase().indexOf (cookieDomain) == -1) + resCookieDomain = null; + } // check if we need to create a session id. also handle the // case that the session id doesn't match the remote host address if (reqtrans.session == null || !reqtrans.session.startsWith (remotehost)) { @@ -165,8 +175,8 @@ public abstract class AbstractServletClient extends HttpServlet { System.currentTimeMillis (), 36); Cookie c = new Cookie("HopSession", reqtrans.session); c.setPath ("/"); - if (cookieDomain != null) - c.setDomain (cookieDomain); + if (resCookieDomain != null) + c.setDomain (resCookieDomain); response.addCookie(c); } @@ -183,6 +193,16 @@ public abstract class AbstractServletClient extends HttpServlet { reqtrans.path = getPathInfo (request); ResponseTrans restrans = execute (reqtrans); + // set cookies + int ncookies = restrans.countCookies(); + if (restrans.countCookies() > 0) { + CookieTrans[] resCookies = restrans.getCookies (); + for (int i = 0; i < resCookies.length; i++) try { + Cookie c = resCookies[i].getCookie ("/", resCookieDomain); + response.addCookie(c); + } catch (Exception ignore) {} + } + // write response writeResponse (request, response, restrans); } catch (Exception x) { @@ -210,15 +230,6 @@ public abstract class AbstractServletClient extends HttpServlet { HttpServletResponse res, ResponseTrans hopres) { - int ncookies = hopres.countCookies(); - if (hopres.countCookies() > 0) { - CookieTrans[] cookies = hopres.getCookies (); - for (int i = 0; i < cookies.length; i++) try { - Cookie c = cookies[i].getCookie ("/", cookieDomain); - res.addCookie(c); - } catch (Exception ignore) {} - } - if (hopres.getETag() != null) { res.setHeader ("ETag", hopres.getETag()); } diff --git a/src/helma/util/HtmlEncoder.java b/src/helma/util/HtmlEncoder.java index 14916d41..29c587ad 100644 --- a/src/helma/util/HtmlEncoder.java +++ b/src/helma/util/HtmlEncoder.java @@ -363,11 +363,14 @@ public final class HtmlEncoder { if (i < l-2) { if (!insideMacroTag && '%' == str.charAt(i+1)) { // this is the beginning of a Helma macro tag - insideMacroTag = insideTag = true; - macroQuoteChar = '\u0000'; + if (!insideCodeTag) { + insideMacroTag = insideTag = true; + macroQuoteChar = '\u0000'; + } } else if ('!' == str.charAt(i+1) && '-' == str.charAt(i+2)) { // the beginning of an HTML comment? - insideComment = insideTag = (i