db-torque-user mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From "Vitzethum, Daniel" <Daniel.Vitzet...@gigatronik.com>
Subject Introducing "JoinHelper"
Date Mon, 06 Dec 2004 08:52:18 GMT
Hello list,

currently we are working with Torque and are trying to
add a litle bit functionality. From our point of view,
it is not sufficient to have join methods generated for
two tables. In many cases, we need three or even more
tables to retrieve data that should be stored back later -
what makes it hard to use database views.

Facing that problem, we tried to write a helper class that
could deal with multiple joins and builds an object tree
from the result set dynamically via reflection, one instance
per "object".

There are still some problems, but by now it seems to work
quite well, at least for common cases.

My question to the reader's of the list and torque's people:
is anything like our JoinHelper existing, even in a beta
version? Or are you interested in our development, thought as
contribution to Torque?

Here's an example of how to use JoinHelper. I assume that
Publisher to Author to Book are both 1:n relationships.
("Publisher has Authors has Books")

  ---snip---
  // describe the participating peer classes
  Class[] peers = new Class[]
    {PublisherPeer.class, AuthorPeer.class,BookPeer.class};

  // announce the join conditions, foreign keys (n-side) first!
  Criteria crit = new Criteria();
  crit.addJoin(AuthorPeer.FK_PUBL_ID, PublisherPeer.MA_ID);
  crit.addJoin(BookPeer.FK_AUTHOR_ID, AuthorPeer.AUTHOR_ID);
  // some additional criteria
  crit.add(...);

  List data = new JoinHelper().doSelectCustomJoin(crit, peers);
  ---snip---

That will return a List with n Publisher instances, filled
with m Author instances, each filled with o Book instances.
Outer joins will _not_ be performed.

It is _possible_ to use JoinHelper even if more than one
relationship exists between the same two entities, regardless
of which direction.

It is _not_ possible to use the same table twice or more due to
limitations of Criteria, which uses a UniqueList for select
columns. That's where I need your help later...


So far,
I'm looking forward to your feedback!
Below is the java source code of join helper for testing purposes.
By now, method comments are a bit rough. Will be improved...

Enjoy,

Daniel
(with thanks to my colleague, Robert)

P.S.: I don't claim completeness - please ask if s.th. is unclear!

-------JoinHelper.java-------
/*
 * This is a contribution to Apache's Torque framework
 * that comes with absolutely no warranty. As it is a
 * beta version, it is intended for testing and evaluation
 * purposes, but is free to be modified.
 * 
 * Feedback requested to
 * daniel.vitzethum ( a t ) gigatronik.com
 */
package com.gigatronik.framework.data.torque;

import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.Hashtable;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.StringTokenizer;

import org.apache.commons.lang.StringUtils;
import org.apache.torque.TorqueException;
import org.apache.torque.om.BaseObject;
import org.apache.torque.om.ObjectKey;
import org.apache.torque.util.BasePeer;
import org.apache.torque.util.Criteria;

import com.workingdogs.village.Record;

/**
 * <p>A helper class for Torque that provides support for the retrieval
 * of several (> 2) joined tables (refer to the exclusions below!) and
 * creation of an object tree from it.
 * 
 * <p>Motivation is the fact that Torque soleley generates join methods
 * over two tables (that have a foreign key relationship). Now it is
 * possible to join more tables and retrieve a valid object tree with
 * unified instances.
 * 
 * <p>Exclusions:<br />
 * By now it is not possible to perform outer joins or self joins. The
 * results may be invalid (or exceptions) in these cases!
 * 
 * <p>In opposition, JoinHelper can deal with multiple relations
 * between two entities, for example when two foreign keys of table A
 * exist in table B.
 * 
 * <p>You can turn debug output on by setting JoinHelper.debug to true.
 *  
 * @author D. Vitzethum
 * @author R. Schulze
 * @version $Id: JoinHelper.java,v 1.2 2004/12/01 17:22:03 Vitzethum Exp
$
 */
public class JoinHelper {

	public static boolean debug = false;
    private String defaultPackage;
    
