jackrabbit-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From thom...@apache.org
Subject svn commit: r937293 [1/3] - in /jackrabbit/sandbox/jackrabbit-j3: ./ src/main/java/org/apache/jackrabbit/j3/ src/main/java/org/apache/jackrabbit/j3/api/ src/main/java/org/apache/jackrabbit/j3/api/management/ src/main/java/org/apache/jackrabbit/j3/api/o...
Date Fri, 23 Apr 2010 13:43:37 GMT
Author: thomasm
Date: Fri Apr 23 13:43:36 2010
New Revision: 937293

URL: http://svn.apache.org/viewvc?rev=937293&view=rev
Log:
Copied data store and XA tests

Added:
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryManagerImpl.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionListener.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepository.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepositoryFactory.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitSession.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/DataStoreGarbageCollector.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/MarkEventListener.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/RepositoryManager.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/observation/
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/observation/SynchronousEventListener.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/AbstractDataRecord.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataIdentifier.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataRecord.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStore.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreException.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreFactory.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataRecord.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataStore.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/GarbageCollector.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/LazyFileInputStream.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/mc/Utils.java
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/TestSessionGC.java
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/xa/
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/xa/TestXA.java
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/xa/UserTransactionImpl.java
Modified:
    jackrabbit/sandbox/jackrabbit-j3/pom.xml
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/NodeImpl.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryFactoryImpl.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryImpl.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionImpl.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/mc/StorageSession.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/mc/Val.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/mc/jdbc/JdbcStorage.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/mc/jdbc/JdbcStorageSession.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/mc/mem/MemStorage.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/mc/mem/MemStorageSession.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/util/Constants.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/util/LobStore.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/util/Log.java
    jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/util/StringUtils.java
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/TestAll.java
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/TestBase.java
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/TestBundle.java
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/TestEventJournal.java
    jackrabbit/sandbox/jackrabbit-j3/src/test/java/org/apache/jackrabbit/j3/TestObservation.java

Modified: jackrabbit/sandbox/jackrabbit-j3/pom.xml
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/pom.xml?rev=937293&r1=937292&r2=937293&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/pom.xml (original)
+++ jackrabbit/sandbox/jackrabbit-j3/pom.xml Fri Apr 23 13:43:36 2010
@@ -17,59 +17,73 @@
    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>
+<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.jackrabbit</groupId>
+        <artifactId>parent</artifactId>
+        <version>5</version>
+    </parent>
 
- <parent>
-  <groupId>org.apache.jackrabbit</groupId>
-  <artifactId>parent</artifactId>
-  <version>5</version>
- </parent>
+    <artifactId>jackrabbit-j3</artifactId>
+    <version>2.0-SNAPSHOT</version>
+    <name>Jackrabbit J3</name>
+    <description>Jackrabbit J3</description>
 
- <artifactId>jackrabbit-j3</artifactId>
- <version>2.0-SNAPSHOT</version>
- <name>Jackrabbit J3</name>
-  <description>
-    Jackrabbit J3
-  </description>
+    <dependencies>
+        <dependency>
+            <groupId>javax.jcr</groupId>
+            <artifactId>jcr</artifactId>
+            <version>2.0</version>
+        </dependency>
+        <dependency>
+            <groupId>junit</groupId>
+            <artifactId>junit</artifactId>
+            <version>3.8.1</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>com.h2database</groupId>
+            <artifactId>h2</artifactId>
+            <version>1.2.131</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>commons-io</groupId>
+            <artifactId>commons-io</artifactId>
+            <version>1.4</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-api</artifactId>
+            <version>1.5.8</version>
+        </dependency>
+        <dependency>
+            <groupId>org.slf4j</groupId>
+            <artifactId>slf4j-log4j12</artifactId>
+            <version>1.5.8</version>
+            <scope>test</scope>
+        </dependency>
+        <dependency>
+            <groupId>org.apache.geronimo.specs</groupId>
+            <artifactId>geronimo-jta_1.0.1B_spec</artifactId>
+            <version>1.0.1</version>
+            <scope>test</scope>
+        </dependency>
+    </dependencies>
 
- <dependencies>
-  <dependency>
-   <groupId>javax.jcr</groupId>
-   <artifactId>jcr</artifactId>
-   <version>2.0</version>
-  </dependency>
-  <dependency>
-   <groupId>junit</groupId>
-   <artifactId>junit</artifactId>
-   <version>3.8.1</version>
-   <scope>test</scope>
-  </dependency>
-  <dependency>
-   <groupId>com.h2database</groupId>
-   <artifactId>h2</artifactId>
-   <version>1.2.131</version>
-   <scope>test</scope>
-  </dependency>
-  <dependency>
-   <groupId>commons-io</groupId>
-   <artifactId>commons-io</artifactId>
-   <version>1.4</version>
-   <scope>test</scope>
-  </dependency>
- </dependencies>
- 
-   <build>
-    <plugins>
-      <plugin>
-        <artifactId>maven-compiler-plugin</artifactId>
-        <configuration>
-          <target>1.5</target>
-          <source>1.5</source>
-        </configuration>
-      </plugin>
-    </plugins>
-  </build>
+    <build>
+        <plugins>
+            <plugin>
+                <artifactId>maven-compiler-plugin</artifactId>
+                <configuration>
+                    <target>1.5</target>
+                    <source>1.5</source>
+                </configuration>
+            </plugin>
+        </plugins>
+    </build>
  
 </project>

