You are viewing a plain text version of this content. The canonical link for it is here.
Posted to ojb-dev@db.apache.org by ar...@apache.org on 2006/07/15 16:09:48 UTC

svn commit: r422225 [2/2] - in /db/ojb/trunk/src: java/org/apache/ojb/broker/ java/org/apache/ojb/broker/accesslayer/ java/org/apache/ojb/broker/accesslayer/conversions/ java/org/apache/ojb/broker/metadata/ java/org/apache/ojb/broker/metadata/fieldacce...

Added: db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldCGLibImpl.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldCGLibImpl.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldCGLibImpl.java (added)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/metadata/fieldaccess/PersistentFieldCGLibImpl.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,244 @@
+package org.apache.ojb.broker.metadata.fieldaccess;
+
+/* Copyright 2003-2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.beans.PropertyDescriptor;
+import java.lang.reflect.Method;
+import java.util.ArrayList;
+import java.util.List;
+
+import net.sf.cglib.reflect.FastClass;
+import net.sf.cglib.reflect.FastMethod;
+import org.apache.commons.lang.StringUtils;
+import org.apache.ojb.broker.metadata.MetadataException;
+import org.apache.ojb.broker.util.ClassHelper;
+
+/**
+ * A {@link org.apache.ojb.broker.metadata.fieldaccess.PersistentField} implementation using
+ * JavaBeans compliant calls only to access persistent attributes.
+ * No Reflection is needed. But for each attribute xxx there must be
+ * public getXxx() and setXxx() methods. In metadata the field name must be
+ * the bean compliant 'xxx'.
+ *
+ * @version $Id: PersistentFieldIntrospectorImpl.java,v 1.11.2.2 2005/12/21 22:26:41 tomdz Exp $
+ */
+public final class PersistentFieldCGLibImpl extends PersistentFieldBase
+{
+    private Class type;
+    private transient FastMethod[] methodGraphGetter;
+    private transient FastMethod[] methodGraphSetter;
+    private transient FastMethod singleMethodGraphGetter;
+    private transient FastMethod singleMethodGraphSetter;
+    private transient boolean initialized;
+    private boolean nonNested = false;
+
+    public PersistentFieldCGLibImpl(Class aClass, String aPropertyName)
+    {
+        super(aClass, aPropertyName);
+    }
+
+    public Class getType()
+    {
+        if(type == null)
+        {
+            if(!initialized) init();
+            type = methodGraphGetter[methodGraphGetter.length - 1].getReturnType();
+        }
+        return type;
+    }
+
+    public void set(Object target, final Object value) throws MetadataException
+    {
+        if(target == null) return;
+        if(!initialized) init();
+
+        if(nonNested)
+        {
+            setValueFor(singleMethodGraphSetter, target, value);
+        }
+        else
+        {
+            int size = methodGraphSetter.length - 1;
+            for(int i = 0; i < size; i++)
+            {
+                Object attribute;
+                attribute = getValueFrom(methodGraphGetter[i], target);
+                if(attribute != null || value != null)
+                {
+                    if(attribute == null)
+                    {
+                        try
+                        {
+                            attribute = ClassHelper.newInstance(methodGraphGetter[i].getReturnType());
+                        }
+                        catch(Exception e)
+                        {
+                            throw new MetadataException("Can't instantiate nested object of type '"
+                                    + methodGraphGetter[i].getReturnType() + "' for field '"
+                                    + methodGraphGetter[i].getName() + "'", e);
+                        }
+                    }
+                    setValueFor(methodGraphSetter[i], target, attribute);
+                }
+                else
+                {
+                    return;
+                }
+                target = attribute;
+            }
+            setValueFor(methodGraphSetter[size], target, value);
+        }
+    }
+
+    public Object get(Object target) throws MetadataException
+    {
+        if(!initialized) init();
+        if(nonNested)
+        {
+            return getValueFrom(singleMethodGraphGetter, target);
+        }
+        else
+        {
+            for(int i = 0; i < methodGraphGetter.length; i++)
+            {
+                FastMethod fastMethod = methodGraphGetter[i];
+                target = getValueFrom(fastMethod, target);
+                if(target == null) break;
+            }
+            return target;
+        }
+    }
+
+    private Object getValueFrom(final FastMethod fastMethodGetter, final Object target)
+    {
+        try
+        {
+            return fastMethodGetter.invoke(target, null);
+        }
+        catch(Throwable e)
+        {
+            String msg = buildGetterErrorMsg(fastMethodGetter.getReturnType(), target, "Can't read value from given object");
+            getLog().error(msg);
+            throw new MetadataException("Error invoking method:" + fastMethodGetter.getName() + " in object " + target.getClass().getName(), e);
+        }
+    }
+
+    private void setValueFor(final FastMethod fastMethodSetter, final Object target, Object value)
+    {
+        try
+        {
+            /**
+             * it is safe to call getParameterTypes()[0] because this is
+             * the "set" method and it needs to take one parameter only.
+             * we need to be able to set values to null. We can only set something to null if
+             * the type is not a primitive (assignable from Object).
+             */
+            if((value != null) || !fastMethodSetter.getParameterTypes()[0].isPrimitive())
+            {
+                fastMethodSetter.invoke(target, new Object[]{value});
+            }
+        }
+        catch(Throwable e)
+        {
+            Class[] argTypes = fastMethodSetter.getParameterTypes();
+            Class argType = argTypes != null ? argTypes[0] : null;
+            String msg = buildSetterErrorMsg(argType, target, value, "Can't set value on given object.");
+            getLog().error(msg);
+            throw new MetadataException("Error invoking method:" + fastMethodSetter.getName() + " in object:" + target.getClass().getName(), e);
+        }
+    }
+
+    private void init()
+    {
+        methodGraphGetter = buildMethodGraphGetter();
+        methodGraphSetter = buildMethodGraphSetter();
+        if(methodGraphGetter.length == 1)
+        {
+            singleMethodGraphGetter = methodGraphGetter[0];
+            singleMethodGraphSetter = methodGraphSetter[0];
+            nonNested = true;
+        }
+        initialized = true;
+    }
+
+    private FastMethod[] buildMethodGraphGetter()
+    {
+        List result = new ArrayList();
+        String[] fields = StringUtils.split(getName(), PATH_TOKEN);
+        PropertyDescriptor pd = null;
+        for(int i = 0; i < fields.length; i++)
+        {
+            String fieldName = fields[i];
+            Class clazz;
+            if(pd == null)
+            {
+                clazz = getDeclaringClass();
+                pd = findPropertyDescriptor(clazz, fieldName);
+            }
+            else
+            {
+                clazz = pd.getPropertyType();
+                pd = findPropertyDescriptor(clazz, fieldName);
+            }
+            Method method = pd.getReadMethod();
+            if(method == null)
+            {
+                throw new MetadataException("Can't get readMethod for property '"
+                        + pd.getName() + "' in object '" + clazz.getName() + "'");
+            }
+            FastMethod m = FastClass.create(method.getDeclaringClass()).getMethod(method);
+            result.add(m);
+        }
+        return (FastMethod[]) result.toArray(new FastMethod[result.size()]);
+    }
+
+    private FastMethod[] buildMethodGraphSetter()
+    {
+        List result = new ArrayList();
+        String[] fields = StringUtils.split(getName(), PATH_TOKEN);
+        PropertyDescriptor pd = null;
+        for(int i = 0; i < fields.length; i++)
+        {
+            String fieldName = fields[i];
+            Class clazz;
+            if(pd == null)
+            {
+                clazz = getDeclaringClass();
+                pd = findPropertyDescriptor(clazz, fieldName);
+            }
+            else
+            {
+                clazz = pd.getPropertyType();
+                pd = findPropertyDescriptor(clazz, fieldName);
+            }
+            Method method = pd.getWriteMethod();
+            if(method == null)
+            {
+                throw new MetadataException("Can't get writeMethod for property '"
+                        + pd.getName() + "' in object '" + clazz.getName() + "'");
+            }
+            FastMethod m = FastClass.create(method.getDeclaringClass()).getMethod(method);
+            result.add(m);
+        }
+        return (FastMethod[]) result.toArray(new FastMethod[result.size()]);
+    }
+
+    /** This implementation returns always 'false'. */
+    public boolean makeAccessible()
+    {
+        return false;
+    }
+}

Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/GUIDFactory.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/GUIDFactory.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/util/GUIDFactory.java (added)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/GUIDFactory.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,118 @@
+package org.apache.ojb.broker.util;
+
+/* Copyright 2002-2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.net.InetAddress;
+import java.net.UnknownHostException;
+
+/**
+ * A simple (and fast) factory for (human readable) GUID (Globally Unique ID) strings.
+ * <br/>
+ * A GUID is composed of three parts (separator is ':'):
+ * 1. A two part unique UID for this host
+ * 2. The IP-Address of the local machine
+ * <br/>
+ * An example GUID would be: -32766:1135679889968:84.157.57.223.
+ *
+ * @version $Id: GUIDFactory.java 365264 2005-12-31 19:42:46 +0100 (Sa, 31 Dez 2005) arminw $
+ */
+public class GUIDFactory
+{
+    private static final char SEPERATOR = ':';
+    private static final Object lock = new Object();
+
+    private static long WAIT_PERIOD = 100;
+    private static long lastTime = System.currentTimeMillis();
+    private static short lastCount = Short.MIN_VALUE;
+    /** the end piece of the GUID */
+    private static String endPiece;
+
+    /** holds the hostname of the local machine. */
+    private static String localIPAddress;
+
+    /**
+     * compute the local IP-Address
+     */
+    static
+    {
+        try
+        {
+            localIPAddress = InetAddress.getLocalHost().getHostAddress();
+        }
+        catch(UnknownHostException e)
+        {
+            System.err.println("[" + GUIDFactory.class.getName() + "]"
+                    + " Can't lookup current IPAddress - GUID isn't globally unique, only for host. "
+                    + e.getMessage());
+            localIPAddress = "localhost";
+        }
+        endPiece = new StringBuffer()
+                .append(Long.toString(lastTime))
+                .append(SEPERATOR)
+                .append(localIPAddress)
+                .toString();
+    }
+
+    public GUIDFactory()
+    {
+    }
+
+    /** Returns the next GUID string. */
+    public String next()
+    {
+        short count;
+
+        synchronized(lock)
+        {
+            if(lastCount == Short.MAX_VALUE)
+            {
+                boolean done = false;
+                /*
+                Use a similar algorithm as in java.rmi.server.UID
+                */
+                while(!done)
+                {
+                    long current = System.currentTimeMillis();
+                    if(current < lastTime + WAIT_PERIOD)
+                    {
+                        try
+                        {
+                            Thread.currentThread().sleep(WAIT_PERIOD);
+                        }
+                        catch(InterruptedException ignore)
+                        {
+                            // ignore exception
+                        }
+                    }
+                    else
+                    {
+                        lastTime = current;
+                        lastCount = Short.MIN_VALUE;
+                        endPiece = new StringBuffer()
+                                .append(SEPERATOR)
+                                .append(lastTime)
+                                .append(SEPERATOR)
+                                .append(localIPAddress)
+                                .toString();
+                        done = true;
+                    }
+                }
+            }
+            count = lastCount++;
+        }
+        return count + endPiece;
+    }
+}

Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/ReaderInputStream.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/ReaderInputStream.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/util/ReaderInputStream.java (added)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/ReaderInputStream.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,234 @@
+package org.apache.ojb.broker.util;
+
+/* Copyright 2002-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.Reader;
+
+/**
+ * This class is a copy of {@link org.apache.tools.ant.util.ReaderInputStream}.
+ *
+ * @version $Id: $
+ */
+public class ReaderInputStream extends InputStream
+{
+
+    /** Source Reader */
+    private Reader in;
+
+    private String encoding = System.getProperty("file.encoding");
+
+    private byte[] slack;
+
+    private int begin;
+
+    /**
+     * Construct a <CODE>ReaderInputStream</CODE>
+     * for the specified <CODE>Reader</CODE>.
+     *
+     * @param reader <CODE>Reader</CODE>.  Must not be <code>null</code>.
+     */
+    public ReaderInputStream(Reader reader)
+    {
+        in = reader;
+    }
+
+    /**
+     * Construct a <CODE>ReaderInputStream</CODE>
+     * for the specified <CODE>Reader</CODE>,
+     * with the specified encoding.
+     *
+     * @param reader non-null <CODE>Reader</CODE>.
+     * @param encoding non-null <CODE>String</CODE> encoding.
+     */
+    public ReaderInputStream(Reader reader, String encoding)
+    {
+        this(reader);
+        if(encoding == null)
+        {
+            throw new IllegalArgumentException("encoding must not be null");
+        }
+        else
+        {
+            this.encoding = encoding;
+        }
+    }
+
+    /**
+     * Reads from the <CODE>Reader</CODE>, returning the same value.
+     *
+     * @return the value of the next character in the <CODE>Reader</CODE>.
+     * @throws IOException if the original <code>Reader</code> fails to be read
+     */
+    public synchronized int read() throws IOException
+    {
+        if(in == null)
+        {
+            throw new IOException("Stream Closed");
+        }
+
+        byte result;
+        if(slack != null && begin < slack.length)
+        {
+            result = slack[begin];
+            if(++begin == slack.length)
+            {
+                slack = null;
+            }
+        }
+        else
+        {
+            byte[] buf = new byte[1];
+            if(read(buf, 0, 1) <= 0)
+            {
+                result = -1;
+            }
+            result = buf[0];
+        }
+
+        if(result < -1)
+        {
+            result += 256;
+        }
+
+        return result;
+    }
+
+    /**
+     * Reads from the <code>Reader</code> into a byte array
+     *
+     * @param b the byte array to read into
+     * @param off the offset in the byte array
+     * @param len the length in the byte array to fill
+     * @return the actual number read into the byte array, -1 at
+     *         the end of the stream
+     * @throws IOException if an error occurs
+     */
+    public synchronized int read(byte[] b, int off, int len)
+            throws IOException
+    {
+        if(in == null)
+        {
+            throw new IOException("Stream Closed");
+        }
+
+        while(slack == null)
+        {
+            char[] buf = new char[len]; // might read too much
+            int n = in.read(buf);
+            if(n == -1)
+            {
+                return -1;
+            }
+            if(n > 0)
+            {
+                slack = new String(buf, 0, n).getBytes(encoding);
+                begin = 0;
+            }
+        }
+
+        if(len > slack.length - begin)
+        {
+            len = slack.length - begin;
+        }
+
+        System.arraycopy(slack, begin, b, off, len);
+
+        if((begin += len) >= slack.length)
+        {
+            slack = null;
+        }
+
+        return len;
+    }
+
+    /**
+     * Marks the read limit of the StringReader.
+     *
+     * @param limit the maximum limit of bytes that can be read before the
+     * mark position becomes invalid
+     */
+    public synchronized void mark(final int limit)
+    {
+        try
+        {
+            in.mark(limit);
+        }
+        catch(IOException ioe)
+        {
+            throw new RuntimeException(ioe.getMessage());
+        }
+    }
+
+
+    /**
+     * @return the current number of bytes ready for reading
+     * @throws IOException if an error occurs
+     */
+    public synchronized int available() throws IOException
+    {
+        if(in == null)
+        {
+            throw new IOException("Stream Closed");
+        }
+        if(slack != null)
+        {
+            return slack.length - begin;
+        }
+        if(in.ready())
+        {
+            return 1;
+        }
+        else
+        {
+            return 0;
+        }
+    }
+
+    /** @return false - mark is not supported */
+    public boolean markSupported()
+    {
+        return false;   // would be imprecise
+    }
+
+    /**
+     * Resets the StringReader.
+     *
+     * @throws IOException if the StringReader fails to be reset
+     */
+    public synchronized void reset() throws IOException
+    {
+        if(in == null)
+        {
+            throw new IOException("Stream Closed");
+        }
+        slack = null;
+        in.reset();
+    }
+
+    /**
+     * Closes the Stringreader.
+     *
+     * @throws IOException if the original StringReader fails to be closed
+     */
+    public synchronized void close() throws IOException
+    {
+        in.close();
+        slack = null;
+        in = null;
+    }
+}

Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/UnwrapHelper.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/UnwrapHelper.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/util/UnwrapHelper.java (added)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/UnwrapHelper.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,468 @@
+package org.apache.ojb.broker.util;
+
+/* Copyright 2002-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.reflect.Field;
+import java.lang.reflect.Method;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.Statement;
+import java.util.List;
+import java.util.ArrayList;
+
+import org.apache.commons.lang.ArrayUtils;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.ToStringStyle;
+import org.apache.ojb.broker.util.logging.Logger;
+import org.apache.ojb.broker.util.logging.LoggerFactory;
+
+/**
+ * This a helper class make available methods to unwrap {@link java.sql.Connection} and
+ * {@link java.sql.Statement} instances of connection-pool libraries,
+ * application server and other.
+ * <p/>
+ * This class supports predefined unwrap patterns for:
+ * <ul>
+ *     <li>commons-DBCP</li>
+ *     <li>JBoss</li>
+ *     <li>enhydra.XAPool</li>
+ *     <li>BEA Weblogic</li>
+ *     <li>p6spy</li>
+ * </ul>
+ * To add new unwrap patterns use method {@link #addUnwrapPattern(Object[])}.
+ *
+ * @version $Id: $
+ */
+public class UnwrapHelper
+{
+    private Logger log = LoggerFactory.getLogger(UnwrapHelper.class);
+
+    private static final int UNWARP_CONNECTION = 1;
+    private static final int UNWARP_STATEMENT = 2;
+    private static final Object[] EMPTY_ARG = new Object[]{};
+    public static final Class[] EMPTY_TYPE = new Class[]{};
+    public static final Integer TYPE_METHOD = new Integer(5);
+    public static final Integer TYPE_FIELD = new Integer(6);
+    /**
+     * Represents the information of how to unwrap wrapped {@link java.sql.Connection}
+     * and {@link java.sql.PreparedStatement} instances of popular connection-pool libraries,
+     * application server and other libraries.
+     * <p/>
+     * The array have to be a [n][7] length object array. The following arguments will be expected:
+     * <ul>
+     * <li>predefinedPatterns[i][0] = the vendor, product name as {@link String}</li>
+     *
+     * <li>predefinedPatterns[i][1] = use {@link #TYPE_METHOD} if access to the wrapped connection via method call
+     * is possible, use {@link #TYPE_FIELD} if access to a field can be used</li>
+     * <li>predefinedPatterns[i][2] = use {@link #EMPTY_TYPE} if a no-arg method call is needed, otherwise
+     * specify the argument type class array - in this case always a single argument
+     * type is expected and the source object (object to unwarp) was used as argument object when
+     * calling {@link #unwrapConnection(Class, java.sql.Connection)} or
+     * {@link #unwrapStatement(Class, java.sql.Statement)}</li>
+     * <li>predefinedPatterns[i][3] = the name of the method/field to lookup the wrapped connection
+     * as {@link String}<br/><br/></li>
+     *
+     * <li>predefinedPatterns[i][4] = use {@link #TYPE_METHOD} if access to the wrapped statment via method call
+     * is possible, use {@link #TYPE_FIELD} if access to a field can be used</li></li>
+     * <li>predefinedPatterns[i][5] = use {@link #EMPTY_TYPE} if a no-arg method call is needed, otherwise
+     * specify the argument type class array - in this case always a single argument
+     * type is expected and the source object (object to unwarp) was used as argument object when
+     * calling {@link #unwrapConnection(Class, java.sql.Connection)} or
+     * {@link #unwrapStatement(Class, java.sql.Statement)}</li>
+     * <li>predefinedPatterns[i][6] = the name of the method/field to unwrap the statement<br/><br/></li>
+     * </ul>
+     * Example:
+     * <code>
+     * protected static Object[][] predefinedPatterns = {
+     * {"common-DBCP", TYPE_METHOD, PARAM_TYPE_EMPTY, "getInnermostDelegate", TYPE_METHOD, PARAM_TYPE_EMPTY, "getInnermostDelegate"},
+     * {"enhydra.XAPool", TYPE_FIELD, null, "con", TYPE_FIELD, null, "ps"},
+     * ....
+     * </code>
+     */
+    private static final Object[][] predefinedPatterns = {
+        {"common-DBCP", TYPE_METHOD, EMPTY_TYPE, "getInnermostDelegate", TYPE_METHOD, EMPTY_TYPE, "getInnermostDelegate"},
+        {"JBoss", TYPE_METHOD, EMPTY_TYPE, "getUnderlyingConnection", TYPE_METHOD, EMPTY_TYPE, "getUnderlyingStatement"},
+        {"enhydra.XAPool", TYPE_FIELD, null, "con", TYPE_FIELD, null, "ps"},
+        {"BEA Weblogic", TYPE_METHOD, EMPTY_TYPE, "getVendorConnection", null, null, null},
+        {"p6spy", TYPE_METHOD, EMPTY_TYPE, "getJDBC", TYPE_METHOD, EMPTY_TYPE, "getJDBC"}
+    };
+
+    /**
+     * The active unwrap patterns.
+     */
+    private Object[][] unwrapPatterns = new Object[][]{};
+    private Object[][] matchedPatterns;
+    private boolean optimized;
+
+
+    /**
+     * Default constructor, use predefined unwrap patterns and optimized
+     * search, see {@link #UnwrapHelper(boolean, boolean)}.
+     */
+    public UnwrapHelper()
+    {
+        this(true, true);
+    }
+
+    /**
+     * Alternative constructor.
+     * @param usePredefined If <em>true</em> the predefined unwrap patterns will be used,
+     * else no patterns are provided (add pattern with {@link #addUnwrapPattern(Object[])}).
+     * @param optimized If <em>true</em> all matching patterns will be bundled and used to unwrap,
+     * if <em>false</em> always all patterns are used to search for a unwrap match.
+     */
+    public UnwrapHelper(boolean usePredefined, boolean optimized)
+    {
+        resetMatchedPatterns();
+        this.optimized = optimized;
+        if(usePredefined)
+        {
+            if(log.isDebugEnabled()) log.debug("Activate predefined unwrap patterns");
+            for(int i = 0; i < predefinedPatterns.length; i++)
+            {
+                Object[] pattern = predefinedPatterns[i];
+                addUnwrapPattern(pattern);
+            }
+        }
+    }
+
+    /** Return the current unwrap patterns. */
+    public Object[][] getUnwrapPatterns()
+    {
+        return unwrapPatterns;
+    }
+
+    /**
+     * Add a new unwrap pattern. The object array of length 7 have to be
+     * the following arguments (a vendor string, 3 arguments to unwrap connections,
+     * 3 arguments to unwrap statements):<br/><br/>
+     * <ul>
+     * <li>
+     * pattern[0] = the vendor, product name as {@link String}.<br/><br/>
+     * </li>
+     *
+     * <li>
+     * pattern[1] = use {@link #TYPE_METHOD} if access to the wrapped connection is done via
+     * method call or use {@link #TYPE_FIELD} to specify access via field.
+     * </li>
+     * <li>
+     * pattern[2] = specify the method argument for connection unwrap. Only two types are
+     * supported: non-argument methods and single argument type methods. If field access
+     * is used nullify this argument.
+     * <br/>
+     * Non argument methods can use {@link #EMPTY_TYPE} or simply <em>null</em>.
+     * <br/>
+     * The single argument type (specified as class, class array or full qualified
+     * class name string) use the source object (object to unwarp)  as argument
+     * object when calling
+     * {@link #unwrapConnection(Class, java.sql.Connection)}.
+     * </li>
+     * <li>
+     * pattern[3] = the name of the method/field to unwrap the connection.<br/><br/>
+     * </li>
+     *
+     * <li>
+     * pattern[4] = use {@link #TYPE_METHOD} if access to the wrapped statment via method call
+     * is possible, use {@link #TYPE_FIELD} if access to a field can be used
+     * </li>
+     * <li>
+     * pattern[5] = specify the method argument for statement unwrap. Only two types are
+     * supported: non-argument methods and single argument type methods. If field access
+     * is used nullify this argument.
+     * <br/>
+     * Non argument methods can use {@link #EMPTY_TYPE} or simply <em>null</em>.
+     * <br/>
+     * The single argument type (specified as class, class array or full qualified
+     * class name string) use the source object (object to unwarp) as argument
+     * object when calling {@link #unwrapStatement(Class, java.sql.Statement)}.
+     * </li>
+     * <li>
+     * pattern[6] = the name of the method/field to unwrap the statement.<br/><br/>
+     * </li>
+     * </ul>
+     * Here is an example how to use this method:
+     * <code>
+     * Object pattern = new Object[]{"JBoss", TYPE_METHOD, EMPTY_TYPE, "getUnderlyingConnection",
+     * TYPE_METHOD, EMPTY_TYPE, "getUnderlyingStatement"};
+     * unwarpHelper.addUnwrapPattern(pattern);
+     * </code>
+     *
+     * @param pattern The unwrap pattern array.
+     */
+    public void addUnwrapPattern(final Object[] pattern)
+    {
+        // check connection method argument
+        Object argType = pattern[2];
+        if(argType == null)
+        {
+            pattern[2] = EMPTY_TYPE;
+        }
+        else if(argType instanceof String)
+        {
+            String arg = (String) argType;
+            try
+            {
+                Class tmp = ClassHelper.getClass(arg);
+                pattern[2] = new Class[]{tmp};
+            }
+            catch(ClassNotFoundException e)
+            {
+                log.error("Can't lookup method argument type "
+                        + arg + " of pattern " + ArrayUtils.toString(pattern), e);
+            }
+        }
+        else if(argType instanceof Class)
+        {
+            pattern[2] = new Class[]{(Class)argType};
+        }
+        // check statement method argument
+        argType = pattern[5];
+        if(argType == null)
+        {
+            pattern[5] = EMPTY_TYPE;
+        }
+        else if(argType instanceof String)
+        {
+            String arg = (String) argType;
+            try
+            {
+                Class tmp = ClassHelper.getClass(arg);
+                pattern[5] = new Class[]{tmp};
+            }
+            catch(ClassNotFoundException e)
+            {
+                log.error("Can't lookup method argument type "
+                        + arg + " of pattern " + ArrayUtils.toString(pattern), e);
+            }
+        }
+        else if(argType instanceof Class)
+        {
+            pattern[5] = new Class[]{(Class)argType};
+        }
+
+        Object[][] tmp = new Object[][]{pattern};
+        unwrapPatterns = (Object[][]) ArrayUtils.addAll(unwrapPatterns, tmp);
+        // force synchronization of optimized patterns
+        if(optimized) resetMatchedPatterns();
+    }
+
+    protected Object genericUnwrap(final Class classToMatch, final Object toUnwrap, final int type)
+    {
+        Object result;
+        if(optimized)
+        {
+            if(matchedPatterns.length == 0)
+            {
+                ArrayList matches = new ArrayList();
+                result = loopUnwrap(unwrapPatterns, classToMatch, toUnwrap, type, matches);
+                matchedPatterns = (Object[][]) matches.toArray(new Object[matches.size()][]);
+            }
+            else
+            {
+                result = loopUnwrap(matchedPatterns, classToMatch, toUnwrap, type, null);
+            }
+        }
+        else
+        {
+            result = loopUnwrap(unwrapPatterns, classToMatch, toUnwrap, type, null);
+        }
+        return result;
+    }
+
+    protected Object loopUnwrap(Object[][] patterns, final Class classToMatch, final Object toUnwrap, final int type, final List matchedPatterns)
+    {
+        if(classToMatch == null)
+        {
+            return null;
+        }
+        if(classToMatch.isAssignableFrom(toUnwrap.getClass()))
+        {
+            return toUnwrap;
+        }
+        Object unwrapped = null;
+
+        int i = 0;
+        try
+        {
+            for(; i < patterns.length; i++)
+            {
+                Object[] pattern = patterns[i];
+                unwrapped = unwrap(pattern, toUnwrap, type);
+                if(unwrapped != null)
+                {
+                    if(matchedPatterns != null)
+                    {
+                        matchedPatterns.add(pattern);
+                    }
+                    break;
+                }
+            }
+        }
+        catch(Exception e)
+        {
+            //e.printStackTrace();
+            // ignore
+            if(log.isDebugEnabled())
+            {
+                if(type == UNWARP_CONNECTION)
+                {
+                    log.debug("Unwrap of connection failed using unwrap rule for " + unwrapPatterns[i][0] + " connections", e);
+                }
+                else
+                {
+                    log.debug("Unwrap of prepared stmt failed using unwrap rule for " + unwrapPatterns[i][0] + " statements", e);
+                }
+            }
+        }
+
+        if(unwrapped != null)
+        {
+            if(classToMatch.isAssignableFrom(unwrapped.getClass()))
+            {
+                // Bingo!
+            }
+            else
+            {
+                // When using e.g. both DBCP and P6Spy we have to recursively unwrap
+                unwrapped = loopUnwrap(patterns, classToMatch, unwrapped, type, matchedPatterns);
+            }
+        }
+        return unwrapped;
+    }
+
+    private Object unwrap(final Object[] pattern, final Object toUnwrap, final int type) throws Exception
+    {
+        Object unwrapped = null;
+        if(type == UNWARP_CONNECTION && pattern[3] != null)
+        {
+            unwrapped = reflectObject(toUnwrap, pattern[1].equals(TYPE_METHOD), (Class[]) pattern[2], (String) pattern[3]);
+        }
+        else if(type == UNWARP_STATEMENT && pattern[6] != null)
+        {
+            unwrapped = reflectObject(toUnwrap, pattern[4].equals(TYPE_METHOD), (Class[]) pattern[5], (String) pattern[6]);
+        }
+        else
+        {
+            if(log.isDebugEnabled())
+            {
+                log.debug("Can't use the specified pattern to unwarp "
+                        + (type == UNWARP_CONNECTION ? "connection" : "statement")
+                        + ": " + ArrayUtils.toString(pattern));
+            }
+        }
+        return unwrapped;
+    }
+
+    private Object reflectObject(final Object source, final boolean methodType, final Class[] paramType, final String fieldOrMethodName) throws Exception
+    {
+        Object unwrapped = null;
+        if(methodType)
+        {
+            final Method method;
+            method = ClassHelper.getMethod(source, fieldOrMethodName, paramType);
+            if(method != null)
+            {
+                if(!method.isAccessible())
+                {
+                    method.setAccessible(true);
+                }
+                Object[] arg = (paramType == null || paramType.length == 0) ? EMPTY_ARG : new Object[]{source};
+                unwrapped = method.invoke(source, arg);
+            }
+        }
+        else
+        {
+            final Field field;
+            field = ClassHelper.getField(source.getClass(), fieldOrMethodName);
+            if(field != null)
+            {
+                if(!field.isAccessible())
+                {
+                    field.setAccessible(true);
+                }
+                unwrapped = field.get(source);
+            }
+        }
+        return unwrapped;
+    }
+
+    /**
+     * Rekursive unwrap specified {@link java.sql.Connection} till target
+     * class is matched.
+     *
+     * @param target The target class (subclass of {@link java.sql.Connection} is mandatory).
+     * @param source The wrapped connection instance.
+     * @return The unwrapped connection or <em>null</em> if no match is possible.
+     */
+    public Connection unwrapConnection(final Class target, final Connection source)
+    {
+        Connection result = (Connection) genericUnwrap(target, source, UNWARP_CONNECTION);
+        if(result == null && log.isDebugEnabled())
+        {
+            log.debug("Could not unwrap source " + source.getClass().getName()
+                    + " to target class " + target.getName());
+        }
+        return result;
+    }
+
+    /**
+     * Rekursive unwrap specified {@link java.sql.Statement} till target
+     * class is matched.
+     *
+     * @param target The target class (subclass of {@link java.sql.PreparedStatement} is mandatory).
+     * @param source The wrapped statement instance.
+     * @return The unwrapped statement or <em>null</em> if no match is possible.
+     */
+    public Statement unwrapStatement(final Class target, final Statement source)
+    {
+        Statement result = (Statement) genericUnwrap(target, source, UNWARP_STATEMENT);
+        if(result == null && log.isDebugEnabled())
+        {
+            log.debug("Could not unwrap source " + source.getClass().getName()
+                    + " to target class " + target.getName());
+        }
+        return result;
+    }
+
+    /**
+     * Rekursive unwrap specified {@link java.sql.Statement} till target
+     * class is matched.
+     *
+     * @param target The target class (subclass of {@link java.sql.PreparedStatement} is mandatory).
+     * @param source The wrapped statement instance.
+     * @return The unwrapped statement or <em>null</em> if no match is possible.
+     */
+    public PreparedStatement unwrapStatement(final Class target, final PreparedStatement source)
+    {
+        return (PreparedStatement) unwrapStatement(target, (Statement) source);
+    }
+
+    private void resetMatchedPatterns()
+    {
+        matchedPatterns = new Object[][]{};
+    }
+
+    public String toString()
+    {
+        ToStringBuilder buf = new ToStringBuilder(this, ToStringStyle.MULTI_LINE_STYLE);
+        buf.append("Supported unwrap pattern: ");
+        for(int i = 0; i < unwrapPatterns.length; i++)
+        {
+            buf.append(ArrayUtils.toString(unwrapPatterns[i]));
+        }
+        return buf.toString();
+    }
+}

