db-ojb-dev mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From mka...@apache.org
Subject cvs commit: db-ojb/src/java/org/apache/ojb/broker/platforms PlatformOracle9iImpl.java PlatformWLOracle9iImpl.java
Date Tue, 15 Mar 2005 02:50:46 GMT
mkalen      2005/03/14 18:50:45

  Modified:    src/java/org/apache/ojb/broker/platforms
                        PlatformOracle9iImpl.java
                        PlatformWLOracle9iImpl.java
  Log:
  Merge with OJB_1_0_RELEASE branch: Add support for Oracle 10g JDBC-driver. Add generic unwrapping
of Connection and PreparedStatement (current implementation handles large CLOB/BLOB for Oracle
10g, DBPC and P6Spy). Add (untested) support for BEA WebLogic and deprecate WLOracle9i platform.
  
  Revision  Changes    Path
  1.17      +261 -90   db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java
  
  Index: PlatformOracle9iImpl.java
  ===================================================================
  RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformOracle9iImpl.java,v
  retrieving revision 1.16
  retrieving revision 1.17
  diff -u -r1.16 -r1.17
  --- PlatformOracle9iImpl.java	14 Mar 2005 22:48:32 -0000	1.16
  +++ PlatformOracle9iImpl.java	15 Mar 2005 02:50:45 -0000	1.17
  @@ -16,9 +16,7 @@
    */
   
   import org.apache.ojb.broker.metadata.JdbcConnectionDescriptor;
  -import org.apache.ojb.broker.metadata.JdbcType;
   import org.apache.ojb.broker.util.ClassHelper;
  -import org.apache.ojb.broker.util.JdbcTypesHelper;
   import org.apache.ojb.broker.util.logging.Logger;
   import org.apache.ojb.broker.util.logging.LoggerFactory;
   
  @@ -27,6 +25,7 @@
   import java.sql.Connection;
   import java.sql.PreparedStatement;
   import java.sql.SQLException;
  +import java.sql.Statement;
   import java.sql.Types;
   import java.util.Collections;
   import java.util.Map;
  @@ -36,6 +35,12 @@
    * This class is a concrete implementation of <code>Platform</code>. Provides
    * an implementation that uses Oracle specific optimizations and LOB-handling.
    *
  + * NOTE: When using BEA WebLogic and BLOB/CLOB datatypes, the physical connection will
be
  + * used causing WebLogic to mark it as "infected" and discard it when
  + * the logicical connection is closed. You can change this behavior by setting the
  + * RemoveInfectedConnectionsEnabled attribute on a connection pool.
  + * see <a href="http://e-docs.bea.com/wls/docs81/jdbc/thirdparty.html#1043646">WebLogic