    private static final String LEFT_CLASS = "leftClass";
    private static final String RIGHT_CLASS = "rightClass";
    private static final String LEFT_FIELD_NAME = "leftFieldName";
    private static final String LINK_METHOD_LEFT =
"linkMethodLeftClass";
    private static final String LINK_METHOD_RIGHT =
"linkMethodRightClass";
    private static final String INIT_METHOD_LEFT =
"initMethodLeftClass";
    private static final String INIT_METHOD_RIGHT =
"initMethodRightClass";
    
    private List joinInformation;
    
    protected static Logger logger =
Logger.getLogger(JoinHelper.class.getName());
    
	/**
	 * <p>Dynamically do a Join and consider a variable(!) number of
	 * peers. Uses reflection. Still experimental! RTFM! Check the
	 * results properly.
	 * 
	 * <p>Note that the criteria object has to know the join clauses
	 * when passed! This is performed by calling
	 * <code>criteria.addJoin(String, String)</code>
	 * for each needed join. For example, providing three peers
needs
	 * two join clauses.
	 * 
	 * <p><strong>Important:</strong><br />
	 * If two relationships exist between two entities, the left
join 
	 * argument will be considered as the n-side! This will affect
the
	 * establishment of object linkage!
	 * 
	 * <p>The array of peers has to contain one Class object for
each
	 * peer that is involved. The base peer (e.g. the base table
from
	 * Db) has to be passed <strong>first</strong>. The resulting
List
	 * will contain objects of this peer's OM class with links to
the
	 * related objects.
	 * 
	 * <p><strong>Example:</strong><br>
	 * Calling this method like
	 * <pre>
	 * 	Class[] peers = new Class[] {PersonPeer.class,
GroupPeer.class, DepartmentPeer.class};
	 *  criteria.addJoin(PersonPeer.PERSON_ID, GroupPeer.GROUP_ID);
	 *	criteria.addJoin(GroupPeer.GROUP_ID,
DepartmentPeer.DEPT_ID);
	 *	List result = JoinHelper.doSelectCustomJoin(criteria,
peers);
	 * </pre>
	 * returns a List with the object model, containing instances of
	 * <code>Person</code>, associated with related instances of
	 * <code>Group</code> objects, which again have associated
	 * <code>Department</code> objects. These are available by
calling
	 * e.g. <code>aPerson.getGroup().getDepartment</code>.
	 *  
	 * @param c
	 * 			the Criteria, already containing the
join clause(s)
	 * @param peers
	 * 			the Classes of the peers, base table
first
	 * 			(<code>PersonPeer.class</code>,
according to the
	 * 			example above)
	 * @return
	 * 			a List containing the base om classes
	 * 			(e.g. <code>Person</code>s) with
associated objects
	 * @throws
	 *         TorqueException
	 */
    public List doSelectCustomJoin(Criteria c, Class[] peers) throws
TorqueException {
		Method method;
		
		debug("=== ENTERING ===", false);
		
		if(peers.length < 2) {
		    throw new TorqueException("insufficient number of
peer classes specified");
		}
		String classNameOfBasePeer = peers[0].getName();
		this.defaultPackage = classNameOfBasePeer.substring(0,
classNameOfBasePeer.lastIndexOf(".")+1);
		try {
	        this.joinInformation = getJoinInformation(c);
	        //caluclate the offsets for extracting the objects of
the record rows & modify criteria object c
		    List offsets = calculateOffsets(c, peers);
 			//select the record rows
			List rows = BasePeer.doSelect(c);
			// a list containing Sets, one per TO type
			List _tos = new ArrayList();
			
			if (rows.size() > 0) {
				// process each raw row and build object
tree from it
				for (int i = 0; i < rows.size(); i++) {
					Record row = (Record)
rows.get(i);
					debug("Processing row no " +
(i+1) + ": " + row.toString(), false);
					//extract DTOs & return a Map
containin the objects to be linked
					Map objectsToLink =
extractDTOs(peers, offsets, _tos, row);
					//link the objects
					linkObjects(objectsToLink);
				}
				return new ArrayList(((Map)
_tos.get(0)).values());
			}
			return new ArrayList();
			
		} catch (Exception e) {
			if (e instanceof TorqueException) {
				throw (TorqueException) e;
			}
			throw new TorqueException(e.getClass().getName()
+ ": " + e.getMessage(), e);
		} finally {
			debug("=== LEAVING  ===", false);
		}
	}
    
    //-----------------------------------------------------------------
    // private methods
    //-----------------------------------------------------------------

    /**
     * @param objectsToLink
     * @throws Exception
     */
    private void linkObjects(Map objectsToLink) throws Exception {
        Iterator iter = this.joinInformation.iterator();
        while(iter.hasNext()) {
            boolean doLinkObjectOfTypeLeftClass = true;
            boolean doLinkObjectOfTypeRightClass = true;
            
            Map currentJoinInformation = (Map) iter.next();
            Class leftClass = (Class)
currentJoinInformation.get(JoinHelper.LEFT_CLASS);
            Class rightClass = (Class)
currentJoinInformation.get(JoinHelper.RIGHT_CLASS);
            Method linkMethodLeftClass = (Method)
currentJoinInformation.get(JoinHelper.LINK_METHOD_LEFT);
            Method linkMethodRightClass = (Method)
currentJoinInformation.get(JoinHelper.LINK_METHOD_RIGHT);
            String leftSideField = (String)
currentJoinInformation.get(LEFT_FIELD_NAME);
            Object objectOfTypeLeftClass = objectsToLink.get(leftClass);
            Object objectOfTypeRightClass =
objectsToLink.get(rightClass);
            
            if (linkMethodLeftClass == null || linkMethodRightClass ==
null) {
            	debug("Could not link " + leftClass + " and " +
rightClass, true);
            	return;
            }
            
 
if(linkMethodLeftClass.getName().substring(0,3).equals("add")) {
                List dummyCollection = null;
                
                String rightClassName = rightClass.getName();
                int dotIndex = rightClassName.lastIndexOf(".");
                rightClassName = rightClassName.substring(dotIndex+1);
                
                Method getMethod = getGetMethod(leftClass,
rightClassName, leftSideField);
                try {
                    dummyCollection = (List)
getMethod.invoke(objectOfTypeLeftClass, null);
                }
                catch(Exception ex) {
                    System.out.println("exception thrown:
"+ex.getMessage());
                    throw ex;
                }
                if(dummyCollection.contains(objectOfTypeRightClass)) {
                    doLinkObjectOfTypeLeftClass = false;
                }
            }
            
 
if(linkMethodRightClass.getName().substring(0,3).equals("add")) {
                List dummyCollection = null;
                
                String leftClassName = leftClass.getName();
                int dotIndex = leftClassName.lastIndexOf(".");
                leftClassName = leftClassName.substring(dotIndex+1);
                
                Method getMethod = getGetMethod(rightClass,
leftClassName, leftSideField);
                try {
                    dummyCollection = (List)
getMethod.invoke(objectOfTypeRightClass, null);
                }
                catch(Exception ex) {
                    System.out.println("exception thrown:
"+ex.getMessage());
                    throw ex;
                }
                if(dummyCollection.contains(objectOfTypeLeftClass)) {
                    doLinkObjectOfTypeRightClass = false;
                }
            }
            try {
                if(doLinkObjectOfTypeLeftClass) {
                    linkMethodLeftClass.invoke(objectOfTypeLeftClass,
new Object[] {objectOfTypeRightClass});
                }
                if(doLinkObjectOfTypeRightClass) {
                    linkMethodRightClass.invoke(objectOfTypeRightClass,
new Object[] {objectOfTypeLeftClass});
                }
            }
            catch(Exception ex) {
                debug("exception thrown: " + ex.getMessage(), true);
                throw ex;
            }
            debug("Linked " + leftClass + " and " + rightClass, false);
        }
    }

    /**
     * Extract the table name and column name information from
     * criteria's joins.
     * @param c
     * @return
     * @throws Exception
     */
    private List getJoinInformation(Criteria c) throws Exception {
        List joinInformation = new ArrayList();
        List leftSidesOfJoins = c.getJoinL();
        List rightSidesOfJoins = c.getJoinR();
        for(int i=0; i<leftSidesOfJoins.size(); i++) {
            Map currentJoinInformation = new HashMap();
            String leftSideOfCurrentJoin = (String)
leftSidesOfJoins.get(i);
            String rightSideOfCurrentJoin = (String)
rightSidesOfJoins.get(i);
            try {
                String leftSideOfCurrentJoinClassName =
leftSideOfCurrentJoin.substring(0, leftSideOfCurrentJoin.indexOf("."));
                leftSideOfCurrentJoinClassName =
this.makeJavaName(leftSideOfCurrentJoinClassName);
                String leftSideField =
leftSideOfCurrentJoin.substring(leftSideOfCurrentJoin.indexOf(".") + 1,
leftSideOfCurrentJoin.length());
                leftSideField = this.makeJavaName(leftSideField);

                String rightSideOfCurrentJoinClassName =
rightSideOfCurrentJoin.substring(0,
rightSideOfCurrentJoin.indexOf("."));
                rightSideOfCurrentJoinClassName =
this.makeJavaName(rightSideOfCurrentJoinClassName);
                // left side has to be the "n" entity, skip right field
name
                //String rightSideField =
rightSideOfCurrentJoin.substring(rightSideOfCurrentJoin.indexOf(".") +
1, rightSideOfCurrentJoin.length());
                //rightSideField = this.makeJavaName(rightSideField);

                // create class objects for joined classes
	            Class leftSideOfCurrentJoinClass = 
	                Class.forName(defaultPackage +
leftSideOfCurrentJoinClassName);
	            Class rightSideOfCurrentJoinClass = 
	                Class.forName(defaultPackage +
rightSideOfCurrentJoinClassName);
	            Method linkMethodForLeftSidedJoinClass =
getLinkMethod(leftSideOfCurrentJoinClass,
rightSideOfCurrentJoinClassName, true, leftSideField);
	            Method linkMethodForRightSidedJoinClass =
getLinkMethod(rightSideOfCurrentJoinClass,
leftSideOfCurrentJoinClassName, false, leftSideField);
	            
	            currentJoinInformation.put(LEFT_CLASS,
leftSideOfCurrentJoinClass);
	            currentJoinInformation.put(RIGHT_CLASS,
rightSideOfCurrentJoinClass);
	            currentJoinInformation.put(LINK_METHOD_LEFT,
linkMethodForLeftSidedJoinClass);
	            currentJoinInformation.put(LINK_METHOD_RIGHT,
linkMethodForRightSidedJoinClass);
	            currentJoinInformation.put(LEFT_FIELD_NAME,
leftSideField);
	            
	            Method initMethodForLeftSidedJoinClass =
getInitMethod(leftSideOfCurrentJoinClass,
rightSideOfCurrentJoinClassName, leftSideField);
	            Method initMethodForRightSidedJoinClass =
getInitMethod(rightSideOfCurrentJoinClass,
leftSideOfCurrentJoinClassName, leftSideField);
	            currentJoinInformation.put(INIT_METHOD_LEFT,
initMethodForLeftSidedJoinClass);
	            currentJoinInformation.put(INIT_METHOD_RIGHT,
initMethodForRightSidedJoinClass);
	            
	            joinInformation.add(i, currentJoinInformation);
            }
            catch(Exception ex) {
                debug("Exception:\n" + ex, true);
                throw ex;
            }
        }
        return joinInformation;
    }

	/**
	 * Find the approp. setter oder adder method for the given case.
	 * Left side of join searches for "set" first, which is the "1"
	 * side of a "1:n" relationship.
	 *  
	 * @param leftSideOfCurrentJoinClass
	 * @param leftSideOfCurrentJoinMethodTrunk
	 * @param isLeftSide indicates the "n" cardinality
	 * @param leftSideField the foreign key if multiple relations
have to be checked
	 */
	private Method getLinkMethod(Class classSpec, String
methodNameTrunk, boolean isLeftSide, String leftSideField) {
	    Method returnMethod;
	    Method[] methods = classSpec.getMethods();
	    String firstMethodToSearch, secondMethodToSearch;
	    String thirdMethodToSearch, fourthMethodToSearch;
	    
	    if (isLeftSide) {
	    	firstMethodToSearch = "set" + methodNameTrunk;
	    	secondMethodToSearch = "add" + methodNameTrunk;
	    	thirdMethodToSearch = "set" + methodNameTrunk +
"RelatedBy" + leftSideField;
	    	fourthMethodToSearch = "add" + methodNameTrunk +
"RelatedBy" + leftSideField;
	    } else {
	    	firstMethodToSearch = "add" + methodNameTrunk;
	    	secondMethodToSearch = "set" + methodNameTrunk;
	    	thirdMethodToSearch = "add" + methodNameTrunk +
"RelatedBy" + leftSideField;
	    	fourthMethodToSearch = "set" + methodNameTrunk +
"RelatedBy" + leftSideField;
	    }
	    for(int i=0; i<methods.length; i++) {
	        if(methods[i].getName().equals(firstMethodToSearch)) {
	            return methods[i];
	        }
	    }    
	    for(int i=0; i<methods.length; i++) {
	        if(methods[i].getName().equals(secondMethodToSearch)) {
	            return methods[i];
	        }
	    }
	    for(int i=0; i<methods.length; i++) {
	        if(methods[i].getName().equals(thirdMethodToSearch)) {
	            return methods[i];
	        }
	    }
	    for(int i=0; i<methods.length; i++) {
	        if(methods[i].getName().equals(fourthMethodToSearch)) {
	            return methods[i];
	        }
	    }
	    debug("Warning: no method for linkage found on
'"+classSpec+"', neither '"+firstMethodToSearch+"' nor
'"+secondMethodToSearch+"'!", true);
	    return null;
	}

	/**
	 * @param leftSideOfCurrentJoinClass
	 * @param leftSideOfCurrentJoinMethodTrunk
	 */
	private static Method getGetMethod(Class classSpec, String
methodNameTrunk, String leftSideField) {
	    Method returnMethod;
	    Method[] methods = classSpec.getMethods();
	    for(int i=0; i<methods.length; i++) {
	
if((methods[i].getName().equals("get"+methodNameTrunk+"s")) && 
	           (methods[i].getParameterTypes().length == 0)) {
	            return methods[i];
	        }
	    }
	    // try other name if nothing found by now
	    for(int i=0; i<methods.length; i++) {
	
if((methods[i].getName().equals("get"+methodNameTrunk+"sRelatedBy" +
leftSideField)) && 
	           (methods[i].getParameterTypes().length == 0)) {
	            return methods[i];
	        }
	    }
	    return null;
	}

	/**
	 * @param leftSideOfCurrentJoinClass
	 * @param leftSideOfCurrentJoinMethodTrunk
	 */
	private static Method getInitMethod(Class classSpec, String
methodNameTrunk, String leftSideField) {
	    Method returnMethod;
	    Method[] methods =
classSpec.getSuperclass().getDeclaredMethods();
	    for(int i=0; i<methods.length; i++) {
	
if((methods[i].getName().equals("init"+methodNameTrunk+"s")) &&
	          (methods[i].getParameterTypes().length == 0)) { 
	            return methods[i];
	        }
	    }
	    // try other name if nothing found by now
	    for(int i=0; i<methods.length; i++) {
	
if((methods[i].getName().equals("init"+methodNameTrunk+"sRelatedBy" +
leftSideField)) &&
	          (methods[i].getParameterTypes().length == 0)) { 
	            return methods[i];
	        }
	    }
	    return null;
	}

	/**
	 * Create one instance per PK and type from the result set.
     * @param peers
     * @param offsets
     * @param _tos
     * @param row
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
	 * @throws TorqueException
     */
    private Map extractDTOs(Class[] peers, List offsets, List _tos,
Record row)
    	throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, TorqueException
	{
        Method method;
        Map extractedObjects = new HashMap();
        // build TOs from result record (including base peers)
        for (int j = 0; j < peers.length; j++) {
        	Map tosOfThisType;
        	if (_tos.size() <= j) {
        		tosOfThisType = new Hashtable();
        		_tos.add(tosOfThisType);
        	} else {
        		tosOfThisType = (Map) _tos.get(j);
        	}
        	Class peerClass = peers[j];
        	method = peerClass.getMethod("getOMClass", null);
        	Class omClass = (Class) method.invoke(peerClass, null);

        	Integer offset = (Integer) offsets.get(j);
        	method = peerClass.getMethod(
        			"row2Object", new Class[] {Record.class,
int.class, Class.class});
        	BaseObject _to = (BaseObject) method.invoke(
        			peerClass, new Object[] {row, offset,
omClass});
        	
        	ObjectKey pk = _to.getPrimaryKey();
        	if (pk == null) {
        		throw new TorqueException("Could not handle
objects that dont't have a primary key: class='" +
_to.getClass().getName() + "', pk='" + pk + "'");
        	}
        	if (! tosOfThisType.containsKey(pk)) {
        	    tosOfThisType.put(pk, _to);
        		initPossiblyExistingCollection(_to, omClass);
        	    //tosOfThisType.put(String.class, _to.getClass());
        		extractedObjects.put(omClass, _to);
        		debug("Created new object " +
_to.getClass().getName() + " with PK " + pk + ".", false);
        	} else {
        	    extractedObjects.put(omClass,
tosOfThisType.get(pk));
        	    debug(" (Object " + _to.getClass().getName() + "
with PK " + pk + " already exists.)", false);
        	}
        }
        return extractedObjects;
    } 

	/**
	 * @param _to
	 * @param omClass
	 */
	private void initPossiblyExistingCollection(BaseObject _dto,
Class omClass) {
	    Iterator iter = this.joinInformation.iterator();
	    while(iter.hasNext()) {
	        Map currentJoinInformation = (Map) iter.next();
	        Method initMethod = null;
	        if(omClass == currentJoinInformation.get(LEFT_CLASS)) {
	            initMethod = 
	                (Method)
currentJoinInformation.get(INIT_METHOD_LEFT);
	        }
	        else if(omClass ==
currentJoinInformation.get(RIGHT_CLASS)) {
	            initMethod = 
	                (Method)
currentJoinInformation.get(INIT_METHOD_RIGHT);
	        }
	        if(initMethod != null) {
		        try {
		        	initMethod.setAccessible(true);
		            initMethod.invoke(_dto, null);
		            initMethod.setAccessible(false);
		        } catch (Exception ex) {
		            debug("Exception caught in
JoinHelper.initPossiblyExistingCollection(BaseObject, Class):\n"
		            		+ ex.getMessage(), true);
		        }
	        }    
	    }
	}

	/**
	 * Calculate the offsets defined by the number of cols of each
	 * involved peer in order to make objects from a raw record. 
     * @param c
     * @param peers
     * @throws NoSuchMethodException
     * @throws IllegalAccessException
     * @throws InvocationTargetException
     * @throws NoSuchFieldException
     */
    private List calculateOffsets(Criteria c, Class[] peers)
    	throws NoSuchMethodException, IllegalAccessException,
InvocationTargetException, NoSuchFieldException
	{
        Method method;
        Class criteriaClass = c.getClass();
        List offsets = new ArrayList();
        offsets.add(new Integer(1));
        
        // call addSelectColumns on each Peer
        for (int i = 0; i < peers.length; i++) {
        	Class peerClass = peers[i];
        	method = peerClass.getMethod("addSelectColumns", new
Class[] {criteriaClass});
        	method.invoke(peerClass, new Object[] {c});
        	if (i > 0) {
        		Field field = peers[i -
1].getField("numColumns");
        		int numColumns = field.getInt(peerClass);
        		int newOffset = ((Integer) offsets.get(i -
1)).intValue() + numColumns;
        		offsets.add(new Integer(newOffset));
        	}
        }
        return offsets;
    }
    
    /**
     * Transforms a sql name ("SOME_NAME") to a Java name ("SomeName").
	* Stolen from Torque's JavaNameGenerator
     * @param schemaName
     * @return
     */
    private String makeJavaName(String schemaName) {
    	StringBuffer name = new StringBuffer();
    	String namePart;
    	for (StringTokenizer tok = new StringTokenizer(schemaName,
String.valueOf('_')); tok.hasMoreTokens();
name.append(StringUtils.capitalise(namePart))) {
    		namePart = ((String)tok.nextElement()).toLowerCase();
    	}
    	return name.toString();
    }

    //-------------------------------------------------------------
    /**
	 * @param message
	 */
	private void debug(String message, boolean error) {
		if (debug == true) {
			if (error) {
				logger.warning(message);
	
//System.err.println(this.getClass().getName() + ": " + message);
			} else {
				logger.info(message);
	
//System.out.println(this.getClass().getName() + ": " + message);
			}
		}
	}
}

---------------------------------------------------------------------
To unsubscribe, e-mail: torque-user-unsubscribe@db.apache.org
For additional commands, e-mail: torque-user-help@db.apache.org


Mime
View raw message