Added: db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerTransientImpl.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerTransientImpl.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerTransientImpl.java (added)
+++ db/ojb/trunk/src/java/org/apache/ojb/broker/util/sequence/SequenceManagerTransientImpl.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,51 @@
+package org.apache.ojb.broker.util.sequence;
+
+/* Copyright 2002-2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import org.apache.ojb.broker.PersistenceBroker;
+import org.apache.ojb.broker.PersistenceBrokerInternal;
+import org.apache.ojb.broker.metadata.FieldDescriptor;
+
+/**
+ * For internal use only!
+ * This class is used to create transient primary key values for transient
+ * {@link org.apache.ojb.broker.Identity} objects.
+ *
+ * @version $Id: SequenceManagerTransientImpl.java 365232 2005-12-21 23:36:07 +0100 (Mi, 21 Dez 2005) tomdz $
+ */
+public class SequenceManagerTransientImpl extends AbstractSequenceManager
+{
+    /*
+     Use keyword 'volatile' to make decrement of a long value an
+     atomic operation
+     */
+    private static volatile long tempKey = -1000;
+
+    public SequenceManagerTransientImpl(PersistenceBrokerInternal broker)
+    {
+        super(broker);
+    }
+
+    protected long getUniqueLong(FieldDescriptor field) throws SequenceManagerException
+    {
+        /*
+        arminw:
+        We need unique 'dummy keys' for new objects before storing.
+        Variable 'tempKey' is declared volatile, thus decrement should be atomic
+        */
+        return --tempKey;
+    }
+}