docs</a>.
  + *
    * Optimization: Oracle Batching (not standard JDBC batching)
    * see <a href="http://technet.oracle.com/products/oracle9i/daily/jun07.html">OTN</a>.
    *
  @@ -82,17 +87,81 @@
       protected static final int STATEMENTS_PER_BATCH = 20;
       protected static Map m_batchStatementsInProgress = Collections.synchronizedMap(new
WeakHashMap(STATEMENTS_PER_BATCH));
   
  +    protected static final Class[] PARAM_TYPE_EMPTY = {};
       protected static final Class[] PARAM_TYPE_INTEGER = {Integer.TYPE};
       protected static final Class[] PARAM_TYPE_BOOLEAN = {Boolean.TYPE};
       protected static final Class[] PARAM_TYPE_STRING = {String.class};
   
  +    protected static final Object[] PARAM_EMPTY = new Object[]{};
       protected static final Object[] PARAM_STATEMENT_CACHE_SIZE = new Object[]{new Integer(STATEMENT_CACHE_SIZE)};
       protected static final Object[] PARAM_ROW_PREFETCH_SIZE = new Object[]{new Integer(ROW_PREFETCH_SIZE)};
       protected static final Object[] PARAM_STATEMENT_BATCH_SIZE = new Object[]{new Integer(STATEMENTS_PER_BATCH)};
       protected static final Object[] PARAM_BOOLEAN_TRUE = new Object[]{Boolean.TRUE};
   
  -    protected static final JdbcType BASE_CLOB = JdbcTypesHelper.getJdbcTypeByName("clob");
  -    protected static final JdbcType BASE_BLOB = JdbcTypesHelper.getJdbcTypeByName("blob");
  +    protected static Class ORA_CONN_CLASS;
  +    protected static Class ORA_PS_CLASS;
  +    protected static Class ORA_CLOB_CLASS;
  +    protected static Class ORA_BLOB_CLASS;
  +    protected static Class[] PARAM_TYPE_INT_ORACLOB;
  +    protected static Class[] PARAM_TYPE_INT_ORABLOB;
  +    protected static Method METHOD_SET_STATEMENT_CACHE_SIZE;
  +    protected static Method METHOD_SET_IMPLICIT_CACHING_ENABLED;
  +    protected static Method METHOD_SET_ROW_PREFETCH;
  +    protected static Method METHOD_SET_BLOB = null;
  +    protected static Method METHOD_SET_CLOB = null;
  +    protected static boolean ORA_STATEMENT_CACHING_AVAILABLE;
  +    protected static boolean ORA_ROW_PREFETCH_AVAILABLE;
  +    protected static boolean ORA_CLOB_HANDLING_AVAILABLE;
  +    protected static boolean ORA_BLOB_HANDLING_AVAILABLE;
  +
  +    /** Method names used by {@link #unwrapConnection}. */
  +    protected static final String UNWRAP_CONN_METHOD_NAMES[] =
  +            {
  +                "unwrapCompletely"      /* Oracle 10g */,
  +                "getInnermostDelegate"  /* Commons DBCP */,
  +                "getVendorConnection"   /* BEA WebLogic */,
  +                "getJDBC"               /* P6Spy */
  +            };
  +    /**
  +     * Method parameter signature used by {@link #unwrapConnection} for corresponding
  +     * {@link #UNWRAP_CONN_METHOD_NAMES}-index.
  +     * If signature is not {@link #PARAM_TYPE_EMPTY}, the actual connection object
  +     * will be passed at runtime. (NB: Requires special handling of param type in constructor.)
  +     */
  +    protected static final Class[][] UNWRAP_CONN_PARAM_TYPES =
  +            {
  +                null  /* Index 0 reserved for Oracle 10g - initialized in constructor */,
  +                PARAM_TYPE_EMPTY        /* Commons DBCP */,
  +                PARAM_TYPE_EMPTY        /* BEA WebLogic */,
  +                PARAM_TYPE_EMPTY        /* P6Spy */
  +            };
  +    /** Method names used by {@link #unwrapStatement}. */
  +    protected static final String UNWRAP_PS_METHOD_NAMES[] =
  +            {
  +                "getInnermostDelegate"  /* Commons DBCP */,
  +                "getJDBC"               /* P6Spy */
  +            };
  +    /**
  +     * Method parameter signature used by {@link #unwrapStatement} for corresponding
  +     * {@link #UNWRAP_PS_METHOD_NAMES}-index.
  +     * If signature is not {@link #PARAM_TYPE_EMPTY}, the actual Statement object
  +     * will be passed at runtime. (NB: Requires special handling of param type in constructor.)
  +     */
  +    protected static final Class[][] UNWRAP_PS_PARAM_TYPES =
  +            {
  +                PARAM_TYPE_EMPTY        /* Commons DBCP */,
  +                PARAM_TYPE_EMPTY        /* P6Spy */
  +            };
  +
  +
  +    /**
  +     * Default constructor.
  +     * Runs static init needed for Oracle-extensions and large BLOB/CLOB support to function.
  +     */
  +    public PlatformOracle9iImpl()
  +    {
  +        initOracleReflectedVars();
  +    }
   
       /**
        * Enables Oracle statement caching and row prefetching if supported by the JDBC-driver.
  @@ -104,25 +173,29 @@
        * @see <a href="http://otn.oracle.com/sample_code/tech/java/sqlj_jdbc/files/advanced/RowPrefetchSample/Readme.html">
        * Oracle TechNet Row Pre-fetch Sample<a>
        */
  -    public void initializeJdbcConnection(JdbcConnectionDescriptor jcd, Connection conn)
