[exo-jcr-commits] exo-jcr SVN: r1123 - in jcr/trunk/exo.jcr.component.core/src: main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation and 2 other directories.

do-not-reply at jboss.org do-not-reply at jboss.org
Thu Dec 17 09:14:15 EST 2009


Author: sergiykarpenko
Date: 2009-12-17 09:14:15 -0500 (Thu, 17 Dec 2009)
New Revision: 1123

Added:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/DBConstants.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewJDBCStorageConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewJDBCWorkspaceDataContainer.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewSQLExceptionHandler.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/HSQLDBConnectionFactory.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/HSQLDBMultiDbJDBCConnection.java
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java
Modified:
   jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/PrimaryTypeNotFoundException.java
   jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/writing/TestNodeReference.java
Log:
EXOJCR-302: NewJDBCStorageConnection instances added

Modified: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/PrimaryTypeNotFoundException.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/PrimaryTypeNotFoundException.java	2009-12-17 14:02:12 UTC (rev 1122)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/PrimaryTypeNotFoundException.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -46,7 +46,7 @@
     * @param path
     *          - context path
     */
-   PrimaryTypeNotFoundException(String message, QPath path)
+   public PrimaryTypeNotFoundException(String message, QPath path)
    {
       super(message);
       this.path = path;

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/DBConstants.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/DBConstants.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/DBConstants.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -0,0 +1,326 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.storage.jdbc.optimisation;
+
+/**
+ * Created by The eXo Platform SAS.
+ * 
+ * @author Gennady Azarenkov
+ * @version $Id: DBConstants.java 34801 2009-07-31 15:44:50Z dkatayev $
+ */
+
+public class DBConstants
+{
+   // ======================== Error constants ======================== 
+   /**
+    * JCR_PK_ITEM.
+    */
+   protected String JCR_PK_ITEM;
+
+   /**
+    * JCR_FK_ITEM_PARENT.
+    */
+   protected String JCR_FK_ITEM_PARENT;
+
+   /**
+    * JCR_IDX_ITEM_PARENT.
+    */
+   protected String JCR_IDX_ITEM_PARENT;
+
+   /**
+    * JCR_IDX_ITEM_PARENT_NAME.
+    */
+   protected String JCR_IDX_ITEM_PARENT_NAME;
+
+   /**
+    * JCR_IDX_ITEM_PARENT_ID.
+    */
+   protected String JCR_IDX_ITEM_PARENT_ID;
+
+   /**
+    * JCR_PK_VALUE.
+    */
+   protected String JCR_PK_VALUE;
+
+   /**
+    * JCR_FK_VALUE_PROPERTY.
+    */
+   protected String JCR_FK_VALUE_PROPERTY;
+
+   /**
+    * JCR_IDX_VALUE_PROPERTY.
+    */
+   protected String JCR_IDX_VALUE_PROPERTY;
+
+   /**
+    * JCR_PK_REF.
+    */
+   protected String JCR_PK_REF;
+
+   /**
+    * JCR_IDX_REF_PROPERTY.
+    */
+   protected String JCR_IDX_REF_PROPERTY;
+
+   // ======================== SQL scripts ======================== 
+   /**
+    * FIND_ITEM_BY_ID.
+    */
+   protected String FIND_ITEM_BY_ID;
+
+   /**
+    * FIND_ITEM_BY_PATH.
+    */
+   protected String FIND_ITEM_BY_PATH;
+
+   /**
+    * FIND_ITEM_BY_NAME.
+    */
+   protected String FIND_ITEM_BY_NAME;
+
+   /**
+    * FIND_CHILD_PROPERTY_BY_PATH.
+    */
+   protected String FIND_CHILD_PROPERTY_BY_PATH;
+
+   /**
+    * FIND_PROPERTY_BY_NAME.
+    */
+   protected String FIND_PROPERTY_BY_NAME;
+
+   /**
+    * FIND_REFERENCES.
+    */
+   protected String FIND_REFERENCES;
+
+   /**
+    * FIND_VALUES_BY_PROPERTYID.
+    */
+   protected String FIND_VALUES_BY_PROPERTYID;
+
+   /**
+    * FIND_VALUE_BY_PROPERTYID_OREDERNUMB.
+    */
+   protected String FIND_VALUES_VSTORAGE_DESC_BY_PROPERTYID;
+
+   @Deprecated
+   protected String FIND_VALUE_BY_PROPERTYID_OREDERNUMB;
+
+   /**
+    * FIND_NODES_BY_PARENTID.
+    */
+   protected String FIND_NODES_BY_PARENTID;
+   
+   /**
+    * FIND_NODES_COUNT_BY_PARENTID.
+    */
+   protected String FIND_NODES_COUNT_BY_PARENTID;
+
+   /**
+    * FIND_PROPERTIES_BY_PARENTID.
+    */
+   protected String FIND_PROPERTIES_BY_PARENTID;
+
+   /**
+    * INSERT_NODE.
+    */
+   protected String INSERT_NODE;
+
+   /**
+    * INSERT_PROPERTY.
+    */
+   protected String INSERT_PROPERTY;
+
+   /**
+    * INSERT_VALUE.
+    */
+   protected String INSERT_VALUE;
+
+   /**
+    * INSERT_REF.
+    */
+   protected String INSERT_REF;
+
+   /**
+    * RENAME_NODE.
+    */
+   protected String RENAME_NODE;
+
+   /**
+    * UPDATE_NODE.
+    */
+   protected String UPDATE_NODE;
+
+   /**
+    * UPDATE_PROPERTY.
+    */
+   protected String UPDATE_PROPERTY;
+
+   /**
+    * DELETE_ITEM.
+    */
+   protected String DELETE_ITEM;
+
+   /**
+    * DELETE_VALUE.
+    */
+   protected String DELETE_VALUE;
+
+   /**
+    * DELETE_REF.
+    */
+   protected String DELETE_REF;
+
+   // ======================== ITEMS table ======================== 
+   /**
+    * COLUMN_ID.
+    */
+   protected static final String COLUMN_ID = "ID";
+
+   /**
+    * COLUMN_PARENTID.
+    */
+   protected static final String COLUMN_PARENTID = "PARENT_ID";
+
+   /**
+    * COLUMN_NAME.
+    */
+   protected static final String COLUMN_NAME = "NAME";
+
+   /**
+    * COLUMN_VERSION.
+    */
+   protected static final String COLUMN_VERSION = "VERSION";
+
+   /**
+    * COLUMN_CLASS.
+    */
+   protected static final String COLUMN_CLASS = "I_CLASS";
+
+   /**
+    * COLUMN_INDEX.
+    */
+   protected static final String COLUMN_INDEX = "I_INDEX";
+
+   /**
+    * COLUMN_NORDERNUM.
+    */
+   protected static final String COLUMN_NORDERNUM = "N_ORDER_NUM";
+
+   /**
+    * COLUMN_PTYPE.
+    */
+   protected static final String COLUMN_PTYPE = "P_TYPE";
+
+   /**
+    * COLUMN_PMULTIVALUED.
+    */
+   protected static final String COLUMN_PMULTIVALUED = "P_MULTIVALUED";
+
+   // VALUE table
+   /**
+    * COLUMN_VDATA.
+    */
+   protected static final String COLUMN_VDATA = "DATA";
+
+   /**
+    * COLUMN_VORDERNUM.
+    */
+   protected static final String COLUMN_VORDERNUM = "ORDER_NUM";
+
+   /**
+    * COLUMN_VSTORAGE_DESC.
+    */
+   protected static final String COLUMN_VSTORAGE_DESC = "STORAGE_DESC";
+
+   // Dialects
+   /**
+    * DB_DIALECT_GENERIC.
+    */
+   public final static String DB_DIALECT_GENERIC = "Generic".intern();
+
+   /**
+    * DB_DIALECT_ORACLE.
+    */
+   public final static String DB_DIALECT_ORACLE = "Oracle".intern();
+
+   /**
+    * DB_DIALECT_ORACLEOCI.
+    */
+   public final static String DB_DIALECT_ORACLEOCI = "Oracle-OCI".intern();
+
+   /**
+    * DB_DIALECT_PGSQL.
+    */
+   public final static String DB_DIALECT_PGSQL = "PgSQL".intern();
+
+   /**
+    * DB_DIALECT_MYSQL.
+    */
+   public final static String DB_DIALECT_MYSQL = "MySQL".intern();
+
+   /**
+    * DB_DIALECT_MYSQL_UTF8.
+    */
+   public final static String DB_DIALECT_MYSQL_UTF8 = "MySQL-UTF8".intern();
+
+   /**
+    * DB_DIALECT_HSQLDB.
+    */
+   public final static String DB_DIALECT_HSQLDB = "HSQLDB".intern();
+
+   /**
+    * DB_DIALECT_DB2.
+    */
+   public final static String DB_DIALECT_DB2 = "DB2".intern();
+
+   /**
+    * DB_DIALECT_DB2V8.
+    */
+   public final static String DB_DIALECT_DB2V8 = "DB2V8".intern();
+
+   /**
+    * DB_DIALECT_MSSQL.
+    */
+   public final static String DB_DIALECT_MSSQL = "MSSQL".intern();
+
+   /**
+    * DB_DIALECT_SYBASE.
+    */
+   public final static String DB_DIALECT_SYBASE = "Sybase".intern();
+
+   /**
+    * DB_DIALECT_DERBY.
+    */
+   public final static String DB_DIALECT_DERBY = "Derby".intern();
+
+   /**
+    * DB_DIALECT_INGRES.
+    */
+   public final static String DB_DIALECT_INGRES = "Ingres".intern();
+
+   /**
+    * DB_DIALECTS.
+    */
+   public final static String[] DB_DIALECTS =
+      {DB_DIALECT_GENERIC, DB_DIALECT_ORACLE, DB_DIALECT_ORACLEOCI, DB_DIALECT_PGSQL, DB_DIALECT_MYSQL,
+         DB_DIALECT_HSQLDB, DB_DIALECT_DB2, DB_DIALECT_DB2V8, DB_DIALECT_MSSQL, DB_DIALECT_SYBASE, DB_DIALECT_DERBY,
+         DB_DIALECT_MYSQL_UTF8, DB_DIALECT_INGRES};
+
+}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewJDBCStorageConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewJDBCStorageConnection.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewJDBCStorageConnection.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -0,0 +1,1931 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.storage.jdbc.optimisation;
+
+import org.exoplatform.services.jcr.access.AccessControlEntry;
+import org.exoplatform.services.jcr.access.AccessControlList;
+import org.exoplatform.services.jcr.dataflow.ItemState;
+import org.exoplatform.services.jcr.dataflow.persistent.PersistedNodeData;
+import org.exoplatform.services.jcr.dataflow.persistent.PersistedPropertyData;
+import org.exoplatform.services.jcr.datamodel.IllegalACLException;
+import org.exoplatform.services.jcr.datamodel.IllegalNameException;
+import org.exoplatform.services.jcr.datamodel.InternalQName;
+import org.exoplatform.services.jcr.datamodel.ItemData;
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.datamodel.PropertyData;
+import org.exoplatform.services.jcr.datamodel.QPath;
+import org.exoplatform.services.jcr.datamodel.QPathEntry;
+import org.exoplatform.services.jcr.datamodel.ValueData;
+import org.exoplatform.services.jcr.impl.Constants;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.ByteArrayPersistedValueData;
+import org.exoplatform.services.jcr.impl.dataflow.persistent.CleanableFileStreamValueData;
+import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
+import org.exoplatform.services.jcr.impl.storage.jdbc.PrimaryTypeNotFoundException;
+import org.exoplatform.services.jcr.impl.storage.value.ValueStorageNotFoundException;
+import org.exoplatform.services.jcr.impl.storage.value.fs.operations.ValueFileIOHelper;
+import org.exoplatform.services.jcr.impl.util.io.FileCleaner;
+import org.exoplatform.services.jcr.impl.util.io.SwapFile;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
+import org.exoplatform.services.jcr.storage.value.ValueIOChannel;
+import org.exoplatform.services.jcr.storage.value.ValueStoragePluginProvider;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.StringTokenizer;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+/**
+ * Created by The eXo Platform SAS.
+ * 
+ * @author <a href="mailto:gennady.azarenkov at exoplatform.com">Gennady Azarenkov</a>
+ * @version $Id: JDBCStorageConnection.java 34801 2009-07-31 15:44:50Z dkatayev $
+ */
+abstract public class NewJDBCStorageConnection extends DBConstants implements WorkspaceStorageConnection
+{
+
+   /**
+    * Connection logger.
+    */
+   protected static final Log LOG = ExoLogger.getLogger("jcr.JDBCStorageConnection");
+
+   /**
+    * NODE type.
+    */
+   public static final int I_CLASS_NODE = 1;
+
+   /**
+    * PROPERTY type.
+    */
+   public static final int I_CLASS_PROPERTY = 2;
+
+   protected final ValueStoragePluginProvider valueStorageProvider;
+
+   protected final int maxBufferSize;
+
+   protected final File swapDirectory;
+
+   protected final FileCleaner swapCleaner;
+
+   protected final Connection dbConnection;
+
+   protected final String containerName;
+
+   protected final NewSQLExceptionHandler exceptionHandler;
+
+   protected final List<ValueIOChannel> valueChanges;
+
+   /**
+    * Read-only flag, if true the connection is marked as READ-ONLY.
+    */
+   protected final boolean readOnly;
+
+   /**
+     * JDBCStorageConnection constructor.
+     * 
+     * @param dbConnection
+     *          JDBC connection
+     * @param containerName
+     *          Workspace conatiner name
+     * @param valueStorageProvider
+     *          External Value Storage provider
+     * @param maxBufferSize
+     *          maximum buffer size (config)
+     * @param swapDirectory
+     *          swap directory (config)
+     * @param swapCleaner
+     *          swap cleaner (FileCleaner)
+     * @throws SQLException
+     *           database error
+     */
+   protected NewJDBCStorageConnection(Connection dbConnection, boolean readOnly, String containerName,
+      ValueStoragePluginProvider valueStorageProvider, int maxBufferSize, File swapDirectory, FileCleaner swapCleaner)
+      throws SQLException
+   {
+
+      this.valueStorageProvider = valueStorageProvider;
+
+      this.maxBufferSize = maxBufferSize;
+      this.swapDirectory = swapDirectory;
+      this.swapCleaner = swapCleaner;
+      this.containerName = containerName;
+
+      this.dbConnection = dbConnection;
+      this.readOnly = readOnly;
+
+      // Fix for Sybase jConnect JDBC driver bug.
+      // Which throws SQLException(JZ016: The AutoCommit option is already set to
+      // false)
+      // if conn.setAutoCommit(false) called twise or more times with value
+      // 'false'.
+      if (dbConnection.getAutoCommit())
+      {
+         dbConnection.setAutoCommit(false);
+      }
+
+      prepareQueries();
+      this.exceptionHandler = new NewSQLExceptionHandler(containerName, this);
+
+      this.valueChanges = new ArrayList<ValueIOChannel>();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   public boolean equals(Object obj)
+   {
+      if (obj == this)
+         return true;
+
+      if (obj instanceof NewJDBCStorageConnection)
+      {
+         NewJDBCStorageConnection another = (NewJDBCStorageConnection)obj;
+         return getJdbcConnection() == another.getJdbcConnection();
+      }
+
+      return false;
+   }
+
+   /**
+    * Return JDBC connection obtained from initialized data source. NOTE: Helper can obtain one new
+    * connection per each call of the method or return one obtained once.
+    */
+   public Connection getJdbcConnection()
+   {
+      return dbConnection;
+   }
+
+   /**
+    * Prepared queries at start time.
+    * 
+    * @throws SQLException
+    *           database error
+    */
+   abstract protected void prepareQueries() throws SQLException;
+
+   /**
+    * Used in Single Db Connection classes for Identifier related queries.
+    * 
+    * @param identifier
+    *          Item id
+    * @return String with container internal id
+    */
+   protected abstract String getInternalId(String identifier);
+
+   /**
+    * Used in loadXYZRecord methods for extract real Identifier from container value.
+    * 
+    * @param internalId
+    * @return
+    */
+   protected abstract String getIdentifier(String internalId);
+
+   // ---------------- WorkspaceStorageConnection -------------
+
+   /**
+    * @throws IllegalStateException
+    *           if connection is closed.
+    */
+   protected void checkIfOpened() throws IllegalStateException
+   {
+      if (!isOpened())
+         throw new IllegalStateException("Connection is closed");
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public boolean isOpened()
+   {
+      try
+      {
+         return !dbConnection.isClosed();
+      }
+      catch (SQLException e)
+      {
+         LOG.error(e);
+         return false;
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public final void rollback() throws IllegalStateException, RepositoryException
+   {
+      checkIfOpened();
+      try
+      {
+         dbConnection.rollback();
+         dbConnection.close();
+
+         // rollback from the end
+         for (int p = valueChanges.size() - 1; p >= 0; p--)
+            valueChanges.get(p).rollback();
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+      catch (IOException e)
+      {
+         throw new RepositoryException(e);
+      }
+      finally
+      {
+         valueChanges.clear();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public final void close() throws IllegalStateException, RepositoryException
+   {
+      checkIfOpened();
+      try
+      {
+         // If READ-ONLY status back it to READ-WRITE (we assume it was original state)
+         if (readOnly)
+            dbConnection.setReadOnly(true);
+
+         dbConnection.close();
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public final void commit() throws IllegalStateException, RepositoryException
+   {
+      checkIfOpened();
+      try
+      {
+         dbConnection.commit();
+         dbConnection.close();
+
+         try
+         {
+            for (ValueIOChannel vo : valueChanges)
+               vo.commit();
+
+         }
+         catch (IOException e)
+         {
+            throw new RepositoryException(e);
+         }
+         finally
+         {
+            valueChanges.clear();
+         }
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void add(NodeData data) throws RepositoryException, UnsupportedOperationException, InvalidItemStateException,
+      IllegalStateException
+   {
+      checkIfOpened();
+      try
+      {
+         addNodeRecord(data);
+         if (LOG.isDebugEnabled())
+            LOG.debug("Node added " + data.getQPath().getAsString() + ", " + data.getIdentifier() + ", "
+               + data.getPrimaryTypeName().getAsString());
+
+      }
+      catch (SQLException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Node add. Database error: " + e);
+
+         exceptionHandler.handleAddException(e, data);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void add(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+      InvalidItemStateException, IllegalStateException
+   {
+      checkIfOpened();
+
+      try
+      {
+         addPropertyRecord(data);
+
+         if (data.getType() == PropertyType.REFERENCE)
+         {
+            try
+            {
+               addReference(data);
+            }
+            catch (IOException e)
+            {
+               throw new RepositoryException("Can't read REFERENCE property (" + data.getQPath() + " "
+                  + data.getIdentifier() + ") value: " + e.getMessage(), e);
+            }
+         }
+
+         addValues(getInternalId(data.getIdentifier()), data);
+
+         if (LOG.isDebugEnabled())
+            LOG.debug("Property added " + data.getQPath().getAsString() + ", " + data.getIdentifier()
+               + (data.getValues() != null ? ", values count: " + data.getValues().size() : ", NULL data"));
+
+      }
+      catch (IOException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Property add. IO error: " + e, e);
+         throw new RepositoryException("Error of Property Value add " + e, e);
+      }
+      catch (SQLException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Property add. Database error: " + e, e);
+         exceptionHandler.handleAddException(e, data);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void rename(NodeData data) throws RepositoryException, UnsupportedOperationException,
+      InvalidItemStateException, IllegalStateException
+   {
+
+      checkIfOpened();
+      try
+      {
+         if (renameNode(data) <= 0)
+            throw new JCRInvalidItemStateException("(rename) Node not found " + data.getQPath().getAsString() + " "
+               + data.getIdentifier() + ". Probably was deleted by another session ", data.getIdentifier(),
+               ItemState.RENAMED);
+      }
+      catch (SQLException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Property add. Database error: " + e, e);
+         exceptionHandler.handleAddException(e, data);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void delete(NodeData data) throws RepositoryException, UnsupportedOperationException,
+      InvalidItemStateException, IllegalStateException
+   {
+      checkIfOpened();
+
+      final String cid = getInternalId(data.getIdentifier());
+
+      try
+      {
+         int nc = deleteItemByIdentifier(cid);
+         if (nc <= 0)
+            throw new JCRInvalidItemStateException("(delete) Node not found " + data.getQPath().getAsString() + " "
+               + data.getIdentifier() + ". Probably was deleted by another session ", data.getIdentifier(),
+               ItemState.DELETED);
+
+         if (LOG.isDebugEnabled())
+            LOG.debug("Node deleted " + data.getQPath().getAsString() + ", " + data.getIdentifier() + ", "
+               + ((NodeData)data).getPrimaryTypeName().getAsString());
+
+      }
+      catch (SQLException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Node remove. Database error: " + e, e);
+         exceptionHandler.handleDeleteException(e, data);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void delete(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+      InvalidItemStateException, IllegalStateException
+   {
+      checkIfOpened();
+
+      final String cid = getInternalId(data.getIdentifier());
+
+      try
+      {
+         deleteValues(cid, data, false);
+
+         // delete references
+         deleteReference(cid);
+
+         // delete item
+         int nc = deleteItemByIdentifier(cid);
+         if (nc <= 0)
+            throw new JCRInvalidItemStateException("(delete) Property not found " + data.getQPath().getAsString() + " "
+               + data.getIdentifier() + ". Probably was deleted by another session ", data.getIdentifier(),
+               ItemState.DELETED);
+
+         if (LOG.isDebugEnabled())
+            LOG.debug("Property deleted "
+               + data.getQPath().getAsString()
+               + ", "
+               + data.getIdentifier()
+               + (((PropertyData)data).getValues() != null ? ", values count: "
+                  + ((PropertyData)data).getValues().size() : ", NULL data"));
+
+      }
+      catch (IOException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Property remove. IO error: " + e, e);
+         throw new RepositoryException("Error of Property Value delete " + e, e);
+      }
+      catch (SQLException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Property remove. Database error: " + e, e);
+         exceptionHandler.handleDeleteException(e, data);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void update(NodeData data) throws RepositoryException, UnsupportedOperationException,
+      InvalidItemStateException, IllegalStateException
+   {
+      checkIfOpened();
+      try
+      {
+         String cid = getInternalId(data.getIdentifier());
+         // order numb update
+         if (updateNodeByIdentifier(data.getPersistedVersion(), data.getQPath().getIndex(), data.getOrderNumber(), cid) <= 0)
+            throw new JCRInvalidItemStateException("(update) Node not found " + data.getQPath().getAsString() + " "
+               + data.getIdentifier() + ". Probably was deleted by another session ", data.getIdentifier(),
+               ItemState.UPDATED);
+
+         if (LOG.isDebugEnabled())
+            LOG.debug("Node updated " + data.getQPath().getAsString() + ", " + data.getIdentifier() + ", "
+               + data.getPrimaryTypeName().getAsString());
+
+      }
+      catch (SQLException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Node update. Database error: " + e, e);
+         exceptionHandler.handleUpdateException(e, data);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void update(PropertyData data) throws RepositoryException, UnsupportedOperationException,
+      InvalidItemStateException, IllegalStateException
+   {
+      checkIfOpened();
+
+      try
+      {
+         String cid = getInternalId(data.getIdentifier());
+
+         // update type
+         if (updatePropertyByIdentifier(data.getPersistedVersion(), data.getType(), cid) <= 0)
+            throw new JCRInvalidItemStateException("(update) Property not found " + data.getQPath().getAsString() + " "
+               + data.getIdentifier() + ". Probably was deleted by another session ", data.getIdentifier(),
+               ItemState.UPDATED);
+
+         // update reference
+         try
+         {
+            deleteReference(cid);
+
+            if (data.getType() == PropertyType.REFERENCE)
+            {
+               addReference(data);
+            }
+         }
+         catch (IOException e)
+         {
+            throw new RepositoryException("Can't update REFERENCE property (" + data.getQPath() + " "
+               + data.getIdentifier() + ") value: " + e.getMessage(), e);
+         }
+
+         // do Values update: delete all and add all
+         deleteValues(cid, data, true);
+         addValues(cid, data);
+
+         if (LOG.isDebugEnabled())
+            LOG.debug("Property updated " + data.getQPath().getAsString() + ", " + data.getIdentifier()
+               + (data.getValues() != null ? ", values count: " + data.getValues().size() : ", NULL data"));
+
+      }
+      catch (IOException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Property update. IO error: " + e, e);
+         throw new RepositoryException("Error of Property Value update " + e, e);
+      }
+      catch (SQLException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.error("Property update. Database error: " + e, e);
+         exceptionHandler.handleUpdateException(e, data);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<NodeData> getChildNodesData(NodeData parent) throws RepositoryException, IllegalStateException
+   {
+      checkIfOpened();
+      try
+      {
+         ResultSet node = findChildNodesByParentIdentifier(getInternalId(parent.getIdentifier()));
+         List<NodeData> childrens = new ArrayList<NodeData>();
+         while (node.next())
+            childrens.add((NodeData)itemData(parent.getQPath(), node, I_CLASS_NODE, parent.getACL()));
+
+         return childrens;
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+      catch (IOException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public int getChildNodesCount(NodeData parent) throws RepositoryException
+   {
+      checkIfOpened();
+      try
+      {
+         ResultSet count = findChildNodesCountByParentIdentifier(getInternalId(parent.getIdentifier()));
+         if (count.next())
+         {
+            return count.getInt(1);
+         }
+         else
+         {
+            throw new RepositoryException("FATAL No resulton childNodes count for " + parent.getQPath().getAsString());
+         }
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<PropertyData> getChildPropertiesData(NodeData parent) throws RepositoryException, IllegalStateException
+   {
+      checkIfOpened();
+      try
+      {
+         ResultSet prop = findChildPropertiesByParentIdentifier(getInternalId(parent.getIdentifier()));
+         List<PropertyData> children = new ArrayList<PropertyData>();
+         while (prop.next())
+            children.add((PropertyData)itemData(parent.getQPath(), prop, I_CLASS_PROPERTY, null));
+
+         return children;
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+      catch (IOException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<PropertyData> listChildPropertiesData(NodeData parent) throws RepositoryException, IllegalStateException
+   {
+      checkIfOpened();
+      try
+      {
+         ResultSet prop = findChildPropertiesByParentIdentifier(getInternalId(parent.getIdentifier()));
+         List<PropertyData> children = new ArrayList<PropertyData>();
+         while (prop.next())
+            children.add(propertyData(parent.getQPath(), prop));
+
+         return children;
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+      catch (IOException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public ItemData getItemData(String identifier) throws RepositoryException, IllegalStateException
+   {
+      return getItemByIdentifier(getInternalId(identifier));
+   }
+
+   public ItemData getItemData(NodeData parentData, QPathEntry name) throws RepositoryException, IllegalStateException
+   {
+
+      if (parentData != null)
+      {
+         return getItemByName(parentData, getInternalId(parentData.getIdentifier()), name);
+      }
+
+      // it's a root node
+      return getItemByName(null, null, name);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public List<PropertyData> getReferencesData(String nodeIdentifier) throws RepositoryException, IllegalStateException
+   {
+      checkIfOpened();
+      try
+      {
+         ResultSet refProps = findReferences(getInternalId(nodeIdentifier));
+         List<PropertyData> references = new ArrayList<PropertyData>();
+         while (refProps.next())
+         {
+            references.add((PropertyData)itemData(null, refProps, I_CLASS_PROPERTY, null));
+         }
+         return references;
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+      catch (IOException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   // ------------------ Private methods ---------------
+
+   /**
+    * Get Item By Identifier.
+    * 
+    * @param cid
+    *          Item id (container internal)
+    * @return ItemData
+    * @throws RepositoryException
+    *           Repository error
+    * @throws IllegalStateException
+    *           if connection is closed
+    */
+   protected ItemData getItemByIdentifier(String cid) throws RepositoryException, IllegalStateException
+   {
+      checkIfOpened();
+      ResultSet item = null;
+      try
+      {
+         item = findItemByIdentifier(cid);
+         if (item.next())
+         {
+            return itemData(null, item, item.getInt(COLUMN_CLASS), null);
+         }
+         return null;
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException("getItemData() error", e);
+      }
+      catch (IOException e)
+      {
+         throw new RepositoryException("getItemData() error", e);
+      }
+      finally
+      {
+         try
+         {
+            if (item != null)
+               item.close();
+         }
+         catch (SQLException e)
+         {
+            LOG.error("getItemData() Error close resultset " + e.getMessage());
+         }
+      }
+   }
+
+   /**
+    * Gets an item data from database.
+    * 
+    * @param parentPath
+    *          - parent QPath
+    * @param parentId
+    *          - parent container internal id (depends on Multi/Single DB)
+    * @param name
+    *          - item name
+    * @return - ItemData instance
+    * @throws RepositoryException
+    *           Repository error
+    * @throws IllegalStateException
+    *           if connection is closed
+    */
+   protected ItemData getItemByName(NodeData parent, String parentId, QPathEntry name) throws RepositoryException,
+      IllegalStateException
+   {
+      checkIfOpened();
+      ResultSet item = null;
+      try
+      {
+         item = findItemByName(parentId, name.getAsString(), name.getIndex());
+         if (item.next())
+            return itemData(parent.getQPath(), item, item.getInt(COLUMN_CLASS), parent.getACL());
+         return null;
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+      catch (IOException e)
+      {
+         throw new RepositoryException(e);
+      }
+      finally
+      {
+         try
+         {
+            if (item != null)
+               item.close();
+         }
+         catch (SQLException e)
+         {
+            LOG.error("getItemData() Error close resultset " + e.getMessage());
+         }
+      }
+   }
+
+   /**
+    * Build Item path by id.
+    * 
+    * @param cpid
+    *          - Item id (container id)
+    * @return Item QPath
+    * @throws SQLException
+    *           - if database error occurs
+    * @throws InvalidItemStateException
+    *           - if parent not found
+    * @throws IllegalNameException
+    *           - if name on the path is wrong
+    */
+   private QPath traverseQPath(String cpid) throws SQLException, InvalidItemStateException, IllegalNameException
+   {
+      // get item by Identifier usecase
+      List<QPathEntry> qrpath = new ArrayList<QPathEntry>(); // reverted path
+      String caid = cpid; // container ancestor id
+      do
+      {
+         ResultSet parent = null;
+         try
+         {
+            parent = findItemByIdentifier(caid);
+            if (!parent.next())
+               throw new InvalidItemStateException("Parent not found, uuid: " + getIdentifier(caid));
+
+            QPathEntry qpe =
+               new QPathEntry(InternalQName.parse(parent.getString(COLUMN_NAME)), parent.getInt(COLUMN_INDEX));
+            qrpath.add(qpe);
+            caid = parent.getString(COLUMN_PARENTID);
+         }
+         finally
+         {
+            parent.close();
+         }
+      }
+      while (!caid.equals(Constants.ROOT_PARENT_UUID));
+
+      QPathEntry[] qentries = new QPathEntry[qrpath.size()];
+      int qi = 0;
+      for (int i = qrpath.size() - 1; i >= 0; i--)
+      {
+         qentries[qi++] = qrpath.get(i);
+      }
+      return new QPath(qentries);
+   }
+
+   /**
+    * ItemLocationInfo.
+    * 
+    */
+   class ItemLocationInfo
+   {
+      /**
+       * Item qpath
+       */
+      final QPath qpath;
+
+      /**
+       * All ancestors of the item with qpath
+       */
+      final List<String> ancestors;
+
+      /**
+       * Item id.
+       */
+      final String itemId;
+
+      /**
+       * ItemLocationInfo constructor.
+       * 
+       * @param qpath
+       *          Item path
+       * @param ancestors
+       *          ancesstors id list
+       * @param itemId
+       *          Item id
+       */
+      ItemLocationInfo(QPath qpath, List<String> ancestors, String itemId)
+      {
+         this.qpath = qpath;
+         this.ancestors = ancestors;
+         this.itemId = itemId;
+      }
+   }
+
+   /**
+    * Find ancestor permissions by cpid. Will search till find the permissions or meet a root node.
+    * 
+    * @param cpid
+    *          - initial parent node id
+    * @return Collection<String>
+    * @throws SQLException
+    *           if database error
+    * @throws IllegalACLException
+    *           if wrong ACL
+    * @throws IllegalNameException
+    *           if wrong QName
+    * @throws RepositoryException
+    *           if Repository error
+    */
+   private List<AccessControlEntry> traverseACLPermissions(String cpid) throws SQLException, IllegalACLException,
+      IllegalNameException, RepositoryException
+   {
+      String caid = cpid;
+      while (!caid.equals(Constants.ROOT_PARENT_UUID))
+      {
+         MixinInfo naMixins = readMixins(caid);
+         if (naMixins.hasPrivilegeable())
+            return readACLPermisions(caid);
+
+         if (naMixins.parentId == null)
+            caid = findParentId(caid);
+         else
+            caid = naMixins.parentId;
+      }
+
+      throw new IllegalACLException("Can not find permissions for a node with id " + getIdentifier(cpid));
+   }
+
+   protected String findParentId(String cid) throws SQLException, RepositoryException
+   {
+      ResultSet pidrs = findItemByIdentifier(cid);
+      try
+      {
+         if (pidrs.next())
+            return pidrs.getString(COLUMN_PARENTID);
+         else
+            throw new RepositoryException("Item not found id: " + getIdentifier(cid));
+      }
+      finally
+      {
+         pidrs.close();
+      }
+   }
+
+   /**
+    * Find ancestor owner by cpid. Will search till find the owner or meet a root node.
+    * 
+    * @param cpid
+    *          - initial parent node id
+    * @return owner name
+    * @throws SQLException
+    *           if database error
+    * @throws IllegalACLException
+    *           if wrong ACL
+    * @throws IllegalNameException
+    *           if wrong QName
+    * @throws RepositoryException
+    *           if Repository error
+    */
+   private String traverseACLOwner(String cpid) throws SQLException, IllegalACLException, IllegalNameException,
+      RepositoryException
+   {
+      String caid = cpid;
+
+      while (!caid.equals(Constants.ROOT_PARENT_UUID))
+      {
+         MixinInfo naMixins = readMixins(caid);
+         if (naMixins.hasOwneable())
+            return readACLOwner(caid);
+
+         if (naMixins.parentId == null)
+            caid = findParentId(caid);
+         else
+            caid = naMixins.parentId;
+      }
+
+      throw new IllegalACLException("Can not find owner for a node with id " + getIdentifier(cpid));
+   }
+
+   /**
+    * Find ancestor ACL by cpid. Will search till find the ACL or meet a root node.
+    * 
+    * @param cpid
+    *          - initial parent node id
+    * @return owner name
+    * @throws SQLException
+    *           if database error
+    * @throws IllegalACLException
+    *           if wrong ACL
+    * @throws IllegalNameException
+    *           if wrong QName
+    * @throws RepositoryException
+    *           if Repository error
+    */
+   private AccessControlList traverseACL(String cpid) throws SQLException, IllegalACLException, IllegalNameException,
+      RepositoryException
+   {
+      String naOwner = null;
+      List<AccessControlEntry> naPermissions = null;
+
+      String caid = cpid;
+
+      while (!caid.equals(Constants.ROOT_PARENT_UUID))
+      {
+         MixinInfo naMixins = readMixins(caid);
+         if (naOwner == null && naMixins.hasOwneable())
+         {
+            naOwner = readACLOwner(caid);
+            if (naPermissions != null)
+               break;
+         }
+         if (naPermissions == null && naMixins.hasPrivilegeable())
+         {
+            naPermissions = readACLPermisions(caid);
+            if (naOwner != null)
+               break;
+         }
+
+         if (naMixins.parentId == null)
+            caid = findParentId(caid);
+         else
+            caid = naMixins.parentId;
+      }
+
+      if (naOwner != null && naPermissions != null)
+      {
+         // got all
+         return new AccessControlList(naOwner, naPermissions);
+      }
+      else if (naOwner == null && naPermissions == null)
+      {
+         // Default values (i.e. ACL is disabled in repository)
+         return new AccessControlList();
+      }
+      else
+         throw new IllegalACLException("ACL is not found for node with id " + getIdentifier(cpid)
+            + " or for its ancestors. But repository is ACL enabled.");
+   }
+
+   /**
+    * [PN] Experimental. Use SP for traversing Qpath on the database server side. Hm, I haven't a
+    * good result for that yet. Few seconds only for TCK execution. PGSQL SP: CREATE OR REPLACE
+    * FUNCTION get_qpath(parentId VARCHAR) RETURNS SETOF record AS $$ DECLARE cur_item RECORD; cur_id
+    * varchar; BEGIN cur_id := parentId; WHILE NOT cur_id = ' ' LOOP SELECT id, name, parent_id,
+    * i_index INTO cur_item FROM JCR_SITEM WHERE ID=cur_id; IF NOT found THEN RETURN; END IF; RETURN
+    * NEXT cur_item; cur_id := cur_item.parent_id; END LOOP; RETURN; END; $$ LANGUAGE plpgsql;
+    * 
+    * @param cpid
+    * @return
+    * @throws SQLException
+    *           if database error
+    * @throws InvalidItemStateException
+    *           if Item state is obsolete
+    * @throws IllegalNameException
+    *           if invalid QName
+    */
+   private QPath traverseQPath_SP_PGSQL(String cpid) throws SQLException, InvalidItemStateException,
+      IllegalNameException
+   {
+      // get item by Identifier usecase:
+      // find parent path in db by cpid
+      if (cpid == null)
+      {
+         // root node
+         return null; // Constants.ROOT_PATH
+      }
+      else
+      {
+         List<QPathEntry> qrpath = new ArrayList<QPathEntry>(); // reverted path
+         PreparedStatement cstmt = null;
+         try
+         {
+            cstmt =
+               dbConnection
+                  .prepareStatement("select * from get_qpath(?) AS (id varchar, name varchar, parent_id varchar, i_index int)");
+            cstmt.setString(1, cpid);
+            // cstmt.setString(2, caid);
+            ResultSet parent = cstmt.executeQuery();
+
+            while (parent.next())
+            {
+               QPathEntry qpe =
+                  new QPathEntry(InternalQName.parse(parent.getString(COLUMN_NAME)), parent.getInt(COLUMN_INDEX));
+               qrpath.add(qpe);
+            }
+
+            // parent = findItemByIdentifier(caid);
+            if (qrpath.size() <= 0)
+               throw new InvalidItemStateException("Parent not found, uuid: " + getIdentifier(cpid));
+         }
+         finally
+         {
+            if (cstmt != null)
+               cstmt.close();
+         }
+
+         QPathEntry[] qentries = new QPathEntry[qrpath.size()];
+         int qi = 0;
+         for (int i = qrpath.size() - 1; i >= 0; i--)
+         {
+            qentries[qi++] = qrpath.get(i);
+         }
+         return new QPath(qentries);
+      }
+   }
+
+   /**
+    * Build ItemData.
+    * 
+    * @param parentPath
+    *          - parent path
+    * @param item
+    *          database - ResultSet with Item record(s)
+    * @param itemClass
+    *          - Item type (Node or Property)
+    * @param parentACL
+    *          - parent ACL
+    * @return ItemData instance
+    * @throws RepositoryException
+    *           Repository error
+    * @throws SQLException
+    *           database error
+    * @throws IOException
+    *           I/O error
+    */
+   private ItemData itemData(QPath parentPath, ResultSet item, int itemClass, AccessControlList parentACL)
+      throws RepositoryException, SQLException, IOException
+   {
+      String cid = item.getString(COLUMN_ID);
+      String cname = item.getString(COLUMN_NAME);
+      int cversion = item.getInt(COLUMN_VERSION);
+
+      String cpid = item.getString(COLUMN_PARENTID);
+      // if parent ID is empty string - it's a root node
+      // cpid = cpid.equals(Constants.ROOT_PARENT_UUID) ? null : cpid;
+
+      try
+      {
+         if (itemClass == I_CLASS_NODE)
+         {
+            int cindex = item.getInt(COLUMN_INDEX);
+            int cnordernumb = item.getInt(COLUMN_NORDERNUM);
+            return loadNodeRecord(parentPath, cname, cid, cpid, cindex, cversion, cnordernumb, parentACL);
+         }
+
+         int cptype = item.getInt(COLUMN_PTYPE);
+         boolean cpmultivalued = item.getBoolean(COLUMN_PMULTIVALUED);
+         return loadPropertyRecord(parentPath, cname, cid, cpid, cversion, cptype, cpmultivalued);
+      }
+      catch (InvalidItemStateException e)
+      {
+         throw new InvalidItemStateException("FATAL: Can't build item path for name " + cname + " id: "
+            + getIdentifier(cid) + ". " + e);
+      }
+   }
+
+   /**
+    * Read property data without value data. For listChildPropertiesData(NodeData).
+    * 
+    * @param parentPath
+    *          - parent path
+    * @param item
+    *          database - ResultSet with Item record(s)
+    * @return PropertyData instance
+    * @throws RepositoryException
+    *           Repository error
+    * @throws SQLException
+    *           database error
+    * @throws IOException
+    *           I/O error
+    */
+   private PropertyData propertyData(QPath parentPath, ResultSet item) throws RepositoryException, SQLException,
+      IOException
+   {
+      String cid = item.getString(COLUMN_ID);
+      String cname = item.getString(COLUMN_NAME);
+      int cversion = item.getInt(COLUMN_VERSION);
+      String cpid = item.getString(COLUMN_PARENTID);
+      int cptype = item.getInt(COLUMN_PTYPE);
+      boolean cpmultivalued = item.getBoolean(COLUMN_PMULTIVALUED);
+
+      try
+      {
+         InternalQName qname = InternalQName.parse(cname);
+
+         QPath qpath = QPath.makeChildPath(parentPath == null ? traverseQPath(cpid) : parentPath, qname);
+
+         PersistedPropertyData pdata =
+            new PersistedPropertyData(getIdentifier(cid), qpath, getIdentifier(cpid), cversion, cptype, cpmultivalued);
+
+         pdata.setValues(new ArrayList<ValueData>());
+         return pdata;
+      }
+      catch (InvalidItemStateException e)
+      {
+         throw new InvalidItemStateException("FATAL: Can't build property path for name " + cname + " id: "
+            + getIdentifier(cid) + ". " + e);
+      }
+      catch (IllegalNameException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * Mixin types description (internal use).
+    * 
+    */
+   class MixinInfo
+   {
+
+      /**
+       * OWNEABLE constant.
+       */
+      static final int OWNEABLE = 0x0001; // bits 0001
+
+      /**
+       * PRIVILEGEABLE constant.
+       */
+      static final int PRIVILEGEABLE = 0x0002; // bits 0010
+
+      /**
+       * OWNEABLE_PRIVILEGEABLE constant.
+       */
+      static final int OWNEABLE_PRIVILEGEABLE = OWNEABLE | PRIVILEGEABLE; // bits 0011
+
+      /**
+       * Mixin types.
+       */
+      final List<InternalQName> mixinTypes;
+
+      /**
+       * oexo:owneable flag.
+       */
+      final boolean owneable;
+
+      /**
+       * exo:privilegeable flag.
+       */
+      final boolean privilegeable;
+
+      /**
+       * Parent Id.
+       */
+      final String parentId = null;
+
+      /**
+       * MixinInfo constructor.
+       * 
+       * @param mixinTypes
+       *          mixin types
+       * @param owneable
+       *          exo:owneable flag
+       * @param privilegeable
+       *          exo:privilegeable flag
+       */
+      MixinInfo(List<InternalQName> mixinTypes, boolean owneable, boolean privilegeable)
+      {
+         this.mixinTypes = mixinTypes;
+         this.owneable = owneable;
+         this.privilegeable = privilegeable;
+      }
+
+      /**
+       * Return Mixin names array.
+       * 
+       * @return InternalQName[] Mixin names array
+       */
+      InternalQName[] mixinNames()
+      {
+         if (mixinTypes != null)
+         {
+            InternalQName[] mns = new InternalQName[mixinTypes.size()];
+            mixinTypes.toArray(mns);
+            return mns;
+         }
+         else
+            return new InternalQName[0];
+      }
+
+      /**
+       * Tell is exo:privilegeable.
+       * 
+       * @return boolean
+       */
+      boolean hasPrivilegeable()
+      {
+         return privilegeable;
+      }
+
+      /**
+       * Tell is exo:owneable.
+       * 
+       * @return boolean
+       */
+      boolean hasOwneable()
+      {
+         return owneable;
+      }
+   }
+
+   /**
+    * Read mixins from database.
+    * 
+    * @param cid
+    *          - Item id (internal)
+    * @return MixinInfo
+    * @throws SQLException
+    *           database error
+    * @throws IllegalNameException
+    *           if nodetype name in mixin record is wrong
+    */
+   protected MixinInfo readMixins(String cid) throws SQLException, IllegalNameException
+   {
+      ResultSet mtrs = findPropertyByName(cid, Constants.JCR_MIXINTYPES.getAsString());
+
+      try
+      {
+         List<InternalQName> mts = null;
+         boolean owneable = false;
+         boolean privilegeable = false;
+         if (mtrs.next())
+         {
+            mts = new ArrayList<InternalQName>();
+            do
+            {
+               byte[] mxnb = mtrs.getBytes(COLUMN_VDATA);
+               if (mxnb != null)
+               {
+                  InternalQName mxn = InternalQName.parse(new String(mxnb));
+                  mts.add(mxn);
+
+                  if (!privilegeable && Constants.EXO_PRIVILEGEABLE.equals(mxn))
+                     privilegeable = true;
+                  else if (!owneable && Constants.EXO_OWNEABLE.equals(mxn))
+                     owneable = true;
+               } // else, if SQL NULL - skip it
+            }
+            while (mtrs.next());
+         }
+
+         return new MixinInfo(mts, owneable, privilegeable);
+      }
+      finally
+      {
+         mtrs.close();
+      }
+   }
+
+   /**
+    * Return permission values or throw an exception. We assume the node is mix:privilegeable.
+    * 
+    * @param cid
+    *          Node id
+    * @return list of ACL entries
+    * @throws SQLException
+    *           database error
+    * @throws IllegalACLException
+    *           if property exo:permissions is not found for node
+    */
+   protected List<AccessControlEntry> readACLPermisions(String cid) throws SQLException, IllegalACLException
+   {
+      List<AccessControlEntry> naPermissions = new ArrayList<AccessControlEntry>();
+      ResultSet exoPerm = findPropertyByName(cid, Constants.EXO_PERMISSIONS.getAsString());
+      try
+      {
+         if (exoPerm.next())
+         {
+            do
+            {
+               StringTokenizer parser =
+                  new StringTokenizer(new String(exoPerm.getBytes(COLUMN_VDATA)), AccessControlEntry.DELIMITER);
+               naPermissions.add(new AccessControlEntry(parser.nextToken(), parser.nextToken()));
+            }
+            while (exoPerm.next());
+
+            return naPermissions;
+         }
+         else
+            throw new IllegalACLException("Property exo:permissions is not found for node with id: "
+               + getIdentifier(cid));
+      }
+      finally
+      {
+         exoPerm.close();
+      }
+   }
+
+   /**
+    * Return owner value or throw an exception. We assume the node is mix:owneable.
+    * 
+    * @param cid
+    *          Node id
+    * @return ACL owner
+    * @throws SQLException
+    *           database error
+    * @throws IllegalACLException
+    *           Property exo:owner is not found for node
+    */
+   protected String readACLOwner(String cid) throws SQLException, IllegalACLException
+   {
+      ResultSet exoOwner = findPropertyByName(cid, Constants.EXO_OWNER.getAsString());
+      try
+      {
+         if (exoOwner.next())
+            return new String(exoOwner.getBytes(COLUMN_VDATA));
+         else
+            throw new IllegalACLException("Property exo:owner is not found for node with id: " + getIdentifier(cid));
+      }
+      finally
+      {
+         exoOwner.close();
+      }
+   }
+
+   /**
+    * Load NodeData record.
+    * 
+    * @param parentPath
+    *          parent path
+    * @param cname
+    *          Node name
+    * @param cid
+    *          Node id
+    * @param cpid
+    *          Node parent id
+    * @param cindex
+    *          Node index
+    * @param cversion
+    *          Node persistent version
+    * @param cnordernumb
+    *          Node order number
+    * @param parentACL
+    *          Node parent ACL
+    * @return PersistedNodeData
+    * @throws RepositoryException
+    *           Repository error
+    * @throws SQLException
+    *           database error
+    */
+   protected PersistedNodeData loadNodeRecord(QPath parentPath, String cname, String cid, String cpid, int cindex,
+      int cversion, int cnordernumb, AccessControlList parentACL) throws RepositoryException, SQLException
+   {
+
+      try
+      {
+         InternalQName qname = InternalQName.parse(cname);
+
+         QPath qpath;
+         String parentCid;
+         if (parentPath != null)
+         {
+            // get by parent and name
+            qpath = QPath.makeChildPath(parentPath, qname, cindex);
+            parentCid = cpid;
+         }
+         else
+         {
+            // get by id
+            if (cpid.equals(Constants.ROOT_PARENT_UUID))
+            {
+               // root node
+               qpath = Constants.ROOT_PATH;
+               parentCid = null;
+            }
+            else
+            {
+               qpath = QPath.makeChildPath(traverseQPath(cpid), qname, cindex);
+               parentCid = cpid;
+            }
+         }
+
+         try
+         {
+            // PRIMARY
+            ResultSet ptProp = findPropertyByName(cid, Constants.JCR_PRIMARYTYPE.getAsString());
+
+            if (!ptProp.next())
+               throw new PrimaryTypeNotFoundException("FATAL ERROR primary type record not found. Node "
+                  + qpath.getAsString() + ", id " + cid + ", container " + this.containerName, null);
+
+            byte[] data = ptProp.getBytes(COLUMN_VDATA);
+            InternalQName ptName = InternalQName.parse(new String((data != null ? data : new byte[]{})));
+
+            // MIXIN
+            MixinInfo mixins = readMixins(cid);
+
+            // ACL
+            AccessControlList acl; // NO DEFAULT values!
+
+            if (mixins.hasOwneable())
+            {
+               // has own owner
+               if (mixins.hasPrivilegeable())
+               {
+                  // and permissions
+                  acl = new AccessControlList(readACLOwner(cid), readACLPermisions(cid));
+               }
+               else if (parentACL != null)
+               {
+                  // use permissions from existed parent
+                  acl =
+                     new AccessControlList(readACLOwner(cid), parentACL.hasPermissions() ? parentACL
+                        .getPermissionEntries() : null);
+               }
+               else
+               {
+                  // have to search nearest ancestor permissions in ACL manager
+                  // acl = new AccessControlList(readACLOwner(cid), traverseACLPermissions(cpid));
+                  acl = new AccessControlList(readACLOwner(cid), null);
+               }
+            }
+            else if (mixins.hasPrivilegeable())
+            {
+               // has own permissions
+               if (mixins.hasOwneable())
+               {
+                  // and owner
+                  acl = new AccessControlList(readACLOwner(cid), readACLPermisions(cid));
+               }
+               else if (parentACL != null)
+               {
+                  // use owner from existed parent
+                  acl = new AccessControlList(parentACL.getOwner(), readACLPermisions(cid));
+               }
+               else
+               {
+                  // have to search nearest ancestor owner in ACL manager
+                  // acl = new AccessControlList(traverseACLOwner(cpid), readACLPermisions(cid));
+                  acl = new AccessControlList(null, readACLPermisions(cid));
+               }
+            }
+            else
+            {
+               if (parentACL != null)
+                  // construct ACL from existed parent ACL
+                  acl =
+                     new AccessControlList(parentACL.getOwner(), parentACL.hasPermissions() ? parentACL
+                        .getPermissionEntries() : null);
+               else
+                  // have to search nearest ancestor owner and permissions in ACL manager
+                  // acl = traverseACL(cpid);
+                  acl = null;
+            }
+
+            return new PersistedNodeData(getIdentifier(cid), qpath, getIdentifier(parentCid), cversion, cnordernumb,
+               ptName, mixins.mixinNames(), acl);
+         }
+         catch (IllegalACLException e)
+         {
+            throw new RepositoryException("FATAL ERROR Node " + getIdentifier(cid) + " " + qpath.getAsString()
+               + " has wrong formed ACL. ", e);
+         }
+      }
+      catch (IllegalNameException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * Load PropertyData record.
+    * 
+    * @param parentPath
+    *          parent path
+    * @param cname
+    *          Property name
+    * @param cid
+    *          Property id
+    * @param cpid
+    *          Property parent id
+    * @param cversion
+    *          Property persistent verison
+    * @param cptype
+    *          Property type
+    * @param cpmultivalued
+    *          Property multivalued status
+    * @return PersistedPropertyData
+    * @throws RepositoryException
+    *           Repository error
+    * @throws SQLException
+    *           database error
+    * @throws IOException
+    *           I/O error
+    */
+   protected PersistedPropertyData loadPropertyRecord(QPath parentPath, String cname, String cid, String cpid,
+      int cversion, int cptype, boolean cpmultivalued) throws RepositoryException, SQLException, IOException
+   {
+
+      // NOTE: cpid never should be null or root parent (' ')
+
+      try
+      {
+         QPath qpath =
+            QPath.makeChildPath(parentPath == null ? traverseQPath(cpid) : parentPath, InternalQName.parse(cname));
+
+         PersistedPropertyData pdata =
+            new PersistedPropertyData(getIdentifier(cid), qpath, getIdentifier(cpid), cversion, cptype, cpmultivalued);
+
+         pdata.setValues(readValues(cid, pdata));
+         return pdata;
+      }
+      catch (IllegalNameException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+
+   /**
+    * Delete Property Values.
+    * 
+    * @param cid
+    *          Property id
+    * @param pdata
+    *          PropertyData
+    * @param update
+    *          boolean true if it's delete-add sequence (update operation)
+    * @throws IOException
+    *           i/O error
+    * @throws SQLException
+    *           if database error occurs
+    * @throws ValueStorageNotFoundException
+    *           if no such storage found with Value storageId
+    */
+   private void deleteValues(String cid, PropertyData pdata, boolean update) throws IOException, SQLException,
+      ValueStorageNotFoundException
+   {
+
+      final ResultSet valueRecords = findValuesStorageDescriptorsByPropertyId(cid);
+      try
+      {
+         if (valueRecords.next())
+         {
+            // delete all Values in database
+            deleteValueData(cid);
+
+            do
+            {
+               final String storageId = valueRecords.getString(COLUMN_VSTORAGE_DESC);
+               if (!valueRecords.wasNull())
+               {
+                  final ValueIOChannel channel = valueStorageProvider.getChannel(storageId);
+                  try
+                  {
+                     channel.delete(pdata.getIdentifier());
+                     valueChanges.add(channel);
+                  }
+                  finally
+                  {
+                     channel.close();
+                  }
+               }
+            }
+            while (valueRecords.next());
+         }
+      }
+      finally
+      {
+         valueRecords.close();
+      }
+   }
+
+   /**
+    * Read Property Values.
+    * 
+    * @param cid
+    *          Property id
+    * @param pdata
+    *          PropertyData
+    * @return list of ValueData
+    * @throws IOException
+    *           i/O error
+    * @throws SQLException
+    *           if database errro occurs
+    * @throws ValueStorageNotFoundException
+    *           if no such storage found with Value storageId
+    */
+   private List<ValueData> readValues(String cid, PropertyData pdata) throws IOException, SQLException,
+      ValueStorageNotFoundException
+   {
+
+      List<ValueData> data = new ArrayList<ValueData>();
+
+      final ResultSet valueRecords = findValuesByPropertyId(cid);
+      try
+      {
+         while (valueRecords.next())
+         {
+            final int orderNum = valueRecords.getInt(COLUMN_VORDERNUM);
+            final String storageId = valueRecords.getString(COLUMN_VSTORAGE_DESC);
+            ValueData vdata =
+               valueRecords.wasNull() ? readValueData(cid, orderNum, pdata.getPersistedVersion(), valueRecords
+                  .getBinaryStream(COLUMN_VDATA)) : readValueData(pdata, orderNum, storageId);
+            data.add(vdata);
+         }
+      }
+      finally
+      {
+         valueRecords.close();
+      }
+
+      return data;
+   }
+
+   /**
+    * Read ValueData from External Storage.
+    * 
+    * @param pdata
+    *          PropertyData
+    * @param orderNumber
+    *          Value order number
+    * @param storageId
+    *          external Value storage id
+    * @return ValueData
+    * @throws SQLException
+    *           database error
+    * @throws IOException
+    *           I/O error
+    * @throws ValueStorageNotFoundException
+    *           if no such storage found with Value storageId
+    */
+   protected ValueData readValueData(PropertyData pdata, int orderNumber, String storageId) throws SQLException,
+      IOException, ValueStorageNotFoundException
+   {
+      ValueIOChannel channel = valueStorageProvider.getChannel(storageId);
+      try
+      {
+         return channel.read(pdata.getIdentifier(), orderNumber, maxBufferSize);
+      }
+      finally
+      {
+         channel.close();
+      }
+   }
+
+   /**
+    * Read ValueData from database.
+    * 
+    * @param cid
+    *          Property id
+    * @param orderNumber
+    *          Value order number
+    * @param version
+    *          persistent version (used for BLOB swapping)
+    * @param content
+    * @return ValueData
+    * @throws SQLException
+    *           database error
+    * @throws IOException
+    *           I/O error (swap)
+    */
+   protected ValueData readValueData(String cid, int orderNumber, int version, final InputStream content)
+      throws SQLException, IOException
+   {
+
+      ResultSet valueResultSet = null;
+
+      byte[] buffer = new byte[0];
+      byte[] spoolBuffer = new byte[ValueFileIOHelper.IOBUFFER_SIZE];
+      int read;
+      int len = 0;
+      OutputStream out = null;
+
+      SwapFile swapFile = null;
+      try
+      {
+         // stream from database
+         if (content != null)
+            while ((read = content.read(spoolBuffer)) >= 0)
+            {
+               if (out != null)
+               {
+                  // spool to temp file
+                  out.write(spoolBuffer, 0, read);
+                  len += read;
+               }
+               else if (len + read > maxBufferSize)
+               {
+                  // threshold for keeping data in memory exceeded;
+                  // create temp file and spool buffer contents
+                  swapFile = SwapFile.get(swapDirectory, cid + orderNumber + "." + version);
+                  if (swapFile.isSpooled())
+                  {
+                     // break, value already spooled
+                     buffer = null;
+                     break;
+                  }
+                  out = new FileOutputStream(swapFile);
+                  out.write(buffer, 0, len);
+                  out.write(spoolBuffer, 0, read);
+                  buffer = null;
+                  len += read;
+               }
+               else
+               {
+                  // reallocate new buffer and spool old buffer contents
+                  byte[] newBuffer = new byte[len + read];
+                  System.arraycopy(buffer, 0, newBuffer, 0, len);
+                  System.arraycopy(spoolBuffer, 0, newBuffer, len, read);
+                  buffer = newBuffer;
+                  len += read;
+               }
+            }
+      }
+      finally
+      {
+         if (valueResultSet != null)
+            valueResultSet.close();
+         if (out != null)
+         {
+            out.close();
+            swapFile.spoolDone();
+         }
+      }
+
+      if (buffer == null)
+         return new CleanableFileStreamValueData(swapFile, orderNumber, swapCleaner);
+
+      return new ByteArrayPersistedValueData(buffer, orderNumber);
+   }
+
+   /**
+    * Add Values to Property record.
+    * 
+    * @param data
+    *          PropertyData
+    * @throws SQLException
+    *           database error
+    * @throws IOException
+    *           I/O error
+    */
+   protected void addValues(String cid, PropertyData data) throws IOException, SQLException
+   {
+      List<ValueData> vdata = data.getValues();
+
+      for (int i = 0; i < vdata.size(); i++)
+      {
+         ValueData vd = vdata.get(i);
+         vd.setOrderNumber(i);
+         ValueIOChannel channel = valueStorageProvider.getApplicableChannel(data, i);
+         InputStream stream;
+         int streamLength;
+         String storageId;
+         if (channel == null)
+         {
+            // prepare write of Value in database
+            if (vd.isByteArray())
+            {
+               byte[] dataBytes = vd.getAsByteArray();
+               stream = new ByteArrayInputStream(dataBytes);
+               streamLength = dataBytes.length;
+            }
+            else
+            {
+               // will spool TransienValueData to a temp file
+               stream = vd.getAsStream();
+               streamLength = stream.available(); // ask on FileInputStream actually
+            }
+            storageId = null;
+         }
+         else
+         {
+            // write Value in external VS
+            channel.write(data.getIdentifier(), vd);
+            valueChanges.add(channel);
+            storageId = channel.getStorageId();
+            stream = null;
+            streamLength = 0;
+         }
+         addValueData(cid, i, stream, streamLength, storageId);
+      }
+   }
+
+   protected abstract int addNodeRecord(NodeData data) throws SQLException;
+
+   protected abstract int addPropertyRecord(PropertyData prop) throws SQLException;
+
+   protected abstract ResultSet findItemByIdentifier(String identifier) throws SQLException;
+
+   protected abstract ResultSet findPropertyByName(String parentId, String name) throws SQLException;
+
+   protected abstract ResultSet findItemByName(String parentId, String name, int index) throws SQLException;
+
+   protected abstract ResultSet findChildNodesByParentIdentifier(String parentIdentifier) throws SQLException;
+
+   protected abstract ResultSet findChildNodesCountByParentIdentifier(String parentIdentifier) throws SQLException;
+
+   protected abstract ResultSet findChildPropertiesByParentIdentifier(String parentIdentifier) throws SQLException;
+
+   protected abstract int addReference(PropertyData data) throws SQLException, IOException;
+
+   protected abstract int renameNode(NodeData data) throws SQLException;
+
+   protected abstract int deleteReference(String propertyIdentifier) throws SQLException;
+
+   protected abstract ResultSet findReferences(String nodeIdentifier) throws SQLException;
+
+   protected abstract int deleteItemByIdentifier(String identifier) throws SQLException;
+
+   protected abstract int updateNodeByIdentifier(int version, int index, int orderNumb, String identifier)
+      throws SQLException;
+
+   protected abstract int updatePropertyByIdentifier(int version, int type, String identifier) throws SQLException;
+
+   // -------- values processing ------------
+   protected abstract int addValueData(String cid, int orderNumber, InputStream stream, int streamLength,
+      String storageId) throws SQLException;
+
+   protected abstract int deleteValueData(String cid) throws SQLException;
+
+   protected abstract ResultSet findValuesByPropertyId(String cid) throws SQLException;
+
+   protected abstract ResultSet findValuesStorageDescriptorsByPropertyId(String cid) throws SQLException;
+
+   @Deprecated
+   protected abstract ResultSet findValueByPropertyIdOrderNumber(String cid, int orderNumb) throws SQLException;
+}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewJDBCWorkspaceDataContainer.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewJDBCWorkspaceDataContainer.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewJDBCWorkspaceDataContainer.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -0,0 +1,802 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.storage.jdbc.optimisation;
+
+import org.exoplatform.services.jcr.config.RepositoryConfigurationException;
+import org.exoplatform.services.jcr.config.RepositoryEntry;
+import org.exoplatform.services.jcr.config.WorkspaceEntry;
+import org.exoplatform.services.jcr.impl.storage.WorkspaceDataContainerBase;
+import org.exoplatform.services.jcr.impl.storage.jdbc.DBConstants;
+import org.exoplatform.services.jcr.impl.storage.jdbc.db.GenericConnectionFactory;
+import org.exoplatform.services.jcr.impl.storage.jdbc.db.MySQLConnectionFactory;
+import org.exoplatform.services.jcr.impl.storage.jdbc.db.OracleConnectionFactory;
+import org.exoplatform.services.jcr.impl.storage.jdbc.db.WorkspaceStorageConnectionFactory;
+import org.exoplatform.services.jcr.impl.storage.jdbc.init.DBInitializer;
+import org.exoplatform.services.jcr.impl.storage.jdbc.init.DBInitializerException;
+import org.exoplatform.services.jcr.impl.storage.jdbc.init.IngresSQLDBInitializer;
+import org.exoplatform.services.jcr.impl.storage.jdbc.init.OracleDBInitializer;
+import org.exoplatform.services.jcr.impl.storage.jdbc.init.PgSQLDBInitializer;
+import org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.db.HSQLDBConnectionFactory;
+import org.exoplatform.services.jcr.impl.storage.jdbc.update.StorageUpdateManager;
+import org.exoplatform.services.jcr.impl.util.io.FileCleaner;
+import org.exoplatform.services.jcr.storage.WorkspaceDataContainer;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
+import org.exoplatform.services.jcr.storage.value.ValueStoragePluginProvider;
+import org.exoplatform.services.log.ExoLogger;
+import org.exoplatform.services.log.Log;
+import org.exoplatform.services.naming.InitialContextInitializer;
+import org.picocontainer.Startable;
+
+import java.io.File;
+import java.io.IOException;
+import java.sql.Connection;
+
+import javax.jcr.RepositoryException;
+import javax.naming.InitialContext;
+import javax.naming.NamingException;
+import javax.sql.DataSource;
+
+/**
+ * Created by The eXo Platform SAS.
+ * 
+ * @author <a href="mailto:peter.nedonosko at exoplatform.com.ua">Peter Nedonosko</a>
+ * @version $Id:GenericWorkspaceDataContainer.java 13433 2007-03-15 16:07:23Z peterit $
+ */
+public class NewJDBCWorkspaceDataContainer extends WorkspaceDataContainerBase implements Startable
+{
+
+   protected static final Log LOG = ExoLogger.getLogger("jcr.JDBCWorkspaceDataContainer");
+
+   //configuration params
+
+   public final static String SOURCE_NAME = "source-name";
+
+   public final static String MULTIDB = "multi-db";
+
+   public final static String SINGLEDB = "single-db";
+
+   /**
+    * Describe which type of RDBMS will be used (DB creation metadata etc.)
+    */
+   public final static String DB_DIALECT = "dialect";
+
+   public final static String DB_DRIVER = "driverClassName";
+
+   public final static String DB_URL = "url";
+
+   public final static String DB_USERNAME = "username";
+
+   public final static String DB_PASSWORD = "password";
+
+   protected final String containerName;
+
+   protected final String dbSourceName;
+
+   protected final boolean multiDb;
+
+   protected final String dbDriver;
+
+   protected final String dbDialect;
+
+   protected final String dbUrl;
+
+   protected final String dbUserName;
+
+   protected final String dbPassword;
+
+   protected final ValueStoragePluginProvider valueStorageProvider;
+
+   protected String storageVersion;
+
+   protected int maxBufferSize;
+
+   protected File swapDirectory;
+
+   protected FileCleaner swapCleaner;
+
+   protected GenericConnectionFactory connFactory;
+
+   /**
+    * Shared connection factory.
+    * 
+    * Issued to share JDBC connection between system and regular workspace in case of same database
+    * used for storage.
+    * 
+    */
+   class SharedConnectionFactory extends GenericConnectionFactory
+   {
+
+      /**
+       * JDBC connection.
+       */
+      final private Connection connection;
+
+      /**
+       * SharedConnectionFactory constructor.
+       * 
+       * @param connection
+       *          JDBC - connection
+       * @param containerName
+       *          - container name
+       * @param multiDb
+       *          - multidatabase status
+       * @param valueStorageProvider
+       *          - external Value Storages provider
+       * @param maxBufferSize
+       *          - Maximum buffer size (see configuration)
+       * @param swapDirectory
+       *          - Swap directory (see configuration)
+       * @param swapCleaner
+       *          - Swap cleaner (internal FileCleaner).
+       */
+      SharedConnectionFactory(Connection connection, String containerName, boolean multiDb,
+         ValueStoragePluginProvider valueStorageProvider, int maxBufferSize, File swapDirectory, FileCleaner swapCleaner)
+      {
+
+         super(null, null, null, null, null, containerName, multiDb, valueStorageProvider, maxBufferSize,
+            swapDirectory, swapCleaner);
+
+         this.connection = connection;
+      }
+
+      /**
+       * {@inheritDoc}
+       */
+      public Connection getJdbcConnection() throws RepositoryException
+      {
+         return connection;
+      }
+   }
+
+   /**
+    * Constructor with value storage plugins.
+    * 
+    * @param wsConfig
+    *          Workspace configuration
+    * @param valueStrorageProvider
+    *          External Value Stprages provider
+    * @throws RepositoryConfigurationException
+    *           if Repository configuration is wrong
+    * @throws NamingException
+    *           if JNDI exception (on DataSource lookup)
+    */
+   public NewJDBCWorkspaceDataContainer(WorkspaceEntry wsConfig, RepositoryEntry repConfig,
+      InitialContextInitializer contextInit, ValueStoragePluginProvider valueStorageProvider)
+      throws RepositoryConfigurationException, NamingException, RepositoryException, IOException
+   {
+
+      checkIntegrity(wsConfig, repConfig);
+
+      this.containerName = wsConfig.getName();
+      this.multiDb = Boolean.parseBoolean(wsConfig.getContainer().getParameterValue(MULTIDB));
+      this.valueStorageProvider = valueStorageProvider;
+
+      // ------------- Database config ------------------
+      String pDbDialect = null;
+      try
+      {
+         pDbDialect = detectDialect(wsConfig.getContainer().getParameterValue(DB_DIALECT));
+         LOG.info("Using a dialect '" + pDbDialect + "'");
+      }
+      catch (RepositoryConfigurationException e)
+      {
+         LOG.info("Using a default dialect '" + DBConstants.DB_DIALECT_GENERIC + "'");
+         pDbDialect = DBConstants.DB_DIALECT_GENERIC;
+      }
+      this.dbDialect = pDbDialect;
+
+      String pDbDriver = null;
+      String pDbUrl = null;
+      String pDbUserName = null;
+      String pDbPassword = null;
+      try
+      {
+         pDbDriver = wsConfig.getContainer().getParameterValue(DB_DRIVER);
+
+         // username/passwd may not pesent
+         try
+         {
+            pDbUserName = wsConfig.getContainer().getParameterValue(DB_USERNAME);
+            pDbPassword = wsConfig.getContainer().getParameterValue(DB_PASSWORD);
+         }
+         catch (RepositoryConfigurationException e)
+         {
+            pDbUserName = pDbPassword = null;
+         }
+
+         pDbUrl = wsConfig.getContainer().getParameterValue(DB_URL); // last here!
+      }
+      catch (RepositoryConfigurationException e)
+      {
+      }
+
+      if (pDbUrl != null)
+      {
+         this.dbDriver = pDbDriver;
+         this.dbUrl = pDbUrl;
+         this.dbUserName = pDbUserName;
+         this.dbPassword = pDbPassword;
+         this.dbSourceName = null;
+         LOG.info("Connect to JCR database as user '" + this.dbUserName + "'");
+      }
+      else
+      {
+         this.dbDriver = null;
+         this.dbUrl = null;
+         this.dbUserName = null;
+         this.dbPassword = null;
+
+         String sn;
+         try
+         {
+            sn = wsConfig.getContainer().getParameterValue(SOURCE_NAME);
+         }
+         catch (RepositoryConfigurationException e)
+         {
+            sn = wsConfig.getContainer().getParameterValue("sourceName"); // TODO for backward comp,
+            // remove in rel.2.0
+         }
+         this.dbSourceName = sn;
+      }
+
+      // ------------- Values swap config ------------------
+      try
+      {
+         this.maxBufferSize = wsConfig.getContainer().getParameterInteger(MAXBUFFERSIZE_PROP);
+      }
+      catch (RepositoryConfigurationException e)
+      {
+         this.maxBufferSize = DEF_MAXBUFFERSIZE;
+      }
+
+      try
+      {
+         String sdParam = wsConfig.getContainer().getParameterValue(SWAPDIR_PROP);
+         this.swapDirectory = new File(sdParam);
+      }
+      catch (RepositoryConfigurationException e1)
+      {
+         this.swapDirectory = new File(DEF_SWAPDIR);
+      }
+      if (!swapDirectory.exists())
+         swapDirectory.mkdirs();
+
+      this.swapCleaner = new FileCleaner(false);
+
+      initDatabase();
+
+      String suParam = null;
+      boolean enableStorageUpdate = false;
+      try
+      {
+         suParam = wsConfig.getContainer().getParameterValue("update-storage");
+         enableStorageUpdate = Boolean.parseBoolean(suParam);
+      }
+      catch (RepositoryConfigurationException e)
+      {
+         if (LOG.isDebugEnabled())
+            LOG.debug("update-storage parameter is not set " + dbSourceName);
+      }
+
+      this.storageVersion =
+         StorageUpdateManager.checkVersion(dbSourceName, this.connFactory.getJdbcConnection(), multiDb,
+            enableStorageUpdate);
+
+      LOG.info(getInfo());
+   }
+
+   /**
+    * Prepare sefault connection factory.
+    * 
+    * @return GenericConnectionFactory
+    * @throws NamingException
+    *           on JNDI error
+    * @throws RepositoryException
+    *           on Storage error
+    */
+   protected GenericConnectionFactory defaultConnectionFactory() throws NamingException, RepositoryException
+   {
+      // by default
+      if (dbSourceName != null)
+      {
+         DataSource ds = (DataSource)new InitialContext().lookup(dbSourceName);
+         if (ds != null)
+            return new GenericConnectionFactory(ds, containerName, multiDb, valueStorageProvider, maxBufferSize,
+               swapDirectory, swapCleaner);
+
+         throw new RepositoryException("Datasource '" + dbSourceName + "' is not bound in this context.");
+      }
+
+      return new GenericConnectionFactory(dbDriver, dbUrl, dbUserName, dbPassword, containerName, multiDb,
+         valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+   }
+
+   /**
+    * Prepare default DB initializer.
+    * 
+    * @param sqlPath
+    *          - path to SQL script (database creation script)
+    * @return DBInitializer instance
+    * @throws NamingException
+    *           on JNDI error
+    * @throws RepositoryException
+    *           on Storage error
+    * @throws IOException
+    *           on I/O error
+    */
+   protected DBInitializer defaultDBInitializer(String sqlPath) throws NamingException, RepositoryException,
+      IOException
+   {
+      return new DBInitializer(containerName, this.connFactory.getJdbcConnection(), sqlPath, multiDb);
+   }
+
+   /**
+    * Checks if DataSources used in right manner.
+    * 
+    * @param wsConfig
+    *          Workspace configuration
+    * @param repConfig
+    *          Repository configuration
+    * @throws RepositoryConfigurationException
+    *           in case of configuration errors
+    */
+   protected void checkIntegrity(WorkspaceEntry wsConfig, RepositoryEntry repConfig)
+      throws RepositoryConfigurationException
+   {
+      boolean isMulti;
+      for (WorkspaceEntry wsEntry : repConfig.getWorkspaceEntries())
+      {
+         if (wsEntry.getName().equals(wsConfig.getName())
+            || !wsEntry.getContainer().getType().equals(wsConfig.getContainer().getType())
+            || !wsEntry.getContainer().getType().equals(this.getClass().getName()))
+            continue;
+
+         // MULTIDB
+         if (!wsEntry.getContainer().getParameterValue(MULTIDB).equals(
+            wsConfig.getContainer().getParameterValue(MULTIDB)))
+         {
+            throw new RepositoryConfigurationException("All workspaces must be " + MULTIDB + " or " + SINGLEDB
+               + ". But " + wsEntry.getName() + "- multi-db=" + wsEntry.getContainer().getParameterValue(MULTIDB)
+               + " and " + wsConfig.getName() + "- multi-db=" + wsConfig.getContainer().getParameterValue(MULTIDB));
+         }
+
+         isMulti = Boolean.parseBoolean(wsConfig.getContainer().getParameterValue(MULTIDB));
+
+         // source name
+         String wsSourceName = null;
+         String newWsSourceName = null;
+         try
+         {
+            wsSourceName = wsEntry.getContainer().getParameterValue("sourceName");
+            newWsSourceName = wsConfig.getContainer().getParameterValue("sourceName");
+         }
+         catch (RepositoryConfigurationException e)
+         {
+         }
+
+         if (wsSourceName != null && newWsSourceName != null)
+         {
+            if (isMulti)
+            {
+               if (wsSourceName.equals(newWsSourceName))
+               {
+                  throw new RepositoryConfigurationException("SourceName " + wsSourceName + " alredy in use in "
+                     + wsEntry.getName() + ". SourceName must be different in " + MULTIDB
+                     + ". Check configuration for " + wsConfig.getName());
+               }
+            }
+            else
+            {
+               if (!wsSourceName.equals(newWsSourceName))
+               {
+                  throw new RepositoryConfigurationException("SourceName must be equals in " + SINGLEDB + " "
+                     + "repository." + " Check " + wsEntry.getName() + " and " + wsConfig.getName());
+               }
+            }
+            continue;
+         }
+
+         // db-url
+         String wsUri = null;
+         String newWsUri = null;
+         try
+         {
+            wsUri = wsEntry.getContainer().getParameterValue("db-url");
+            newWsUri = wsConfig.getContainer().getParameterValue("db-url");
+         }
+         catch (RepositoryConfigurationException e)
+         {
+         }
+
+         if (wsUri != null && newWsUri != null)
+         {
+            if (isMulti)
+            {
+               if (wsUri.equals(newWsUri))
+               {
+                  throw new RepositoryConfigurationException("db-url  " + wsUri + " alredy in use in "
+                     + wsEntry.getName() + ". db-url must be different in " + MULTIDB + ". Check configuration for "
+                     + wsConfig.getName());
+
+               }
+            }
+            else
+            {
+               if (!wsUri.equals(newWsUri))
+               {
+                  throw new RepositoryConfigurationException("db-url must be equals in " + SINGLEDB + " "
+                     + "repository." + " Check " + wsEntry.getName() + " and " + wsConfig.getName());
+               }
+            }
+         }
+      }
+   }
+
+   /**
+    * Init storage database.
+    * 
+    * @throws NamingException
+    *           on JNDI error
+    * @throws RepositoryException
+    *           on storage error
+    * @throws IOException
+    *           on I/O error
+    */
+   protected void initDatabase() throws NamingException, RepositoryException, IOException
+   {
+
+      DBInitializer dbInitilizer = null;
+      String sqlPath = null;
+      if (dbDialect == DBConstants.DB_DIALECT_ORACLEOCI)
+      {
+         LOG.warn(DBConstants.DB_DIALECT_ORACLEOCI + " dialect is experimental!");
+         // sample of connection factory customization
+         if (dbSourceName != null)
+            this.connFactory = defaultConnectionFactory();
+         else
+            this.connFactory =
+               new OracleConnectionFactory(dbDriver, dbUrl, dbUserName, dbPassword, containerName, multiDb,
+                  valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.ora.sql";
+
+         // a particular db initializer may be configured here too
+         dbInitilizer = new OracleDBInitializer(containerName, this.connFactory.getJdbcConnection(), sqlPath, multiDb);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_ORACLE)
+      {
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.ora.sql";
+         dbInitilizer = new OracleDBInitializer(containerName, this.connFactory.getJdbcConnection(), sqlPath, multiDb);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_PGSQL)
+      {
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.pgsql.sql";
+         dbInitilizer = new PgSQLDBInitializer(containerName, this.connFactory.getJdbcConnection(), sqlPath, multiDb);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_MYSQL)
+      {
+         // [PN] 28.06.07
+         if (dbSourceName != null)
+         {
+            DataSource ds = (DataSource)new InitialContext().lookup(dbSourceName);
+            if (ds != null)
+               this.connFactory =
+                  new MySQLConnectionFactory(ds, containerName, multiDb, valueStorageProvider, maxBufferSize,
+                     swapDirectory, swapCleaner);
+            else
+               throw new RepositoryException("Datasource '" + dbSourceName + "' is not bound in this context.");
+         }
+         else
+            this.connFactory =
+               new MySQLConnectionFactory(dbDriver, dbUrl, dbUserName, dbPassword, containerName, multiDb,
+                  valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.mysql.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_MYSQL_UTF8)
+      {
+         // [PN] 13.07.08
+         if (dbSourceName != null)
+         {
+            DataSource ds = (DataSource)new InitialContext().lookup(dbSourceName);
+            if (ds != null)
+               this.connFactory =
+                  new MySQLConnectionFactory(ds, containerName, multiDb, valueStorageProvider, maxBufferSize,
+                     swapDirectory, swapCleaner);
+            else
+               throw new RepositoryException("Datasource '" + dbSourceName + "' is not bound in this context.");
+         }
+         else
+            this.connFactory =
+               new MySQLConnectionFactory(dbDriver, dbUrl, dbUserName, dbPassword, containerName, multiDb,
+                  valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.mysql-utf8.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_MSSQL)
+      {
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.mssql.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_DERBY)
+      {
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.derby.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_DB2)
+      {
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.db2.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_DB2V8)
+      {
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.db2v8.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_SYBASE)
+      {
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.sybase.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_INGRES)
+      {
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.ingres.sql";
+         // using Postgres initializer
+         dbInitilizer =
+            new IngresSQLDBInitializer(containerName, this.connFactory.getJdbcConnection(), sqlPath, multiDb);
+      }
+      else if (dbDialect == DBConstants.DB_DIALECT_HSQLDB)
+      {
+         if (dbSourceName != null)
+         {
+            DataSource ds = (DataSource)new InitialContext().lookup(dbSourceName);
+            if (ds != null)
+               this.connFactory =
+                  new HSQLDBConnectionFactory(ds, containerName, multiDb, valueStorageProvider, maxBufferSize,
+                     swapDirectory, swapCleaner);
+            else
+               throw new RepositoryException("Datasource '" + dbSourceName + "' is not bound in this context.");
+         }
+         else
+            this.connFactory =
+               new HSQLDBConnectionFactory(dbDriver, dbUrl, dbUserName, dbPassword, containerName, multiDb,
+                  valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+      else
+      {
+         // generic, DB_HSQLDB
+         this.connFactory = defaultConnectionFactory();
+         sqlPath = "/conf/storage/jcr-" + (multiDb ? "m" : "s") + "jdbc.sql";
+         dbInitilizer = defaultDBInitializer(sqlPath);
+      }
+
+      // database type
+      try
+      {
+         dbInitilizer.init();
+      }
+      catch (DBInitializerException e)
+      {
+         LOG.error("Error of init db " + e, e);
+      }
+   }
+
+   /**
+    * Return ConnectionFactory.
+    * 
+    * @return WorkspaceStorageConnectionFactory connection
+    */
+   protected GenericConnectionFactory getConnectionFactory()
+   {
+      return connFactory;
+   }
+
+   protected String detectDialect(String confParam)
+   {
+      for (String dbType : DBConstants.DB_DIALECTS)
+      {
+         if (dbType.equalsIgnoreCase(confParam))
+            return dbType;
+      }
+
+      return DBConstants.DB_DIALECT_GENERIC; // by default
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public WorkspaceStorageConnection openConnection() throws RepositoryException
+   {
+
+      return connFactory.openConnection();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public WorkspaceStorageConnection openConnection(boolean readOnly) throws RepositoryException
+   {
+
+      return connFactory.openConnection(readOnly);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public WorkspaceStorageConnection reuseConnection(WorkspaceStorageConnection original) throws RepositoryException
+   {
+
+      if (original instanceof NewJDBCStorageConnection)
+      {
+         WorkspaceStorageConnectionFactory cFactory =
+            new SharedConnectionFactory(((NewJDBCStorageConnection)original).getJdbcConnection(), containerName,
+               multiDb, valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+
+         return cFactory.openConnection();
+      }
+      else
+      {
+         return openConnection();
+      }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public String getName()
+   {
+      return containerName;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public String getInfo()
+   {
+      String str =
+         "JDBC based JCR Workspace Data container \n" + "container name: " + containerName + " \n"
+            + "data source JNDI name: " + dbSourceName + "\n" + "is multi database: " + multiDb + "\n"
+            + "storage version: " + storageVersion + "\n" + "value storage provider: " + valueStorageProvider + "\n"
+            + "max buffer size (bytes): " + maxBufferSize + "\n" + "swap directory path: "
+            + swapDirectory.getAbsolutePath();
+      return str;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public String getStorageVersion()
+   {
+      return storageVersion;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void start()
+   {
+      this.swapCleaner.start();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public void stop()
+   {
+      this.swapCleaner.halt();
+      this.swapCleaner.interrupt();
+
+      // TODO HSQLDB Stop (debug)
+      // if (dbDialect.equals(DB_DIALECT_GENERIC) ||
+      // dbDialect.equals(DB_DIALECT_HSQLDB)) {
+      // // shutdown in-process HSQLDB database
+      // System.out.println("Shutdown in-process HSQLDB database...");
+      // try {
+      // JDBCStorageConnection conn = (JDBCStorageConnection) openConnection();
+      // Connection jdbcConn = conn.getJdbcConnection();
+      // String dbUrl = jdbcConn.getMetaData().getURL();
+      // if (dbUrl.startsWith("jdbc:hsqldb:file") ||
+      // dbUrl.startsWith("jdbc:hsqldb:mem")) {
+      // // yeah, there is in-process hsqldb, shutdown it now
+      // jdbcConn.createStatement().execute("SHUTDOWN");
+      // System.out.println("Shutdown in-process HSQLDB database... done.");
+      // }
+      // } catch (Throwable e) {
+      // log.error("JDBC Data container stop error " + e);
+      // e.printStackTrace();
+      // }
+      // }
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public boolean isSame(WorkspaceDataContainer another)
+   {
+      if (another == this)
+         return true;
+
+      if (another instanceof NewJDBCWorkspaceDataContainer)
+      {
+         NewJDBCWorkspaceDataContainer anotherJdbc = (NewJDBCWorkspaceDataContainer)another;
+
+         if (getDbSourceName() != null)
+            // by jndi ds name
+            return getDbSourceName().equals(anotherJdbc.getDbSourceName());
+
+         // by db connection params
+         return getDbDriver().equals(anotherJdbc.getDbDriver()) && getDbUrl().equals(anotherJdbc.getDbUrl())
+            && getDbUserName().equals(anotherJdbc.getDbUserName());
+      }
+
+      return false;
+   }
+
+   /**
+    * Used in <code>equals()</code>.
+    * 
+    * @return DataSource name
+    */
+   protected String getDbSourceName()
+   {
+      return dbSourceName;
+   }
+
+   /**
+    * Used in <code>equals()</code>.
+    * 
+    * @return JDBC driver
+    */
+   protected String getDbDriver()
+   {
+      return dbDriver;
+   }
+
+   /**
+    * Used in <code>equals()</code>.
+    * 
+    * @return Database URL
+    */
+   protected String getDbUrl()
+   {
+      return dbUrl;
+   }
+
+   /**
+    * Used in <code>equals()</code>.
+    * 
+    * @return Database username
+    */
+   protected String getDbUserName()
+   {
+      return dbUserName;
+   }
+}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewSQLExceptionHandler.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewSQLExceptionHandler.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/NewSQLExceptionHandler.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -0,0 +1,395 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.storage.jdbc.optimisation;
+
+import org.exoplatform.services.jcr.dataflow.ItemState;
+import org.exoplatform.services.jcr.datamodel.ItemData;
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.datamodel.QPathEntry;
+import org.exoplatform.services.jcr.impl.storage.JCRInvalidItemStateException;
+import org.exoplatform.services.jcr.impl.storage.JCRItemExistsException;
+
+import java.io.IOException;
+import java.sql.SQLException;
+
+import javax.jcr.InvalidItemStateException;
+import javax.jcr.ItemExistsException;
+import javax.jcr.RepositoryException;
+
+/**
+ * Created by The eXo Platform SAS.
+ * 
+ * The eXo JCR database has constraints can be violated.
+ * 
+ * JCR_PK_XCONTAINER - Can not be exisits two containers with same version
+ * 
+ * JCR_PK_XITEM - Item already exists with this ID JCR_FK_XITEM_PARENT - Parent not found by ID
+ * JCR_IDX_XITEM_PARENT - Item already exists with the parent, name, index, type(N/P), persisted
+ * version JCR_IDX_XITEM_PARENT_NAME - Item already exists with the type(N/P), parent, name, index,
+ * persisted version JCR_IDX_XITEM_PARENT_ID - Item already exists with the type(N/P), parent, ID,
+ * persisted version
+ * 
+ * JCR_PK_XVALUE - Value already exists with the ID (impossible, ID is autiincremented)
+ * JCR_FK_XVALUE_PROPERTY - There is no property exists for the value. JCR_IDX_XVALUE_PROPERTY -
+ * Value already exists with the property and order number
+ * 
+ * JCR_PK_XREF - Reference already exists to the node from property with order number
+ * JCR_IDX_XREF_PROPERTY - Reference already exists with the property and order number
+ * 
+ * @author Peter Nedonosko
+ * @version $Id: SQLExceptionHandler.java 34801 2009-07-31 15:44:50Z dkatayev $
+ */
+
+public class NewSQLExceptionHandler
+{
+
+   /**
+    * Ccontainer name.
+    */
+   private final String containerName;
+
+   /**
+    * Storage connection.
+    */
+   private final NewJDBCStorageConnection conn;
+
+   // ---------------- SQLException handler -------------------
+
+   /**
+    * SQLExceptionHandler constructor.
+    * 
+    * @param containerName
+    *          - workspace container name
+    * @param conn
+    *          - storage connection
+    */
+   NewSQLExceptionHandler(String containerName, NewJDBCStorageConnection conn)
+   {
+      this.containerName = containerName;
+      this.conn = conn;
+   }
+
+   /**
+    * Handle Add SQLException.
+    * 
+    * @param e
+    *          - an SQLException
+    * @param item
+    *          - context ItemData
+    * @return String with error message
+    * @throws RepositoryException
+    *           if <code>RepositoryException</code> should be thrown
+    * @throws InvalidItemStateException
+    *           if <code>InvalidItemStateException</code> should be thrown
+    */
+   protected String handleAddException(SQLException e, ItemData item) throws RepositoryException,
+      InvalidItemStateException
+   {
+      String message = "[" + containerName + "] ADD " + (item.isNode() ? "NODE. " : "PROPERTY. ");
+      String errMessage = e.getMessage();
+      String itemInfo =
+         item.getQPath().getAsString() + ", ID: " + item.getIdentifier() + ", ParentID: " + item.getParentIdentifier()
+            + (errMessage != null ? ". Cause >>>> " + errMessage : "");
+
+      if (errMessage != null)
+      {
+         // try detect error by foreign key names
+         String umsg = errMessage.toLowerCase().toUpperCase();
+         if (umsg.indexOf(conn.JCR_FK_ITEM_PARENT) >= 0)
+         {
+            message += "Parent not found. Item " + itemInfo;
+            throw new JCRInvalidItemStateException(message, item.getIdentifier(), ItemState.ADDED, e);
+         }
+         else if (umsg.indexOf(conn.JCR_PK_ITEM) >= 0)
+         {
+            message += "Item already exists. Condition: ID. " + itemInfo;
+            // InvalidItemStateException ! - because it's impossible add new item with existed UUID
+            throw new JCRInvalidItemStateException(message, item.getIdentifier(), ItemState.ADDED, e);
+         }
+         else if (umsg.indexOf(conn.JCR_IDX_ITEM_PARENT) >= 0 || umsg.indexOf(conn.JCR_IDX_ITEM_PARENT_NAME) >= 0)
+         {
+            message += "Item already exists. Condition: parent ID, name, index. " + itemInfo;
+            throw new ItemExistsException(message, e);
+         }
+         else if (umsg.indexOf(conn.JCR_IDX_ITEM_PARENT_ID) >= 0)
+         {
+            message += "Item already exists. Condition: parent ID and ID. " + itemInfo;
+            throw new ItemExistsException(message, e);
+         }
+         else if (umsg.indexOf(conn.JCR_FK_VALUE_PROPERTY) >= 0)
+         {
+            message += "Property is not exist but the value is being created. Condition: property ID. " + itemInfo;
+            throw new RepositoryException(message, e);
+         }
+         else if (umsg.indexOf(conn.JCR_IDX_VALUE_PROPERTY) >= 0)
+         {
+            message += "Property already exists. Condition: property ID, order number. " + itemInfo;
+            throw new RepositoryException(message, e);
+         }
+         else if (umsg.indexOf(conn.JCR_PK_VALUE) >= 0)
+         {
+            message +=
+               "[FATAL] Value already exists with the ValueID. Impossible state, check is ValueID is autoincremented. "
+                  + itemInfo;
+            throw new RepositoryException(message, e);
+         }
+         else if (umsg.indexOf(conn.JCR_PK_REF) >= 0)
+         {
+            message += "Reference chain already exists. Condition: node ID, property ID, order number. " + itemInfo;
+            throw new RepositoryException(message, e);
+         }
+         else if (umsg.indexOf(conn.JCR_IDX_REF_PROPERTY) >= 0)
+         {
+            message += "Referenceable property value already exists. Condition: property ID, order number. " + itemInfo;
+            throw new RepositoryException(message, e);
+         }
+      }
+
+      // try detect integrity violation
+      RepositoryException ownException = null;
+      try
+      {
+         NodeData parent = (NodeData)conn.getItemData(item.getParentIdentifier());
+         if (parent != null)
+         {
+            // have a parent
+            try
+            {
+               ItemData me = conn.getItemData(item.getIdentifier());
+               if (me != null)
+               {
+                  // item already exists
+                  message += "Item already exists in storage: " + itemInfo;
+                  ownException = new JCRItemExistsException(message, me.getIdentifier(), ItemState.ADDED, e);
+                  throw ownException;
+               }
+
+               me = conn.getItemData(parent, new QPathEntry(item.getQPath().getName(), item.getQPath().getIndex()));
+               if (me != null)
+               {
+                  message += "Item already exists in storage: " + itemInfo;
+                  ownException = new JCRItemExistsException(message, me.getIdentifier(), ItemState.ADDED, e);
+                  throw ownException;
+               }
+
+            }
+            catch (Exception ep)
+            {
+               // item not found or other things but error of item reading
+               if (ownException != null)
+                  throw ownException;
+            }
+
+            // MySQL violation
+            if (e.getClass().getName().indexOf("MySQLIntegrityConstraintViolationException") >= 0
+               && errMessage.indexOf(item.getIdentifier()) >= 0)
+            {
+               // it's JCR_PK_ITEM violation 
+               message += "Item already exists. Condition: ID. " + itemInfo;
+               throw new JCRInvalidItemStateException(message, item.getIdentifier(), ItemState.ADDED, e);
+            }
+
+            message += "Error of item add. " + itemInfo;
+            ownException = new RepositoryException(message, e);
+            throw ownException;
+         }
+      }
+      catch (Exception ep)
+      {
+         // no parent or error access it
+         if (ownException != null)
+            throw ownException;
+      }
+      message += "Error of item add. " + itemInfo;
+      throw new JCRInvalidItemStateException(message, item.getIdentifier(), ItemState.ADDED, e);
+   }
+
+   /**
+    * Handle Add IOException.
+    * 
+    * @param e
+    *          - an IOException
+    * @param item
+    *          - context ItemData
+    * @return String with error message
+    * @throws RepositoryException
+    *           if <code>RepositoryException</code> should be thrown
+    * @throws InvalidItemStateException
+    *           if <code>InvalidItemStateException</code> should be thrown
+    */
+   protected String handleAddException(IOException e, ItemData item) throws RepositoryException,
+      InvalidItemStateException
+   {
+      String message = "[" + containerName + "] ADD " + (item.isNode() ? "NODE. " : "PROPERTY. ");
+      String errMessage = e.getMessage();
+      String itemInfo =
+         item.getQPath().getAsString() + ", ID: " + item.getIdentifier() + ", ParentID: " + item.getParentIdentifier()
+            + (errMessage != null ? ". Cause >>>> " + errMessage : "");
+
+      // try detect integrity violation
+      RepositoryException ownException = null;
+      try
+      {
+         NodeData parent = (NodeData)conn.getItemData(item.getParentIdentifier());
+         if (parent != null)
+         {
+            // have a parent
+            try
+            {
+               ItemData me = conn.getItemData(item.getIdentifier());
+               if (me != null)
+               {
+                  // item already exists
+                  message += "Item already exists in storage: " + itemInfo;
+                  ownException = new ItemExistsException(message, e);
+                  throw ownException;
+               }
+
+               me = conn.getItemData(parent, new QPathEntry(item.getQPath().getName(), item.getQPath().getIndex()));
+               if (me != null)
+               {
+                  message += "Item already exists in storage: " + itemInfo;
+                  ownException = new ItemExistsException(message, e);
+                  throw ownException;
+               }
+
+            }
+            catch (Exception ep)
+            {
+               // item not found or other things but error of item reading
+               if (ownException != null)
+                  throw ownException;
+            }
+            message += "Error of item add. " + itemInfo;
+            ownException = new RepositoryException(message, e);
+            throw ownException;
+         }
+      }
+      catch (Exception ep)
+      {
+         // no parent or error access it
+         if (ownException != null)
+            throw ownException;
+      }
+      message += "Error of item add. " + itemInfo;
+      throw new JCRInvalidItemStateException(message, item.getIdentifier(), ItemState.ADDED, e);
+   }
+
+   /**
+    * Handle delete Exceptions.
+    * 
+    * @param e
+    *          - an SQLException
+    * @param item
+    *          - context ItemData
+    * @return String with error message
+    * @throws RepositoryException
+    *           if <code>RepositoryException</code> should be thrown
+    * @throws InvalidItemStateException
+    *           if <code>InvalidItemStateException</code> should be thrown
+    */
+   protected String handleDeleteException(SQLException e, ItemData item) throws RepositoryException,
+      InvalidItemStateException
+   {
+      String message = "[" + containerName + "] DELETE " + (item.isNode() ? "NODE. " : "PROPERTY. ");
+      String errMessage = e.getMessage();
+      String itemInfo =
+         item.getQPath().getAsString() + " " + item.getIdentifier()
+            + (errMessage != null ? ". Cause >>>> " + errMessage : "");
+
+      if (errMessage != null)
+      {
+         // try detect error by foreign key names
+         String umsg = errMessage.toLowerCase().toUpperCase();
+         if (umsg.indexOf(conn.JCR_FK_ITEM_PARENT) >= 0)
+         {
+            message += "Can not delete parent till childs exists. Item " + itemInfo;
+            throw new JCRInvalidItemStateException(message, item.getIdentifier(), ItemState.DELETED, e);
+         }
+         else if (umsg.indexOf(conn.JCR_FK_VALUE_PROPERTY) >= 0)
+         {
+            message +=
+               "[FATAL] Can not delete property item till it contains values. Condition: property ID. " + itemInfo;
+            throw new RepositoryException(message, e);
+         }
+      }
+
+      message += "Error of item delete " + itemInfo;
+      throw new RepositoryException(message, e);
+   }
+
+   /**
+    * Handle update Exceptions.
+    * 
+    * @param e
+    *          - an SQLException
+    * @param item
+    *          - context ItemData
+    * @return String with error message
+    * @throws RepositoryException
+    *           if <code>RepositoryException</code> should be thrown
+    * @throws InvalidItemStateException
+    *           if <code>InvalidItemStateException</code> should be thrown
+    */
+   protected String handleUpdateException(SQLException e, ItemData item) throws RepositoryException,
+      InvalidItemStateException
+   {
+      String message = "[" + containerName + "] EDIT " + (item.isNode() ? "NODE. " : "PROPERTY. ");
+      String errMessage = e.getMessage();
+      String itemInfo =
+         item.getQPath().getAsString() + " " + item.getIdentifier()
+            + (errMessage != null ? ". Cause >>>> " + errMessage : "");
+
+      if (errMessage != null)
+         // try detect error by foreign key names
+         if (errMessage.toLowerCase().toUpperCase().indexOf(conn.JCR_FK_VALUE_PROPERTY) >= 0)
+         {
+            message += "Property is not exists but the value is being created. Condition: property ID. " + itemInfo;
+            throw new RepositoryException(message, e);
+         }
+         else if (errMessage.toLowerCase().toUpperCase().indexOf(conn.JCR_PK_ITEM) >= 0)
+         {
+            message += "Item already exists. Condition: ID. " + itemInfo;
+            throw new JCRInvalidItemStateException(message, item.getIdentifier(), ItemState.UPDATED, e);
+         }
+
+      // try detect integrity violation
+      RepositoryException ownException = null;
+      try
+      {
+         ItemData me = conn.getItemData(item.getIdentifier());
+         if (me != null)
+         {
+            // item already exists
+            message += "Item already exists. But update errors. " + itemInfo;
+            ownException = new RepositoryException(message, e);
+            throw ownException;
+         }
+      }
+      catch (Exception ep)
+      {
+         // item not found or other things but error of item reading
+         if (ownException != null)
+            throw ownException;
+      }
+      message += "Error of item update. " + itemInfo;
+      throw new JCRInvalidItemStateException(message, item.getIdentifier(), ItemState.UPDATED, e);
+   }
+
+}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/HSQLDBConnectionFactory.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/HSQLDBConnectionFactory.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/HSQLDBConnectionFactory.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -0,0 +1,127 @@
+/*
+ * Copyright (C) 2003-2007 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.db;
+
+import org.exoplatform.services.jcr.impl.storage.jdbc.db.GenericConnectionFactory;
+import org.exoplatform.services.jcr.impl.storage.jdbc.db.HSQLDBSingleDbJDBCConnection;
+import org.exoplatform.services.jcr.impl.util.io.FileCleaner;
+import org.exoplatform.services.jcr.storage.WorkspaceStorageConnection;
+import org.exoplatform.services.jcr.storage.value.ValueStoragePluginProvider;
+
+import java.io.File;
+import java.sql.SQLException;
+
+import javax.jcr.RepositoryException;
+import javax.sql.DataSource;
+
+/**
+ * Created by The eXo Platform SAS
+ * 
+ * 26.08.2009
+ * 
+ * @author <a href="mailto:dezder at bk.ru">Denis Grebenyuk</a>
+ * @version $Id:$
+ */
+public class HSQLDBConnectionFactory extends GenericConnectionFactory
+{
+
+   /**
+      * HSQLDBConnectionFactory constructor.
+      * 
+      * @param dataSource
+      *          - DataSource
+      * @param containerName
+      *          - Container name (see configuration)
+      * @param multiDb
+      *          - multidatabase state flag
+      * @param valueStorageProvider
+      *          - external Value Storages provider
+      * @param maxBufferSize
+      *          - Maximum buffer size (see configuration)
+      * @param swapDirectory
+      *          - Swap directory (see configuration)
+      * @param swapCleaner
+      *          - Swap cleaner (internal FileCleaner).
+      */
+   public HSQLDBConnectionFactory(DataSource dataSource, String containerName, boolean multiDb,
+      ValueStoragePluginProvider valueStorageProvider, int maxBufferSize, File swapDirectory, FileCleaner swapCleaner)
+   {
+
+      super(dataSource, containerName, multiDb, valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+   }
+
+   /**
+    * HSQLDBConnectionFactory constructor.
+    * 
+    *@param dataSource
+    *          - DataSource
+    * @param dbDriver
+    *          - JDBC Driver
+    * @param dbUrl
+    *          - JDBC URL
+    * @param dbUserName
+    *          - database username
+    * @param dbPassword
+    *          - database user password
+    * @param containerName
+    *          - Container name (see configuration)
+    * @param multiDb
+    *          - multidatabase state flag
+    * @param valueStorageProvider
+    *          - external Value Storages provider
+    * @param maxBufferSize
+    *          - Maximum buffer size (see configuration)
+    * @param swapDirectory
+    *          - Swap directory (see configuration)
+    * @param swapCleaner
+    *          - Swap cleaner (internal FileCleaner).
+    * @throws RepositoryException
+    *           if error eccurs
+    */
+   public HSQLDBConnectionFactory(String dbDriver, String dbUrl, String dbUserName, String dbPassword,
+      String containerName, boolean multiDb, ValueStoragePluginProvider valueStorageProvider, int maxBufferSize,
+      File swapDirectory, FileCleaner swapCleaner) throws RepositoryException
+   {
+
+      super(dbDriver, dbUrl, dbUserName, dbPassword, containerName, multiDb, valueStorageProvider, maxBufferSize,
+         swapDirectory, swapCleaner);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   public WorkspaceStorageConnection openConnection(boolean readOnly) throws RepositoryException
+   {
+      try
+      {
+
+         if (multiDb)
+         {
+            return new HSQLDBMultiDbJDBCConnection(getJdbcConnection(readOnly), readOnly, containerName,
+               valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+         }
+
+         return new HSQLDBSingleDbJDBCConnection(getJdbcConnection(readOnly), readOnly, containerName,
+            valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+
+      }
+      catch (SQLException e)
+      {
+         throw new RepositoryException(e);
+      }
+   }
+}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/HSQLDBMultiDbJDBCConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/HSQLDBMultiDbJDBCConnection.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/HSQLDBMultiDbJDBCConnection.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -0,0 +1,80 @@
+/*
+ * Copyright (C) 2003-2007 eXo Platform SAS.
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU Affero General Public License
+ * as published by the Free Software Foundation; either version 3
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see<http://www.gnu.org/licenses/>.
+ */
+package org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.db;
+
+import org.exoplatform.services.jcr.impl.util.io.FileCleaner;
+import org.exoplatform.services.jcr.storage.value.ValueStoragePluginProvider;
+
+import java.io.File;
+import java.sql.Connection;
+import java.sql.SQLException;
+
+/**
+ * Created by The eXo Platform SAS
+ * 
+ * 26.08.2009
+ * 
+ * @author <a href="mailto:dezder at bk.ru">Denis Grebenyuk</a>
+ * @version $Id:$
+ */
+public class HSQLDBMultiDbJDBCConnection extends MultiDbJDBCConnection
+{
+
+   /**
+      * HSQLDB Multidatabase JDBC Connection constructor.
+      * 
+      * @param dbConnection
+      *          JDBC connection, shoudl be opened before
+      * @param readOnly
+      *          boolean if true the dbConnection was marked as READ-ONLY.
+      * @param containerName
+      *          Workspace Storage Container name (see configuration)
+      * @param valueStorageProvider
+      *          External Value Storages provider
+      * @param maxBufferSize
+      *          Maximum buffer size (see configuration)
+      * @param swapDirectory
+      *          Swap directory File (see configuration)
+      * @param swapCleaner
+      *          Swap cleaner (internal FileCleaner).
+      * @throws SQLException
+      * 
+      * @see org.exoplatform.services.jcr.impl.util.io.FileCleaner
+      */
+   public HSQLDBMultiDbJDBCConnection(Connection dbConnection, boolean readOnly, String containerName,
+      ValueStoragePluginProvider valueStorageProvider, int maxBufferSize, File swapDirectory, FileCleaner swapCleaner)
+      throws SQLException
+   {
+      super(dbConnection, readOnly, containerName, valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected void prepareQueries() throws SQLException
+   {
+
+      super.prepareQueries();
+      FIND_PROPERTY_BY_NAME =
+         "select V.DATA" + " from JCR_MITEM I, JCR_MVALUE V"
+            + " where I.PARENT_ID=? and I.I_CLASS=2 and I.NAME=? and I.ID=V.PROPERTY_ID order by V.ORDER_NUM";
+      FIND_NODES_BY_PARENTID = "select * from JCR_MITEM" + " where PARENT_ID=? and I_CLASS=1" + " order by N_ORDER_NUM";
+      FIND_NODES_COUNT_BY_PARENTID = "select count(ID) from JCR_MITEM" + " where PARENT_ID=? and I_CLASS=1";
+      FIND_PROPERTIES_BY_PARENTID = "select * from JCR_MITEM" + " where PARENT_ID=? and I_CLASS=2" + " order by ID";
+   }
+}

Added: jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java	                        (rev 0)
+++ jcr/trunk/exo.jcr.component.core/src/main/java/org/exoplatform/services/jcr/impl/storage/jdbc/optimisation/db/MultiDbJDBCConnection.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -0,0 +1,571 @@
+/*
+ * Copyright (C) 2009 eXo Platform SAS.
+ *
+ * This is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as
+ * published by the Free Software Foundation; either version 2.1 of
+ * the License, or (at your option) any later version.
+ *
+ * This software is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public
+ * License along with this software; if not, write to the Free
+ * Software Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA
+ * 02110-1301 USA, or see the FSF site: http://www.fsf.org.
+ */
+package org.exoplatform.services.jcr.impl.storage.jdbc.optimisation.db;
+
+import org.exoplatform.services.jcr.datamodel.NodeData;
+import org.exoplatform.services.jcr.datamodel.PropertyData;
+import org.exoplatform.services.jcr.datamodel.ValueData;
+import org.exoplatform.services.jcr.impl.Constants;
+import org.exoplatform.services.jcr.impl.dataflow.ValueDataConvertor;
+import org.exoplatform.services.jcr.impl.storage.jdbc.JDBCStorageConnection;
+import org.exoplatform.services.jcr.impl.util.io.FileCleaner;
+import org.exoplatform.services.jcr.storage.value.ValueStoragePluginProvider;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Types;
+import java.util.List;
+
+/**
+ * Single database connection implementation.
+ * 
+ * Created by The eXo Platform SAS. </br>
+ * 
+ * @author <a href="mailto:gennady.azarenkov at exoplatform.com">Gennady
+ *         Azarenkov</a>
+ * @version $Id: MultiDbJDBCConnection.java 20950 2008-10-06 14:23:07Z
+ *          pnedonosko $
+ */
+
+public class MultiDbJDBCConnection extends JDBCStorageConnection
+{
+
+   protected PreparedStatement findItemById;
+
+   protected PreparedStatement findItemByPath;
+
+   protected PreparedStatement findItemByName;
+
+   protected PreparedStatement findChildPropertyByPath;
+
+   protected PreparedStatement findPropertyByName;
+
+   protected PreparedStatement findDescendantNodes;
+
+   protected PreparedStatement findDescendantProperties;
+
+   protected PreparedStatement findReferences;
+
+   protected PreparedStatement findValuesByPropertyId;
+
+   protected PreparedStatement findValuesDataByPropertyId;
+
+   protected PreparedStatement findValuesStorageDescriptorsByPropertyId;
+
+   @Deprecated
+   protected PreparedStatement findValueByPropertyIdOrderNumber;
+
+   protected PreparedStatement findNodesByParentId;
+   
+   protected PreparedStatement findNodesCountByParentId;
+
+   protected PreparedStatement findPropertiesByParentId;
+
+   protected PreparedStatement insertNode;
+
+   protected PreparedStatement insertProperty;
+
+   protected PreparedStatement insertReference;
+
+   protected PreparedStatement insertValue;
+
+   protected PreparedStatement updateItem;
+
+   protected PreparedStatement updateItemPath;
+
+   protected PreparedStatement updateNode;
+
+   protected PreparedStatement updateProperty;
+
+   protected PreparedStatement updateValue;
+
+   protected PreparedStatement deleteItem;
+
+   protected PreparedStatement deleteNode;
+
+   protected PreparedStatement deleteProperty;
+
+   protected PreparedStatement deleteReference;
+
+   protected PreparedStatement deleteValue;
+
+   protected PreparedStatement renameNode;
+
+   /**
+    * Multidatabase JDBC Connection constructor.
+    * 
+    * @param dbConnection JDBC connection, shoudl be opened before
+    * @param readOnly, boolean if true the dbConnection was marked as READ-ONLY. 
+    * @param containerName Workspace Storage Container name (see configuration)
+    * @param valueStorageProvider External Value Storages provider
+    * @param maxBufferSize Maximum buffer size (see configuration)
+    * @param swapDirectory Swap directory (see configuration)
+    * @param swapCleaner Swap cleaner (internal FileCleaner).
+    * @throws SQLException
+    * @see org.exoplatform.services.jcr.impl.util.io.FileCleaner
+    */
+   public MultiDbJDBCConnection(Connection dbConnection, boolean readOnly, String containerName,
+      ValueStoragePluginProvider valueStorageProvider, int maxBufferSize, File swapDirectory, FileCleaner swapCleaner)
+      throws SQLException
+   {
+
+      super(dbConnection, readOnly, containerName, valueStorageProvider, maxBufferSize, swapDirectory, swapCleaner);
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   protected String getIdentifier(final String internalId)
+   {
+      return internalId;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   protected String getInternalId(final String identifier)
+   {
+      return identifier;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected void prepareQueries() throws SQLException
+   {
+      JCR_PK_ITEM = "JCR_PK_MITEM";
+      JCR_FK_ITEM_PARENT = "JCR_FK_MITEM_PARENT";
+      JCR_IDX_ITEM_PARENT = "JCR_IDX_MITEM_PARENT";
+      JCR_IDX_ITEM_PARENT_NAME = "JCR_IDX_MITEM_PARENT_NAME";
+      JCR_IDX_ITEM_PARENT_ID = "JCR_IDX_MITEM_PARENT_ID";
+      JCR_PK_VALUE = "JCR_PK_MVALUE";
+      JCR_FK_VALUE_PROPERTY = "JCR_FK_MVALUE_PROPERTY";
+      JCR_IDX_VALUE_PROPERTY = "JCR_IDX_MVALUE_PROPERTY";
+      JCR_PK_REF = "JCR_PK_MREF";
+      JCR_IDX_REF_PROPERTY = "JCR_IDX_MREF_PROPERTY";
+
+      FIND_ITEM_BY_ID = "select * from JCR_MITEM where ID=?";
+
+      FIND_ITEM_BY_NAME =
+         "select * from JCR_MITEM" + " where PARENT_ID=? and NAME=? and I_INDEX=? order by I_CLASS, VERSION DESC";
+
+      FIND_PROPERTY_BY_NAME =
+         "select V.DATA" + " from JCR_MITEM I, JCR_MVALUE V"
+            + " where I.I_CLASS=2 and I.PARENT_ID=? and I.NAME=? and I.ID=V.PROPERTY_ID order by V.ORDER_NUM";
+
+      FIND_REFERENCES =
+         "select P.ID, P.PARENT_ID, P.VERSION, P.P_TYPE, P.P_MULTIVALUED, P.NAME" + " from JCR_MREF R, JCR_MITEM P"
+            + " where R.NODE_ID=? and P.ID=R.PROPERTY_ID and P.I_CLASS=2";
+
+      FIND_VALUES_BY_PROPERTYID =
+         "select PROPERTY_ID, ORDER_NUM, DATA, STORAGE_DESC from JCR_MVALUE where PROPERTY_ID=? order by ORDER_NUM";
+
+      FIND_VALUES_VSTORAGE_DESC_BY_PROPERTYID = "select distinct STORAGE_DESC from JCR_MVALUE where PROPERTY_ID=?";
+
+      FIND_VALUE_BY_PROPERTYID_OREDERNUMB = "select DATA from JCR_MVALUE where PROPERTY_ID=? and ORDER_NUM=?";
+
+      FIND_NODES_BY_PARENTID = "select * from JCR_MITEM" + " where I_CLASS=1 and PARENT_ID=?" + " order by N_ORDER_NUM";
+      
+      FIND_NODES_COUNT_BY_PARENTID = "select count(ID) from JCR_MITEM" + " where I_CLASS=1 and PARENT_ID=?";
+
+      FIND_PROPERTIES_BY_PARENTID = "select * from JCR_MITEM" + " where I_CLASS=2 and PARENT_ID=?" + " order by ID";
+
+      INSERT_NODE =
+         "insert into JCR_MITEM(ID, PARENT_ID, NAME, VERSION, I_CLASS, I_INDEX, N_ORDER_NUM) VALUES(?,?,?,?,"
+            + I_CLASS_NODE + ",?,?)";
+      INSERT_PROPERTY =
+         "insert into JCR_MITEM(ID, PARENT_ID, NAME, VERSION, I_CLASS, I_INDEX, P_TYPE, P_MULTIVALUED) VALUES(?,?,?,?,"
+            + I_CLASS_PROPERTY + ",?,?,?)";
+
+      INSERT_VALUE = "insert into JCR_MVALUE(DATA, ORDER_NUM, PROPERTY_ID, STORAGE_DESC) VALUES(?,?,?,?)";
+      INSERT_REF = "insert into JCR_MREF(NODE_ID, PROPERTY_ID, ORDER_NUM) VALUES(?,?,?)";
+
+      RENAME_NODE = "update JCR_MITEM set PARENT_ID=?, NAME =?, VERSION=?, I_INDEX =?, N_ORDER_NUM =? where ID=?";
+
+      UPDATE_NODE = "update JCR_MITEM set VERSION=?, I_INDEX=?, N_ORDER_NUM=? where ID=?";
+      UPDATE_PROPERTY = "update JCR_MITEM set VERSION=?, P_TYPE=? where ID=?";
+      //UPDATE_VALUE = "update JCR_MVALUE set DATA=?, STORAGE_DESC=? where PROPERTY_ID=?, ORDER_NUM=?";
+
+      DELETE_ITEM = "delete from JCR_MITEM where ID=?";
+      DELETE_VALUE = "delete from JCR_MVALUE where PROPERTY_ID=?";
+      DELETE_REF = "delete from JCR_MREF where PROPERTY_ID=?";
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected int addNodeRecord(NodeData data) throws SQLException
+   {
+      if (insertNode == null)
+         insertNode = dbConnection.prepareStatement(INSERT_NODE);
+      else
+         insertNode.clearParameters();
+
+      insertNode.setString(1, data.getIdentifier());
+      insertNode.setString(2, data.getParentIdentifier() == null ? Constants.ROOT_PARENT_UUID : data
+         .getParentIdentifier());
+      insertNode.setString(3, data.getQPath().getName().getAsString());
+      insertNode.setInt(4, data.getPersistedVersion());
+      insertNode.setInt(5, data.getQPath().getIndex());
+      insertNode.setInt(6, data.getOrderNumber());
+      return insertNode.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected int addPropertyRecord(PropertyData data) throws SQLException
+   {
+      if (insertProperty == null)
+         insertProperty = dbConnection.prepareStatement(INSERT_PROPERTY);
+      else
+         insertProperty.clearParameters();
+
+      insertProperty.setString(1, data.getIdentifier());
+      insertProperty.setString(2, data.getParentIdentifier());
+      insertProperty.setString(3, data.getQPath().getName().getAsString());
+      insertProperty.setInt(4, data.getPersistedVersion());
+      insertProperty.setInt(5, data.getQPath().getIndex());
+      insertProperty.setInt(6, data.getType());
+      insertProperty.setBoolean(7, data.isMultiValued());
+
+      return insertProperty.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected int addReference(PropertyData data) throws SQLException, IOException
+   {
+      if (insertReference == null)
+         insertReference = dbConnection.prepareStatement(INSERT_REF);
+      else
+         insertReference.clearParameters();
+
+      if (data.getQPath().getAsString().indexOf("versionableUuid") > 0)
+         LOG.info("add ref versionableUuid " + data.getQPath().getAsString());
+
+      List<ValueData> values = data.getValues();
+      int added = 0;
+      for (int i = 0; i < values.size(); i++)
+      {
+         ValueData vdata = values.get(i);
+         String refNodeIdentifier = ValueDataConvertor.readString(vdata);
+
+         insertReference.setString(1, refNodeIdentifier);
+         insertReference.setString(2, data.getIdentifier());
+         insertReference.setInt(3, i);
+         added += insertReference.executeUpdate();
+      }
+
+      return added;
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected int deleteReference(String propertyIdentifier) throws SQLException
+   {
+      if (deleteReference == null)
+         deleteReference = dbConnection.prepareStatement(DELETE_REF);
+      else
+         deleteReference.clearParameters();
+
+      deleteReference.setString(1, propertyIdentifier);
+      return deleteReference.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected int deleteItemByIdentifier(String identifier) throws SQLException
+   {
+      if (deleteItem == null)
+         deleteItem = dbConnection.prepareStatement(DELETE_ITEM);
+      else
+         deleteItem.clearParameters();
+
+      deleteItem.setString(1, identifier);
+      return deleteItem.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected int updateNodeByIdentifier(int version, int index, int orderNumb, String identifier) throws SQLException
+   {
+      if (updateNode == null)
+         updateNode = dbConnection.prepareStatement(UPDATE_NODE);
+      else
+         updateNode.clearParameters();
+
+      updateNode.setInt(1, version);
+      updateNode.setInt(2, index);
+      updateNode.setInt(3, orderNumb);
+      updateNode.setString(4, identifier);
+      return updateNode.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected int updatePropertyByIdentifier(int version, int type, String identifier) throws SQLException
+   {
+      if (updateProperty == null)
+         updateProperty = dbConnection.prepareStatement(UPDATE_PROPERTY);
+      else
+         updateProperty.clearParameters();
+
+      updateProperty.setInt(1, version);
+      updateProperty.setInt(2, type);
+      updateProperty.setString(3, identifier);
+      return updateProperty.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   protected ResultSet findItemByName(String parentId, String name, int index) throws SQLException
+   {
+      if (findItemByName == null)
+         findItemByName = dbConnection.prepareStatement(FIND_ITEM_BY_NAME);
+      else
+         findItemByName.clearParameters();
+
+      findItemByName.setString(1, parentId);
+      findItemByName.setString(2, name);
+      findItemByName.setInt(3, index);
+      return findItemByName.executeQuery();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findPropertyByName(String parentId, String name) throws SQLException
+   {
+      if (findPropertyByName == null)
+         findPropertyByName = dbConnection.prepareStatement(FIND_PROPERTY_BY_NAME);
+      else
+         findPropertyByName.clearParameters();
+
+      findPropertyByName.setString(1, parentId);
+      findPropertyByName.setString(2, name);
+      return findPropertyByName.executeQuery();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findItemByIdentifier(String identifier) throws SQLException
+   {
+      if (findItemById == null)
+         findItemById = dbConnection.prepareStatement(FIND_ITEM_BY_ID);
+      else
+         findItemById.clearParameters();
+
+      findItemById.setString(1, identifier);
+      return findItemById.executeQuery();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findReferences(String nodeIdentifier) throws SQLException
+   {
+      if (findReferences == null)
+         findReferences = dbConnection.prepareStatement(FIND_REFERENCES);
+      else
+         findReferences.clearParameters();
+
+      findReferences.setString(1, nodeIdentifier);
+      return findReferences.executeQuery();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findChildNodesByParentIdentifier(String parentIdentifier) throws SQLException
+   {
+      if (findNodesByParentId == null)
+         findNodesByParentId = dbConnection.prepareStatement(FIND_NODES_BY_PARENTID);
+      else
+         findNodesByParentId.clearParameters();
+
+      findNodesByParentId.setString(1, parentIdentifier);
+      return findNodesByParentId.executeQuery();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findChildNodesCountByParentIdentifier(String parentIdentifier) throws SQLException
+   {
+      if (findNodesCountByParentId == null)
+         findNodesCountByParentId = dbConnection.prepareStatement(FIND_NODES_COUNT_BY_PARENTID);
+      else
+         findNodesCountByParentId.clearParameters();
+
+      findNodesCountByParentId.setString(1, parentIdentifier);
+      return findNodesCountByParentId.executeQuery();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findChildPropertiesByParentIdentifier(String parentIdentifier) throws SQLException
+   {
+      if (findPropertiesByParentId == null)
+         findPropertiesByParentId = dbConnection.prepareStatement(FIND_PROPERTIES_BY_PARENTID);
+      else
+         findPropertiesByParentId.clearParameters();
+
+      findPropertiesByParentId.setString(1, parentIdentifier);
+      return findPropertiesByParentId.executeQuery();
+   }
+
+   // -------- values processing ------------
+
+   /**
+    * {@inheritDoc}
+    */
+   protected int addValueData(String cid, int orderNumber, InputStream stream, int streamLength, String storageDesc)
+      throws SQLException
+   {
+
+      if (insertValue == null)
+         insertValue = dbConnection.prepareStatement(INSERT_VALUE);
+      else
+         insertValue.clearParameters();
+
+      if (stream == null)
+      {
+         // [PN] store vd reference to external storage etc.
+         insertValue.setNull(1, Types.BINARY);
+         insertValue.setString(4, storageDesc);
+      }
+      else
+      {
+         insertValue.setBinaryStream(1, stream, streamLength);
+         insertValue.setNull(4, Types.VARCHAR);
+      }
+
+      insertValue.setInt(2, orderNumber);
+      insertValue.setString(3, cid);
+      return insertValue.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   protected int deleteValueData(String cid) throws SQLException
+   {
+      if (deleteValue == null)
+         deleteValue = dbConnection.prepareStatement(DELETE_VALUE);
+      else
+         deleteValue.clearParameters();
+
+      deleteValue.setString(1, cid);
+      return deleteValue.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   protected ResultSet findValuesByPropertyId(String cid) throws SQLException
+   {
+      if (findValuesByPropertyId == null)
+         findValuesByPropertyId = dbConnection.prepareStatement(FIND_VALUES_BY_PROPERTYID);
+      else
+         findValuesByPropertyId.clearParameters();
+
+      findValuesByPropertyId.setString(1, cid);
+      return findValuesByPropertyId.executeQuery();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   protected ResultSet findValueByPropertyIdOrderNumber(String cid, int orderNumb) throws SQLException
+   {
+      if (findValueByPropertyIdOrderNumber == null)
+         findValueByPropertyIdOrderNumber = dbConnection.prepareStatement(FIND_VALUE_BY_PROPERTYID_OREDERNUMB);
+      else
+         findValueByPropertyIdOrderNumber.clearParameters();
+
+      findValueByPropertyIdOrderNumber.setString(1, cid);
+      findValueByPropertyIdOrderNumber.setInt(2, orderNumb);
+      return findValueByPropertyIdOrderNumber.executeQuery();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected int renameNode(NodeData data) throws SQLException
+   {
+      if (renameNode == null)
+         renameNode = dbConnection.prepareStatement(RENAME_NODE);
+      else
+         renameNode.clearParameters();
+
+      renameNode.setString(1, data.getParentIdentifier() == null ? Constants.ROOT_PARENT_UUID : data
+         .getParentIdentifier());
+      renameNode.setString(2, data.getQPath().getName().getAsString());
+      renameNode.setInt(3, data.getPersistedVersion());
+      renameNode.setInt(4, data.getQPath().getIndex());
+      renameNode.setInt(5, data.getOrderNumber());
+      renameNode.setString(6, data.getIdentifier());
+      return renameNode.executeUpdate();
+   }
+
+   /**
+    * {@inheritDoc}
+    */
+   @Override
+   protected ResultSet findValuesStorageDescriptorsByPropertyId(String cid) throws SQLException
+   {
+      if (findValuesStorageDescriptorsByPropertyId == null)
+         findValuesStorageDescriptorsByPropertyId =
+            dbConnection.prepareStatement(FIND_VALUES_VSTORAGE_DESC_BY_PROPERTYID);
+      else
+         findValuesStorageDescriptorsByPropertyId.clearParameters();
+
+      findValuesStorageDescriptorsByPropertyId.setString(1, cid);
+      return findValuesStorageDescriptorsByPropertyId.executeQuery();
+   }
+}

Modified: jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/writing/TestNodeReference.java
===================================================================
--- jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/writing/TestNodeReference.java	2009-12-17 14:02:12 UTC (rev 1122)
+++ jcr/trunk/exo.jcr.component.core/src/test/java/org/exoplatform/services/jcr/api/writing/TestNodeReference.java	2009-12-17 14:14:15 UTC (rev 1123)
@@ -19,10 +19,12 @@
 package org.exoplatform.services.jcr.api.writing;
 
 import org.exoplatform.services.jcr.JcrAPIBaseTest;
+import org.exoplatform.services.jcr.impl.core.NodeImpl;
 
 import javax.jcr.Node;
 import javax.jcr.Property;
 import javax.jcr.PropertyIterator;
+import javax.jcr.PropertyType;
 import javax.jcr.ReferentialIntegrityException;
 import javax.jcr.RepositoryException;
 import javax.jcr.Value;
@@ -56,10 +58,13 @@
       Node n2 = root.addNode("n2", "nt:unstructured");
       Node n11 = root.addNode("n11", "nt:unstructured");
 
-      n1.setProperty("p1", testNode);
-      n2.setProperty("p1", testNode);
-      n11.setProperty("p1", testNode1);
+      n1.setProperty("p1", new String[]{((NodeImpl)testNode).getInternalIdentifier(),
+         ((NodeImpl)testNode1).getInternalIdentifier()}, PropertyType.REFERENCE);
 
+      //      n1.setProperty("p1", testNode);
+      // n2.setProperty("p1", testNode);
+      //      n11.setProperty("p1", testNode1);
+
       root.save();
 
       PropertyIterator refs = testNode.getReferences();
@@ -68,7 +73,7 @@
          {
             log.debug("ref >>>" + refs.nextProperty());
          }
-      assertEquals(2, testNode.getReferences().getSize());
+      assertEquals(1, testNode.getReferences().getSize());
       assertEquals(1, testNode1.getReferences().getSize());
 
       testNode.remove();



More information about the exo-jcr-commits mailing list