Added: db/ojb/trunk/src/java/org/apache/ojb/odmg/Image.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/java/org/apache/ojb/odmg/Image.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/java/org/apache/ojb/odmg/Image.java (added)
+++ db/ojb/trunk/src/java/org/apache/ojb/odmg/Image.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,522 @@
+package org.apache.ojb.odmg;
+
+/* Copyright 2002-2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+
+import org.apache.commons.lang.ClassUtils;
+import org.apache.ojb.broker.Identity;
+import org.apache.ojb.broker.IdentityFactory;
+import org.apache.ojb.broker.OJBRuntimeException;
+import org.apache.ojb.broker.PersistenceBrokerInternal;
+import org.apache.ojb.broker.core.proxy.CollectionProxy;
+import org.apache.ojb.broker.core.proxy.CollectionProxyListener;
+import org.apache.ojb.broker.metadata.CollectionDescriptor;
+import org.apache.ojb.broker.metadata.FieldType;
+import org.apache.ojb.broker.metadata.ObjectReferenceDescriptor;
+import org.apache.ojb.broker.util.BrokerHelper;
+import org.apache.ojb.broker.util.logging.Logger;
+import org.apache.ojb.broker.util.logging.LoggerFactory;
+
+/**
+ * This class encapsulates classes used to take persistence capable object
+ * state snapshoot and to detect changed fields or references.
+ *
+ * @version $Id: Image.java 365232 2005-12-21 23:36:07 +0100 (Mi, 21 Dez 2005) tomdz $
+ */
+public abstract class Image
+{
+    static Logger log = LoggerFactory.getLogger(Image.class);
+    private long timestamp = System.currentTimeMillis();
+
+    private Image()
+    {
+    }
+
+    boolean illegalImageComparison(Image oldImage)
+    {
+        return timestamp < oldImage.timestamp;
+    }
+
+    public abstract void cleanup(boolean reuse);
+
+    public abstract boolean modified(Image other);
+
+    abstract void referenceProcessing(Image oldImage);
+
+    public void performReferenceDetection(Image oldImage)
+    {
+        if(illegalImageComparison(oldImage))
+        {
+            throw new ImageException("The specified Image object is newer than current one, wrong Image order!");
+        }
+        referenceProcessing(oldImage);
+    }
+
+    //===================================================================
+    // inner class
+    //===================================================================
+    public static class MultipleRef extends Image implements CollectionProxyListener
+    {
+        static final int IS_NORMAL_OBJECT = 11;
+        static final int IS_MATERIALIZED_PROXY = 13;
+        static final int IS_UNMATERIALIZED_PROXY = 17;
+
+        private ImageListener listener;
+        private final CollectionDescriptor cod;
+        private final Object collectionOrArray;
+        private Map references;
+        private int status;
+        private boolean hasTransientIdentity;
+        private boolean isRefreshed;
+
+        public MultipleRef(ImageListener listener, CollectionDescriptor cod, Object collectionOrArray)
+        {
+            this.listener = listener;
+            this.cod = cod;
+            this.collectionOrArray = collectionOrArray;
+            this.isRefreshed = true;
+            this.hasTransientIdentity = false;
+            this.references = Collections.EMPTY_MAP;
+            init();
+        }
+
+        private void init()
+        {
+            CollectionProxy colProxy = listener.getBroker().getProxyFactory().getCollectionProxy(collectionOrArray);
+            if(colProxy != null)
+            {
+                if(colProxy.isLoaded())
+                {
+                    status = IS_MATERIALIZED_PROXY;
+                    /*
+                    TODO: avoid this cast
+                    e.g. change CollectionProxy interface - CollectionProxy should
+                    extend Collection to support Iterator
+                    */
+                    handleReferencedObjects(colProxy.iterator());
+                }
+                else
+                {
+                    status = IS_UNMATERIALIZED_PROXY;
+                    if(log.isDebugEnabled()) log.debug("Unmaterialized proxy collection, use proxy listener");
+                    colProxy.addListener(this);
+                }
+            }
+            else
+            {
+                status = IS_NORMAL_OBJECT;
+                if(collectionOrArray != null)
+                {
+                    Iterator it = BrokerHelper.getCollectionIterator(listener.getBroker(), collectionOrArray);
+                    handleReferencedObjects(it);
+                }
+            }
+        }
+
+        void handleReferencedObjects(Iterator it)
+        {
+            if(it == null) return;
+            references = new HashMap();
+            if(log.isDebugEnabled()) log.debug("Handle collection references");
+            IdentityFactory idFac = listener.getBroker().serviceIdentity();
+            Identity oid;
+            Object obj;
+            while(it.hasNext())
+            {
+                obj = it.next();
+                oid = idFac.buildIdentity(obj);
+                if(!hasTransientIdentity && oid.isTransient())
+                {
+                    hasTransientIdentity = true;
+                }
+                references.put(oid, obj);
+            }
+        }
+
+        public void cleanup(boolean reuse)
+        {
+            if(log.isDebugEnabled()) log.debug("Cleanup collection image, reuse=" + reuse);
+            if(reuse)
+            {
+                isRefreshed = false;
+            }
+            else
+            {
+                if(status == IS_UNMATERIALIZED_PROXY)
+                {
+                    CollectionProxy colProxy = listener.getBroker().getProxyFactory().getCollectionProxy(collectionOrArray);
+                    if(colProxy != null)
+                    {
+                        colProxy.removeListener(this);
+                    }
+                }
+            }
+        }
+
+        void referenceProcessing(Image oldImage)
+        {
+            MultipleRef oldRefs = (MultipleRef) oldImage;
+            if(incommensurableProxies(oldRefs))
+            {
+                PersistenceBrokerInternal pb = listener.getBroker();
+                if(isUnmaterializedProxy()) handleReferencedObjects(BrokerHelper.getCollectionIterator(pb, collectionOrArray));
+                if(oldRefs.isUnmaterializedProxy()) oldRefs.handleReferencedObjects(BrokerHelper.getCollectionIterator(pb, oldRefs.collectionOrArray));
+            }
+            if(!isRefreshed) refreshIdentities();
+            if(!oldRefs.isRefreshed) oldRefs.refreshIdentities();
+
+            // find deleted reference objects
+            if(oldRefs.references.size() > 0)
+            {
+                Iterator oldIter = oldRefs.references.entrySet().iterator();
+                while(oldIter.hasNext())
+                {
+                    Map.Entry entry = (Map.Entry) oldIter.next();
+                    Identity oldOid = (Identity) entry.getKey();
+                    /*
+                    search for deleted objects: if in the new image an object
+                    from the old image is not contained, we found a deleted object
+                    */
+                    if(!isUnmaterializedProxy() && !containsReference(oldOid))
+                    {
+                        listener.deletedXToN(cod, entry.getValue(), oldOid);
+                    }
+                }
+            }
+
+            // find new reference objects
+            if(references.size() > 0)
+            {
+                Iterator newIter = references.entrySet().iterator();
+                while(newIter.hasNext())
+                {
+                    Map.Entry entry = (Map.Entry) newIter.next();
+                    Identity newOid = (Identity) entry.getKey();
+                    /*
+                    search for added objects: if in the old image an object
+                    from the new image is not contained, we found a added object
+                    */
+                    if(!oldRefs.containsReference(newOid))
+                    {
+                        listener.addedXToN(cod, entry.getValue(), newOid);
+                    }
+                }
+            }
+        }
+
+        /**
+         * To detect deleted (added) collection objects it's necessary iterate over the old (new) image collection.
+         * If the old (new) image collection is a unmaterialized proxy we have to check if the new (old) image collection
+         * is the same proxy instance or not.
+         * E.g. if the user exchange one another the unmaterialized proxy collection objects of two main objects,
+         * then both proxy need to be materialized to assign the changed FK field values.
+         */
+        private boolean incommensurableProxies(MultipleRef oldImage)
+        {
+            boolean result = false;
+            // deleted objects
+            if(oldImage.isUnmaterializedProxy() || isUnmaterializedProxy())
+            {
+                result = collectionOrArray != oldImage.collectionOrArray;
+            }
+            return result;
+        }
+
+        private void refreshIdentities()
+        {
+            // if no transient identities are used, nothing to do
+            if(hasTransientIdentity && references.size() > 0)
+            {
+                hasTransientIdentity = false;
+                // we need independent key list from Map
+                List list = new ArrayList(references.keySet());
+                IdentityFactory idFac = listener.getBroker().serviceIdentity();
+                Identity oid, newOid;
+                Object obj;
+                for(int i = 0; i < list.size(); i++)
+                {
+                    oid = (Identity) list.get(i);
+                    if(oid.isTransient())
+                    {
+                        obj = references.remove(oid);
+                        newOid = idFac.buildIdentity(obj);
+                        references.put(newOid, obj);
+                        if(!hasTransientIdentity && oid.isTransient())
+                        {
+                            hasTransientIdentity = true;
+                        }
+                    }
+                }
+                isRefreshed = true;
+            }
+        }
+
+        /**
+         * Always return 'false', because changed 1:n or m:n references do not
+         * affect the main object.
+         */
+        public boolean modified(Image other)
+        {
+            return false;
+        }
+
+        boolean containsReference(Identity oid)
+        {
+            if(!isRefreshed) refreshIdentities();
+            return references.containsKey(oid);
+        }
+
+        Map getIdentityReferenceObjectMap()
+        {
+            if(!isRefreshed) refreshIdentities();
+            return references;
+        }
+
+        boolean isMaterializedProxy()
+        {
+            return status == IS_MATERIALIZED_PROXY;
+        }
+
+        boolean isUnmaterializedProxy()
+        {
+            return status == IS_UNMATERIALIZED_PROXY;
+        }
+
+
+        // CollectionProxy Listener methods
+        //---------------------------------
+        public void beforeLoading(CollectionProxy colProxy)
+        {
+            //noop
+        }
+
+        public void afterLoading(CollectionProxy colProxy)
+        {
+            if(status == IS_UNMATERIALIZED_PROXY)
+            {
+                status = IS_MATERIALIZED_PROXY;
+                handleReferencedObjects(colProxy.iterator());
+                colProxy.removeListener(this);
+            }
+        }
+
+        public String toString()
+        {
+            return ClassUtils.getShortClassName(this.getClass()) + "[references-size="
+                    + (references != null ? "" + references.size() : "undefined") + "]";
+        }
+    }
+
+    //===================================================================
+    // inner class
+    //===================================================================
+    public static class SingleRef extends Image
+    {
+        private Object referenceObjOrProxy;
+        private Identity oid = null;
+        private final ImageListener listener;
+        private final ObjectReferenceDescriptor ord;
+
+        public SingleRef(ImageListener listener, ObjectReferenceDescriptor ord, Object reference)
+        {
+            this.listener = listener;
+            this.ord = ord;
+            this.referenceObjOrProxy = reference;
+        }
+
+        public void cleanup(boolean reuse)
+        {
+            if(!reuse)
+            {
+                referenceObjOrProxy = null;
+            }
+        }
+
+        void referenceProcessing(Image oldImage)
+        {
+            SingleRef oldRef = (SingleRef) oldImage;
+            boolean isSame = getReferenceObjectOrProxy() == oldRef.getReferenceObjectOrProxy();
+            if(!isSame)
+            {
+                Identity newOid = getIdentity();
+                Identity oldOid = oldRef.getIdentity();
+                if(newOid == null)
+                {
+                    if(oldOid != null)
+                    {
+                        listener.deletedOneToOne(ord, oldRef.getReferenceObjectOrProxy(), oldOid, true);
+                    }
+                }
+                else
+                {
+                    if(oldOid == null)
+                    {
+                        listener.addedOneToOne(ord, getReferenceObjectOrProxy(), newOid);
+                    }
+                    else
+                    {
+                        if(!newOid.equals(oldOid))
+                        {
+                            listener.deletedOneToOne(ord, oldRef.getReferenceObjectOrProxy(), oldOid, false);
+                            listener.addedOneToOne(ord, getReferenceObjectOrProxy(), newOid);
+                        }
+                    }
+                }
+            }
+        }
+
+        public Object getReferenceObjectOrProxy()
+        {
+            return referenceObjOrProxy;
+        }
+
+        private Identity getIdentity()
+        {
+            if(oid == null || oid.isTransient())
+            {
+                if(referenceObjOrProxy != null)
+                {
+                    oid = listener.getBroker().serviceIdentity().buildIdentity(referenceObjOrProxy);
+                }
+            }
+            return oid;
+        }
+
+        /**
+         * If a 1:1 reference has changed it will
+         * affects the main object (FK needs update).
+         */
+        public boolean modified(Image toCompare)
+        {
+            boolean modified = false;
+            if(!(this == toCompare))
+            {
+                if(toCompare instanceof Image.SingleRef)
+                {
+                    Image.SingleRef other = (Image.SingleRef) toCompare;
+                    Identity current = getIdentity();
+                    Identity otherOid = other.getIdentity();
+                    modified = current != null ? !current.equals(otherOid) : !(otherOid == null);
+                }
+            }
+            return modified;
+        }
+
+        public String toString()
+        {
+            return ClassUtils.getShortClassName(this.getClass()) + "[reference=" + getIdentity() + "]";
+        }
+    }
+
+    //===================================================================
+    // inner class
+    //===================================================================
+    public static class Field extends Image
+    {
+        private final FieldType type;
+        private final Object value;
+
+        public Field(FieldType type, Object value)
+        {
+            this.type = type;
+            this.value = value;
+        }
+
+        public void cleanup(boolean reuse)
+        {
+        }
+
+        void referenceProcessing(Image oldImage)
+        {
+            // nothing to do
+        }
+
+        /** If a field value has changed return 'true'. */
+        public boolean modified(Image other)
+        {
+            boolean result = false;
+            if(this == other)
+            {
+                result = true;
+            }
+            else
+            {
+                if(other instanceof Field)
+                {
+                    result = !type.equals(value, ((Field) other).value);
+                }
+            }
+            return result;
+        }
+
+        public String toString()
+        {
+            return ClassUtils.getShortClassName(this.getClass()) + "[type=" + type + ", value=" + value + "]";
+        }
+    }
+
+    //===================================================================
+    // inner interface
+    //===================================================================
+    public static interface ImageListener
+    {
+        public void addedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid);
+
+        public void deletedOneToOne(ObjectReferenceDescriptor ord, Object refObjOrProxy, Identity oid, boolean needsUnlink);
+
+        public void addedXToN(CollectionDescriptor ord, Object refObjOrProxy, Identity oid);
+
+        public void deletedXToN(CollectionDescriptor ord, Object refObjOrProxy, Identity oid);
+
+        public PersistenceBrokerInternal getBroker();
+    }
+
+    //====================================================
+    // inner class
+    //====================================================
+
+    /**
+     * Thrown if something unexpected is happen when handling the
+     * object images for state detection.
+     */
+    public static class ImageException extends OJBRuntimeException
+    {
+        public ImageException()
+        {
+        }
+
+        public ImageException(String msg)
+        {
+            super(msg);
+        }
+
+        public ImageException(Throwable cause)
+        {
+            super(cause);
+        }
+
+        public ImageException(String msg, Throwable cause)
+        {
+            super(msg, cause);
+        }
+    }
+}

