diff --git a/src/helma/objectmodel/db/ApplicationManager.java b/src/helma/objectmodel/db/ApplicationManager.java index ee78376a..515bcdfd 100644 --- a/src/helma/objectmodel/db/ApplicationManager.java +++ b/src/helma/objectmodel/db/ApplicationManager.java @@ -70,7 +70,7 @@ public class ApplicationManager { private void start (String appName) { IServer.getLogger().log ("Building application "+appName); try { - Application app = new Application (appName, Server.sysProps, hopHome); + Application app = new Application (appName, Server.sysProps, Server.dbProps, hopHome); applications.put (appName, app); // if we're running with the embedded web server, set app base uri to /appname if (server.websrv != null) @@ -104,8 +104,8 @@ public class ApplicationManager { try { IServer.getLogger().log ("Binding application "+appName); Application app = (Application) applications.get (appName); - if (server.websrv == null) { Naming.rebind ("//:"+port+"/"+appName, app); + if (server.websrv == null) { } else { AcmeServletClient servlet = new AcmeServletClient (app); server.websrv.addServlet ("/"+appName+"/", servlet); diff --git a/src/helma/objectmodel/db/Node.java b/src/helma/objectmodel/db/Node.java index 717fddea..e525f521 100644 --- a/src/helma/objectmodel/db/Node.java +++ b/src/helma/objectmodel/db/Node.java @@ -505,7 +505,9 @@ public class Node implements INode, Serializable { if (this.dbmap != dbmap) { this.dbmap = dbmap; primaryKey = null; - ((Transactor) Thread.currentThread()).visitCleanNode (this); + try { + ((Transactor) Thread.currentThread()).visitCleanNode (this); + } catch (ClassCastException ignore) {} } } diff --git a/src/helma/objectmodel/db/NodeManager.java b/src/helma/objectmodel/db/NodeManager.java index 612220ff..f0df64e5 100644 --- a/src/helma/objectmodel/db/NodeManager.java +++ b/src/helma/objectmodel/db/NodeManager.java @@ -26,6 +26,7 @@ public final class NodeManager { protected Application app; private CacheMap cache; + private Replicator replicator; protected DbWrapper db; @@ -52,6 +53,12 @@ public final class NodeManager { safe = new WrappedNodeManager (this); nullNode = new Node ("nullNode", "nullNode", null, safe); + String replicationUrl = props.getProperty ("replicationUrl"); + if (replicationUrl != null) + replicator = new Replicator (replicationUrl); + else + replicator = null; + // get the initial id generator value String idb = props.getProperty ("idBaseValue"); if (idb != null) try { @@ -950,6 +957,34 @@ public final class NodeManager { return cache.getEntryArray (); } + protected Replicator getReplicator () { + return replicator; + } + + public void replicateCache (Vector add, Vector delete) { + synchronized (cache) { + for (Enumeration en=add.elements(); en.hasMoreElements(); ) { + Node n = (Node) en.nextElement (); + DbMapping dbm = app.getDbMapping (n.getPrototype ()); + if (dbm != null) + dbm.lastDataChange = System.currentTimeMillis (); + n.setDbMapping (dbm); + n.nmgr = safe; + cache.put (n.getKey(), n); + } + for (Enumeration en=delete.elements(); en.hasMoreElements(); ) { + Node n = (Node) en.nextElement (); + DbMapping dbm = app.getDbMapping (n.getPrototype ()); + if (dbm != null) + dbm.lastDataChange = System.currentTimeMillis (); + n.setDbMapping (dbm); + n.nmgr = safe; + cache.put (n.getKey(), n); + evictNode (n); + } + } + } + } diff --git a/src/helma/objectmodel/db/Server.java b/src/helma/objectmodel/db/Server.java index c945f4e9..d9d1a0b6 100644 --- a/src/helma/objectmodel/db/Server.java +++ b/src/helma/objectmodel/db/Server.java @@ -38,6 +38,7 @@ import com.sleepycat.db.*; static String dbPropfile = "db.properties"; static String appsPropfile; static SystemProperties appsProps; + static SystemProperties dbProps; static int port = 5055; static int webport = 0; @@ -118,6 +119,8 @@ import com.sleepycat.db.*; File helper = new File (hopHome, "db.properties"); dbPropfile = helper.getAbsolutePath (); + dbProps = new SystemProperties (dbPropfile); + DbSource.setDefaultProps (dbProps); getLogger().log ("dbPropfile = "+dbPropfile); appsPropfile = sysProps.getProperty ("appsPropFile"); @@ -161,23 +164,6 @@ import com.sleepycat.db.*; try { - // set up dbSources - try { - dbProps = new SystemProperties (dbPropfile); - String sources = dbProps.getProperty ("sources", ""); - StringTokenizer st = new StringTokenizer (sources, ",; "); - String next = null; - while (st.hasMoreTokens ()) try { - next = st.nextToken (); - new DbSource (next); - } catch (Exception wrong) { - getLogger().log ("Error creating DbSource "+next); - getLogger().log ("Reason: "+wrong); - } - } catch (Exception x) { - getLogger().log ("Error loading data source properties: "+x); - } - // start embedded web server if port is specified if (webport > 0) { websrv = new Acme.Serve.Serve (webport, sysProps); @@ -212,10 +198,10 @@ import com.sleepycat.db.*; RMISocketFactory.setSocketFactory (factory); } - if (websrv == null) { + // if (websrv == null) { getLogger().log ("Starting server on port "+port); LocateRegistry.createRegistry (port); - } + // } // start application framework diff --git a/src/helma/objectmodel/db/Transactor.java b/src/helma/objectmodel/db/Transactor.java index 150fe63e..9ff1632a 100644 --- a/src/helma/objectmodel/db/Transactor.java +++ b/src/helma/objectmodel/db/Transactor.java @@ -127,6 +127,8 @@ public class Transactor extends Thread { int ins = 0, upd = 0, dlt = 0; int l = nodes.size (); + Replicator replicator = nmgr.getReplicator (); + for (Iterator i=nodes.values().iterator(); i.hasNext (); ) { Node node = (Node) i.next (); // update nodes in db @@ -135,17 +137,23 @@ public class Transactor extends Thread { nmgr.registerNode (node); // register node with nodemanager cache nmgr.insertNode (nmgr.db, txn, node); node.setState (Node.CLEAN); + if (replicator != null) + replicator.addNewNode (node); ins++; nmgr.app.logEvent ("inserted: Node "+node.getPrototype ()+"/"+node.getID ()); } else if (nstate == Node.MODIFIED) { nmgr.updateNode (nmgr.db, txn, node); node.setState (Node.CLEAN); + if (replicator != null) + replicator.addModifiedNode (node); upd++; nmgr.app.logEvent ("updated: Node "+node.getPrototype ()+"/"+node.getID ()); } else if (nstate == Node.DELETED) { // nmgr.app.logEvent ("deleted: "+node.getFullName ()+" ("+node.getName ()+")"); nmgr.deleteNode (nmgr.db, txn, node); nmgr.evictNode (node); + if (replicator != null) + replicator.addDeletedNode (node); dlt++; } else { // nmgr.app.logEvent ("noop: "+node.getFullName ()); diff --git a/src/helma/servlet/StandaloneServletClient.java b/src/helma/servlet/StandaloneServletClient.java index eaab2f3c..11c65144 100644 --- a/src/helma/servlet/StandaloneServletClient.java +++ b/src/helma/servlet/StandaloneServletClient.java @@ -37,7 +37,7 @@ public class StandaloneServletClient extends AbstractServletClient { File propfile = new File (serverProps); File hopHome = new File (propfile.getParent()); SystemProperties sysProps = new SystemProperties (propfile.getAbsolutePath()); - app = new Application (appName, sysProps, hopHome); + app = new Application (appName, sysProps, null, hopHome); app.start (); } catch (Exception x) { System.err.println ("Error starting Application "+appName+": "+x); diff --git a/src/helma/util/CacheMap.java b/src/helma/util/CacheMap.java index 18c375ab..13aebb96 100644 --- a/src/helma/util/CacheMap.java +++ b/src/helma/util/CacheMap.java @@ -219,6 +219,9 @@ public class CacheMap { return k; } + public String toString () { + return newTable.toString () + oldTable.toString () + hashCode (); + } } diff --git a/src/helma/xmlrpc/Base64.java b/src/helma/xmlrpc/Base64.java index 6d6380ed..86cf8e5f 100644 --- a/src/helma/xmlrpc/Base64.java +++ b/src/helma/xmlrpc/Base64.java @@ -1,130 +1,286 @@ +//////////////////////license & copyright header///////////////////////// +// // +// Base64 - encode/decode data using the Base64 encoding scheme // +// // +// Copyright (c) 1998 by Kevin Kelley // +// // +// 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, or contact the author: // +// // +// Kevin Kelley - 30718 Rd. 28, La Junta, CO, // +// 81050 USA. // +// // +////////////////////end license & copyright header/////////////////////// + package helma.xmlrpc; +import java.io.*; // needed only for main() method. + + /** - * Provides encoding of raw bytes to base64-encoded characters, and - * decoding of base64 characters to raw bytes. - * - * @author Kevin Kelley (kelley@iguana.ruralnet.net) - * @version 1.0 - * @date 06 August 1998 - */ +* Provides encoding of raw bytes to base64-encoded characters, and +* decoding of base64 characters to raw bytes. +* +* @author Kevin Kelley (kelley@ruralnet.net) +* @version 1.3 +* @date 06 August 1998 +* @modified 14 February 2000 +* @modified 22 September 2000 +*/ +public class Base64 { -public class Base64 +/** +* returns an array of base64-encoded characters to represent the +* passed data array. +* +* @param data the array of bytes to encode +* @return base64-coded character array. +*/ +static public char[] encode(byte[] data) { - /** - * returns an array of base64-encoded characters to represent the - * passed data array. - * - * @param data the array of bytes to encode - * @return base64-coded character array. - */ - - static public char[] encode(byte[] data) { - - char[] out = new char[((data.length + 2) / 3) * 4]; - // - // 3 bytes encode to 4 chars. Output is always an even - // multiple of 4 characters. - // - - for (int i = 0, index = 0; i < data.length; i += 3, - index += 4) { - boolean quad = false; - boolean trip = false; - int val = (0xFF & (int) data[i]); - - val <<= 8; - if ((i + 1) < data.length) { - val |= (0xFF & (int) data[i + 1]); - trip = true; - } - val <<= 8; - if ((i + 2) < data.length) { - val |= (0xFF & (int) data[i + 2]); - quad = true; - } - out[index + 3] = alphabet[(quad ? (val & 0x3F) : 64)]; - val >>= 6; - out[index + 2] = alphabet[(trip ? (val & 0x3F) : 64)]; - val >>= 6; - out[index + 1] = alphabet[val & 0x3F]; - val >>= 6; - out[index + 0] = alphabet[val & 0x3F]; - } - return out; + char[] out = new char[((data.length + 2) / 3) * 4]; + + // + // 3 bytes encode to 4 chars. Output is always an even + // multiple of 4 characters. + // + for (int i=0, index=0; i>= 6; + out[index+2] = alphabet[(trip? (val & 0x3F): 64)]; + val >>= 6; + out[index+1] = alphabet[val & 0x3F]; + val >>= 6; + out[index+0] = alphabet[val & 0x3F]; + } + return out; +} + + /** + * Decodes a BASE-64 encoded stream to recover the original + * data. White space before and after will be trimmed away, + * but no other manipulation of the input will be performed. + * + * As of version 1.2 this method will properly handle input + * containing junk characters (newlines and the like) rather + * than throwing an error. It does this by pre-parsing the + * input and generating from that a count of VALID input + * characters. + **/ +static public byte[] decode(char[] data) +{ + // as our input could contain non-BASE64 data (newlines, + // whitespace of any sort, whatever) we must first adjust + // our count of USABLE data so that... + // (a) we don't misallocate the output array, and + // (b) think that we miscalculated our data length + // just because of extraneous throw-away junk + + int tempLen = data.length; + for( int ix=0; ix 255) || codes[ data[ix] ] < 0 ) + --tempLen; // ignore non-valid chars and padding + } + // calculate required length: + // -- 3 bytes for every 4 valid base64 chars + // -- plus 2 bytes if there are 3 extra base64 chars, + // or plus 1 byte if there are 2 extra. + + int len = (tempLen / 4) * 3; + if ((tempLen % 4) == 3) len += 2; + if ((tempLen % 4) == 2) len += 1; + + byte[] out = new byte[len]; + + + + int shift = 0; // # of excess bits stored in accum + int accum = 0; // excess bits + int index = 0; + + // we now go through the entire array (NOT using the 'tempLen' value) + for (int ix=0; ix255)? -1: codes[ data[ix] ]; + + if ( value >= 0 ) // skip over non-code + { + accum <<= 6; // bits shift up by 6 each time thru + shift += 6; // loop, with new bits being put in + accum |= value; // at the bottom. + if ( shift >= 8 ) // whenever there are 8 or more shifted in, + { + shift -= 8; // write them out (from the top, leaving any + out[index++] = // excess at the bottom for next iteration. + (byte) ((accum >> shift) & 0xff); + } + } + // we will also have skipped processing a padding null byte ('=') here; + // these are used ONLY for padding to an even length and do not legally + // occur as encoded data. for this reason we can ignore the fact that + // no index++ operation occurs in that special case: the out[] array is + // initialized to all-zero bytes to start with and that works to our + // advantage in this combination. } - - - /** - * Returns an array of bytes which were encoded in the passed - * character array. - * - * @param data the array of base64-encoded characters - * @return decoded data array - */ - static public byte[] decode(byte[] data) { - int len = ((data.length + 3) / 4) * 3; - if (data.length > 0 && data[data.length - 1] == '=') - --len; - if (data.length > 1 && data[data.length - 2] == '=') - --len; - byte[] out = new byte[len]; - - int shift = 0; // # of excess bits stored in accum - int accum = 0; // excess bits - int index = 0; - - for (int ix = 0; ix < data.length; ix++) { - int value = codes[data[ix] & 0xFF]; // ignore high byte of char - if (value >= 0) { - // skip over non-code - accum <<= 6; // bits shift up by 6 each time thru - shift += 6; // loop, with new bits being put in - accum |= value; // at the bottom. - if (shift >= 8) { - // whenever there are 8 or more shifted in, - shift -= 8; // write them out (from the top, leaving any - // excess at the bottom for next iteration. - out[index++] = (byte)((accum >> shift) & 0xff); - } - } - } - - if (index != out.length) throw new RuntimeException("Error decoding BASE64 element: miscalculated data length!"); - - return out; + // if there is STILL something wrong we just have to throw up now! + if( index != out.length) + { + throw new Error("Miscalculated data length (wrote " + index + " instead of " + out.length + ")"); } - // - // code characters for values 0..63 - // + return out; +} - static private char[] alphabet = - - "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" - .toCharArray(); - // - // lookup table for converting base64 characters to value in range 0..63 - // +// +// code characters for values 0..63 +// +static private char[] alphabet = + "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/=" + .toCharArray(); - static private byte[] codes = new byte[256]; - - static { - for (int i = 0; i < 256; i++) - codes[i] = -1; - - for (int i = 'A'; i <= 'Z'; i++) - codes[i] = (byte)(i - 'A'); - - for (int i = 'a'; i <= 'z'; i++) - codes[i] = (byte)(26 + i - 'a'); +// +// lookup table for converting base64 characters to value in range 0..63 +// +static private byte[] codes = new byte[256]; +static { + for (int i=0; i<256; i++) codes[i] = -1; + for (int i = 'A'; i <= 'Z'; i++) codes[i] = (byte)( i - 'A'); + for (int i = 'a'; i <= 'z'; i++) codes[i] = (byte)(26 + i - 'a'); + for (int i = '0'; i <= '9'; i++) codes[i] = (byte)(52 + i - '0'); + codes['+'] = 62; + codes['/'] = 63; +} - for (int i = '0'; i <= '9'; i++) - codes[i] = (byte)(52 + i - '0'); - codes['+'] = 62; - codes['/'] = 63; + + +/////////////////////////////////////////////////// +// remainder (main method and helper functions) is +// for testing purposes only, feel free to clip it. +/////////////////////////////////////////////////// + +public static void main(String[] args) +{ + boolean decode = false; + + if (args.length == 0) { + System.out.println("usage: java Base64 [-d[ecode]] filename"); + System.exit(0); + } + for (int i=0; i 0) baos.write(buf, 0, count); + } + is.close(); + } + catch (Exception e) { e.printStackTrace(); } + + return baos.toByteArray(); +} + +private static char[] readChars(File file) +{ + CharArrayWriter caw = new CharArrayWriter(); + try + { + Reader fr = new FileReader(file); + Reader in = new BufferedReader(fr); + int count = 0; + char[] buf = new char[16384]; + while ((count=in.read(buf)) != -1) { + if (count > 0) caw.write(buf, 0, count); + } + in.close(); + } + catch (Exception e) { e.printStackTrace(); } + + return caw.toCharArray(); +} + +private static void writeBytes(File file, byte[] data) { + try { + OutputStream fos = new FileOutputStream(file); + OutputStream os = new BufferedOutputStream(fos); + os.write(data); + os.close(); + } + catch (Exception e) { e.printStackTrace(); } +} + +private static void writeChars(File file, char[] data) { + try { + Writer fos = new FileWriter(file); + Writer os = new BufferedWriter(fos); + os.write(data); + os.close(); + } + catch (Exception e) { e.printStackTrace(); } +} +/////////////////////////////////////////////////// +// end of test code. +/////////////////////////////////////////////////// + +} diff --git a/src/helma/xmlrpc/WebServer.java b/src/helma/xmlrpc/WebServer.java index 97989d30..f6bbd219 100644 --- a/src/helma/xmlrpc/WebServer.java +++ b/src/helma/xmlrpc/WebServer.java @@ -399,7 +399,7 @@ class Connection implements Runnable { private void parseAuth (String line) { try { - byte[] c = Base64.decode (line.substring (21).getBytes()); + byte[] c = Base64.decode (line.substring (21).toCharArray ()); String str = new String (c); int col = str.indexOf (":"); user = str.substring (0, col); diff --git a/src/helma/xmlrpc/XmlRpc.java b/src/helma/xmlrpc/XmlRpc.java index d1c72459..833f6ea6 100644 --- a/src/helma/xmlrpc/XmlRpc.java +++ b/src/helma/xmlrpc/XmlRpc.java @@ -504,7 +504,7 @@ public abstract class XmlRpc extends HandlerBase { } break; case BASE64: - value = Base64.decode (cdata.getBytes()); + value = Base64.decode (cdata.toCharArray ()); break; case STRING: value = cdata;