You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@stanbol.apache.org by su...@apache.org on 2012/07/30 12:32:34 UTC

svn commit: r1367053 - in /incubator/stanbol/branches/contenthub-two-layered-structure/contenthub: ./ bundlelist/src/main/bundles/ index/ index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/ revisionmanager/ revisionmanager/src/ revisionmana...

Author: suat
Date: Mon Jul 30 10:32:33 2012
New Revision: 1367053

URL: http://svn.apache.org/viewvc?rev=1367053&view=rev
Log:
STANBOL-498:
-Adapted the FileStore implementation according to newly added "epoch" mechanism in the Store interface
-Created a separate revisionmanager bundle so that other Store implementations can use the same revision management functionality
-Modified other classes (including tests) based on these changes

Added:
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/   (with props)
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/pom.xml   (with props)
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/RevisionManager.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/StoreDBManager.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/RevisionManagerTest.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/StoreDBManagerTest.java
Removed:
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/src/main/java/org/apache/stanbol/contenthub/store/file/FileRevisionManager.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/src/main/java/org/apache/stanbol/contenthub/store/file/FileStoreDBManager.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/FileRevisionManagerTest.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/FileStoreDBManagerTest.java
Modified:
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/bundlelist/src/main/bundles/list.xml
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/pom.xml
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndex.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndexManager.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/pom.xml
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/pom.xml
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/src/main/java/org/apache/stanbol/contenthub/store/file/FileStore.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/index/ldpath/LDPathSemanticIndexManagerTest.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartDeserializerTest.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartSerializerTest.java
    incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/FileStoreTest.java

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/bundlelist/src/main/bundles/list.xml
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/bundlelist/src/main/bundles/list.xml?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/bundlelist/src/main/bundles/list.xml (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/bundlelist/src/main/bundles/list.xml Mon Jul 30 10:32:33 2012
@@ -24,6 +24,11 @@
     </bundle>
     <bundle>
       <groupId>org.apache.stanbol</groupId>
+      <artifactId>org.apache.stanbol.contenthub.revisionmanager</artifactId>
+      <version>0.10.0-incubating-SNAPSHOT</version>
+    </bundle>
+    <bundle>
+      <groupId>org.apache.stanbol</groupId>
       <artifactId>org.apache.stanbol.contenthub.search.featured</artifactId>
       <version>0.10.0-incubating-SNAPSHOT</version>
     </bundle>

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/pom.xml
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/pom.xml?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/pom.xml (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/pom.xml Mon Jul 30 10:32:33 2012
@@ -58,11 +58,6 @@
     </dependency>
     <dependency>
       <groupId>org.apache.stanbol</groupId>
-      <artifactId>org.apache.stanbol.contenthub.store.solr</artifactId>
-      <version>0.10.0-incubating-SNAPSHOT</version>
-    </dependency>
-    <dependency>
-      <groupId>org.apache.stanbol</groupId>
       <artifactId>org.apache.stanbol.commons.stanboltools.datafileprovider</artifactId>
       <version>0.10.0-incubating-SNAPSHOT</version>
     </dependency>

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndex.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndex.java?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndex.java (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndex.java Mon Jul 30 10:32:33 2012
@@ -882,8 +882,10 @@ public class LDPathSemanticIndex impleme
             long revision = Long.MIN_VALUE;
             boolean noChange = false;
             do {
-                cs = store.changes(revision, batchSize);
-                for (String changed : cs.changed()) {
+                cs = store.changes(store.getEpoch(), revision, batchSize);
+                Iterator<String> changedItr = cs.iterator();
+                while (changedItr.hasNext()) {
+                    String changed = changedItr.next();
                     ContentItem ci = store.get(changed);
                     if (ci == null) {
                         performRemove(changed);
@@ -891,7 +893,7 @@ public class LDPathSemanticIndex impleme
                         performIndex(ci);
                     }
                 }
-                noChange = cs.changed().isEmpty() ? true : false;
+                noChange = cs.iterator().hasNext() ? false : true;
                 if (!noChange) {
                     revision = cs.toRevision();
                 }
@@ -919,14 +921,14 @@ public class LDPathSemanticIndex impleme
 
                 ChangeSet<ContentItem> changeSet = null;
                 try {
-                    changeSet = store.changes(revision, batchSize);
+                    changeSet = store.changes(store.getEpoch(), revision, batchSize);
                 } catch (StoreException e) {
                     logger.error(
                         "Failed to get changes from FileRevisionManager with start revision: {} and batch size: {}",
                         revision, batchSize);
                 }
                 if (changeSet != null) {
-                    Iterator<String> changedItems = changeSet.changed().iterator();
+                    Iterator<String> changedItems = changeSet.iterator();
                     boolean persist = true;
                     while (changedItems.hasNext()) {
                         String changedItem = changedItems.next();
@@ -952,7 +954,7 @@ public class LDPathSemanticIndex impleme
                     }
                     if (persist) {
                         try {
-                            if (changeSet.changed().size() != 0) {
+                            if (changeSet.iterator().hasNext()) {
                                 persist(changeSet.toRevision());
                             }
                         } catch (IndexException e) {

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndexManager.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndexManager.java?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndexManager.java (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/index/src/main/java/org/apache/stanbol/contenthub/index/ldpath/LDPathSemanticIndexManager.java Mon Jul 30 10:32:33 2012
@@ -65,8 +65,6 @@ public class LDPathSemanticIndexManager 
 
     private final Logger logger = LoggerFactory.getLogger(LDPathSemanticIndexManager.class);
 
-    private final static String INDEX_METADATA_FOLDER_PATH = "LDPathSemanticIndexMetadata";
-
     private File indexMetadataDirectory;
 
     private Map<String,Properties> indexMetadataMap = new HashMap<String,Properties>();
@@ -86,7 +84,7 @@ public class LDPathSemanticIndexManager 
         this.bundleContext = context.getBundleContext();
         ldPathUtils = new LDPathUtils(bundleContext.getBundle(), siteManager);
 
-        indexMetadataDirectory = bundleContext.getDataFile(INDEX_METADATA_FOLDER_PATH);
+        indexMetadataDirectory = bundleContext.getDataFile(LDPathSemanticIndexManager.class.getName());
 
         // if directory for programs does not exist, create it
         if (!indexMetadataDirectory.exists()) {
@@ -165,7 +163,7 @@ public class LDPathSemanticIndexManager 
         props.put(LDPathSemanticIndex.PROP_DESCRIPTION, indexDescription);
         return createIndex(props);
     }
-    
+
     /**
      * Creates an {@link LDPathSemanticIndex} instance based on the given index metadata. However, provided
      * {@link Properties} must include the following items.

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/pom.xml
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/pom.xml?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/pom.xml (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/pom.xml Mon Jul 30 10:32:33 2012
@@ -38,13 +38,14 @@
   <modules>
     <module>parent</module>
     <module>servicesapi</module>
+    <module>defaults</module>
+    <module>revisionmanager</module>
     <module>store/file</module>
+    <module>index</module>
     <module>search/solr</module>
     <module>search/featured</module>
     <module>search/related</module>
-    <module>index</module>
     <module>web</module>
-    <module>defaults</module>
     <module>test</module>
     <module>bundlelist</module>
   </modules>

Propchange: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/
------------------------------------------------------------------------------
--- svn:ignore (added)
+++ svn:ignore Mon Jul 30 10:32:33 2012
@@ -0,0 +1,4 @@
+target
+.settings
+.classpath
+.project

Added: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/pom.xml
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/pom.xml?rev=1367053&view=auto
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/pom.xml (added)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/pom.xml Mon Jul 30 10:32:33 2012
@@ -0,0 +1,120 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<!--
+   Licensed to the Apache Software Foundation (ASF) under one or more
+   contributor license agreements.  See the NOTICE file distributed with
+   this work for additional information regarding copyright ownership.
+   The ASF licenses this file to You under the Apache License, Version 2.0
+   (the "License"); you may not use this file except in compliance with
+   the License.  You may obtain a copy of the License at
+
+        http://www.apache.org/licenses/LICENSE-2.0
+
+   Unless required by applicable law or agreed to in writing, software
+   distributed under the License is distributed on an "AS IS" BASIS,
+   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+   See the License for the specific language governing permissions and
+   limitations under the License.
+-->	
+<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
+  xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
+
+  <modelVersion>4.0.0</modelVersion>
+
+  <parent>
+    <groupId>org.apache.stanbol</groupId>
+    <artifactId>org.apache.stanbol.contenthub.parent</artifactId>
+    <version>0.10.0-incubating-SNAPSHOT</version>
+    <relativePath>../parent</relativePath>
+  </parent>
+
+  <groupId>org.apache.stanbol</groupId>
+  <artifactId>org.apache.stanbol.contenthub.revisionmanager</artifactId>
+  <packaging>bundle</packaging>
+
+  <name>Apache Stanbol Contenthub Revision Manager</name>
+
+  <build>
+    <plugins>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-bundle-plugin</artifactId>
+        <extensions>true</extensions>
+          <configuration>
+          <instructions>
+            <!-- Derby is only used through the JDBC API but needs to be in
+            the classpath of the caller to be able to create a new DB. -->
+            <Import-Package>
+              org.apache.derby.jdbc,
+              *
+            </Import-Package>
+          </instructions>
+        </configuration>
+      </plugin>
+      <plugin>
+        <groupId>org.apache.felix</groupId>
+        <artifactId>maven-scr-plugin</artifactId>
+      </plugin>
+    </plugins>
+  </build>
+  <dependencies>
+    <!-- Apache Stanbol dependencies -->
+    <dependency>
+      <groupId>org.apache.stanbol</groupId>
+      <artifactId>org.apache.stanbol.cmsadapter.servicesapi</artifactId>
+      <version>0.10.0-incubating-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.stanbol</groupId>
+      <artifactId>org.apache.stanbol.commons.semanticindex.servicesapi</artifactId>
+      <version>0.10.0-incubating-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.stanbol</groupId>
+      <artifactId>org.apache.stanbol.commons.semanticindex.core</artifactId>
+      <version>0.10.0-incubating-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.stanbol</groupId>
+      <artifactId>org.apache.stanbol.cmsadapter.core</artifactId>
+      <version>0.10.0-incubating-SNAPSHOT</version>
+    </dependency>
+    
+    <dependency>
+      <groupId>org.apache.stanbol</groupId>
+      <artifactId>org.apache.stanbol.contenthub.servicesapi</artifactId>
+      <version>0.10.0-incubating-SNAPSHOT</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.derby</groupId>
+      <artifactId>derby</artifactId>
+      <version>10.7.1.1</version>
+    </dependency>
+    <dependency>
+      <groupId>org.apache.felix</groupId>
+      <artifactId>org.apache.felix.scr.annotations</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.hp.hpl.jena</groupId>
+      <artifactId>jena</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>org.osgi</groupId>
+      <artifactId>org.osgi.compendium</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-lang</groupId>
+      <artifactId>commons-lang</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>commons-collections</groupId>
+      <artifactId>commons-collections</artifactId>
+    </dependency>
+    <dependency>
+      <groupId>com.sun.jersey</groupId>
+      <artifactId>jersey-client</artifactId>
+      <scope>compile</scope>
+    </dependency>
+
+  </dependencies>
+</project>
+

Propchange: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/pom.xml
------------------------------------------------------------------------------
    svn:executable = *

Added: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/RevisionManager.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/RevisionManager.java?rev=1367053&view=auto
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/RevisionManager.java (added)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/RevisionManager.java Mon Jul 30 10:32:33 2012
@@ -0,0 +1,339 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.stanbol.contenthub.revisionmanager;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.LinkedHashSet;
+import java.util.Set;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.stanbol.commons.semanticindex.core.store.ChangeSetImpl;
+import org.apache.stanbol.commons.semanticindex.store.ChangeSet;
+import org.apache.stanbol.commons.semanticindex.store.Store;
+import org.apache.stanbol.commons.semanticindex.store.StoreException;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <p>
+ * This class aims to manage the epochs of several {@link Store} implementations and the revisions regarding
+ * to the {@link ContentItem}s stored in {@link Store} instances. The revisions are kept in a single table in
+ * the scope of Apache Derby database. The system time is set as the new revision of the {@link ContentItem}
+ * when the {@link #updateRevision(Store, String)} method is called. Epochs of the Stores are managed in a
+ * single table initialized by {@link StoreDBManager}.
+ * </p>
+ * <p>
+ * In the scope of this RevisionManager {@link Store}s are identified by according to their names which can be
+ * obtained via the {@link Store#getName()} method.
+ * </p>
+ * 
+ * @author suat
+ * 
+ */
+@Component(immediate = true)
+@Service(value = RevisionManager.class)
+public class RevisionManager {
+    private static Logger log = LoggerFactory.getLogger(RevisionManager.class);
+
+    private String SELECT_REVISION = "SELECT id,revision FROM %s content_item_revision WHERE id = ?";
+
+    private String INSERT_REVISION = "INSERT INTO %s (id, revision) VALUES (?,?)";
+
+    private String UPDATE_REVISION = "UPDATE %s SET revision=? WHERE id=?";
+
+    private String SELECT_CHANGES = "SELECT id, revision FROM %s WHERE revision > ? ORDER BY revision ASC";
+
+    private String SELECT_MORECHANGES = "SELECT id, revision FROM %s WHERE revision >= ? ORDER BY revision ASC";
+
+    private String SELECT_EPOCH = "SELECT * FROM " + StoreDBManager.EPOCH_TABLE_NAME + " WHERE tableName = ?";
+
+    private String INSERT_EPOCH = "INSERT INTO " + StoreDBManager.EPOCH_TABLE_NAME
+                                  + " (epoch, tableName) values (?, ?)";
+
+    private String UPDATE_EPOCH = "UPDATE " + StoreDBManager.EPOCH_TABLE_NAME
+                                  + " SET epoch = ? WHERE tableName = ?";
+
+    @Reference
+    StoreDBManager dbManager;
+
+    /**
+     * Updates revision of the {@link ContentItem} specified with the <code>contentItemID</code> parameter and
+     * managed within the given {@code store}. The system time set as the new revision number by
+     * {@link System#currentTimeMillis()}.
+     * 
+     * @param store
+     *            The {@link Store} instance in which the {@link ContentItem} specified by the
+     *            {@code contentItemID} is stored
+     * @param contentItemID
+     *            ID of the {@link ContentItem} of which revision to be updated
+     * @throws StoreException
+     */
+    public <Item> void updateRevision(Store<Item> store, String contentItemID) throws StoreException {
+        // get connection
+        Connection con = dbManager.getConnection();
+        String revisionTableName = getStoreID(store);
+
+        // check existence of record for the given content item id
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+        boolean recordExist = false;
+        try {
+            ps = con.prepareStatement(String.format(SELECT_REVISION, revisionTableName));
+            ps.setString(1, contentItemID);
+            rs = ps.executeQuery();
+            if (rs.next()) {
+                recordExist = true;
+            }
+
+        } catch (SQLException e) {
+            dbManager.closeConnection(con);
+            log.error("Failed to query revision of content item", e);
+            throw new StoreException("Failed to query revision of content item", e);
+        } finally {
+            dbManager.closeResultSet(rs);
+            dbManager.closeStatement(ps);
+        }
+
+        // update the table
+        try {
+            long newRevision = System.currentTimeMillis();
+            if (!recordExist) {
+                log.debug("New revision: {} for the content item: {} of Store: {} will be added",
+                    new Object[] {newRevision, contentItemID, revisionTableName});
+                ps = con.prepareStatement(String.format(INSERT_REVISION, revisionTableName));
+                ps.setString(1, contentItemID);
+                ps.setLong(2, newRevision);
+            } else {
+                log.debug("New revision: {} for the content item: {} of Store: {} will be updated",
+                    new Object[] {newRevision, contentItemID, revisionTableName});
+
+                ps = con.prepareStatement(String.format(UPDATE_REVISION, revisionTableName));
+                ps.setLong(1, newRevision);
+                ps.setString(2, contentItemID);
+            }
+            int updatedRecordNum = ps.executeUpdate();
+            // exactly one record should be affected
+            if (updatedRecordNum != 1) {
+                log.warn("Unexpected number of updated records: {}, should be 1", updatedRecordNum);
+            }
+        } catch (SQLException e) {
+            log.error("Failed to update revision", e);
+            throw new StoreException("Failed to update revision", e);
+        } finally {
+            dbManager.closeStatement(ps);
+            dbManager.closeConnection(con);
+        }
+    }
+
+    /**
+     * Returns the updates after the given revision number. If the total size of revisions after the given
+     * revision number fit in <code>batchSize</code>, all of the changes for the last revision are also
+     * included in the results. This is because we do not want to include a subset of the changes regarding to
+     * a particular revision in the result.
+     * 
+     * @param store
+     *            {@link Store} instance for which the changes are requested
+     * @param revision
+     *            Starting revision of the to be returned in the results
+     * @param batchSize
+     *            Maximum number of changes to be returned. However to return all changes regarding to
+     *            particular revisions, the results might not have as much items as specified with this
+     *            number. For instance, if there are 10 changes regarding a requested particular revision and
+     *            this parameter is specified as 5, the results will include 10 changes.
+     * @return a {@link ChangeSet} including the changes in the specified Store.
+     * @throws StoreException
+     */
+    public <Item> ChangeSet<Item> getChanges(Store<Item> store, long revision, int batchSize) throws StoreException {
+        // get connection
+        Connection con = dbManager.getConnection();
+        String revisionTableName = getStoreID(store);
+
+        // check existence of record for the given content item id
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+        try {
+            ps = con.prepareStatement(String.format(SELECT_CHANGES, revisionTableName),
+                ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+            ps.setLong(1, revision);
+            ps.setMaxRows(batchSize + 1);
+            rs = ps.executeQuery();
+
+            Set<String> changedUris = new LinkedHashSet<String>();
+
+            if (!rs.first()) {
+                return new ChangeSetImpl<Item>(store, store.getEpoch(), Long.MIN_VALUE, Long.MAX_VALUE,
+                        changedUris);
+            }
+            if (rs.absolute(batchSize + 1)) {
+                long lastRowRevision = rs.getLong(2);
+                rs.previous();
+                long nextToLastRowRevision = rs.getLong(2);
+                rs.beforeFirst();
+                // if we are in the middle of a revision, add all changes in that revision to changedUris
+                if (lastRowRevision == nextToLastRowRevision) {
+                    ps = con.prepareStatement(String.format(SELECT_MORECHANGES, revisionTableName),
+                        ResultSet.TYPE_SCROLL_INSENSITIVE, ResultSet.CONCUR_READ_ONLY);
+                    ps.setLong(1, revision);
+                    rs = ps.executeQuery();
+
+                    while (rs.next()) {
+                        changedUris.add(rs.getString(1));
+                    }
+                } else {
+                    while (rs.next()) {
+                        if (rs.isLast()) {
+                            break;
+                        }
+                        changedUris.add(rs.getString(1));
+                    }
+                }
+
+            } else {
+                rs.beforeFirst();
+                while (rs.next()) {
+                    changedUris.add(rs.getString(1));
+                }
+            }
+
+            // set minimum and maximum revision numbers of the change set
+            if (rs.isLast()) {
+                rs.previous();
+            } else {
+                rs.last();
+            }
+            long to = rs.getLong(2);
+            rs.first();
+            long from = rs.getLong(2);
+
+            return new ChangeSetImpl<Item>(store, store.getEpoch(), from, to, changedUris);
+
+        } catch (SQLException e) {
+            log.error("Failed to get changes", e);
+            throw new StoreException("Failed to get changes", e);
+        } finally {
+            dbManager.closeResultSet(rs);
+            dbManager.closeStatement(ps);
+            dbManager.closeConnection(con);
+        }
+    }
+
+    /**
+     * Updates the epoch of the given {@link Store} with the {@link System#currentTimeMillis()}
+     * 
+     * @param store
+     *            Store instance of which epoch will be updated.
+     * @throws StoreException
+     */
+    public <Item> void updateEpoch(Store<Item> store) throws StoreException {
+        updateEpoch(store, false);
+    }
+
+    private <Item> void updateEpoch(Store<Item> store, boolean insert) throws StoreException {
+        // get connection
+        Connection con = dbManager.getConnection();
+        PreparedStatement ps = null;
+
+        // update the table
+        String storeID = getStoreID(store);
+        try {
+            long newEpoch = System.currentTimeMillis();
+            if (!insert) {
+                log.debug("New epoch: {} for the Store: {} will be updated", newEpoch, storeID);
+                ps = con.prepareStatement(UPDATE_EPOCH);
+            } else {
+                // check existence of the epoch entry for the given Store
+                ResultSet rs = null;
+                boolean recordExist = false;
+                try {
+                    ps = con.prepareStatement(SELECT_EPOCH);
+                    ps.setString(1, storeID);
+                    rs = ps.executeQuery();
+                    if (rs.next()) {
+                        recordExist = true;
+                    }
+
+                } catch (SQLException e) {
+                    dbManager.closeConnection(con);
+                    log.error("Failed to query revision of content item", e);
+                    throw new StoreException("Failed to query revision of content item", e);
+                } finally {
+                    dbManager.closeResultSet(rs);
+                    dbManager.closeStatement(ps);
+                }
+
+                if (recordExist) {
+                    log.debug("New epoch: {} for the Store: {} will be added", newEpoch, storeID);
+                    ps = con.prepareStatement(INSERT_EPOCH);
+                } else {
+                    // if there already exists an entry in the "epochTable" for the given store, return from
+                    // the method
+                    return;
+                }
+            }
+            ps.setLong(1, newEpoch);
+            ps.setString(2, storeID);
+            int updatedRecordNum = ps.executeUpdate();
+            // exactly one record should be affected
+            if (updatedRecordNum != 1) {
+                log.warn("Unexpected number of updated records: {}, should be 1", updatedRecordNum);
+            }
+        } catch (SQLException e) {
+            log.error("Failed to update epoch for Store identified as: {}", storeID, e);
+            throw new StoreException(String.format("Failed to update epoch for identified as: %s", storeID),
+                    e);
+        } finally {
+            dbManager.closeStatement(ps);
+            dbManager.closeConnection(con);
+        }
+    }
+
+    /**
+     * Initializes the revision table for the given {@link Store} if not already done and creates an entry in
+     * the "epochTable" for the given Store. This method is expected to be called while initializing the
+     * {@link Store} implementations. For instance OSGi based Store implementations can call this method
+     * within the @Activate method.
+     * 
+     * @param store
+     *            Store instance of which revision tables are initialized
+     * @throws StoreException
+     */
+    public <Item> void initializeRevisionTables(Store<Item> store) throws StoreException {
+        // initialize tables if not already
+        dbManager.createRevisionTable(store.getName());
+
+        // add initial epoch for the store
+        updateEpoch(store, true);
+    }
+
+    /**
+     * Returns a string identifying the given {@link Store} instance in the scope of the revision management.
+     * Currently, the name of the Store is used as the identifiers of the Store. The name is obtained through
+     * the {@link Store#getName()} method.
+     * 
+     * @param store
+     * @return
+     */
+    public <Item> String getStoreID(Store<Item> store) {
+        return store.getName();
+    }
+}

Added: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/StoreDBManager.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/StoreDBManager.java?rev=1367053&view=auto
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/StoreDBManager.java (added)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/revisionmanager/src/main/java/org/apache/stanbol/contenthub/revisionmanager/StoreDBManager.java Mon Jul 30 10:32:33 2012
@@ -0,0 +1,266 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.stanbol.contenthub.revisionmanager;
+
+import java.net.ConnectException;
+import java.sql.Connection;
+import java.sql.DatabaseMetaData;
+import java.sql.DriverManager;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.sql.Statement;
+
+import org.apache.felix.scr.annotations.Activate;
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.stanbol.commons.semanticindex.store.Store;
+import org.apache.stanbol.commons.semanticindex.store.StoreException;
+import org.osgi.service.component.ComponentContext;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <p>
+ * This class manages the Apache Derby database tables utilized in the scope of {@link FileStore}. It is
+ * responsible only for existence of the tables. Population of the tables is done by dedicated classes.
+ * </p>
+ * <p>
+ * This class also provides common methods regarding with SQL objects e.g obtaining connection; closing
+ * connection, statement, result set.
+ * </p>
+ * 
+ * @author suat
+ * 
+ */
+@Component(immediate = true)
+@Service(value = StoreDBManager.class)
+public class StoreDBManager {
+
+    public static final String EPOCH_TABLE_NAME = "epochTable";
+
+    private static Logger log = LoggerFactory.getLogger(StoreDBManager.class);
+
+    private static int MAX_ID_LENGTH = 1024;
+
+    private static String DB_URL;
+
+    @Activate
+    protected void activate(ComponentContext componentContext) throws StoreException {
+        String stanbolHome = componentContext.getBundleContext().getProperty("sling.home");
+        DB_URL = "jdbc:derby:" + stanbolHome + "/contenthub/store/revisions;create=true";
+        // initialize the epoch table
+        createEpochTable();
+    }
+
+    /**
+     * Creates an empty revision table with the given name. Different {@link Store} implementations are
+     * expected to call this method in their initializations with the value to be obtained by
+     * {@link RevisionManager#getStoreID(Store)} method.
+     * 
+     * @param tableName
+     *            name of the table to be created
+     * @throws StoreException
+     */
+    public void createRevisionTable(String tableName) throws StoreException {
+        Connection con = getConnection();
+        Statement stmt = null;
+        try {
+            // try to create revision table
+            if (!existsTable(tableName)) {
+                String createRevisionTable = "CREATE TABLE " + tableName + " (" + "id VARCHAR("
+                                             + MAX_ID_LENGTH + ") NOT NULL PRIMARY KEY,"
+                                             + "revision BIGINT NOT NULL)";
+                stmt = con.createStatement();
+                stmt.executeUpdate(createRevisionTable);
+                log.info("Revision table created for {}.", tableName);
+            } else {
+                log.info("Revision table already exists for {}", tableName);
+            }
+
+        } catch (SQLException e) {
+            log.error(String.format("Failed to create table %s", tableName), e);
+            throw new StoreException(String.format("Failed to create table %s", tableName), e);
+        } finally {
+            closeStatement(stmt);
+            closeConnection(con);
+        }
+    }
+
+    private void createEpochTable() throws StoreException {
+        Connection con = getConnection();
+        Statement stmt = null;
+        try {
+            // try to create revision table
+            if (!existsTable(EPOCH_TABLE_NAME)) {
+                String createRevisionTable = "CREATE TABLE " + EPOCH_TABLE_NAME + " (" + "tableName VARCHAR("
+                                             + MAX_ID_LENGTH + ") NOT NULL PRIMARY KEY,"
+                                             + "epoch BIGINT NOT NULL)";
+                stmt = con.createStatement();
+                stmt.executeUpdate(createRevisionTable);
+                log.info("'" + EPOCH_TABLE_NAME + "' table has been created.");
+            } else {
+                log.info("'" + EPOCH_TABLE_NAME + "' table already exists.");
+            }
+
+        } catch (SQLException e) {
+            log.error(String.format("Failed to create table %s", EPOCH_TABLE_NAME), e);
+            throw new StoreException(String.format("Failed to create table %s", EPOCH_TABLE_NAME), e);
+        } finally {
+            closeStatement(stmt);
+            closeConnection(con);
+        }
+    }
+
+    /**
+     * Check for the existence of the specified table
+     * 
+     * @param tableName
+     *            name of the table to be checked
+     * @return <code>true</code> if the table already exists, otherwise <code>false</code>.
+     * @throws StoreException
+     */
+    public boolean existsTable(String tableName) throws StoreException {
+        boolean exists = false;
+        ResultSet rs = null;
+        Connection con = getConnection();
+        try {
+            con = DriverManager.getConnection(DB_URL);
+            DatabaseMetaData meta = con.getMetaData();
+            rs = meta.getTables(null, null, null, new String[] {"TABLE"});
+            while (rs.next()) {
+                if (rs.getString("TABLE_NAME").equalsIgnoreCase(tableName)) {
+                    exists = true;
+                    break;
+                }
+            }
+        } catch (SQLException e) {
+            log.error("Failed to check existence of the table: {}", tableName);
+            throw new StoreException(String.format("Failed to check existence of the table: %s", tableName),
+                    e);
+        } finally {
+            closeResultSet(rs);
+            closeConnection(con);
+        }
+        return exists;
+    }
+
+    /**
+     * Truncates the content of the table specified with the {@code tableName}
+     * 
+     * @param tableName
+     *            name of the table to be truncated
+     * @throws StoreException
+     */
+    public void truncateTable(String tableName) throws StoreException {
+        boolean exists = false;
+        ResultSet rs = null;
+        Connection con = getConnection();
+        try {
+            con = DriverManager.getConnection(DB_URL);
+            DatabaseMetaData meta = con.getMetaData();
+            rs = meta.getTables(null, null, null, new String[] {"TABLE"});
+            while (rs.next()) {
+                if (rs.getString("TABLE_NAME").equalsIgnoreCase(tableName)) {
+                    exists = true;
+                    break;
+                }
+            }
+        } catch (SQLException e) {
+            log.error("Failed to check existence of the table: {}", tableName);
+            throw new StoreException(String.format("Failed to check existence of the table: %s", tableName),
+                    e);
+        } finally {
+            closeResultSet(rs);
+            closeConnection(con);
+        }
+        if (!exists) {
+            throw new IllegalArgumentException(String.format("There is no table having name: %s", tableName));
+        }
+        String truncateTable = "TRUNCATE TABLE " + tableName;
+        Statement stmt = null;
+        try {
+            stmt = con.createStatement();
+            stmt.execute(truncateTable);
+        } catch (SQLException e) {
+            log.error("Failed to truncate table: {}", tableName, e);
+            throw new StoreException(String.format("Failed to truncate table: %s", tableName), e);
+        }
+        log.debug("Table having name: {} has been truncated", tableName);
+    }
+
+    /**
+     * Closes the given {@link Connection}
+     * 
+     * @param con
+     */
+    public void closeConnection(Connection con) {
+        if (con != null) {
+            try {
+                con.close();
+            } catch (SQLException e) {
+                log.warn("Failed to close connection", e);
+            }
+        }
+    }
+
+    /**
+     * Closes the given {@link Statement}
+     * 
+     * @param stmt
+     */
+    public void closeStatement(Statement stmt) {
+        if (stmt != null) {
+            try {
+                stmt.close();
+            } catch (SQLException e) {
+                log.warn("Failed to close prepared statement", e);
+            }
+        }
+    }
+
+    /**
+     * Closes the given {@link ResultSet}
+     * 
+     * @param rs
+     */
+    public void closeResultSet(ResultSet rs) {
+        if (rs != null) {
+            try {
+                rs.close();
+            } catch (SQLException e) {
+                log.warn("Failed to close result set", e);
+            }
+        }
+    }
+
+    /**
+     * Creates a {@link ConnectException}
+     * 
+     * @return the {@link Connection} if successfully established
+     * @throws StoreException
+     */
+    public Connection getConnection() throws StoreException {
+        Connection con = null;
+        try {
+            con = DriverManager.getConnection(DB_URL);
+        } catch (SQLException e) {
+            log.error("Failed to obtain Derby connection", e);
+            throw new StoreException("Failed to obtain Derby connection", e);
+        }
+        return con;
+    }
+}

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/pom.xml
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/pom.xml?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/pom.xml (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/pom.xml Mon Jul 30 10:32:33 2012
@@ -33,16 +33,6 @@
         <groupId>org.apache.felix</groupId>
         <artifactId>maven-bundle-plugin</artifactId>
         <extensions>true</extensions>
-          <configuration>
-          <instructions>
-            <!-- Derby is only used through the JDBC API but needs to be in
-            the classpath of the caller to be able to create a new DB. -->
-            <Import-Package>
-              org.apache.derby.jdbc,
-              *
-            </Import-Package>
-          </instructions>
-        </configuration>
       </plugin>
       <plugin>
         <groupId>org.apache.felix</groupId>
@@ -81,12 +71,10 @@
       <artifactId>rdf.core</artifactId>
     </dependency>
     <dependency>
-      <groupId>org.apache.derby</groupId>
-      <artifactId>derby</artifactId>
-      <version>10.7.1.1</version>
+      <groupId>org.apache.stanbol</groupId>
+      <artifactId>org.apache.stanbol.contenthub.revisionmanager</artifactId>
+      <version>0.10.0-incubating-SNAPSHOT</version>
     </dependency>
-    
-
 	
     <dependency>
       <groupId>org.apache.commons</groupId>

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/src/main/java/org/apache/stanbol/contenthub/store/file/FileStore.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/src/main/java/org/apache/stanbol/contenthub/store/file/FileStore.java?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/src/main/java/org/apache/stanbol/contenthub/store/file/FileStore.java (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/store/file/src/main/java/org/apache/stanbol/contenthub/store/file/FileStore.java Mon Jul 30 10:32:33 2012
@@ -28,6 +28,7 @@ import java.sql.Connection;
 import java.sql.PreparedStatement;
 import java.sql.ResultSet;
 import java.sql.SQLException;
+import java.sql.Statement;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.HashMap;
@@ -49,6 +50,7 @@ import org.apache.clerezza.rdf.core.UriR
 import org.apache.commons.io.IOUtils;
 import org.apache.felix.scr.annotations.Activate;
 import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Deactivate;
 import org.apache.felix.scr.annotations.Properties;
 import org.apache.felix.scr.annotations.Property;
 import org.apache.felix.scr.annotations.Reference;
@@ -56,8 +58,11 @@ import org.apache.felix.scr.annotations.
 import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
 import org.apache.stanbol.commons.semanticindex.core.store.ChangeSetImpl;
 import org.apache.stanbol.commons.semanticindex.store.ChangeSet;
+import org.apache.stanbol.commons.semanticindex.store.EpochException;
 import org.apache.stanbol.commons.semanticindex.store.Store;
 import org.apache.stanbol.commons.semanticindex.store.StoreException;
+import org.apache.stanbol.contenthub.revisionmanager.RevisionManager;
+import org.apache.stanbol.contenthub.revisionmanager.StoreDBManager;
 import org.apache.stanbol.contenthub.store.file.serializer.ContentPartDeserializer;
 import org.apache.stanbol.contenthub.store.file.serializer.ContentPartSerializer;
 import org.apache.stanbol.enhancer.servicesapi.Blob;
@@ -106,19 +111,14 @@ import org.slf4j.LoggerFactory;
 @Service
 @Properties(value = {@Property(name = Constants.SERVICE_RANKING, intValue = 100)})
 public class FileStore implements Store<ContentItem> {
-	//TODO: IMPORTANT
-	//  We need to move the Contenthub related business logic out of this class!
-	//  Otherwise one can not implement/use different store implementations.
-	//  
-	//  e.g. the  #put(..) method MUST NOT enhance the contentItem. It is only
-	//  expected to store the parsed content item (without modifications).
-	//  The logic if the Contenthub needs to enhance ContentItems needs to be
-	//  outside of the actual Store implementation
-	
-	
-	
-    // @Property(name = Constants.SERVICE_RANKING)
-    // private int ranking;
+    // TODO: IMPORTANT
+    // We need to move the Contenthub related business logic out of this class!
+    // Otherwise one can not implement/use different store implementations.
+    //
+    // e.g. the #put(..) method MUST NOT enhance the contentItem. It is only
+    // expected to store the parsed content item (without modifications).
+    // The logic if the Contenthub needs to enhance ContentItems needs to be
+    // outside of the actual Store implementation
 
     public static final String RECENTLY_ENHANCED_TABLE_NAME = "recently_enhanced_content_items";
 
@@ -138,9 +138,19 @@ public class FileStore implements Store<
 
     public static final String FILE_STORE_NAME = "filestore";
 
-    private static final String SELECT_RECENTLY_ENHANCED_ITEMS = "SELECT t1.id, mimeType, enhancementCount, title FROM "
-                                                                 + FileRevisionManager.REVISION_TABLE_NAME
-                                                                 + " t1, "
+    private static int MAX_ID_LENGTH = 1024;
+
+    private static final String CREATE_RECENTLY_ENHANCED_TABLE = "CREATE TABLE "
+                                                                 + RECENTLY_ENHANCED_TABLE_NAME + " ("
+                                                                 + FIELD_ID + " VARCHAR(" + MAX_ID_LENGTH
+                                                                 + " ) NOT NULL PRIMARY KEY,"
+                                                                 + FIELD_MIME_TYPE + " VARCHAR("
+                                                                 + MAX_ID_LENGTH + "),"
+                                                                 + FIELD_ENHANCEMENT_COUNT + " BIGINT,"
+                                                                 + FIELD_TITLE + " VARCHAR(" + MAX_ID_LENGTH
+                                                                 + "))";
+
+    private static final String SELECT_RECENTLY_ENHANCED_ITEMS = "SELECT t1.id, mimeType, enhancementCount, title FROM %s t1, "
                                                                  + RECENTLY_ENHANCED_TABLE_NAME
                                                                  + " t2 WHERE"
                                                                  + " t1.id=t2."
@@ -172,13 +182,13 @@ public class FileStore implements Store<
     private File storeFolder;
 
     @Reference
-    ContentItemFactory contentItemFactory;
+    private ContentItemFactory contentItemFactory;
 
     @Reference
-    FileRevisionManager revisionManager;
+    private RevisionManager revisionManager;
 
     @Reference
-    FileStoreDBManager dbManager;
+    private StoreDBManager dbManager;
 
     @Reference
     private ContentPartSerializer contentPartSerializer;
@@ -188,7 +198,7 @@ public class FileStore implements Store<
 
     @Reference
     private EnhancementJobManager jobManager;
-    
+
     Map<String,Object> storeProperties;
 
     @Activate
@@ -198,41 +208,61 @@ public class FileStore implements Store<
         storeFolder = new File(stanbolHome + "/" + FILE_STORE_FOLDER_PATH + "/" + FILE_STORE_NAME);
         if (!storeFolder.exists()) {
             storeFolder.mkdirs();
+        } else {
+            if (!storeFolder.isDirectory()) {
+                log.error(String.format("There must be a directory in the path: %s",
+                    storeFolder.getAbsolutePath()));
+                throw new IllegalStateException(String.format("There must be a directory in the path: %s",
+                    storeFolder.getAbsolutePath()));
+            }
         }
+
         // init the store properties
-		Map<String,Object> properties = new HashMap<String, Object>();
-		properties.put(PROPERTY_NAME, getName());
-		properties.put(PROPERTY_DESCRIPTION, getDescription());
-		properties.put(PROPERTY_ITEM_TYPE, getItemType());
-		storeProperties = Collections.unmodifiableMap(properties);
+        Map<String,Object> properties = new HashMap<String,Object>();
+        properties.put(PROPERTY_NAME, getName());
+        properties.put(PROPERTY_DESCRIPTION, getDescription());
+        properties.put(PROPERTY_ITEM_TYPE, getItemType());
+        storeProperties = Collections.unmodifiableMap(properties);
+
+        // create file store revision and recentlyenhanced tables
+        revisionManager.initializeRevisionTables(this);
+        createRecentlyEnhancedTable();
     }
 
+    @Deactivate
     protected void deactivate(ComponentContext context) {
-    	storeProperties = null;
+        storeProperties = null;
     }
-    
+
     /*
-     * NOTE: This implementation assume a single FileStore instance.
-     * If this is changed this implementations need to be adapted.
+     * NOTE: This implementation assume a single FileStore instance. If this is changed this implementations
+     * need to be adapted.
      */
     @Override
     public String getName() {
-    	return "contenthubFileStore";
+        return "contenthubFileStore";
     }
-    
+
     /*
-     * NOTE: This implementation assume a single FileStore instance.
-     * If this is changed this implementations need to be adapted.
+     * NOTE: This implementation assume a single FileStore instance. If this is changed this implementations
+     * need to be adapted.
      */
     @Override
     public String getDescription() {
-    	return "File based Store implementation for ContentItems used by the Stanbol Contenthub";
+        return "File based Store implementation for ContentItems used by the Stanbol Contenthub";
+    }
+
+    @Override
+    public long getEpoch() {
+        // TODO Auto-generated method stub
+        return 0;
     }
-    
+
     @Override
-    public Map<String, Object> getProperties() {
-    	return storeProperties;
+    public Map<String,Object> getProperties() {
+        return storeProperties;
     }
+
     @Override
     public Class<ContentItem> getItemType() {
         return ContentItem.class;
@@ -267,9 +297,11 @@ public class FileStore implements Store<
 
     @Override
     public void removeAll() throws StoreException {
-        ChangeSet<ContentItem> changes = changes(Long.MIN_VALUE, Integer.MAX_VALUE);
+        ChangeSet<ContentItem> changes = changes(Long.MIN_VALUE, Long.MIN_VALUE, Integer.MAX_VALUE);
         List<ContentItem> removed = new ArrayList<ContentItem>();
-        for (String id : changes.changed()) {
+        Iterator<String> idIterator = changes.iterator();
+        while (idIterator.hasNext()) {
+            String id = idIterator.next();
             ContentItem ci = remove(id);
             if (ci != null) {
                 removed.add(ci);
@@ -279,7 +311,7 @@ public class FileStore implements Store<
 
     private void updateTablesForDelete(String id) throws StoreException {
         // update revision
-        revisionManager.updateRevision(id);
+        revisionManager.updateRevision(this, id);
         // update recently_enhanced table
         removeFromRecentlyEnhancedTable(id);
     }
@@ -392,7 +424,7 @@ public class FileStore implements Store<
             // ignore the exception
         }
         try {
-            revisionManager.updateRevision(htmlMetadata.getString(FIELD_ID));
+            revisionManager.updateRevision(this, htmlMetadata.getString(FIELD_ID));
             updateRecentlyEnhancedItem(htmlMetadata.getString(FIELD_ID), title,
                 htmlMetadata.getString(FIELD_MIME_TYPE), htmlMetadata.getLong(FIELD_ENHANCEMENT_COUNT));
         } catch (JSONException e) {
@@ -400,65 +432,6 @@ public class FileStore implements Store<
         }
     }
 
-    private void updateRecentlyEnhancedItem(String contentItemID,
-                                            String title,
-                                            String mimeType,
-                                            long enhancementCount) throws StoreException {
-        // get connection
-        Connection con = dbManager.getConnection();
-
-        // check existence of record for the given content item id
-        PreparedStatement ps = null;
-        ResultSet rs = null;
-        boolean recordExist = false;
-        try {
-            ps = con.prepareStatement(SELECT_RECENTLY_ENHANCED_ITEM);
-            ps.setString(1, contentItemID);
-            rs = ps.executeQuery();
-            if (rs.next()) {
-                recordExist = true;
-            }
-
-        } catch (SQLException e) {
-            dbManager.closeConnection(con);
-            log.error("Failed to query information of content item", e);
-            throw new StoreException("Failed to query information of content item", e);
-        } finally {
-            dbManager.closeResultSet(rs);
-            dbManager.closeStatement(ps);
-        }
-
-        // update the table
-        try {
-            if (!recordExist) {
-                log.debug("Content item: {} will be added to recently_enhanced table", contentItemID);
-                ps = con.prepareStatement(INSERT_RECENTLY_ENHANCED_ITEM);
-                ps.setString(1, contentItemID);
-                ps.setString(2, title);
-                ps.setString(3, mimeType);
-                ps.setLong(4, enhancementCount);
-            } else {
-                log.debug("Content item: {} will be updated in recently_enhanced table", contentItemID);
-                ps = con.prepareStatement(UPDATE_RECENTLY_ENHANCED_ITEM);
-                ps.setString(1, mimeType);
-                ps.setString(2, title);
-                ps.setLong(3, enhancementCount);
-                ps.setString(4, contentItemID);
-            }
-            int updatedRecordNum = ps.executeUpdate();
-            // exactly one record should be affected
-            if (updatedRecordNum != 1) {
-                log.warn("Unexpected number of updated records: {}, should be 1", updatedRecordNum);
-            }
-        } catch (SQLException e) {
-            log.error("Failed to update recently_enhanced table", e);
-            throw new StoreException("Failed to update recently_enhanced table", e);
-        } finally {
-            dbManager.closeStatement(ps);
-            dbManager.closeConnection(con);
-        }
-    }
-
     private void archiveContentItem(ContentItem ci) throws StoreException {
         String fileName = encodeId(ci.getUri().getUnicodeString());
         File file = new File(storeFolder.getPath() + "/" + fileName + ".zip");
@@ -769,10 +742,12 @@ public class FileStore implements Store<
     }
 
     @Override
-    public ChangeSet<ContentItem> changes(long revision, int batchSize) throws StoreException {
-        ChangeSetImpl<ContentItem> changesSet = (ChangeSetImpl<ContentItem>) revisionManager.getChanges(
+    public ChangeSet<ContentItem> changes(long epoch, long revision, int batchSize) throws StoreException {
+        if (getEpoch() != epoch) {
+            throw new EpochException(this, getEpoch(), epoch);
+        }
+        ChangeSetImpl<ContentItem> changesSet = (ChangeSetImpl<ContentItem>) revisionManager.getChanges(this,
             revision, batchSize);
-        changesSet.setStore(this);
         return changesSet;
     }
 
@@ -791,6 +766,65 @@ public class FileStore implements Store<
         }
     }
 
+    private void updateRecentlyEnhancedItem(String contentItemID,
+                                            String title,
+                                            String mimeType,
+                                            long enhancementCount) throws StoreException {
+        // get connection
+        Connection con = dbManager.getConnection();
+
+        // check existence of record for the given content item id
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+        boolean recordExist = false;
+        try {
+            ps = con.prepareStatement(SELECT_RECENTLY_ENHANCED_ITEM);
+            ps.setString(1, contentItemID);
+            rs = ps.executeQuery();
+            if (rs.next()) {
+                recordExist = true;
+            }
+
+        } catch (SQLException e) {
+            dbManager.closeConnection(con);
+            log.error("Failed to query information of content item", e);
+            throw new StoreException("Failed to query information of content item", e);
+        } finally {
+            dbManager.closeResultSet(rs);
+            dbManager.closeStatement(ps);
+        }
+
+        // update the table
+        try {
+            if (!recordExist) {
+                log.debug("Content item: {} will be added to recently_enhanced table", contentItemID);
+                ps = con.prepareStatement(INSERT_RECENTLY_ENHANCED_ITEM);
+                ps.setString(1, contentItemID);
+                ps.setString(2, title);
+                ps.setString(3, mimeType);
+                ps.setLong(4, enhancementCount);
+            } else {
+                log.debug("Content item: {} will be updated in recently_enhanced table", contentItemID);
+                ps = con.prepareStatement(UPDATE_RECENTLY_ENHANCED_ITEM);
+                ps.setString(1, mimeType);
+                ps.setString(2, title);
+                ps.setLong(3, enhancementCount);
+                ps.setString(4, contentItemID);
+            }
+            int updatedRecordNum = ps.executeUpdate();
+            // exactly one record should be affected
+            if (updatedRecordNum != 1) {
+                log.warn("Unexpected number of updated records: {}, should be 1", updatedRecordNum);
+            }
+        } catch (SQLException e) {
+            log.error("Failed to update recently_enhanced table", e);
+            throw new StoreException("Failed to update recently_enhanced table", e);
+        } finally {
+            dbManager.closeStatement(ps);
+            dbManager.closeConnection(con);
+        }
+    }
+
     public List<JSONObject> getRecentlyEnhancedItems(int limit, int offset) throws StoreException {
         // get connection
         Connection con = dbManager.getConnection();
@@ -799,7 +833,8 @@ public class FileStore implements Store<
         ResultSet rs = null;
         List<JSONObject> recentlyEnhancedList = new ArrayList<JSONObject>();
         try {
-            ps = con.prepareStatement(SELECT_RECENTLY_ENHANCED_ITEMS);
+            ps = con.prepareStatement(String.format(SELECT_RECENTLY_ENHANCED_ITEMS,
+                revisionManager.getStoreID(this)));
             ps.setMaxRows(limit);
             ps.setInt(1, offset);
             rs = ps.executeQuery();
@@ -825,4 +860,25 @@ public class FileStore implements Store<
 
         return recentlyEnhancedList;
     }
+
+    private void createRecentlyEnhancedTable() throws StoreException {
+        Connection con = dbManager.getConnection();
+        Statement stmt = null;
+        try {
+            // try to create recently_enhanced table
+            if (!dbManager.existsTable(RECENTLY_ENHANCED_TABLE_NAME)) {
+                stmt = con.createStatement();
+                stmt.executeUpdate(CREATE_RECENTLY_ENHANCED_TABLE);
+                log.info("RecentlyEnhanced table created.");
+            } else {
+                log.info("RecentlyEnhanced table already exists");
+            }
+        } catch (SQLException e) {
+            log.error("Failed to create recentlyenhanced table", e);
+            throw new StoreException("Failed to create recentlyenhanced table", e);
+        } finally {
+            dbManager.closeStatement(stmt);
+            dbManager.closeConnection(con);
+        }
+    }
 }

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/index/ldpath/LDPathSemanticIndexManagerTest.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/index/ldpath/LDPathSemanticIndexManagerTest.java?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/index/ldpath/LDPathSemanticIndexManagerTest.java (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/index/ldpath/LDPathSemanticIndexManagerTest.java Mon Jul 30 10:32:33 2012
@@ -48,8 +48,6 @@ import org.slf4j.LoggerFactory;
 public class LDPathSemanticIndexManagerTest {
     private static Logger logger = LoggerFactory.getLogger(LDPathSemanticIndexManagerTest.class);
 
-    private final static String INDEX_METADATA_FOLDER_PATH = "LDPathSemanticIndexMetadata";
-
     @TestReference
     private LDPathSemanticIndexManager ldPathSemanticIndexManager;
 
@@ -125,7 +123,7 @@ public class LDPathSemanticIndexManagerT
         // check IndexMetadata folder exists
         File indexMetadataDirectory = bundleContext
                 .getServiceReference(LDPathSemanticIndexManager.class.getName()).getBundle()
-                .getBundleContext().getDataFile(INDEX_METADATA_FOLDER_PATH);
+                .getBundleContext().getDataFile(LDPathSemanticIndexManager.class.getName());
         assertTrue("IndexMetadata Directory does not exist", indexMetadataDirectory.exists());
 
         // check IndexMetadata files of indexes before remove index
@@ -179,7 +177,7 @@ public class LDPathSemanticIndexManagerT
 
         File indexMetadataDirectory = bundleContext
                 .getServiceReference(LDPathSemanticIndexManager.class.getName()).getBundle()
-                .getBundleContext().getDataFile(INDEX_METADATA_FOLDER_PATH);
+                .getBundleContext().getDataFile(LDPathSemanticIndexManager.class.getName());
 
         Map<String,Properties> indexMetadataMap = new HashMap<String,Properties>();
         // load index metadata to memory

Added: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/RevisionManagerTest.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/RevisionManagerTest.java?rev=1367053&view=auto
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/RevisionManagerTest.java (added)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/RevisionManagerTest.java Mon Jul 30 10:32:33 2012
@@ -0,0 +1,285 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.stanbol.contenthub.test.revisionmanager;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.PreparedStatement;
+import java.sql.ResultSet;
+import java.sql.SQLException;
+import java.util.Iterator;
+
+import org.apache.sling.junit.annotations.SlingAnnotationsTestRunner;
+import org.apache.sling.junit.annotations.TestReference;
+import org.apache.stanbol.commons.semanticindex.store.ChangeSet;
+import org.apache.stanbol.commons.semanticindex.store.Store;
+import org.apache.stanbol.commons.semanticindex.store.StoreException;
+import org.apache.stanbol.contenthub.revisionmanager.RevisionManager;
+import org.apache.stanbol.contenthub.revisionmanager.StoreDBManager;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@RunWith(SlingAnnotationsTestRunner.class)
+public class RevisionManagerTest {
+    private static Logger log = LoggerFactory.getLogger(RevisionManagerTest.class);
+
+    @TestReference
+    private RevisionManager revisionManager;
+
+    @TestReference
+    private StoreDBManager dbManager;
+
+    @TestReference
+    private Store<?> store;
+
+    @Test
+    public void revisionManagerTest() {
+        assertNotNull("Expecting revisionManager to be injected by Sling test runner", revisionManager);
+    }
+
+    @Test
+    public void dbManagerTest() {
+        assertNotNull("Expecting FileStoreDBManager to be injected by Sling test runner", dbManager);
+    }
+
+    @Test
+    public void updateChangeTest() throws StoreException, SQLException {
+        // do the update
+        String contentItemID = "contenthub_test_content_item_id";
+        revisionManager.updateRevision(store, contentItemID);
+
+        // check the update
+        String query = String.format("SELECT id, revision FROM %s WHERE id = ?",
+            revisionManager.getStoreID(store));
+        Connection con = dbManager.getConnection();
+        PreparedStatement ps = null;
+        ResultSet rs = null;
+        boolean recordExist = false;
+        long revisionNumber = Long.MAX_VALUE;
+        try {
+            ps = con.prepareStatement(query);
+            ps.setString(1, contentItemID);
+            rs = ps.executeQuery();
+            if (rs.next()) {
+                recordExist = true;
+                revisionNumber = rs.getLong(2);
+            }
+        } finally {
+            dbManager.closeResultSet(rs);
+            dbManager.closeStatement(ps);
+        }
+
+        assertTrue("failed to obtain content item revision", recordExist);
+        assertTrue("wrong revision number", (revisionNumber <= System.currentTimeMillis()));
+
+        // clear the update
+        query = String.format("DELETE FROM %s WHERE id = ?", revisionManager.getStoreID(store));
+        ps = null;
+        try {
+            ps = con.prepareStatement(query);
+            ps.setString(1, contentItemID);
+            int result = ps.executeUpdate();
+            if (result != 1) {
+                log.warn(
+                    "Wrong number of updated records while removing the test record. Updated record number: {}",
+                    result);
+            }
+        } catch (SQLException e) {
+            log.warn("Failed to remove the test record", e);
+        } finally {
+            dbManager.closeConnection(con);
+            dbManager.closeStatement(ps);
+        }
+    }
+
+    /**
+     * IMPORTANT NOTE: At time of executing the test below, there MUST be no other requests updating the
+     * revision table, otherwise the test will fail.
+     * 
+     * @throws InterruptedException
+     * @throws SQLException
+     */
+    @Test
+    public void iterativeChangesTest() throws StoreException, InterruptedException, SQLException {
+        log.warn("DO NOT UPDATE THE STORE: {}DURING EXECUTION OF THIS TEST", store.getName());
+        Connection con = dbManager.getConnection();
+        String contentItemID = "contenthub_test_content_item_id";
+        PreparedStatement ps = null;
+        int insertCount = 0;
+        try {
+            // do the update
+            String query = "INSERT INTO " + revisionManager.getStoreID(store)
+                           + " (id, revision) VALUES (?,?)";
+            long startRevision = System.currentTimeMillis();
+            long revision = System.currentTimeMillis();
+            for (int i = 0; i < 5; i++) {
+                // to ensure different revisions
+                ps = con.prepareStatement(query);
+                ps.setString(1, contentItemID + i);
+                ps.setLong(2, ++revision);
+                ps.executeUpdate();
+                ps.clearParameters();
+                insertCount++;
+            }
+
+            // check changes
+            ChangeSet<?> changeSet = revisionManager.getChanges(store, startRevision, 3);
+            Iterator<String> changedItems = changeSet.iterator();
+            int itemCount = 0;
+            while (changedItems.hasNext()) {
+                changedItems.next();
+                itemCount++;
+            }
+            assertTrue("Wrong number of changed items", (itemCount == 3));
+            for (int i = 0; i < 3; i++) {
+                changedItems = changeSet.iterator();
+                itemCount = 0;
+                while (changedItems.hasNext()) {
+                    String changedItem = changedItems.next();
+                    if (changedItem.equals(contentItemID + i)) break;
+                    itemCount++;
+                }
+                assertTrue("Changes does not include correct URIs", itemCount < 3);
+            }
+            assertTrue("Changes does not include correct fromRevision value",
+                (changeSet.fromRevision() == revision - 4));
+            assertTrue("Changes does not include correct toRevision value",
+                (changeSet.toRevision() == revision - 2));
+
+            changeSet = revisionManager.getChanges(store, revision - 2, 3);
+            itemCount = 0;
+            changedItems = changeSet.iterator();
+            while (changedItems.hasNext()) {
+                changedItems.next();
+                itemCount++;
+            }
+            assertTrue("Wrong number of changed items", (itemCount == 2));
+            for (int i = 0; i < 2; i++) {
+                changedItems = changeSet.iterator();
+                itemCount = 0;
+                while (changedItems.hasNext()) {
+                    String changedItem = changedItems.next();
+                    if (changedItem.equals(contentItemID + (i + 3))) break;
+                    itemCount++;
+                }
+                assertTrue("Changes does not include correct URIs", itemCount < 2);
+            }
+
+            assertTrue("Changes does not include correct fromRevision value",
+                (changeSet.fromRevision() == revision - 1));
+            assertTrue("Changes does not include correct toRevision value",
+                (changeSet.toRevision() == revision));
+        } finally {
+            // clear test changes
+            String query = String.format(
+                "DELETE FROM %s WHERE id = ? OR id = ? OR id = ? OR id = ? OR id = ?",
+                revisionManager.getStoreID(store));
+            try {
+                ps = con.prepareStatement(query);
+                for (int i = 0; i < insertCount; i++) {
+                    ps.setString(i + 1, contentItemID + i);
+                }
+                int result = ps.executeUpdate();
+                if (result != 5) {
+                    log.warn(
+                        "Wrong number of updated records while removing the test record. Updated record number: {}",
+                        result);
+                }
+            } catch (SQLException e) {
+                log.warn("Failed to remove the test record", e);
+            } finally {
+                dbManager.closeStatement(ps);
+                dbManager.closeConnection(con);
+            }
+        }
+    }
+
+    @Test
+    public void batchSizeDoesNotFitToRevisionTest() throws StoreException, SQLException {
+        log.warn("DO NOT UPDATE THE STORE: {} DURING EXECUTION OF THIS TEST", store.getName());
+        Connection con = dbManager.getConnection();
+        String contentItemID = "contenthub_test_content_item_id";
+        PreparedStatement ps = null;
+        int insertCount = 0;
+        try {
+            // do the update
+            String query = "INSERT INTO " + revisionManager.getStoreID(store)
+                           + " (id, revision) VALUES (?,?)";
+            long revision = System.currentTimeMillis();
+            for (int i = 0; i < 2; i++) {
+                ps = con.prepareStatement(query);
+                ps.setString(1, contentItemID + i);
+                ps.setLong(2, revision + 1);
+                ps.executeUpdate();
+                ps.clearParameters();
+                insertCount++;
+            }
+
+            // get changes
+            ChangeSet<?> changeSet = revisionManager.getChanges(store, revision, 1);
+            int itemCount = 0;
+            Iterator<String> changedItems = changeSet.iterator();
+            while (changedItems.hasNext()) {
+                changedItems.next();
+                itemCount++;
+            }
+            assertTrue("Wrong number of changed items", (itemCount == 2));
+            for (int i = 0; i < 2; i++) {
+                changedItems = changeSet.iterator();
+                itemCount = 0;
+                while (changedItems.hasNext()) {
+                    String changedItem = changedItems.next();
+                    if (changedItem.equals(contentItemID + i)) break;
+                    itemCount++;
+                }
+                assertTrue("Changes does not include correct URIs", itemCount < 2);
+            }
+        } finally {
+            // clear test changes
+            String query = String.format("DELETE FROM %s WHERE id = ? OR id = ?",
+                revisionManager.getStoreID(store));
+            try {
+                ps = con.prepareStatement(query);
+                for (int i = 0; i < insertCount; i++) {
+                    ps.setString(i + 1, contentItemID + i);
+                }
+                int result = ps.executeUpdate();
+                if (result != 2) {
+                    log.warn(
+                        "Wrong number of updated records while removing the test record. Updated record number: {}",
+                        result);
+                }
+            } catch (SQLException e) {
+                log.warn("Failed to remove the test record", e);
+            } finally {
+                dbManager.closeStatement(ps);
+                dbManager.closeConnection(con);
+            }
+        }
+    }
+
+    @Test
+    public void emptyChangesTest() throws StoreException {
+        long revision = System.currentTimeMillis();
+        ChangeSet<?> changeSet = revisionManager.getChanges(store, revision, 1);
+        assertTrue("There must be no changes", !changeSet.iterator().hasNext());
+    }
+}

Added: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/StoreDBManagerTest.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/StoreDBManagerTest.java?rev=1367053&view=auto
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/StoreDBManagerTest.java (added)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/revisionmanager/StoreDBManagerTest.java Mon Jul 30 10:32:33 2012
@@ -0,0 +1,75 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+package org.apache.stanbol.contenthub.test.revisionmanager;
+
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertTrue;
+
+import java.sql.Connection;
+import java.sql.SQLException;
+
+import org.apache.sling.junit.annotations.SlingAnnotationsTestRunner;
+import org.apache.sling.junit.annotations.TestReference;
+import org.apache.stanbol.commons.semanticindex.store.Store;
+import org.apache.stanbol.commons.semanticindex.store.StoreException;
+import org.apache.stanbol.contenthub.revisionmanager.RevisionManager;
+import org.apache.stanbol.contenthub.revisionmanager.StoreDBManager;
+import org.apache.stanbol.enhancer.servicesapi.ContentItem;
+import org.junit.Test;
+import org.junit.runner.RunWith;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@RunWith(SlingAnnotationsTestRunner.class)
+public class StoreDBManagerTest {
+    private static final Logger log = LoggerFactory.getLogger(StoreDBManagerTest.class);
+
+    @TestReference
+    private StoreDBManager dbManager;
+
+    @TestReference
+    private RevisionManager revisionManager;
+
+    @TestReference
+    private Store<ContentItem> store;
+
+    @Test
+    public void dbManagerTest() {
+        assertNotNull("Expecting FileStoreDBManager to be injected by Sling test runner", dbManager);
+    }
+
+    @Test
+    public void testConnection() throws StoreException {
+        Connection connection = dbManager.getConnection();
+        assertTrue("Null connection", connection != null);
+        if (connection != null) {
+            try {
+                connection.close();
+            } catch (SQLException e) {
+                log.warn("Failed to close test connection");
+            }
+        }
+    }
+
+    @Test
+    public void testTables() throws StoreException {
+        assertTrue("recently_enhanced_content_items does not exist",
+            dbManager.existsTable("recently_enhanced_content_items"));
+        assertTrue(String.format("%s does not exist", revisionManager.getStoreID(store)),
+            dbManager.existsTable(revisionManager.getStoreID(store)));
+    }
+}

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartDeserializerTest.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartDeserializerTest.java?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartDeserializerTest.java (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartDeserializerTest.java Mon Jul 30 10:32:33 2012
@@ -48,7 +48,7 @@ import org.slf4j.LoggerFactory;
 
 @RunWith(SlingAnnotationsTestRunner.class)
 public class ContentPartDeserializerTest {
-    private static final Logger log = LoggerFactory.getLogger(FileStoreDBManagerTest.class);
+    private static final Logger log = LoggerFactory.getLogger(ContentPartDeserializerTest.class);
 
     @TestReference
     ContentPartDeserializer contentPartDeserializer;

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartSerializerTest.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartSerializerTest.java?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartSerializerTest.java (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/ContentPartSerializerTest.java Mon Jul 30 10:32:33 2012
@@ -45,7 +45,7 @@ import org.slf4j.LoggerFactory;
 
 @RunWith(SlingAnnotationsTestRunner.class)
 public class ContentPartSerializerTest {
-    private static final Logger log = LoggerFactory.getLogger(FileStoreDBManagerTest.class);
+    private static final Logger log = LoggerFactory.getLogger(ContentPartSerializerTest.class);
 
     @TestReference
     ContentPartSerializer contentPartSerializer;

Modified: incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/FileStoreTest.java
URL: http://svn.apache.org/viewvc/incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/FileStoreTest.java?rev=1367053&r1=1367052&r2=1367053&view=diff
==============================================================================
--- incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/FileStoreTest.java (original)
+++ incubator/stanbol/branches/contenthub-two-layered-structure/contenthub/test/src/main/java/org/apache/stanbol/contenthub/test/store/file/FileStoreTest.java Mon Jul 30 10:32:33 2012
@@ -36,8 +36,9 @@ import org.apache.sling.junit.annotation
 import org.apache.stanbol.commons.indexedgraph.IndexedMGraph;
 import org.apache.stanbol.commons.semanticindex.store.Store;
 import org.apache.stanbol.commons.semanticindex.store.StoreException;
+import org.apache.stanbol.contenthub.revisionmanager.RevisionManager;
+import org.apache.stanbol.contenthub.revisionmanager.StoreDBManager;
 import org.apache.stanbol.contenthub.store.file.FileStore;
-import org.apache.stanbol.contenthub.store.file.FileStoreDBManager;
 import org.apache.stanbol.enhancer.servicesapi.Blob;
 import org.apache.stanbol.enhancer.servicesapi.ContentItem;
 import org.apache.stanbol.enhancer.servicesapi.ContentItemFactory;
@@ -63,7 +64,10 @@ public class FileStoreTest {
     private ContentItemFactory contentItemFactory;
 
     @TestReference
-    private FileStoreDBManager dbManager;
+    private StoreDBManager dbManager;
+
+    @TestReference
+    private RevisionManager revisionManager;
 
     @Test
     public void fileStoreTest() {
@@ -171,7 +175,8 @@ public class FileStoreTest {
         f.delete();
 
         // delete the database records
-        String query = "DELETE FROM content_item_revisions WHERE id = ?";
+        String query = String.format("DELETE FROM %s WHERE id = ?",
+            revisionManager.getStoreID(store));
         Connection connection = dbManager.getConnection();
         PreparedStatement ps = null;
         try {