Added: db/ojb/trunk/src/test/org/apache/ojb/Test_Repository_Renamed_DTD.xml
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/test/org/apache/ojb/Test_Repository_Renamed_DTD.xml?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/test/org/apache/ojb/Test_Repository_Renamed_DTD.xml (added)
+++ db/ojb/trunk/src/test/org/apache/ojb/Test_Repository_Renamed_DTD.xml Sat Jul 15 07:09:47 2006
@@ -0,0 +1,112 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+#/* Copyright 2003-2004 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+-->
+<!--
+This is a test repository used in junit test org.apache.ojb.broker.metadata.MetadataTest
+Runtime reading of repository files.
+DO NOT CHANGE!
+-->
+<!DOCTYPE descriptor-repository SYSTEM "test_renamed_repository.dtd">
+
+
+<descriptor-repository version="1.1" isolation-level="read-uncommitted">
+
+    <!--
+        NOTE: This is a test descriptor used within
+        the junit test suite, DO NOT change any value.
+    -->
+
+    <!-- add some custom attributes -->
+    <attribute
+        attribute-name="attribute-repository-1"
+        attribute-value="attribute-repository-test-value-1">
+    </attribute>
+
+    <attribute
+        attribute-name="attribute-repository-2"
+        attribute-value="attribute-repository-test-value-2">
+    </attribute>
+
+    <jdbc-connection-descriptor
+        jcd-alias="runtime_2"
+        platform="Hsqldb"
+        jdbc-level="2.0"
+        driver="org.hsqldb.jdbcDriver"
+        protocol="jdbc"
+        subprotocol="hsqldb"
+        dbalias="../OJB_FarAway"
+        username="sa"
+        password=""
+        batch-mode="false"
+    >
+
+        <object-cache class="org.apache.ojb.broker.cache.ObjectCacheEmptyImpl">
+            <attribute attribute-name="attr_con" attribute-value="555"/>
+        </object-cache>
+
+        <connection-factory
+            class="org.apache.ojb.broker.accesslayer.ConnectionFactoryPooledImpl"
+            validationQuery="select count(*) from OJB_HL_SEQ"
+        >
+            <attribute attribute-name="maxActive" attribute-value="5"/>
+            <attribute attribute-name="whenExhaustedAction" attribute-value="0"/>
+        </connection-factory>
+
+        <sequence-manager className="org.apache.ojb.broker.util.sequence.SequenceManagerHighLowImpl">
+            <attribute attribute-name="grabSize" attribute-value="5"/>
+        </sequence-manager>
+
+        <!-- add some custom attributes -->
+        <attribute
+            attribute-name="attribute-connection-1"
+            attribute-value="attribute-connection-test-value-1">
+        </attribute>
+
+        <attribute
+            attribute-name="attribute-connection-2"
+            attribute-value="attribute-connection-test-value-2">
+        </attribute>
+
+    </jdbc-connection-descriptor>
+
+    <!-- test pc-class, this class is also declared in junit test metadata,
+    so be careful in handle this metadata -->
+    <class-descriptor
+      class="org.apache.ojb.broker.metadata.MetadataTest$CacheObject"
+      table="DO NOT USE"
+    >
+
+        <object-cache class="org.apache.ojb.broker.cache.ObjectCacheDefaultImpl">
+            <attribute attribute-name="attr_class" attribute-value="444"/>
+        </object-cache>
+
+        <field-descriptor
+         name="objId"
+         column="OBJ_ID"
+         jdbc-type="INTEGER"
+         primarykey="true"
+         autoincrement="true"
+        />
+
+        <field-descriptor
+         name="name"
+         column="NAME"
+         jdbc-type="VARCHAR"
+        />
+    </class-descriptor>
+
+</descriptor-repository>