Modified: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/NodeImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/NodeImpl.java?rev=937293&r1=937292&r2=937293&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/NodeImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/NodeImpl.java Fri Apr 23 13:43:36 2010
@@ -98,8 +98,9 @@ public class NodeImpl implements Node, L
     private NodeImpl addNode(String relPath, NodeTypeImpl nt) throws RepositoryException {
         SessionImpl s = state.getSession();
         synchronized (s) {
-            Val nodeId = s.getStorageSession().newNodeId(state.getId());
-            EventAddNode event = new EventAddNode(s.getTransientChanges(), this, Val.get(relPath), nodeId);
+            Val p = Val.get(relPath);
+            Val nodeId = s.getStorageSession().newNodeId(state.getId(), s.getWorkspaceId(), p);
+            EventAddNode event = new EventAddNode(s.getTransientChanges(), this, p, nodeId);
             event.applyAndAdd();
             NodeImpl node = event.getNode();
             Val primaryNodeType;
@@ -285,7 +286,10 @@ public class NodeImpl implements Node, L
     }
 
     private void checkAccess(int privilegeMask) throws LockException {
-        if ((privilegesChecked & privilegeMask) == privilegeMask) {
+        if ((privilegeMask & PrivilegeImpl.WRITE) != 0) {
+            state.session.checkWritingAllowed();
+        }
+        if ((privilegeMask & privilegesChecked) == privilegeMask) {
             return;
         }
         if (state.session.isAdmin()) {

Modified: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryFactoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryFactoryImpl.java?rev=937293&r1=937292&r2=937293&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryFactoryImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryFactoryImpl.java Fri Apr 23 13:43:36 2010
@@ -17,15 +17,18 @@
 package org.apache.jackrabbit.j3;
 
 import java.util.Map;
+import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
-import javax.jcr.RepositoryFactory;
+import org.apache.jackrabbit.j3.api.JackrabbitRepositoryFactory;
+import org.apache.jackrabbit.j3.api.management.RepositoryManager;
 import org.apache.jackrabbit.j3.util.Constants;
+import org.apache.jackrabbit.j3.util.ExceptionFactory;
 import org.apache.jackrabbit.j3.util.Log;
 
 /**
  * The implementation of the corresponding JCR interface.
  */
-public class RepositoryFactoryImpl implements RepositoryFactory {
+public class RepositoryFactoryImpl implements JackrabbitRepositoryFactory {
 
     @SuppressWarnings("unchecked")
     public RepositoryImpl getRepository(Map parameters) throws RepositoryException {
@@ -41,9 +44,20 @@ public class RepositoryFactoryImpl imple
             url = url.substring(0, url.length() - "&log=debug".length());
             log.debug = true;
         }
-        RepositoryImpl  rep = new RepositoryImpl(url, log);
+        RepositoryImpl  rep = new RepositoryImpl(this, url, log);
         log.codeAssign(rep, null, "new " + RepositoryFactoryImpl.class.getName() + "().getRepository", parameters);
         return rep;
     }
 
+    public RepositoryManager getRepositoryManager(Repository repository) throws RepositoryException {
+        if (!(repository instanceof RepositoryImpl)) {
+            throw ExceptionFactory.repository("Repository was not created by this factory: {0}", repository.getClass().getName());
+        }
+        RepositoryImpl rep = (RepositoryImpl) repository;
+        if (rep.getFactory() != this) {
+            throw ExceptionFactory.repository("Repository was not created by this factory: {0}", repository.getClass().getName());
+        }
+        return new RepositoryManagerImpl(this, rep);
+    }
+
 }

Modified: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryImpl.java?rev=937293&r1=937292&r2=937293&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryImpl.java Fri Apr 23 13:43:36 2010
@@ -16,18 +16,17 @@
  */
 package org.apache.jackrabbit.j3;
 
+import java.util.ArrayList;
 import java.util.HashMap;
-import java.util.HashSet;
 import java.util.TreeSet;
+import java.util.WeakHashMap;
 import javax.jcr.Credentials;
-import javax.jcr.LoginException;
-import javax.jcr.NoSuchWorkspaceException;
 import javax.jcr.PropertyType;
-import javax.jcr.Repository;
 import javax.jcr.RepositoryException;
-import javax.jcr.Session;
 import javax.jcr.SimpleCredentials;
 import javax.jcr.Value;
+import org.apache.jackrabbit.j3.api.JackrabbitRepository;
+import org.apache.jackrabbit.j3.data.DataStore;
 import org.apache.jackrabbit.j3.lock.LockSystem;
 import org.apache.jackrabbit.j3.mc.NodeData;
 import org.apache.jackrabbit.j3.mc.Storage;
@@ -47,15 +46,16 @@ import org.apache.jackrabbit.j3.util.Log
 /**
  * The implementation of the corresponding JCR interface.
  */
-public class RepositoryImpl implements Repository, LogObject {
+public class RepositoryImpl implements JackrabbitRepository, LogObject {
 
     private static final SimpleCredentials ANONYMOUS_CREDENTIALS = new SimpleCredentials("", new char[0]);
     private static final HashMap<String, Object> STANDARD_DESCRIPTORS = new HashMap<String, Object>();
 
+    private final RepositoryFactoryImpl factory;
     private final Log log;
     private final String url;
     private final Storage storage;
-    private final HashSet<SessionImpl> sessions = new HashSet<SessionImpl>();
+    private final WeakHashMap<SessionImpl, RepositoryImpl> sessions = new WeakHashMap<SessionImpl, RepositoryImpl>();
     private final Cache<Val, NodeData> readOnlyCache = new Cache<Val, NodeData>(Constants.MEM_CACHE_PER_REPOSITORY);
     private final HashMap<String, ValueImpl> descriptors = new HashMap<String, ValueImpl>();
     private final LobStore lobStore = new LobStore(this);
@@ -66,7 +66,8 @@ public class RepositoryImpl implements R
     private boolean closed;
     private LockSystem lockSystem;
 
-    public RepositoryImpl(String url, Log log) {
+    public RepositoryImpl(RepositoryFactoryImpl factory, String url, Log log) {
+        this.factory = factory;
         this.log = log;
         this.url = url;
         if (url.startsWith("jdbc:")) {
@@ -81,33 +82,31 @@ public class RepositoryImpl implements R
         qomFactory = new QueryObjectModelFactoryImpl(log);
     }
 
-    public synchronized Session login() throws RepositoryException {
+    public synchronized SessionImpl login() throws RepositoryException {
         SessionImpl s = loginInternal(null, null);
         log.codeAssign(s, this, "login");
         return s;
     }
 
-    public synchronized Session login(Credentials credentials) throws RepositoryException {
+    public synchronized SessionImpl login(Credentials credentials) throws RepositoryException {
         SessionImpl s = loginInternal(credentials, null);
         log.codeAssign(s, this, "login", credentials);
         return s;
     }
 
-    public synchronized Session login(String workspaceName) throws RepositoryException {
+    public synchronized SessionImpl login(String workspaceName) throws RepositoryException {
         SessionImpl s = loginInternal(null, workspaceName);
         log.codeAssign(s, this, "login", workspaceName);
         return s;
     }
 
-    public synchronized SessionImpl login(Credentials credentials, String workspaceName) throws LoginException,
-            NoSuchWorkspaceException, RepositoryException {
+    public synchronized SessionImpl login(Credentials credentials, String workspaceName) throws RepositoryException {
         SessionImpl s = loginInternal(credentials, workspaceName);
         log.codeAssign(s, this, "login", credentials, workspaceName);
         return s;
     }
 
-    private synchronized SessionImpl loginInternal(Credentials credentials, String workspaceName) throws LoginException,
-            NoSuchWorkspaceException, RepositoryException {
+    private synchronized SessionImpl loginInternal(Credentials credentials, String workspaceName) throws RepositoryException {
         // TODO workspaceName is ignored
         checkClosed();
         if (credentials == null) {
@@ -118,7 +117,7 @@ public class RepositoryImpl implements R
             String userId = sc.getUserID();
             StorageSession storageSession = storage.openSession(userId, sc.getPassword());
             SessionImpl session = new SessionImpl(this, sc, storageSession, workspaceName, log);
-            sessions.add(session);
+            sessions.put(session, this);
             return session;
         }
         throw ExceptionFactory.login();
@@ -289,7 +288,7 @@ public class RepositoryImpl implements R
     }
 
     public synchronized void dispatchEvents(ChangeSet changes) {
-        for (SessionImpl session : sessions) {
+        for (SessionImpl session : sessions.keySet()) {
             session.dispatchEvents(changes);
         }
     }
@@ -306,53 +305,21 @@ public class RepositoryImpl implements R
         return qomFactory;
     }
 
-    static int getWorkspaceId(long nodeId) {
-        if ((nodeId & 1) == 0) {
-            return 0;
-        } else if ((nodeId & 3) == 1) {
-            return (int) ((nodeId >> 2) & ((1 << 4) - 1));
-        } else if ((nodeId & 7) == 3) {
-            return (int) ((nodeId >> 3) & ((1 << 11) - 1));
-        } else if ((nodeId & 15) == 7) {
-            return (int) ((nodeId >> 4) & ((1 << 28) - 1));
-        } else {
-            throw ExceptionFactory.illegalArgument("Node {0}", nodeId);
+    void shutdown(RepositoryFactoryImpl factory) {
+        if (factory != this.factory) {
+            throw ExceptionFactory.illegalArgument("Repository was not created by this factory");
+        }
+        for (SessionImpl s : new ArrayList<SessionImpl>(sessions.keySet())) {
+            s.logout();
         }
     }
 
-    static long getBaseNodeId(long nodeId) {
-        if ((nodeId & 1) == 0) {
-            return nodeId >> 1;
-        } else if ((nodeId & 3) == 1) {
-            return nodeId >> 6;
-        } else if ((nodeId & 7) == 3) {
-            return nodeId >> 14;
-        } else if ((nodeId & 15) == 7) {
-            return nodeId >> 32;
-        } else {
-            throw ExceptionFactory.illegalArgument("Node {0}", nodeId);
-        }
+    RepositoryFactoryImpl getFactory() {
+        return factory;
     }
 
-    static long getNodeId(int workspaceId, long baseNodeId) {
-        if (workspaceId == 0) {
-            // ... nnnnnnnn nnnnnnn0
-            return baseNodeId << 1;
-        } else if ((workspaceId & ~((1 << 4) - 1)) == 0) {
-            // up to workspace #15
-            // ... nnnnnnnn nnwwww01
-            return (baseNodeId << 6) + (workspaceId << 2) + 1;
-        } else if ((workspaceId & ~((1 << 11) - 1)) == 0) {
-            // up to workspace #2047
-            // ... nnwwwwww wwwww011
-            return (baseNodeId << 14) + (workspaceId << 3) + 3;
-        } else if ((workspaceId & ~((1 << 28) - 1)) == 0) {
-            // up to workspace #268'435'455
-            // ... nnnnnnnn wwwwww ... wwww0111
-            return (baseNodeId << 32) + ((long) workspaceId << 4) + 7;
-        } else {
-            throw ExceptionFactory.illegalArgument("Workspace {0}", workspaceId);
-        }
+    public DataStore getDataStore() {
+        return lobStore.getDataStore();
     }
 
 }

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryManagerImpl.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryManagerImpl.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/RepositoryManagerImpl.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,29 @@
+package org.apache.jackrabbit.j3;
+
+import javax.jcr.RepositoryException;
+import org.apache.jackrabbit.j3.api.management.DataStoreGarbageCollector;
+import org.apache.jackrabbit.j3.api.management.RepositoryManager;
+
+/**
+ * The implementation of the corresponding Jackrabbit interface.
+ */
+public class RepositoryManagerImpl implements RepositoryManager {
+
+    private final RepositoryFactoryImpl factory;
+    private final RepositoryImpl repository;
+
+    public RepositoryManagerImpl(RepositoryFactoryImpl factory, RepositoryImpl repository) {
+        this.factory = factory;
+        this.repository = repository;
+    }
+
+    public DataStoreGarbageCollector createDataStoreGarbageCollector() throws RepositoryException {
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public void stop() {
+        repository.shutdown(factory);
+    }
+
+}

Modified: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionImpl.java?rev=937293&r1=937292&r2=937293&view=diff
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionImpl.java (original)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionImpl.java Fri Apr 23 13:43:36 2010
@@ -21,6 +21,7 @@ import java.io.InputStream;
 import java.io.OutputStream;
 import java.lang.ref.WeakReference;
 import java.security.AccessControlException;
+import java.util.ArrayList;
 import java.util.Iterator;
 import java.util.WeakHashMap;
 import java.util.concurrent.atomic.AtomicInteger;
@@ -45,6 +46,10 @@ import javax.jcr.lock.LockException;
 import javax.jcr.nodetype.ConstraintViolationException;
 import javax.jcr.nodetype.NoSuchNodeTypeException;
 import javax.jcr.version.VersionException;
+import javax.transaction.xa.XAException;
+import javax.transaction.xa.XAResource;
+import javax.transaction.xa.Xid;
+import org.apache.jackrabbit.j3.api.JackrabbitSession;
 import org.apache.jackrabbit.j3.lock.LockManagerImpl;
 import org.apache.jackrabbit.j3.mc.McException;
 import org.apache.jackrabbit.j3.mc.NodeData;
@@ -59,6 +64,7 @@ import org.apache.jackrabbit.j3.util.Con
 import org.apache.jackrabbit.j3.util.ExceptionFactory;
 import org.apache.jackrabbit.j3.util.Log;
 import org.apache.jackrabbit.j3.util.LogObject;
+import org.apache.jackrabbit.j3.util.StringUtils;
 import org.apache.jackrabbit.j3.version.VersionManagerImpl;
 import org.xml.sax.ContentHandler;
 import org.xml.sax.SAXException;
@@ -66,10 +72,11 @@ import org.xml.sax.SAXException;
 /**
  * The implementation of the corresponding JCR interface.
  */
-public class SessionImpl implements Session, LogObject {
+public class SessionImpl implements JackrabbitSession, XAResource, LogObject {
 
     private static final AtomicInteger SESSION_ID = new AtomicInteger(0);
 
+    private final Exception openStackTrace;
     private final RepositoryImpl rep;
     private final int id;
     private final StorageSession storageSession;
@@ -77,6 +84,7 @@ public class SessionImpl implements Sess
     private final ChangeSet changes;
     private final NamespaceRegistryImpl namespaceRegistry;
     private final WorkspaceImpl workspace;
+    private final int workspaceId;
     private final SimpleCredentials credentials;
     private final String userId;
     private final Log log;
@@ -90,6 +98,9 @@ public class SessionImpl implements Sess
     private QueryManagerImpl queryManager;
     private VersionManagerImpl versionManager;
     private RetentionManagerImpl retentionManager;
+    private boolean closed;
+    private ArrayList<SessionListener> sessionListeners;
+    private boolean blockWrites;
 
     public SessionImpl(RepositoryImpl rep, SimpleCredentials credentials, StorageSession storageSession, String workspaceName, Log log) {
         this.rep = rep;
@@ -97,17 +108,48 @@ public class SessionImpl implements Sess
         this.log = log;
         this.credentials = credentials;
         this.userId = credentials.getUserID();
+        boolean logUnclosed = getAttribute(credentials, Constants.LOG_UNCLOSED_SESSIONS, Constants.LOG_UNCLOSED_SESSIONS_DEFAULT);
+        this.openStackTrace = logUnclosed ? new Exception("Stack Trace") : null;
         this.storageSession = storageSession;
         this.workspace = new WorkspaceImpl(this, workspaceName, log);
+        this.workspaceId = StorageSession.MAIN_WORKSPACE_ID;
         this.namespaceRegistry = NamespaceRegistryImpl.createLocalInstance(log);
         int maxMemory = storageSession.supportsTemp() ? Constants.MEM_CACHE_PER_SESSION : Integer.MAX_VALUE;
         this.changes = new ChangeSet(this, storageSession, maxMemory);
     }
 
+    private boolean getAttribute(SimpleCredentials credentials, String key, boolean defaultValue) {
+        Object o = credentials.getAttribute(Constants.LOG_UNCLOSED_SESSIONS);
+        if (o == null) {
+            return defaultValue;
+        }
+        return Boolean.parseBoolean(o.toString());
+    }
+
     public synchronized void logout() {
         log.code(this, "logout");
-        storageSession.close();
-        rep.close(this);
+        if (!closed) {
+            if (sessionListeners != null) {
+                for (SessionListener s : sessionListeners) {
+                    s.loggingOut(this);
+                }
+            }
+            storageSession.close();
+            rep.close(this);
+            closed = true;
+            if (sessionListeners != null) {
+                for (SessionListener s : sessionListeners) {
+                    s.loggedOut(this);
+                }
+            }
+        }
+    }
+
+    public void finalize() {
+        if (!closed && openStackTrace != null) {
+            log.warn("Unclosed session detected. The session was opened here: ", openStackTrace);
+            logout();
+        }
     }
 
     public NodeImpl getNode(NodeImpl parent, Val nodeId) throws RepositoryException {
@@ -193,7 +235,12 @@ public class SessionImpl implements Sess
             rep.cacheNodeData(n);
             s.reset(n);
         }
-        changes.dispatchEvents();
+        try {
+            blockWrites = true;
+            changes.dispatchEvents();
+        } finally {
+            blockWrites = false;
+        }
         changes.clearAll();
     }
 
@@ -396,10 +443,26 @@ public class SessionImpl implements Sess
         return changes;
     }
 
+    public int getWorkspaceId() {
+        return workspaceId;
+    }
+
+    public synchronized void addListener(SessionListener sessionListener) {
+        if (sessionListeners == null) {
+            sessionListeners = new ArrayList<SessionListener>();
+        }
+        sessionListeners.add(sessionListener);
+    }
+
+    void checkWritingAllowed() throws LockException {
+        if (blockWrites) {
+            throw ExceptionFactory.lock("Write are blocked");
+        }
+    }
+
     public boolean isLive() {
         log.code(this, "isLive");
-        // TODO Auto-generated method stub
-        return false;
+        return !closed;
     }
 
     public void addLockToken(String lt) {
@@ -537,6 +600,78 @@ public class SessionImpl implements Sess
 
     }
 
+    public void commit(Xid arg0, boolean arg1) throws XAException {
+        // TODO Auto-generated method stub
+
+    }
+
+    public void end(Xid xid, int onePhase) throws XAException {
+        log.code(this, "end", xid, onePhase);
+        // TODO Auto-generated method stub
+
+    }
+
+    public void forget(Xid xid) throws XAException {
+        log.code(this, "forget", xid);
+        // TODO Auto-generated method stub
+
+    }
+
+    public int getTransactionTimeout() throws XAException {
+        log.code(this, "getTransactionTimeout");
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    /**
+     * {@inheritDoc}
+     * <p/>
+     * Two resources belong to the same resource manager if both connections
+     * (i.e. sessions) have the same credentials.
+     */
+    public boolean isSameRM(XAResource xares) throws XAException {
+        log.code(this, "isSameRM", xares);
+        if (xares instanceof SessionImpl) {
+            SessionImpl xases = (SessionImpl) xares;
+            return StringUtils.equals(userId, xases.userId);
+        }
+        return false;
+    }
+
+    public int prepare(Xid xid) throws XAException {
+        log.code(this, "prepare", xid);
+        // TODO Auto-generated method stub
+        return 0;
+    }
+
+    public Xid[] recover(int flag) throws XAException {
+        log.code(this, "recover", flag);
+        // TODO Auto-generated method stub
+        return null;
+    }
+
+    public void rollback(Xid xid) throws XAException {
+        log.code(this, "rollback", xid);
+        // TODO Auto-generated method stub
+
+    }
+
+    public boolean setTransactionTimeout(int seconds) throws XAException {
+        log.code(this, "setTransactionTimeout", seconds);
+        // TODO Auto-generated method stub
+        return false;
+    }
+
+    public void start(Xid xid, int flags) throws XAException {
+        log.code(this, "start", xid, flags);
+        // TODO Auto-generated method stub
+
+    }
+
+    public XAResource getXAResource() {
+        return this;
+    }
+
     public String toString() {
         return "session #" + id + " " + rep + " changes: " + changes + " weak map: " + weakMap.size();
     }

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionListener.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionListener.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/SessionListener.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,46 @@
+/*
+ * 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.jackrabbit.j3;
+
+import javax.jcr.Session;
+
+/**
+ * Allows an implementing object to be informed about changes on a
+ * <code>Session</code>.
+ *
+ * @see SessionImpl#addListener
+ */
+public interface SessionListener {
+
+    /**
+     * Called when a <code>Session</code> is about to be 'closed' by
+     * calling <code>{@link javax.jcr.Session#logout()}</code.
+     * The session is still valid when the method is called.
+     *
+     * @param session the <code>Session</code> that is about to be closed
+     */
+    void loggingOut(Session session);
+
+    /**
+     * Called when a <code>Session</code> has been closed by
+     * calling <code>{@link javax.jcr.Session#logout()}</code.
+     *
+     * @param session the <code>Session</code> that has been closed
+     */
+    void loggedOut(Session session);
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepository.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepository.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepository.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepository.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,39 @@
+/*
+ * 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.jackrabbit.j3.api;
+
+import javax.jcr.Credentials;
+import javax.jcr.LoginException;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import org.apache.jackrabbit.j3.SessionImpl;
+
+/**
+ * A Jackrabbit repository.
+ */
+public interface JackrabbitRepository extends Repository {
+
+    JackrabbitSession login() throws RepositoryException;
+
+    JackrabbitSession login(Credentials credentials) throws RepositoryException;
+
+    JackrabbitSession login(String workspaceName) throws RepositoryException;
+
+    JackrabbitSession login(Credentials credentials, String workspaceName) throws RepositoryException;
+
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepositoryFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepositoryFactory.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepositoryFactory.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitRepositoryFactory.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,41 @@
+/*
+ * 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.jackrabbit.j3.api;
+
+import java.util.Map;
+import javax.jcr.Repository;
+import javax.jcr.RepositoryException;
+import javax.jcr.RepositoryFactory;
+import org.apache.jackrabbit.j3.api.management.RepositoryManager;
+
+/**
+ * Classes that implement this interface additionally provide management features.
+ */
+public interface JackrabbitRepositoryFactory extends RepositoryFactory {
+
+    /**
+     * Get the repository management component. Only the factory that created
+     * the given repository may retrieve the manager.
+     *
+     * @param repository the repository to manage
+     * @return the manager
+     */
+    RepositoryManager getRepositoryManager(Repository repository) throws RepositoryException;
+
+    Repository getRepository(Map<String, String> map) throws RepositoryException;
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitSession.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitSession.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitSession.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/JackrabbitSession.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,29 @@
+/*
+ * 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.jackrabbit.j3.api;
+
+import javax.jcr.Session;
+import javax.transaction.xa.XAResource;
+
+/**
+ * A Jackrabbit session.
+ */
+public interface JackrabbitSession extends Session {
+
+    XAResource getXAResource();
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/DataStoreGarbageCollector.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/DataStoreGarbageCollector.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/DataStoreGarbageCollector.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/DataStoreGarbageCollector.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,87 @@
+/*
+ * 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.jackrabbit.j3.api.management;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Garbage collector for DataStore. This implementation is iterates through all
+ * nodes and reads the binary properties. To detect nodes that are moved while
+ * the scan runs, event listeners are started. Like the well known garbage
+ * collection in Java, the items that are still in use are marked. Currently
+ * this achieved by updating the modified date of the entries. Newly added
+ * entries are detected because the modified date is changed when they are
+ * added.
+ * <p>
+ * Example code to run the data store garbage collection:
+ * <pre>
+ * DataStoreGarbageCollector gc = ((JackrabbitSession)session).createDataStoreGarbageCollector();
+ * gc.mark();
+ * gc.sweep();
+ * </pre>
+ */
+public interface DataStoreGarbageCollector {
+
+    /**
+     * Set the delay between scanning items.
+     * The main scan loop sleeps this many milliseconds after
+     * scanning a node. The default is 0, meaning the scan should run at full speed.
+     *
+     * @param millis the number of milliseconds to sleep
+     */
+    void setSleepBetweenNodes(long millis);
+
+    /**
+     * Get the delay between scanning items.
+     *
+     * @return the number of milliseconds to sleep
+     */
+    long getSleepBetweenNodes();
+
+    /**
+     * Set the event listener. If set, the event listener will be called
+     * for each item that is scanned. This mechanism can be used
+     * to display the progress.
+     *
+     * @param callback if set, this is called while scanning
+     */
+    void setMarkEventListener(MarkEventListener callback);
+
+    /**
+     * Scan the repository. The garbage collector will iterate over all nodes in the repository
+     * and update the last modified date. If all persistence managers implement the
+     * IterablePersistenceManager interface, this mechanism is used; if not, the garbage
+     * collector scans the repository using the JCR API starting from the root node.
+     *
+     * @throws RepositoryException
+     */
+    void mark() throws RepositoryException;
+
+    /**
+     * Delete all unused items in the data store.
+     *
+     * @return the number of deleted items
+     * @throws RepositoryException
+     */
+    int sweep() throws RepositoryException;
+
+    /**
+     * Cleanup resources used internally by this instance.
+     */
+    void close();
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/MarkEventListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/MarkEventListener.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/MarkEventListener.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/MarkEventListener.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,32 @@
+/*
+ * 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.jackrabbit.j3.api.management;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+/**
+ * The listener interface for receiving garbage collection scan events.
+ */
+public interface MarkEventListener {
+
+    /**
+     * This method is called before a node is scanned.
+     */
+    void beforeScanning(Node n) throws RepositoryException;
+
+}
\ No newline at end of file

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/RepositoryManager.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/RepositoryManager.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/RepositoryManager.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/management/RepositoryManager.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,50 @@
+/*
+ * 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.jackrabbit.j3.api.management;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * The repository manager provides life-cycle management features for
+ * repositories.
+ *
+ * Not all implementations are required to implement all features,
+ * for example some implementations may not support starting a repository after
+ * is has been stopped.
+ */
+public interface RepositoryManager {
+
+    /**
+     * Shuts down the repository. A Jackrabbit repository instance contains
+     * a acquired resources and cached data that needs to be released and
+     * persisted when the repository is no longer used. This method handles
+     * all these shutdown tasks and <em>should</em> therefore be called by the
+     * client application once the repository instance is no longer used.
+     * <p>
+     * Possible errors are logged rather than thrown as exceptions as there
+     * is little that a client application could do in such a case.
+     */
+    void stop();
+
+    /**
+     * Create a data store garbage collector for this repository.
+     *
+     * @return the data store garbage collector if the data store is enabled, null otherwise
+     */
+    DataStoreGarbageCollector createDataStoreGarbageCollector() throws RepositoryException;
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/observation/SynchronousEventListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/observation/SynchronousEventListener.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/observation/SynchronousEventListener.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/api/observation/SynchronousEventListener.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,35 @@
+/*
+ * 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.jackrabbit.j3.api.observation;
+
+import javax.jcr.observation.EventListener;
+
+/**
+ * A marker interface for {@link javax.jcr.observation.EventListener}
+ * implementations that wish a synchronous notification of changes.
+ * A <code>SynchronousEventListener</code> is called before
+ * the call to {@link javax.jcr.Item#save()} returns. In contrast, a regular
+ * {@link javax.jcr.observation.EventListener} might be called after
+ * <code>save()</code> returns.
+ * <p/>
+ * An implementation of {@link SynchronousEventListener}
+ * may not modify content with the thread that calls {@link
+ * #onEvent(EventIterator)}. Trying to do so will result in an exception.
+ */
+public interface SynchronousEventListener extends EventListener {
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/AbstractDataRecord.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/AbstractDataRecord.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/AbstractDataRecord.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/AbstractDataRecord.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,82 @@
+/*
+ * 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.jackrabbit.j3.data;
+
+/**
+ * Abstract data record base class. This base class contains only
+ * a reference to the data identifier of the record and implements
+ * the standard {@link Object} equality, hash code, and string
+ * representation methods based on the identifier.
+ */
+public abstract class AbstractDataRecord implements DataRecord {
+
+    /**
+     * The binary identifier;
+     */
+    private final DataIdentifier identifier;
+
+    /**
+     * Creates a data record with the given identifier.
+     *
+     * @param identifier data identifier
+     */
+    public AbstractDataRecord(DataIdentifier identifier) {
+        this.identifier = identifier;
+    }
+
+    /**
+     * Returns the data identifier.
+     *
+     * @return data identifier
+     */
+    public DataIdentifier getIdentifier() {
+        return identifier;
+    }
+
+    /**
+     * Returns the string representation of the data identifier.
+     *
+     * @return string representation
+     */
+    public String toString() {
+        return identifier.toString();
+    }
+
+    /**
+     * Checks if the given object is a data record with the same identifier
+     * as this one.
+     *
+     * @param object other object
+     * @return <code>true</code> if the other object is a data record and has
+     *         the same identifier as this one, <code>false</code> otherwise
+     */
+    public boolean equals(Object object) {
+        return (object instanceof DataRecord)
+            && identifier.equals(((DataRecord) object).getIdentifier());
+    }
+
+    /**
+     * Returns the hash code of the data identifier.
+     *
+     * @return hash code
+     */
+    public int hashCode() {
+        return identifier.hashCode();
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataIdentifier.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataIdentifier.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataIdentifier.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataIdentifier.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,100 @@
+/*
+ * 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.jackrabbit.j3.data;
+
+import java.io.Serializable;
+
+/**
+ * Opaque data identifier used to identify records in a data store.
+ * All identifiers must be serializable and implement the standard
+ * object equality and hash code methods.
+ */
+public final class DataIdentifier implements Serializable {
+
+    /**
+     * Serial version UID.
+     */
+    private static final long serialVersionUID = -9197191401131100016L;
+
+    /**
+     * Array of hexadecimal digits.
+     */
+    private static final char[] HEX = "0123456789abcdef".toCharArray();
+
+    /**
+     * Data identifier.
+     */
+    private final String identifier;
+
+    /**
+     * Creates a data identifier from the given string.
+     *
+     * @param identifier data identifier
+     */
+    public DataIdentifier(String identifier) {
+        this.identifier = identifier;
+    }
+
+    /**
+     * Creates a data identifier from the hexadecimal string
+     * representation of the given bytes.
+     *
+     * @param identifier data identifier
+     */
+    public DataIdentifier(byte[] identifier) {
+        char[] buffer = new char[identifier.length * 2];
+        for (int i = 0; i < identifier.length; i++) {
+            buffer[2 * i] = HEX[(identifier[i] >> 4) & 0x0f];
+            buffer[2 * i + 1] = HEX[identifier[i] & 0x0f];
+        }
+        this.identifier = new String(buffer);
+    }
+
+    //-------------------------------------------------------------< Object >
+
+    /**
+     * Returns the identifier string.
+     *
+     * @return identifier string
+     */
+    public String toString() {
+        return identifier;
+    }
+
+    /**
+     * Checks if the given object is a data identifier and has the same
+     * string representation as this one.
+     *
+     * @param object other object
+     * @return <code>true</code> if the given object is the same identifier,
+     *         <code>false</code> otherwise
+     */
+    public boolean equals(Object object) {
+        return (object instanceof DataIdentifier)
+            && identifier.equals(object.toString());
+    }
+
+    /**
+     * Returns the hash code of the identifier string.
+     *
+     * @return hash code
+     */
+    public int hashCode() {
+        return identifier.hashCode();
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataRecord.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataRecord.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataRecord.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataRecord.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,55 @@
+/*
+ * 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.jackrabbit.j3.data;
+
+import java.io.InputStream;
+
+/**
+ * Immutable data record that consists of a binary stream.
+ */
+public interface DataRecord {
+
+    /**
+     * Returns the identifier of this record.
+     *
+     * @return data identifier
+     */
+    DataIdentifier getIdentifier();
+
+    /**
+     * Returns the length of the binary stream in this record.
+     *
+     * @return length of the binary stream
+     * @throws DataStoreException if the record could not be accessed
+     */
+    long getLength() throws DataStoreException;
+
+    /**
+     * Returns the the binary stream in this record.
+     *
+     * @return binary stream
+     * @throws DataStoreException if the record could not be accessed
+     */
+    InputStream getStream() throws DataStoreException;
+
+    /**
+     * Returns the last modified of the record.
+     * 
+     * @return last modified time of the binary stream
+     */
+    long getLastModified();
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStore.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStore.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStore.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,140 @@
+/*
+ * 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.jackrabbit.j3.data;
+
+import java.io.InputStream;
+import java.util.Iterator;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Append-only store for binary streams. A data store consists of a number
+ * of identifiable data records that each contain a distinct binary stream.
+ * New binary streams can be added to the data store, but existing streams
+ * are never removed or modified.
+ * <p>
+ * A data store should be fully thread-safe, i.e. it should be possible to
+ * add and access data records concurrently. Optimally even separate processes
+ * should be able to concurrently access the data store with zero interprocess
+ * synchronization.
+ */
+public interface DataStore {
+
+    /**
+     * Check if a record for the given identifier exists, and return it if yes.
+     * If no record exists, this method returns null.
+     * 
+     * @param identifier data identifier
+     * @return the record if found, and null if not
+     */
+    DataRecord getRecordIfStored(DataIdentifier identifier) throws DataStoreException;
+
+    /**
+     * Returns the identified data record. The given identifier should be
+     * the identifier of a previously saved data record. Since records are
+     * never removed, there should never be cases where the identified record
+     * is not found. Abnormal cases like that are treated as errors and
+     * handled by throwing an exception.
+     *
+     * @param identifier data identifier
+     * @return identified data record
+     * @throws DataStoreException if the data store could not be accessed,
+     *                     or if the given identifier is invalid
+     */
+    DataRecord getRecord(DataIdentifier identifier) throws DataStoreException;
+
+    /**
+     * Creates a new data record. The given binary stream is consumed and
+     * a binary record containing the consumed stream is created and returned.
+     * If the same stream already exists in another record, then that record
+     * is returned instead of creating a new one.
+     * <p>
+     * The given stream is consumed and <strong>not closed</strong> by this
+     * method. It is the responsibility of the caller to close the stream.
+     * A typical call pattern would be:
+     * <pre>
+     *     InputStream stream = ...;
+     *     try {
+     *         record = store.addRecord(stream);
+     *     } finally {
+     *         stream.close();
+     *     }
+     * </pre>
+     *
+     * @param stream binary stream
+     * @return data record that contains the given stream
+     * @throws DataStoreException if the data store could not be accessed
+     */
+    DataRecord addRecord(InputStream stream) throws DataStoreException;
+
+    /**
+     * From now on, update the modified date of an object even when accessing it.
+     * Usually, the modified date is only updated when creating a new object,
+     * or when a new link is added to an existing object. When this setting is enabled,
+     * even getLength() will update the modified date.
+     *
+     * @param before - update the modified date to the current time if it is older than this value
+     */
+    void updateModifiedDateOnAccess(long before);
+
+    /**
+     * Delete objects that have a modified date older than the specified date.
+     *
+     * @param min the minimum time
+     * @return the number of data records deleted
+     * @throws DataStoreException
+     */
+    int deleteAllOlderThan(long min) throws DataStoreException;
+
+    /**
+     * Get all identifiers.
+     *
+     * @return an iterator over all DataIdentifier objects
+     * @throws DataStoreException if the list could not be read
+     */
+    Iterator<DataIdentifier> getAllIdentifiers() throws DataStoreException;
+
+    /**
+     * Initialized the data store
+     *
+     * @param homeDir the home directory of the repository
+     * @throws RepositoryException
+     */
+    void init(String homeDir) throws RepositoryException;
+
+    /**
+     * Get the minimum size of an object that should be stored in this data store.
+     * Depending on the overhead and configuration, each store may return a different value.
+     *
+     * @return the minimum size in bytes
+     */
+    int getMinRecordLength();
+
+    /**
+     * Close the data store
+     *
+     * @throws DataStoreException if a problem occured
+     */
+    void close() throws DataStoreException;
+
+    /**
+     * Clear the in-use list. This is only used for testing to make the the garbage collection
+     * think that objects are no longer in use.
+     */
+    void clearInUse();
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreException.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreException.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreException.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreException.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,56 @@
+/*
+ * 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.jackrabbit.j3.data;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Exception thrown by the Data Store module.
+ */
+public class DataStoreException extends RepositoryException {
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message.
+     *
+     * @param message the detailed message.
+     */
+    public DataStoreException(String message) {
+        super(message);
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified detail
+     * message and root cause.
+     *
+     * @param message the detailed message.
+     * @param cause root failure cause
+     */
+    public DataStoreException(String message, Throwable cause) {
+        super(message, cause);
+    }
+
+    /**
+     * Constructs a new instance of this class with the specified root cause.
+     *
+     * @param rootCause root failure cause
+     */
+    public DataStoreException(Throwable rootCause) {
+        super(rootCause);
+    }
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreFactory.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreFactory.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreFactory.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/DataStoreFactory.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,42 @@
+/*
+ * 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.jackrabbit.j3.data;
+
+import javax.jcr.RepositoryException;
+
+/**
+ * Factory interface for creating {@link DataStore} instances. Used
+ * to decouple the repository internals from the repository configuration
+ * mechanism.
+ *
+ * @since Jackrabbit 1.5
+ * @see <a href="https://issues.apache.org/jira/browse/JCR-1438">JCR-1438</a>
+ */
+public interface DataStoreFactory {
+
+    /**
+     * Creates, initializes, and returns a {@link DataStore} instance
+     * for use by the repository. Note that no information is passed from
+     * the client, so all required configuration information must be
+     * encapsulated in the factory.
+     *
+     * @return initialized data store
+     * @throws RepositoryException if the data store can not be created
+     */
+    DataStore getDataStore() throws RepositoryException;
+
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataRecord.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataRecord.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataRecord.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataRecord.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,69 @@
+/*
+ * 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.jackrabbit.j3.data;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+
+/**
+ * Data record that is based on a normal file.
+ */
+public class FileDataRecord extends AbstractDataRecord {
+
+    /**
+     * The file that contains the binary stream.
+     */
+    private final File file;
+
+    /**
+     * Creates a data record based on the given identifier and file.
+     *
+     * @param identifier data identifier
+     * @param file file that contains the binary stream
+     */
+    public FileDataRecord(DataIdentifier identifier, File file) {
+        super(identifier);
+        assert file.isFile();
+        this.file = file;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public long getLength() {
+        return file.length();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public InputStream getStream() throws DataStoreException {
+        try {
+            return new LazyFileInputStream(file);
+        } catch (IOException e) {
+            throw new DataStoreException("Error opening input stream of " + file.getAbsolutePath(), e);
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public long getLastModified() {
+        return file.lastModified();
+    }
+}

Added: jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataStore.java
URL: http://svn.apache.org/viewvc/jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataStore.java?rev=937293&view=auto
==============================================================================
--- jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataStore.java (added)
+++ jackrabbit/sandbox/jackrabbit-j3/src/main/java/org/apache/jackrabbit/j3/data/FileDataStore.java Fri Apr 23 13:43:36 2010
@@ -0,0 +1,417 @@
+/*
+ * 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.jackrabbit.j3.data;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.lang.ref.WeakReference;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.security.NoSuchAlgorithmException;
+import java.sql.Timestamp;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+import java.util.Map;
+import java.util.WeakHashMap;
+
+import org.apache.commons.io.IOUtils;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Simple file-based data store. Data records are stored as normal files
+ * named using a message digest of the contained binary stream.
+ *
+ * Configuration:
+ * <pre>
+ * &lt;DataStore class="org.apache.jackrabbit.core.data.FileDataStore">
+ *     &lt;param name="{@link #setPath(String) path}" value="/data/datastore"/>
+ *     &lt;param name="{@link #setMinRecordLength(int) minRecordLength}" value="1024"/>
+ * &lt/DataStore>
+ * </pre>
+ * <p>
+ * If the directory is not set, the directory &lt;repository home&gt;/repository/datastore is used.
+ * <p>
+ * A three level directory structure is used to avoid placing too many
+ * files in a single directory. The chosen structure is designed to scale
+ * up to billions of distinct records.
+ * <p>
+ * This implementation relies on the underlying file system to support
+ * atomic O(1) move operations with {@link File#renameTo(File)}.
+ */
+public class FileDataStore implements DataStore {
+
+    /**
+     * Logger instance
+     */
+    private static Logger log = LoggerFactory.getLogger(FileDataStore.class);
+
+    /**
+     * The digest algorithm used to uniquely identify records.
+     */
+    private static final String DIGEST = "SHA-1";
+
+    /**
+     * The default value for the minimum object size.
+     */
+    private static final int DEFAULT_MIN_RECORD_LENGTH = 100;
+
+    /**
+     * The maximum last modified time resolution of the file system.
+     */
+    private static final int ACCESS_TIME_RESOLUTION = 2000;
+
+    /**
+     * Name of the directory used for temporary files.
+     * Must be at least 3 characters.
+     */
+    private static final String TMP = "tmp";
+
+    /**
+     * The minimum modified date. If a file is accessed (read or write) with a modified date
+     * older than this value, the modified date is updated to the current time.
+     */
+    private long minModifiedDate;
+
+    /**
+     * The directory that contains all the data record files. The structure
+     * of content within this directory is controlled by this class.
+     */
+    private File directory;
+
+    /**
+     * The name of the directory that contains all the data record files. The structure
+     * of content within this directory is controlled by this class.
+     */
+    private String path;
+
+    /**
+     * The minimum size of an object that should be stored in this data store.
+     */
+    private int minRecordLength = DEFAULT_MIN_RECORD_LENGTH;
+
+    /**
+     * All data identifiers that are currently in use are in this set until they are garbage collected.
+     */
+    protected Map<DataIdentifier, WeakReference<DataIdentifier>> inUse =
+        Collections.synchronizedMap(new WeakHashMap<DataIdentifier, WeakReference<DataIdentifier>>());
+
+    /**
+     * Creates a uninitialized data store.
+     *
+     */
+    public FileDataStore() {
+    }
+
+    /**
+     * Initialized the data store.
+     * If the path is not set, &lt;repository home&gt;/repository/datastore is used.
+     * This directory is automatically created if it does not yet exist.
+     *
+     * @param homeDir
+     */
+    public void init(String homeDir) {
+        if (path == null) {
+            path = homeDir + "/repository/datastore";
+        }
+        directory = new File(path);
+        directory.mkdirs();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public DataRecord getRecordIfStored(DataIdentifier identifier) {
+        File file = getFile(identifier);
+        synchronized (this) {
+            if (!file.exists()) {
+                return null;
+            }
+            if (minModifiedDate != 0 && file.canWrite()) {
+                if (file.lastModified() < minModifiedDate) {
+                    file.setLastModified(System.currentTimeMillis() + ACCESS_TIME_RESOLUTION);
+                }
+            }
+            usesIdentifier(identifier);
+            return new FileDataRecord(identifier, file);
+        }
+    }
+
+    /**
+     * Returns the record with the given identifier. Note that this method
+     * performs no sanity checks on the given identifier. It is up to the
+     * caller to ensure that only identifiers of previously created data
+     * records are used.
+     *
+     * @param identifier data identifier
+     * @return identified data record
+     */
+    public DataRecord getRecord(DataIdentifier identifier) throws DataStoreException {
+        DataRecord record = getRecordIfStored(identifier);
+        if (record == null) {
+            throw new DataStoreException("Record not found: " + identifier);
+        }
+        return record;
+    }
+
+    private void usesIdentifier(DataIdentifier identifier) {
+        inUse.put(identifier, new WeakReference<DataIdentifier>(identifier));
+    }
+
+    /**
+     * Creates a new data record.
+     * The stream is first consumed and the contents are saved in a temporary file
+     * and the SHA-1 message digest of the stream is calculated. If a
+     * record with the same SHA-1 digest (and length) is found then it is
+     * returned. Otherwise the temporary file is moved in place to become
+     * the new data record that gets returned.
+     *
+     * @param input binary stream
+     * @return data record that contains the given stream
+     * @throws DataStoreException if the record could not be created
+     */
+    public DataRecord addRecord(InputStream input) throws DataStoreException {
+        File temporary = null;
+        try {
+            temporary = newTemporaryFile();
+            DataIdentifier tempId = new DataIdentifier(temporary.getName());
+            usesIdentifier(tempId);
+            // Copy the stream to the temporary file and calculate the
+            // stream length and the message digest of the stream
+            long length = 0;
+            MessageDigest digest = MessageDigest.getInstance(DIGEST);
+            OutputStream output = new DigestOutputStream(
+                    new FileOutputStream(temporary), digest);
+            try {
+                length = IOUtils.copyLarge(input, output);
+            } finally {
+                output.close();
+            }
+            DataIdentifier identifier = new DataIdentifier(digest.digest());
+            File file;
+
+            synchronized (this) {
+                // Check if the same record already exists, or
+                // move the temporary file in place if needed
+                usesIdentifier(identifier);
+                file = getFile(identifier);
+                File parent = file.getParentFile();
+                if (!parent.isDirectory()) {
+                    parent.mkdirs();
+                }
+                if (!file.exists()) {
+                    temporary.renameTo(file);
+                    if (!file.exists()) {
+                        throw new IOException(
+                                "Can not rename " + temporary.getAbsolutePath()
+                                + " to " + file.getAbsolutePath()
+                                + " (media read only?)");
+                    }
+                } else {
+                    long now = System.currentTimeMillis();
+                    if (file.lastModified() < now) {
+                        file.setLastModified(now);
+                    }
+                }
+                // Sanity checks on the record file. These should never fail,
+                // but better safe than sorry...
+                if (!file.isFile()) {
+                    throw new IOException("Not a file: " + file);
+                }
+                if (file.length() != length) {
+                    throw new IOException(DIGEST + " collision: " + file);
+                }
+            }
+            // this will also make sure that
+            // tempId is not garbage collected until here
+            inUse.remove(tempId);
+            return new FileDataRecord(identifier, file);
+        } catch (NoSuchAlgorithmException e) {
+            throw new DataStoreException(DIGEST + " not available", e);
+        } catch (IOException e) {
+            throw new DataStoreException("Could not add record", e);
+        } finally {
+            if (temporary != null) {
+                temporary.delete();
+            }
+        }
+    }
+
+    /**
+     * Returns the identified file. This method implements the pattern
+     * used to avoid problems with too many files in a single directory.
+     * <p>
+     * No sanity checks are performed on the given identifier.
+     *
+     * @param identifier data identifier
+     * @return identified file
+     */
+    private File getFile(DataIdentifier identifier) {
+        usesIdentifier(identifier);
+        String string = identifier.toString();
+        File file = directory;
+        file = new File(file, string.substring(0, 2));
+        file = new File(file, string.substring(2, 4));
+        file = new File(file, string.substring(4, 6));
+        return new File(file, string);
+    }
+
+    /**
+     * Returns a unique temporary file to be used for creating a new
+     * data record.
+     *
+     * @return temporary file
+     * @throws IOException
+     */
+    private File newTemporaryFile() throws IOException {
+        if (!directory.isDirectory()) {
+            directory.mkdirs();
+        }
+        return File.createTempFile(TMP, null, directory);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void updateModifiedDateOnAccess(long before) {
+        minModifiedDate = before;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int deleteAllOlderThan(long min) {
+        return deleteOlderRecursive(directory, min);
+    }
+
+    private int deleteOlderRecursive(File file, long min) {
+        int count = 0;
+        if (file.isFile() && file.exists() && file.canWrite()) {
+            synchronized (this) {
+                String fileName = file.getName();
+                if (file.lastModified() < min) {
+                    DataIdentifier id = new DataIdentifier(fileName);
+                    if (!inUse.containsKey(id)) {
+                        if (log.isInfoEnabled()) {
+                            log.info("Deleting old file " + file.getAbsolutePath() +
+                                    " modified: " + new Timestamp(file.lastModified()).toString() +
+                                    " length: " + file.length());
+                        }
+                        file.delete();
+                        count++;
+                    }
+                }
+            }
+        } else if (file.isDirectory()) {
+            for (File f: file.listFiles()) {
+                count += deleteOlderRecursive(f, min);
+            }
+
+            // JCR-1396: FileDataStore Garbage Collector and empty directories
+            // Automatic removal of empty directories (but not the root!)
+            synchronized (this) {
+                if (file != directory && file.list().length == 0) {
+                    file.delete();
+                }
+            }
+        }
+        return count;
+    }
+
+    private void listRecursive(List<File> list, File file) {
+        File[] files = file.listFiles();
+        if (files != null) {
+            for (File f : files) {
+                if (f.isDirectory()) {
+                    listRecursive(list, f);
+                } else {
+                    list.add(f);
+                }
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Iterator<DataIdentifier> getAllIdentifiers() {
+        ArrayList<File> files = new ArrayList<File>();
+        listRecursive(files, directory);
+        ArrayList<DataIdentifier> identifiers = new ArrayList<DataIdentifier>();
+        for (File f: files) {
+            String name = f.getName();
+            if (!name.startsWith(TMP)) {
+                DataIdentifier id = new DataIdentifier(name);
+                identifiers.add(id);
+            }
+        }
+        return identifiers.iterator();
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void clearInUse() {
+        inUse.clear();
+    }
+
+    /**
+     * Get the name of the directory where this data store keeps the files.
+     *
+     * @return the full path name
+     */
+    public String getPath() {
+        return path;
+    }
+
+    /**
+     * Set the name of the directory where this data store keeps the files.
+     *
+     * @param directoryName the path name
+     */
+    public void setPath(String directoryName) {
+        this.path = directoryName;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int getMinRecordLength() {
+        return minRecordLength;
+    }
+
+    /**
+     * Set the minimum object length.
+     *
+     * @param minRecordLength the length
+     */
+    public void setMinRecordLength(int minRecordLength) {
+        this.minRecordLength = minRecordLength;
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void close() {
+    }
+
+}



Mime
View raw message