throws PlatformException
  +    public void initializeJdbcConnection(final JdbcConnectionDescriptor jcd,
  +                                         final Connection conn)
  +            throws PlatformException
       {
           // Do all the generic initialization in PlatformDefaultImpl first
           super.initializeJdbcConnection(jcd, conn);
   
  -        // Check for OracleConnection-specific statement caching methods
  -        final Method methodSetStatementCacheSize;
  -        final Method methodSetImplicitCachingEnabled;
  -        methodSetStatementCacheSize = ClassHelper.getMethod(conn, "setStatementCacheSize",
PARAM_TYPE_INTEGER);
  -        methodSetImplicitCachingEnabled = ClassHelper.getMethod(conn, "setImplicitCachingEnabled",
PARAM_TYPE_BOOLEAN);
  +        // Check if this is a wrapped connection and if so unwrap it
  +        final Connection oraConn = unwrapConnection(conn);
  +        if (oraConn == null)
  +        {
  +            return;
  +        }
   
  -        final boolean statementCachingSupported = methodSetStatementCacheSize != null &&
methodSetImplicitCachingEnabled != null;
  -        if (statementCachingSupported)
  +        // At this point we know that we have an OracleConnection instance and can thus
  +        // try to invoke methods via reflection (if available)
  +        if (ORA_STATEMENT_CACHING_AVAILABLE)
           {
               try
               {
                   // Set number of cached statements and enable implicit caching
  -                methodSetStatementCacheSize.invoke(conn, PARAM_STATEMENT_CACHE_SIZE);
  -                methodSetImplicitCachingEnabled.invoke(conn, PARAM_BOOLEAN_TRUE);
  +                METHOD_SET_STATEMENT_CACHE_SIZE.invoke(oraConn, PARAM_STATEMENT_CACHE_SIZE);
  +                METHOD_SET_IMPLICIT_CACHING_ENABLED.invoke(oraConn, PARAM_BOOLEAN_TRUE);
               }
               catch (Exception e)
               {
  @@ -130,17 +203,12 @@
               }
           }
   
  -        // Check for OracleConnection-specific row pre-fetching support
  -        final Method methodSetRowPrefetch;
  -        methodSetRowPrefetch = ClassHelper.getMethod(conn, "setDefaultRowPrefetch", PARAM_TYPE_INTEGER);
  -
  -        final boolean rowPrefetchingSupported = methodSetRowPrefetch != null;
  -        if (rowPrefetchingSupported)
  +        if (ORA_ROW_PREFETCH_AVAILABLE)
           {
               try
               {
                   // Set number of prefetched rows
  -                methodSetRowPrefetch.invoke(conn, PARAM_ROW_PREFETCH_SIZE);
  +                METHOD_SET_ROW_PREFETCH.invoke(oraConn, PARAM_ROW_PREFETCH_SIZE);
               }
               catch (Exception e)
               {
  @@ -150,6 +218,18 @@
       }
   
       /**
  +     * Performs platform-specific operations on each statement.
  +     * @param stmt the statement just created
  +     */
  +    public void afterStatementCreate(Statement stmt)
  +    {
  +        // mkalen:  do NOT call super#afterStatementCreate since escape processing for
SQL92
  +        //          syntax is enabled by default for Oracle9i and higher, and explicit
calls
  +        //          to setEscapeProcessing for PreparedStatements will make Oracle 10g
JDBC-
  +        //          driver throw exceptions (and is functionally useless).
  +    }
  +
  +    /**
        * Try Oracle update batching and call setExecuteBatch or revert to
        * JDBC update batching. See 12-2 Update Batching in the Oracle9i
        * JDBC Developer's Guide and Reference.
  @@ -250,45 +330,23 @@
       /** @see Platform#setObjectForStatement */
       public void setObjectForStatement(PreparedStatement ps, int index, Object value, int
sqlType) throws SQLException
       {
  -        boolean nativeOraBlobsSupported = false;
  -        boolean nativeOraClobsSupported = false;
  -        Method methodSetBlob = null;
  -        Method methodSetClob = null;
  -
           // Check for Oracle JDBC-driver LOB-support
  -        if (sqlType == Types.CLOB)
  -        {
  -            try
  -            {
  -                Class clobClass = ClassHelper.getClass("oracle.sql.CLOB", false);
  -                methodSetClob = ClassHelper.getMethod(ps, "setCLOB", new Class[]{Integer.TYPE,
clobClass});
  -                nativeOraClobsSupported = methodSetClob != null;
  -                if (!nativeOraClobsSupported && logger.isDebugEnabled())
  -                {
  -                    logger.debug("Oracle CLOB JDBC extensions not available for connection
" + ps.getConnection().getClass());
  -                }
  -            }
  -            catch (Exception e)
  -            {
  -                logger.warn("Exception while checking for Oracle CLOB JDBC extensions",
e);
  -            }
  +        final Statement oraStmt;
  +        final Connection oraConn;
  +        final boolean oraLargeLobSupportAvailable;
  +        if (sqlType == Types.CLOB || sqlType == Types.BLOB)
  +        {
  +            oraStmt = unwrapStatement(ps);
  +            oraConn = unwrapConnection(ps.getConnection());
  +            oraLargeLobSupportAvailable =
  +                    oraStmt != null && oraConn != null &&
  +                    (sqlType == Types.CLOB ? ORA_CLOB_HANDLING_AVAILABLE : ORA_BLOB_HANDLING_AVAILABLE);
           }
  -        else if (sqlType == Types.BLOB)
  +        else
           {
  -            try
  -            {
  -                Class blobClass = ClassHelper.getClass("oracle.sql.BLOB", false);
  -                methodSetBlob = ClassHelper.getMethod(ps, "setBLOB", new Class[]{Integer.TYPE,
blobClass});
  -                nativeOraBlobsSupported = methodSetBlob != null;
  -                if (!nativeOraBlobsSupported && logger.isDebugEnabled())
  -                {
  -                    logger.debug("Oracle CLOB JDBC extensions not available for connection
" + ps.getConnection().getClass());
  -                }
  -            }
  -            catch (Exception e)
  -            {
  -                logger.warn("Exception while checking for Oracle BLOB JDBC extensions",
e);
  -            }
  +            oraStmt = null;
  +            oraConn = null;
  +            oraLargeLobSupportAvailable = false;
           }
   
           // Type-specific Oracle conversions
  @@ -313,48 +371,30 @@
           {
               ps.setLong(index, ((Long) value).longValue());
           }
  -        else if (sqlType == Types.CLOB && value instanceof String)
  +        else if (sqlType == Types.CLOB && oraLargeLobSupportAvailable &&
value instanceof String)
           {
  -            // First try native Oracle CLOB handling
  -            if (nativeOraClobsSupported)
  +            // TODO: If using Oracle update batching with the thin driver, throw exception
on 4k limit
  +            try
               {
  -                // TODO: If using Oracle update batching with the thin driver, throw exception
on 4k limit
  -                try
  -                {
  -                    Object clob = Oracle9iLobHandler.createCLOBFromString(ps.getConnection(),
(String) value);
  -                    methodSetClob.invoke(ps, new Object[]{new Integer(index), clob});
  -                }
  -                catch (Exception e)
  -                {
  -                    throw new SQLException(e.getLocalizedMessage());
  -                }
  +                Object clob = Oracle9iLobHandler.createCLOBFromString(oraConn, (String)
value);
  +                METHOD_SET_CLOB.invoke(oraStmt, new Object[]{new Integer(index), clob});
               }
  -            else
  -            // Fall back to default Oracle platform capabilities
  +            catch (Exception e)
               {
  -                super.setObjectForStatement(ps, index, value, sqlType);
  +                throw new SQLException(e.getLocalizedMessage());
               }
           }
  -        else if (sqlType == Types.BLOB && value instanceof byte[])
  +        else if (sqlType == Types.BLOB && oraLargeLobSupportAvailable &&
value instanceof byte[])
           {
  -            // First try native Oracle BLOB handling
  -            if (nativeOraBlobsSupported)
  +            // TODO: If using Oracle update batching with the thin driver, throw exception
on 2k limit
  +            try
               {
  -                // TODO: If using Oracle update batching with the thin driver, throw exception
on 2k limit
  -                try
  -                {
  -                    Object blob = Oracle9iLobHandler.createBLOBFromByteArray(ps.getConnection(),
(byte[]) value);
  -                    methodSetBlob.invoke(ps, new Object[]{new Integer(index), blob});
  -                }
  -                catch (Exception e)
  -                {
  -                    throw new SQLException(e.getLocalizedMessage());
  -                }
  +                Object blob = Oracle9iLobHandler.createBLOBFromByteArray(oraConn, (byte[])
value);
  +                METHOD_SET_BLOB.invoke(oraStmt, new Object[]{new Integer(index), blob});
               }
  -            else
  -            // Fall back to default Oracle platform capabilities
  +            catch (Exception e)
               {
  -                super.setObjectForStatement(ps, index, value, sqlType);
  +                throw new SQLException(e.getLocalizedMessage());
               }
           }
           else
  @@ -374,4 +414,135 @@
           return SQL92_NOPAREN_JOIN_SYNTAX;
       }
   
  +    /**
  +     * Return an OracleConnection after trying to unwrap from known Connection wrappers.
  +     * @param conn the connection to unwrap (if needed)
  +     * @return OracleConnection or null if not able to unwrap
  +     */
  +    protected Connection unwrapConnection(Connection conn)
  +    {
  +        final Object unwrapped;
  +        unwrapped = genericUnwrap(ORA_CONN_CLASS, conn, UNWRAP_CONN_METHOD_NAMES, UNWRAP_CONN_PARAM_TYPES);
  +        if (unwrapped == null && logger.isDebugEnabled())
  +        {
  +            // mkalen:  only log this as debug since it will be logged for every connection
  +            //          (ie only useful during development).
  +            logger.debug("PlatformOracle9iImpl could not unwrap " + conn.getClass().getName()
+
  +                         ", Oracle-extensions disabled.");
  +        }
  +        return (Connection) unwrapped;
  +    }
  +
  +    /**
  +     * Return an OraclePreparedStatement after trying to unwrap from known Statement wrappers.
  +     * @param ps the PreparedStatement to unwrap (if needed)
  +     * @return OraclePreparedStatement or null if not able to unwrap
  +     */
  +    protected Statement unwrapStatement(Statement ps)
  +    {
  +        final Object unwrapped;
  +        unwrapped = genericUnwrap(ORA_PS_CLASS, ps, UNWRAP_PS_METHOD_NAMES, UNWRAP_PS_PARAM_TYPES);
  +        if (unwrapped == null && logger.isDebugEnabled())
  +        {
  +            // mkalen:  only log this as debug since it will be logged for every connection
  +            //          (ie only useful during development).
  +            logger.debug("PlatformOracle9iImpl could not unwrap " + ps.getClass().getName()
+
  +                         ", large CLOB/BLOB support disabled.");
  +        }
  +        return (Statement) unwrapped;
  +    }
  +    
  +    protected Object genericUnwrap(Class classToMatch, Object toUnwrap,
  +                                   String[] methodNameCandidates,
  +                                   Class[][] methodTypeCandidates)
  +    {
  +        if (classToMatch == null)
  +        {
  +            return null;
  +        }
  +
  +        Object unwrapped;
  +        final Class psClass = toUnwrap.getClass();
  +        if (classToMatch.isAssignableFrom(psClass))
  +        {
  +            return toUnwrap;
  +        }
  +        try
  +        {
  +            String methodName;
  +            Class[] paramTypes;
  +            Object[] args;
  +            for (int i = 0; i < methodNameCandidates.length; i++)
  +            {
  +                methodName = methodNameCandidates[i];
  +                paramTypes = methodTypeCandidates[i];
  +                final Method method = ClassHelper.getMethod(toUnwrap, methodName, paramTypes);
  +                if (method != null)
  +                {
  +                    args = paramTypes == PARAM_TYPE_EMPTY ? PARAM_EMPTY : new Object[]{
toUnwrap };
  +                    unwrapped = method.invoke(toUnwrap, args);
  +                    if (unwrapped != null)
  +                    {
  +                        if (classToMatch.isAssignableFrom(unwrapped.getClass()))
  +                        {
  +                            return unwrapped;
  +                        }
  +                        // When using eg both DBCP and P6Spy we have to recursively unwrap
  +                        return genericUnwrap(classToMatch, unwrapped,
  +                                methodNameCandidates, methodTypeCandidates);
  +                    }
  +                }
  +            }
  +        }
  +        catch (Exception e)
  +        {
  +            // ignore
  +            if (logger.isDebugEnabled())
  +            {
  +                logger.debug("genericUnwrap failed", e);
  +            }
  +        }
  +        return null;
  +    }
  +
  +    protected static void initOracleReflectedVars() {
  +        try
  +        {
  +            /*
  +            Check for Oracle-specific classes, OracleConnection-specific
  +            statement caching/row pre-fetch methods and Oracle BLOB/CLOB access methods.
  +            We can do this in constructor in spite of possible mixing of instance being
  +            able vs unable passed at runtime (since withouth these classes and methods
  +            it's impossible to enable ORA-extensions at all even if instances are capable).
  +            */
  +            ORA_CONN_CLASS = ClassHelper.getClass("oracle.jdbc.OracleConnection", false);
  +            ORA_PS_CLASS = ClassHelper.getClass("oracle.jdbc.OraclePreparedStatement",
false);
  +            ORA_CLOB_CLASS = ClassHelper.getClass("oracle.sql.CLOB", false);
  +            ORA_BLOB_CLASS = ClassHelper.getClass("oracle.sql.BLOB", false);
  +            PARAM_TYPE_INT_ORACLOB = new Class[]{ Integer.TYPE, ORA_CLOB_CLASS };
  +            PARAM_TYPE_INT_ORABLOB = new Class[]{ Integer.TYPE, ORA_BLOB_CLASS };
  +
  +            // Index 0 reserved for Oracle 10g
  +            UNWRAP_CONN_PARAM_TYPES[0] = new Class[]{ ORA_CONN_CLASS };
  +
  +            METHOD_SET_STATEMENT_CACHE_SIZE =
  +                    ClassHelper.getMethod(ORA_CONN_CLASS, "setStatementCacheSize", PARAM_TYPE_INTEGER);
  +            METHOD_SET_IMPLICIT_CACHING_ENABLED =
  +                    ClassHelper.getMethod(ORA_CONN_CLASS, "setImplicitCachingEnabled",
PARAM_TYPE_BOOLEAN);
  +            METHOD_SET_ROW_PREFETCH = ClassHelper.getMethod(ORA_CONN_CLASS, "setDefaultRowPrefetch",
PARAM_TYPE_INTEGER);
  +            METHOD_SET_CLOB = ClassHelper.getMethod(ORA_PS_CLASS, "setCLOB", PARAM_TYPE_INT_ORACLOB);
  +            METHOD_SET_BLOB = ClassHelper.getMethod(ORA_PS_CLASS, "setBLOB", PARAM_TYPE_INT_ORABLOB);
  +
  +            ORA_STATEMENT_CACHING_AVAILABLE =
  +                    METHOD_SET_STATEMENT_CACHE_SIZE != null && METHOD_SET_IMPLICIT_CACHING_ENABLED
!= null;
  +            ORA_ROW_PREFETCH_AVAILABLE = METHOD_SET_ROW_PREFETCH != null;
  +            ORA_CLOB_HANDLING_AVAILABLE = METHOD_SET_CLOB != null;
  +            ORA_BLOB_HANDLING_AVAILABLE = METHOD_SET_BLOB != null;
  +        }
  +        catch (ClassNotFoundException e)
  +        {
  +            // ignore (we tried...)
  +        }
  +    }
  +
   }
  
  
  
  1.3       +3 -1      db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformWLOracle9iImpl.java
  
  Index: PlatformWLOracle9iImpl.java
  ===================================================================
  RCS file: /home/cvs/db-ojb/src/java/org/apache/ojb/broker/platforms/PlatformWLOracle9iImpl.java,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- PlatformWLOracle9iImpl.java	22 May 2004 09:55:33 -0000	1.2
  +++ PlatformWLOracle9iImpl.java	15 Mar 2005 02:50:45 -0000	1.3
  @@ -58,6 +58,8 @@
    * @see PlatformDefaultImpl
    * @see PlatformOracleImpl
    * @see PlatformOracle9iImpl
  + *
  + * @deprecated since OJB 1.0.2 the default {@link PlatformOracle9iImpl} should be usable
in WebLogic
    */
   public class PlatformWLOracle9iImpl extends PlatformOracleImpl
   {
  
  
  

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


Mime
View raw message