Added: db/ojb/trunk/src/test/org/apache/ojb/broker/JdbcJavaObjectTest.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/test/org/apache/ojb/broker/JdbcJavaObjectTest.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/test/org/apache/ojb/broker/JdbcJavaObjectTest.java (added)
+++ db/ojb/trunk/src/test/org/apache/ojb/broker/JdbcJavaObjectTest.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,227 @@
+package org.apache.ojb.broker;
+
+/* Copyright 2002-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.lang.Object;
+import java.io.Serializable;
+
+import org.apache.ojb.junit.PBTestCase;
+import org.apache.ojb.broker.query.QueryFactory;
+import org.apache.ojb.broker.query.Query;
+import org.apache.ojb.broker.query.Criteria;
+import org.apache.commons.lang.builder.ToStringBuilder;
+import org.apache.commons.lang.builder.EqualsBuilder;
+
+/**
+ * This test class provide tests for JAVA_OBJECT handling.
+ *
+ * @version $Id: $
+ */
+public class JdbcJavaObjectTest extends PBTestCase
+{
+    public static void main(String[] args)
+    {
+        String[] arr = {JdbcJavaObjectTest.class.getName()};
+        junit.textui.TestRunner.main(arr);
+    }
+
+    public void testInsert()
+    {
+        Container c = new Container("testInsert", new Editor(2, "object_test"), new Editor(3, "editor_test"));
+        doInsert(c);
+    }
+
+    public void testDelete()
+    {
+        Container c = new Container("testDelete", new Editor(2, "object_test"), new Editor(3, "editor_test"));
+        doInsert(c);
+        doDelete(c);
+    }
+
+    public void testRead()
+    {
+        Container c = new Container("testRead", new Editor(2, "object_test"), new Editor(3, "editor_test"));
+        doInsert(c);
+
+        Identity oid = broker.serviceIdentity().buildIdentity(c);
+        Container read = (Container) broker.getObjectByIdentity(oid);
+        assertEquals(c, read);
+
+        broker.clearCache();
+        read = (Container) broker.getObjectByIdentity(oid);
+        assertEquals(c, read);
+
+        doDelete(c);
+    }
+
+    public void testQuery()
+    {
+        Container c = new Container("testQuery", new Editor(2, "object_test"), new Editor(3, "editor_test"));
+        doInsert(c);
+
+        Criteria crit = new Criteria().addEqualTo("id", c.getId());
+        Query q = QueryFactory.newQuery(Container.class, crit);
+        Container read = (Container) broker.getObjectByQuery(q);
+        assertEquals(c, read);
+
+        broker.clearCache();
+        read = (Container) broker.getObjectByQuery(q);
+        assertEquals(c, read);
+
+        doDelete(c);
+    }
+
+    public void testUpdate()
+    {
+        Container c = new Container("testUpdate", new Editor(2, "object_test"), new Editor(3, "editor_test"));
+        doInsert(c);
+
+        broker.beginTransaction();
+        c.setName(getName() + "_update_1");
+        c.getEditor().str = c.getEditor().str + "_update_1";
+        ((Editor) c.getTarget()).str = ((Editor) c.getTarget()).str + "_update_1";
+        broker.store(c);
+        broker.commitTransaction();
+
+        Identity oid = broker.serviceIdentity().buildIdentity(c);
+        Container read = (Container) broker.getObjectByIdentity(oid);
+        assertEquals(c, read);
+
+        broker.clearCache();
+        read = (Container) broker.getObjectByIdentity(oid);
+        assertEquals(c, read);
+
+        broker.clearCache();
+        broker.beginTransaction();
+        c = read;
+        c.setName(getName() + "_update_2");
+        c.getEditor().str = c.getEditor().str + "_update_2";
+        ((Editor) c.getTarget()).str = ((Editor) c.getTarget()).str + "_update_2";
+        broker.store(c);
+        broker.commitTransaction();
+
+        broker.clearCache();
+        read = (Container) broker.getObjectByIdentity(oid);
+        assertEquals(c, read);
+
+        doDelete(c);
+    }
+
+    void doInsert(Container c)
+    {
+        broker.beginTransaction();
+        broker.store(c);
+        broker.commitTransaction();
+    }
+
+    void doDelete(Container c)
+    {
+        broker.beginTransaction();
+        broker.delete(c);
+        broker.commitTransaction();
+    }
+
+    public static class Container implements Serializable
+    {
+        private Integer id;
+        private String name;
+        private Object target;
+        private Editor editor;
+
+        public Container()
+        {
+        }
+
+        public Container(String name, Object target, Editor editor)
+        {
+            this.name = name;
+            this.target = target;
+            this.editor = editor;
+        }
+
+        public Integer getId()
+        {
+            return id;
+        }
+
+        public void setId(Integer id)
+        {
+            this.id = id;
+        }
+
+        public String getName()
+        {
+            return name;
+        }
+
+        public void setName(String name)
+        {
+            this.name = name;
+        }
+
+        public Object getTarget()
+        {
+            return target;
+        }
+
+        public void setTarget(Object target)
+        {
+            this.target = target;
+        }
+
+        public Editor getEditor()
+        {
+            return editor;
+        }
+
+        public void setEditor(Editor editor)
+        {
+            this.editor = editor;
+        }
+
+        public boolean equals(Object obj)
+        {
+            return EqualsBuilder.reflectionEquals(this, obj);
+        }
+
+        public String toString()
+        {
+            return ToStringBuilder.reflectionToString(this);
+        }
+    }
+
+    public static class Editor implements Serializable
+    {
+        String str;
+        int aInt;
+
+        public Editor(int aInt, String str)
+        {
+            this.aInt = aInt;
+            this.str = str;
+        }
+
+        public boolean equals(Object obj)
+        {
+            return EqualsBuilder.reflectionEquals(this, obj);
+        }
+
+        public String toString()
+        {
+            return ToStringBuilder.reflectionToString(this);
+        }
+    }
+}

