Reworked the sidebox implementation to depend less on the database and more

on XML config files...the implementation should now be much more customizable
and less klunky.  Added a provision for implementing "generic" (JSP-driven)
sideboxes.  Implemented the sidebox configure button on the front page
(finally!).  Implemented a random password generator class which will be used
in a future implementation of reminder-driven automatic forgotten-password
changing.  Fixed some minor funnies in SIG menu generation.
This commit is contained in:
Eric J. Bowersox
2001-11-04 05:57:58 +00:00
parent bf040973ba
commit 1c69955046
32 changed files with 2049 additions and 495 deletions
@@ -23,12 +23,8 @@ public interface SideBoxDescriptor
public abstract int getSequence();
public abstract String getParameter();
public abstract String getTitle(boolean anonymous);
public abstract String getDescription();
public abstract String getParamDescription();
public abstract String getClassname();
public abstract String getFactoryClassName();
} // end interface SideBoxDescriptor
+45 -44
View File
@@ -48,7 +48,48 @@ public class Startup
*--------------------------------------------------------------------------------
*/
private static Document loadConfiguration(String configname) throws ConfigException
private static String getEngineClassName(Document config) throws ConfigException
{
// Make sure the configuration is valid...
Element root = config.getDocumentElement();
if (!(root.getTagName().equals("venice-config")))
{ // not the correct root tag name
logger.fatal("config document is not a <venice-config/> document (root tag: <"
+ root.getTagName() + "/>)");
throw new ConfigException("document is not a <venice-config/> document",root);
} // end if
// Get the <engine/> section.
DOMElementHelper root_h = new DOMElementHelper(root);
Element engine_sect = root_h.getSubElement("engine");
if (engine_sect==null)
{ // no <engine/> section - bail out now!
logger.fatal("config document has no <engine/> section");
throw new ConfigException("no <engine/> section found in config file",root);
} // end if
// Get the classname out of that section.
DOMElementHelper engine_sect_h = new DOMElementHelper(engine_sect);
String rc = engine_sect_h.getSubElementText("classname");
if (rc==null)
{ // no <classname/> specified - bail out now!
logger.fatal("config document <engine/> section has no <classname/>");
throw new ConfigException("no <classname/> found in <engine/> section",engine_sect);
} // end if
return rc;
} // end getEngineClassName
/*--------------------------------------------------------------------------------
* External static methods (global functions)
*--------------------------------------------------------------------------------
*/
public static Document loadConfiguration(String configname) throws ConfigException
{
try
{ // create a simple DOM parser by using the Java XML parsing API
@@ -105,48 +146,8 @@ public class Startup
} // end loadConfiguration
private static String getEngineClassName(Document config) throws ConfigException
{
// Make sure the configuration is valid...
Element root = config.getDocumentElement();
if (!(root.getTagName().equals("venice-config")))
{ // not the correct root tag name
logger.fatal("config document is not a <venice-config/> document (root tag: <"
+ root.getTagName() + "/>)");
throw new ConfigException("document is not a <venice-config/> document",root);
} // end if
// Get the <engine/> section.
DOMElementHelper root_h = new DOMElementHelper(root);
Element engine_sect = root_h.getSubElement("engine");
if (engine_sect==null)
{ // no <engine/> section - bail out now!
logger.fatal("config document has no <engine/> section");
throw new ConfigException("no <engine/> section found in config file",root);
} // end if
// Get the classname out of that section.
DOMElementHelper engine_sect_h = new DOMElementHelper(engine_sect);
String rc = engine_sect_h.getSubElementText("classname");
if (rc==null)
{ // no <classname/> specified - bail out now!
logger.fatal("config document <engine/> section has no <classname/>");
throw new ConfigException("no <classname/> found in <engine/> section",engine_sect);
} // end if
return rc;
} // end getEngineClassName
/*--------------------------------------------------------------------------------
* External static methods (global functions)
*--------------------------------------------------------------------------------
*/
public static VeniceEngine createEngine(String configname) throws ConfigException, DataException
public static VeniceEngine createEngine(String configname, String app_root)
throws ConfigException, DataException
{
// load the configuration data
Document config = loadConfiguration(configname);
@@ -168,7 +169,7 @@ public class Startup
logger.debug("VeniceEngine created successfully");
// initialize the engine
engine.initialize(config);
engine.initialize(config,app_root);
// and return it
return engine;
@@ -89,6 +89,8 @@ public interface UserContext extends SearchMode
public abstract List getSideBoxList() throws DataException;
public abstract void addSideBox(int id) throws DataException;
public abstract List getConferenceHotlist() throws DataException;
public abstract boolean hasAdminAccess();
@@ -0,0 +1,26 @@
/*
* The contents of this file are subject to the Mozilla Public License Version 1.1
* (the "License"); you may not use this file except in compliance with the License.
* You may obtain a copy of the License at <http://www.mozilla.org/MPL/>.
*
* Software distributed under the License is distributed on an "AS IS" basis, WITHOUT
* WARRANTY OF ANY KIND, either express or implied. See the License for the specific
* language governing rights and limitations under the License.
*
* The Original Code is the Venice Web Communities System.
*
* The Initial Developer of the Original Code is Eric J. Bowersox <erbo@silcom.com>,
* for Silverwrist Design Studios. Portions created by Eric J. Bowersox are
* Copyright (C) 2001 Eric J. Bowersox/Silverwrist Design Studios. All Rights Reserved.
*
* Contributor(s):
*/
package com.silverwrist.venice.core;
public interface UserSideBoxDescriptor extends SideBoxDescriptor
{
public abstract void setSequence(int seq) throws DataException;
public abstract void remove() throws DataException;
} // end interface UserSideBoxDescriptor
@@ -25,7 +25,7 @@ import com.silverwrist.venice.htmlcheck.HTMLChecker;
public interface VeniceEngine extends SearchMode
{
public abstract void initialize(Document config) throws ConfigException, DataException;
public abstract void initialize(Document config, String app_root) throws ConfigException, DataException;
public abstract int getNumFeatures();
@@ -1237,24 +1237,32 @@ class SIGUserContextImpl implements SIGContext, SIGBackend
SIGData my_sig = getSIGData();
my_sig.testMembership(level,is_member);
// Prepare the message text to be sent to the user.
boolean is_pub = my_sig.isPublicSIG();
String msg = engine.getStockMessage(is_pub ? "invite-public" : "invite-private");
String signame = my_sig.getName();
msg = StringUtil.replaceAllInstances(msg,"$SIGNAME",signame);
msg = StringUtil.replaceAllInstances(msg,"$SIGALIAS",my_sig.getAlias());
if (!is_pub)
msg = StringUtil.replaceAllInstances(msg,"$JOINKEY",my_sig.getJoinKey());
msg = StringUtil.replaceAllInstances(msg,"$PERSONAL",personal_message);
msg = StringUtil.replaceAllInstances(msg,"$FULLNAME",user.realFullName());
String uname = user.realUserName();
msg = StringUtil.replaceAllInstances(msg,"$USERNAME",uname);
StringBuffer msg_buf = new StringBuffer(msg);
msg_buf.append("\n\n--\n").append(engine.getStockMessage("signature"));
// Prepare the subject line to be sent to the user.
String subject = engine.getStockMessage("subj-invite");
subject = StringUtil.replaceAllInstances(subject,"$SIGNAME",signame);
HashMap vars = new HashMap(5);
vars.put("signame",my_sig.getName());
subject = StringUtil.replaceAllVariables(subject,vars);
// Prepare the message text to be sent to the user.
String msg;
if (my_sig.isPublicSIG())
msg = engine.getStockMessage("invite-public");
else
{ // get the private invite message and set the join key variable
msg = engine.getStockMessage("invite-private");
vars.put("joinkey",my_sig.getJoinKey());
} // end else
// Set the remaining variables and replace them.
vars.put("sigalias",my_sig.getAlias());
vars.put("personal",personal_message);
vars.put("fullname",user.realFullName());
String uname = user.realUserName();
vars.put("username",uname);
msg = StringUtil.replaceAllVariables(msg,vars);
StringBuffer msg_buf = new StringBuffer(msg);
msg_buf.append("\n\n--\n").append(engine.getStockMessage("signature"));
// Get a SimpleEmailer object, set it up, and send it.
SimpleEmailer em = engine.createEmailer();
@@ -17,29 +17,41 @@
*/
package com.silverwrist.venice.core.impl;
import com.silverwrist.venice.core.SideBoxDescriptor;
import java.sql.*;
import org.apache.log4j.*;
import com.silverwrist.venice.core.*;
import com.silverwrist.venice.db.*;
class SideBoxDescriptorImpl implements SideBoxDescriptor
class SideBoxDescriptorImpl implements UserSideBoxDescriptor
{
/*--------------------------------------------------------------------------------
* Static data members
*--------------------------------------------------------------------------------
*/
private static Category logger = Category.getInstance(SideBoxDescriptorImpl.class);
/*--------------------------------------------------------------------------------
* Attributes
*--------------------------------------------------------------------------------
*/
private SideBoxDescriptor parent;
private int sequence;
private String param;
private int uid; // the user ID
private SideBoxDescriptor parent; // the master side box descriptor
private DataPool datapool; // the data pool used by this object
private int sequence; // the sequence number
/*--------------------------------------------------------------------------------
* Constructor
*--------------------------------------------------------------------------------
*/
SideBoxDescriptorImpl(SideBoxDescriptor parent, int sequence, String param)
SideBoxDescriptorImpl(int uid, SideBoxDescriptor parent, DataPool datapool, int sequence)
{
this.uid = uid;
this.parent = parent;
this.datapool = datapool;
this.sequence = sequence;
this.param = param;
} // end constructor
@@ -60,28 +72,85 @@ class SideBoxDescriptorImpl implements SideBoxDescriptor
} // end getSequence
public String getParameter()
public String getTitle(boolean anonymous)
{
return param;
return parent.getTitle(anonymous);
} // end getParameter
} // end getTitle
public String getDescription()
public String getFactoryClassName()
{
return parent.getDescription();
return parent.getFactoryClassName();
} // end getDescription
} // end getFactoryClassName
public String getParamDescription()
/*--------------------------------------------------------------------------------
* Implementations from interface UserSideBoxDescriptor
*--------------------------------------------------------------------------------
*/
public void setSequence(int seq) throws DataException
{
return parent.getParamDescription();
Connection conn = null;
} // end getParamDescription
try
{ // retrieve a connection from the datapool
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
public String getClassname()
// create the UPDATE statement and just execute it blind (if this sidebox is not in the list,
// the UPDATE is a no-op).
StringBuffer sql = new StringBuffer("UPDATE sideboxes SET sequence = ");
sql.append(seq).append(" WHERE uid = ").append(uid).append(" AND boxid = ").append(parent.getID());
sql.append(';');
stmt.executeUpdate(sql.toString());
this.sequence = seq;
} // end try
catch (SQLException e)
{ // this becomes a DataException
logger.error("DB error setting sidebox sequence: " + e.getMessage(),e);
throw new DataException("error setting sidebox sequence: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
} // end setSequence
public void remove() throws DataException
{
return parent.getClassname();
Connection conn = null;
} // end getClassname
try
{ // retrieve a connection from the datapool
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
// create the DELETE statement and just execute it blind (if this conference is not in the hotlist,
// the DELETE is a no-op).
StringBuffer sql = new StringBuffer("DELETE FROM sideboxes WHERE uid = ");
sql.append(uid).append(" AND boxid = ").append(parent.getID()).append(';');
stmt.executeUpdate(sql.toString());
} // end try
catch (SQLException e)
{ // this becomes a DataException
logger.error("DB error removing from sidebox list: " + e.getMessage(),e);
throw new DataException("error removing from sidebox list: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
} // end remove
} // end class SideBoxDescriptorImpl
@@ -202,13 +202,20 @@ class UserContextImpl implements UserContext, UserBackend
} // end if
message = StringUtil.replaceAllInstances(message,"$USERNAME",username);
message = StringUtil.replaceAllInstances(message,"$CONFNUM",String.valueOf(confirm_num));
// Replace the variables in the text.
HashMap vars = new HashMap(2);
vars.put("username",username);
vars.put("confnum",String.valueOf(confirm_num));
message = StringUtil.replaceAllVariables(message,vars);
String subject = engine.getStockMessage("email-confirm-subject");
if (subject==null)
subject = "Venice Email Confirmation";
// Create the emailer and send the message.
SimpleEmailer emailer = engine.createEmailer();
emailer.setTo(my_email);
emailer.setSubject("Venice Email Confirmation");
emailer.setSubject(subject);
emailer.setText(message);
emailer.send();
if (logger.isDebugEnabled())
@@ -974,12 +981,12 @@ class UserContextImpl implements UserContext, UserBackend
Statement stmt = conn.createStatement();
// retrieve the necessary rows from the sideboxes table
ResultSet rs = stmt.executeQuery("SELECT boxid, sequence, param FROM sideboxes WHERE uid = " + uid
ResultSet rs = stmt.executeQuery("SELECT boxid, sequence FROM sideboxes WHERE uid = " + uid
+ " ORDER BY sequence;");
while (rs.next())
{ // create the implementation objects and return them all
SideBoxDescriptor sbd = new SideBoxDescriptorImpl(engine.getMasterSideBoxDescriptor(rs.getInt(1)),
rs.getInt(2),rs.getString(3));
SideBoxDescriptor sbd = new SideBoxDescriptorImpl(uid,engine.getMasterSideBoxDescriptor(rs.getInt(1)),
datapool,rs.getInt(2));
rc.add(sbd);
} // end while
@@ -1002,6 +1009,65 @@ class UserContextImpl implements UserContext, UserBackend
} // end getSideBoxList
public void addSideBox(int id) throws DataException
{
if (engine.getMasterSideBoxDescriptor(id)==null)
throw new DataException("invalid sidebox ID: " + id);
Connection conn = null;
try
{ // retrieve a connection from the datapool
conn = datapool.getConnection();
Statement stmt = conn.createStatement();
stmt.executeUpdate("LOCK TABLES sideboxes WRITE;");
try
{ // do a quickie query to see if we're already in the sidebox list
StringBuffer sql = new StringBuffer("SELECT sequence FROM sideboxes WHERE uid = ");
sql.append(uid).append(" AND boxid = ").append(id).append(';');
ResultSet rs = stmt.executeQuery(sql.toString());
if (rs.next())
return; // already in sidebox list - this is a no-op
// find a sequence number for the new entry
sql.setLength(0);
sql.append("SELECT MAX(sequence) FROM sideboxes WHERE uid = ").append(uid).append(';');
rs = stmt.executeQuery(sql.toString());
if (!(rs.next()))
throw new InternalStateError("bogus query result on addSideBox");
int new_sequence = rs.getInt(1) + 100;
// add the new record
sql.setLength(0);
sql.append("INSERT INTO sideboxes (uid, sequence, boxid) VALUES (").append(uid).append(", ");
sql.append(new_sequence).append(", ").append(id).append(");");
stmt.executeUpdate(sql.toString());
} // end try
finally
{ // make sure the table is unlocked before we go
Statement ulk_stmt = conn.createStatement();
ulk_stmt.executeUpdate("UNLOCK TABLES;");
} // end finally
} // end try
catch (SQLException e)
{ // this becomes a DataException
logger.error("DB error adding to sidebox list: " + e.getMessage(),e);
throw new DataException("error adding to sidebox list: " + e.getMessage(),e);
} // end catch
finally
{ // make sure we release the connection before we go
if (conn!=null)
datapool.releaseConnection(conn);
} // end finally
} // end addSideBox
public List getConferenceHotlist() throws DataException
{
return ConferenceUserContextImpl.getUserHotlist(engine,this,datapool);
@@ -200,16 +200,58 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
class MasterSideBox implements SideBoxDescriptor
{
private int id;
private String description;
private String param_descr;
private String title;
private String anon_title;
private String classname;
MasterSideBox(ResultSet rs) throws SQLException
MasterSideBox(Element section) throws ConfigException
{
this.id = rs.getInt("boxid");
this.description = rs.getString("description");
this.param_descr = rs.getString("param_descr");
this.classname = rs.getString("classname");
try
{ // get the ID value for this sidebox
String tmp = section.getAttribute("id");
if (StringUtil.isStringEmpty(tmp))
{ // there's no
logger.fatal("<sidebox/> specified with no ID!");
throw new ConfigException("no ID specified in <sidebox/>",section);
} // end if
id = Integer.parseInt(tmp);
} // end try
catch (NumberFormatException e)
{ // sidebox ID not numeric
logger.fatal("<sidebox/> ID not numeric!");
throw new ConfigException("non-numeric ID specified in <sidebox/>",section);
} // end catch
DOMElementHelper section_h = new DOMElementHelper(section);
title = section_h.getSubElementText("title");
anon_title = section_h.getSubElementText("anon-title");
classname = section_h.getSubElementText("factory-class");
if (StringUtil.isStringEmpty(title) || StringUtil.isStringEmpty(classname))
{ // the configuration is not complete
logger.fatal("<sidebox/> config incomplete (missing <title/> or <factory-class/>)!");
throw new ConfigException("configuration of <sidebox/> is not complete!",section);
} // end if
try
{ // try to resolve the classname here so we can be sure it's cool
Class.forName(classname);
} // end try
catch (ClassNotFoundException e)
{ // the class can't be resolved - we are h0sed
logger.fatal("<sidebox/> config <factory-class/> not valid!");
throw new ConfigException("<factory-class/> in <sidebox/> is not a valid class!",section);
} // end catch
if (StringUtil.isStringEmpty(anon_title))
anon_title = title; // just set as the same in the event of difficulty
} // end constructor
@@ -225,25 +267,13 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end getSequence
public String getParameter()
public String getTitle(boolean anonymous)
{
return null;
return (anonymous ? anon_title : title);
} // end getParameter
} // end getTitle
public String getDescription()
{
return description;
} // end getDescription
public String getParamDescription()
{
return param_descr;
} // end getParamDescription
public String getClassname()
public String getFactoryClassName()
{
return classname;
@@ -471,12 +501,13 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
* Loads the Venice engine configuration from the named XML file and creates appropriate subobjects,
* such as the data pool.
*
* @param config The XML document conating the configuration information for the engine.
* @param config The XML document containing the configuration information for the engine.
* @param app_root The root directory of the Web application, which ends with a "/".
* @exception com.silverwrist.venice.core.ConfigException An error occurred during engine configuration;
* the engine could not be initialized.
* @exception com.silverwrist.venice.core.DataException The Venice engine data could not be loaded.
*/
public void initialize(Document config) throws ConfigException, DataException
public void initialize(Document config, String app_root) throws ConfigException, DataException
{
int i; // loop counter
@@ -501,8 +532,56 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end if
// Get the <database/> section.
DOMElementHelper root_h = new DOMElementHelper(root);
// Get the <engine/> section.
Element engine_sect = root_h.getSubElement("engine");
if (engine_sect==null)
{ // unable to find the engine section
logger.fatal("config document has no <engine/> section");
throw new ConfigException("no <engine/> section found in config file",root);
} // end if
DOMElementHelper engine_h = new DOMElementHelper(engine_sect);
// Get the name of the sidebox config file.
String sidebox_config = engine_h.getSubElementText("sidebox-config");
if (sidebox_config==null)
{ // unable to find the sidebox config file
logger.fatal("<engine/> section has no <sidebox-config/> element");
throw new ConfigException("no <sidebox-config/> element found in <engine/> section",engine_sect);
} // end if
if (!(sidebox_config.startsWith("/")))
sidebox_config = app_root + sidebox_config;
Document sidebox_cfg_file = Startup.loadConfiguration(sidebox_config);
Element sb_root = sidebox_cfg_file.getDocumentElement();
if (!(sb_root.getTagName().equals("sidebox-config")))
{ // the sidebox configuration file isn't the right type
logger.fatal("config document is not a <sidebox-config/> document (root tag: <"
+ sb_root.getTagName() + "/>)");
throw new ConfigException("document is not a <sidebox-config/> document",sb_root);
} // end if
NodeList sb_nodes = sb_root.getChildNodes();
ArrayList sidebox_tmp = new ArrayList();
for (i=0; i<sb_nodes.getLength(); i++)
{ // extract each sidebox section from the list and build a new master sidebox object
Node s = sb_nodes.item(i);
if ((s.getNodeType()==Node.ELEMENT_NODE) && (s.getNodeName().equals("sidebox")))
sidebox_tmp.add(new MasterSideBox((Element)s));
} // end for
// store the real master sidebox table as an array
sideboxes = (MasterSideBox[])(sidebox_tmp.toArray(new MasterSideBox[0]));
if (logger.isDebugEnabled())
logger.debug(sideboxes.length + " sidebox definitions loaded from database");
// Get the <database/> section.
Element db_sect = root_h.getSubElement("database");
if (db_sect==null)
{ // unable to find the database section
@@ -642,6 +721,9 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end catch
for (i=0; i<sideboxes.length; i++) // insert sideboxes into hashtable
sidebox_ids.put(new Integer(sideboxes[i].getID()),sideboxes[i]);
// Allocate the global parameter arrays.
gp_ints = new int[IPC_NUM_PARAMS];
@@ -671,17 +753,6 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
if (logger.isDebugEnabled())
logger.debug(max_value + " features loaded from database");
// load the master sidebox table
ArrayList sidebox_tmp = new ArrayList();
rs = stmt.executeQuery("SELECT * FROM refsidebox ORDER BY boxid;");
while (rs.next())
sidebox_tmp.add(new MasterSideBox(rs));
// store the real master sidebox table as an array
sideboxes = (MasterSideBox[])(sidebox_tmp.toArray(new MasterSideBox[0]));
if (logger.isDebugEnabled())
logger.debug(sideboxes.length + " sidebox definitions loaded from database");
// load the global defaults
loadDefaults(stmt);
@@ -702,9 +773,6 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
for (i=0; i<features.length; i++) // insert feature symbols into hashtable
feature_syms.put(features[i].getSymbol(),features[i]);
for (i=0; i<sideboxes.length; i++) // insert sideboxes into hashtable
sidebox_ids.put(new Integer(sideboxes[i].getID()),sideboxes[i]);
// Here is where we create the HTML Checker and all the goodies it relies on.
// Start by creating some of the subsidiary objects that get added to HTML Checker configs.
EmailRewriter email_rewriter = new EmailRewriter();
@@ -982,13 +1050,21 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
} // end if
message = StringUtil.replaceAllInstances(message,"$USERNAME",username);
message = StringUtil.replaceAllInstances(message,"$REMINDER",rs.getString(2));
// Replace the message variables.
HashMap vars = new HashMap(2);
vars.put("username",username);
vars.put("reminder",rs.getString(2));
message = StringUtil.replaceAllVariables(message,vars);
// Find the message subject.
String subject = getStockMessage("reminder-subject");
if (subject==null)
subject = "Venice Password Reminder Message";
// Create the emailer and send the message.
SimpleEmailer emailer = createEmailer();
emailer.setTo(email_addr);
emailer.setSubject("Venice Password Reminder Message");
emailer.setSubject(subject);
emailer.setText(message);
emailer.send();
if (logger.isDebugEnabled())
@@ -1074,17 +1150,17 @@ public class VeniceEngineImpl implements VeniceEngine, EngineBackend
logger.debug("...created userprefs");
// get the sidebox configuration for this user
rs = stmt.executeQuery("SELECT sideboxes.boxid, sideboxes.sequence, sideboxes.param FROM sideboxes, "
rs = stmt.executeQuery("SELECT sideboxes.boxid, sideboxes.sequence FROM sideboxes, "
+ "users WHERE sideboxes.uid = users.uid AND users.is_anon = 1;");
sql.setLength(0);
while (rs.next())
{ // set up to insert into the sideboxes table
if (sql.length()==0)
sql.append("INSERT INTO sideboxes (uid, boxid, sequence, param) VALUES ");
sql.append("INSERT INTO sideboxes (uid, boxid, sequence) VALUES ");
else
sql.append(", ");
sql.append("(").append(new_uid).append(", ").append(rs.getInt(1)).append(", ");
sql.append(rs.getInt(2)).append(", ").append(SQLUtil.encodeStringArg(rs.getString(3))).append(')');
sql.append(rs.getInt(2)).append(')');
} // end while