Added: db/ojb/trunk/src/test/org/apache/ojb/broker/UnwrapHelperTest.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/test/org/apache/ojb/broker/UnwrapHelperTest.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/test/org/apache/ojb/broker/UnwrapHelperTest.java (added)
+++ db/ojb/trunk/src/test/org/apache/ojb/broker/UnwrapHelperTest.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,171 @@
+package org.apache.ojb.broker;
+
+/* Copyright 2002-2006 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+
+import org.apache.ojb.broker.platforms.Platform;
+import org.apache.ojb.broker.platforms.PlatformHsqldbImpl;
+import org.apache.ojb.broker.util.ClassHelper;
+import org.apache.ojb.broker.util.UnwrapHelper;
+import org.apache.ojb.junit.PBTestCase;
+
+/**
+ * Simple test for unwrap helper class.
+ *
+ * @version $Id: $
+ */
+public class UnwrapHelperTest extends PBTestCase
+{
+    private UnwrapHelper helper;
+    private String statementClassName = "org.hsqldb.jdbc.jdbcPreparedStatement";
+    private String connectionClassName = "org.hsqldb.jdbc.jdbcConnection";
+    private String selectExample = "select 1 from OJB_HL_SEQ";
+
+    public static void main(String[] args)
+    {
+        String[] arr = {UnwrapHelperTest.class.getName()};
+        junit.textui.TestRunner.main(arr);
+    }
+
+    public UnwrapHelperTest(String name)
+    {
+        super(name);
+    }
+
+    public void setUp() throws Exception
+    {
+        super.setUp();
+        helper = new UnwrapHelper();
+    }
+
+    public void tearDown() throws Exception
+    {
+        super.tearDown();
+    }
+
+    public void testAddPattern()
+    {
+        int length = helper.getUnwrapPatterns().length;
+        Object[] pattern = helper.getUnwrapPatterns()[0];
+        helper.addUnwrapPattern(pattern);
+        helper.addUnwrapPattern(new Object[][]{null, null, null, null, null, null, null});
+        //System.out.println("## " + helper.toString());
+        assertEquals(length + 2, helper.getUnwrapPatterns().length);
+    }
+
+    public void testUnwrapConnection() throws Exception
+    {
+        Platform pf = broker.serviceConnectionManager().getSupportedPlatform();
+        if(!PlatformHsqldbImpl.class.isAssignableFrom(pf.getClass()))
+        {
+            return;
+        }
+        Connection con = broker.serviceConnectionManager().getConnection();
+        Class target = ClassHelper.getClass(connectionClassName);
+        Connection result = helper.unwrapConnection(target, con);
+        assertNotNull(result);
+        assertTrue(target.isAssignableFrom(result.getClass()));
+    }
+
+    public void testUnwrapStatement() throws Exception
+    {
+        Platform pf = broker.serviceConnectionManager().getSupportedPlatform();
+        if(!PlatformHsqldbImpl.class.isAssignableFrom(pf.getClass()))
+        {
+            return;
+        }
+        Connection con = broker.serviceConnectionManager().getConnection();
+        PreparedStatement ps = con.prepareStatement(selectExample);
+        Class target = ClassHelper.getClass(statementClassName);
+        PreparedStatement result = helper.unwrapStatement(target, ps);
+        assertNotNull(result);
+        assertTrue(target.isAssignableFrom(result.getClass()));
+    }
+
+    /**
+     * Test useful for development.
+     */
+    public void testUnwrapLoop() throws Exception
+    {
+        Platform pf = broker.serviceConnectionManager().getSupportedPlatform();
+        if(!PlatformHsqldbImpl.class.isAssignableFrom(pf.getClass()))
+        {
+            return;
+        }
+        Connection con = broker.serviceConnectionManager().getConnection();
+        PreparedStatement ps = con.prepareStatement(selectExample);
+        Class targetC = ClassHelper.getClass(connectionClassName);
+        Class targetS = ClassHelper.getClass(statementClassName);
+        int loops = 5000;
+        long t = System.currentTimeMillis();
+        for(int i = 0;i<loops;i++)
+        {
+            Connection resultC = helper.unwrapConnection(targetC, con);
+            assertNotNull(resultC);
+            PreparedStatement resultS = helper.unwrapStatement(targetS, ps);
+            assertNotNull(resultS);
+        }
+        t = System.currentTimeMillis() - t;
+        System.out.println("Unwarp " + loops + " con/stmt (optimized mode): " + t + " ms");
+
+        Object[] pattern = helper.getUnwrapPatterns()[0];
+        helper.addUnwrapPattern(pattern);
+        helper.addUnwrapPattern(new Object[][]{null, null, null, null, null, null, null});
+        //System.out.println("Patterns: " + helper);
+
+        t = System.currentTimeMillis();
+        for(int i = 0;i<loops;i++)
+        {
+            Connection resultC = helper.unwrapConnection(targetC, con);
+            assertNotNull(resultC);
+            PreparedStatement resultS = helper.unwrapStatement(targetS, ps);
+            assertNotNull(resultS);
+        }
+        t = System.currentTimeMillis() - t;
+        System.out.println("Unwarp " + loops + " con/stmt (optimized mode): " + t + " ms");
+
+
+        helper = new UnwrapHelper(true, false);
+        t = System.currentTimeMillis();
+        for(int i = 0;i<loops;i++)
+        {
+            Connection resultC = helper.unwrapConnection(targetC, con);
+            assertNotNull(resultC);
+            PreparedStatement resultS = helper.unwrapStatement(targetS, ps);
+            assertNotNull(resultS);
+        }
+        t = System.currentTimeMillis() - t;
+        System.out.println("Unwarp " + loops + " con/stmt (search all patterns): " + t + " ms");
+
+        pattern = helper.getUnwrapPatterns()[0];
+        helper.addUnwrapPattern(pattern);
+        helper.addUnwrapPattern(new Object[][]{null, null, null, null, null, null, null});
+        //System.out.println("Patterns: " + helper);
+
+        t = System.currentTimeMillis();
+        for(int i = 0;i<loops;i++)
+        {
+            Connection resultC = helper.unwrapConnection(targetC, con);
+            assertNotNull(resultC);
+            PreparedStatement resultS = helper.unwrapStatement(targetS, ps);
+            assertNotNull(resultS);
+        }
+        t = System.currentTimeMillis() - t;
+        System.out.println("Unwarp " + loops + " con/stmt (search all patterns): " + t + " ms");
+    }
+}

Added: db/ojb/trunk/src/test/org/apache/ojb/performance/PerfRunner.java
URL: http://svn.apache.org/viewvc/db/ojb/trunk/src/test/org/apache/ojb/performance/PerfRunner.java?rev=422225&view=auto
==============================================================================
--- db/ojb/trunk/src/test/org/apache/ojb/performance/PerfRunner.java (added)
+++ db/ojb/trunk/src/test/org/apache/ojb/performance/PerfRunner.java Sat Jul 15 07:09:47 2006
@@ -0,0 +1,213 @@
+package org.apache.ojb.performance;
+
+/* Copyright 2002-2005 The Apache Software Foundation
+ *
+ * Licensed under the Apache License, Version 2.0 (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.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+
+/**
+ * Derivate this class to implement a test instance for the performance test.
+ *
+ * @version $Id: PerfRunner.java 366953 2006-01-08 01:27:44 +0100 (So, 08 Jan 2006) arminw $
+ */
+class PerfRunner
+{
+    private final String PREFIX_LOG = "[" + this.getClass().getName() + "] ";
+
+    /**
+     * testTimes[0] startTime/test length
+     * testTimes[1] inserting times
+     * testTimes[2] fetching times
+     * testTimes[3] fetching repeat times
+     * testTimes[4] get by Identity times
+     * testTimes[5] updating times
+     * testTimes[6] deleting times
+     */
+    private long[] testTimes;
+    private ThreadGroup threadGroup;
+    private PerfMain perfMain;
+    private long perfTestId;
+    private boolean checked;
+    private boolean isValid;
+
+    /**
+     * The threads that are executing.
+     */
+    private Thread threads[] = null;
+    private Class testClass;
+    private PerfTest test;
+
+
+    public PerfRunner(ThreadGroup group, Class perfTestClass)
+    {
+        this.perfTestId = System.currentTimeMillis();
+        this.checked = false;
+        this.testClass = perfTestClass;
+        // create a tmp test instance
+        this.test = createTest();
+        this.threadGroup = group;
+        this.isValid = true;
+    }
+
+    private PerfTest createTest()
+    {
+        try
+        {
+            PerfTest result = (PerfTest) testClass.newInstance();
+            result.setPerfRunner(this);
+            return result;
+        }
+        catch(Exception e)
+        {
+            e.printStackTrace();
+            throw new RuntimeException("Can't create test instance: " + e.getMessage());
+        }
+    }
+
+    /**
+     * Returns the name of the test
+     */
+    public String testName()
+    {
+        return test.testName();
+    }
+
+    private void checkApi() throws Exception
+    {
+        String name = testName() + "_Pre_Test_Object";
+        PerfArticle article = test.getPreparedPerfArticle(name);
+        PerfArticle[] arr = new PerfArticle[]{article};
+        test.insertNewArticles(arr);
+        test.readArticlesByCursor(name);
+        test.updateArticles(arr);
+        test.deleteArticles(arr);
+        checked = true;
+    }
+
+    /**
+     * Interrupt the running threads.
+     */
+    protected void interruptThreads()
+    {
+        if (threads != null)
+        {
+            for (int i = 0; i < threads.length; i++)
+            {
+                threads[i].interrupt();
+            }
+        }
+        PerfMain.printer().println("## Test failed! ##");
+        PerfMain.printer().println("## Test failed! ##");
+    }
+
+    /**
+     * Run the threads.
+     */
+    protected void runTestHandles(final PerfTest[] runnables)
+    {
+        if (runnables == null)
+        {
+            throw new IllegalArgumentException("runnables is null");
+        }
+        threads = new Thread[runnables.length];
+        for (int i = 0; i < threads.length; i++)
+        {
+            threads[i] = new Thread(threadGroup, runnables[i]);
+        }
+        for (int i = 0; i < threads.length; i++)
+        {
+            threads[i].start();
+        }
+        try
+        {
+            for (int i = 0; i < threads.length; i++)
+            {
+                threads[i].join();
+            }
+        }
+        catch (InterruptedException ignore)
+        {
+            PerfMain.printer().println(PREFIX_LOG + "Thread join interrupted.");
+        }
+    }
+
+    public void performTest()
+    {
+        try
+        {
+            // prepare tmp used test
+            test.init();
+
+            if (!checked)
+            {
+                checkApi();
+                //PerfMain.printer().println("# PerfTest: " + testName() + " # ");
+            }
+
+            int objectCount;
+            int objectCountAfter;
+
+            testTimes = new long[7];
+
+            objectCount = test.articleCount();
+
+            // now we start the test threads
+            PerfTest[] perfHandles = new PerfTest[PerfMain.getConcurrentThreads()];
+            for (int i = 0; i < PerfMain.getConcurrentThreads(); i++)
+            {
+                perfHandles[i] = createTest();
+            }
+            runTestHandles(perfHandles);
+            
+            // end of test threads
+            objectCountAfter = test.articleCount();
+            perfMain.addPeriodResult(testName(), testTimes, isValid);
+            perfMain.addConsistentResult(testName(), objectCount, objectCountAfter);
+
+            // tear down tmp used test
+            test.tearDown();
+        }
+        catch (Exception e)
+        {
+            e.printStackTrace();
+            perfMain.registerException(PREFIX_LOG, e);
+        }
+    }
+
+    public void registerException(String causer, Exception e)
+    {
+        this.isValid = false;
+        perfMain.registerException(causer, e);
+    }
+
+    public synchronized void addTime(short position, long time)
+    {
+        testTimes[position] += time;
+    }
+
+    public void registerPerfMain(PerfMain aPerfMain)
+    {
+        this.perfMain = aPerfMain;
+    }
+
+    public ThreadGroup getThreadGroup()
+    {
+        return threadGroup;
+    }
+
+    public long getPerfTestId()
+    {
+        return perfTestId;
+    }
+}



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