Author: amitj
Date: Wed Sep 14 09:03:33 2016
New Revision: 1760661

URL: http://svn.apache.org/viewvc?rev=1760661&view=rev
Log:
OAK-4712: Publish S3DataStore stats in JMX MBean

Modified patch from Matt Ryan

Added:
    
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/stats/
    
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/stats/S3DataStoreStatsMBean.java
   (with props)
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStats.java
   (with props)
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStatsTest.java
   (with props)
    
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentS3DataStoreStatsTest.java
   (with props)
    
jackrabbit/oak/trunk/oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentS3DataStoreStatsTest.java
   (with props)
Modified:
    jackrabbit/oak/trunk/oak-blob-cloud/pom.xml
    
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/S3DataStore.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreService.java
    
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/SharedS3DataStoreService.java

Modified: jackrabbit/oak/trunk/oak-blob-cloud/pom.xml
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud/pom.xml?rev=1760661&r1=1760660&r2=1760661&view=diff
==============================================================================
--- jackrabbit/oak/trunk/oak-blob-cloud/pom.xml (original)
+++ jackrabbit/oak/trunk/oak-blob-cloud/pom.xml Wed Sep 14 09:03:33 2016
@@ -41,7 +41,7 @@
                 <artifactId>maven-bundle-plugin</artifactId>
                 <configuration>
                     <instructions>
-                        
<Export-Package>org.apache.jackrabbit.oak.blob.cloud.aws.s3</Export-Package>
+                        
<Export-Package>org.apache.jackrabbit.oak.blob.cloud.aws.s3,org.apache.jackrabbit.oak.blob.cloud.aws.s3.stats</Export-Package>
                         <DynamicImport-Package>sun.io</DynamicImport-Package>
                     </instructions>
                 </configuration>
@@ -101,6 +101,13 @@
             <version>${jackrabbit.version}</version>
         </dependency>
 
+        <!-- Dependencies to other Oak components -->
+        <dependency>
+            <groupId>org.apache.jackrabbit</groupId>
+            <artifactId>oak-commons</artifactId>
+            <version>${project.version}</version>
+        </dependency>
+
         <!-- Amazon AWS dependency -->
         <dependency>
             <groupId>com.amazonaws</groupId>

Modified: 
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/S3DataStore.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/S3DataStore.java?rev=1760661&r1=1760660&r2=1760661&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/S3DataStore.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/S3DataStore.java
 Wed Sep 14 09:03:33 2016
@@ -17,14 +17,26 @@
 package org.apache.jackrabbit.oak.blob.cloud.aws.s3;
 
 import java.util.Properties;
+
+import com.google.common.base.Strings;
 import org.apache.jackrabbit.core.data.Backend;
 import org.apache.jackrabbit.core.data.CachingDataStore;
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import org.apache.jackrabbit.core.data.DataStoreException;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
 
 
 /**
  * An Amazon S3 data store.
  */
 public class S3DataStore extends CachingDataStore {
+
+    /**
+     * Logger instance.
+     */
+    private static final Logger LOG = 
LoggerFactory.getLogger(S3DataStore.class);
+
     protected Properties properties;
 
     @Override
@@ -47,4 +59,24 @@ public class S3DataStore extends Caching
     public void setProperties(Properties properties) {
         this.properties = properties;
     }
+
+    /**
+     * Look in the backend for a record matching the given identifier.  
Returns true
+     * if such a record exists.
+     *
+     * @param identifier - An identifier for the record.
+     * @return true if a record for the provided identifier can be found.
+     */
+    public boolean haveRecordForIdentifier(final String identifier) {
+        try {
+            if (!Strings.isNullOrEmpty(identifier)) {
+                return this.getBackend().exists(new 
DataIdentifier(identifier));
+            }
+        }
+        catch (DataStoreException e) {
+            LOG.warn(String.format("Data Store Exception caught checking for 
%s in pending uploads",
+                identifier), e);
+        }
+        return false;
+    }
 }

Added: 
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/stats/S3DataStoreStatsMBean.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/stats/S3DataStoreStatsMBean.java?rev=1760661&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/stats/S3DataStoreStatsMBean.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/stats/S3DataStoreStatsMBean.java
 Wed Sep 14 09:03:33 2016
@@ -0,0 +1,44 @@
+/*
+ * 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.oak.blob.cloud.aws.s3.stats;
+
+/**
+ * MBean for JMX statistics pertaining to an S3DataStore.
+ */
+public interface S3DataStoreStatsMBean {
+    String TYPE = "S3DataStoreStats";
+
+    /**
+     * Obtains the number of records that are in the process
+     * of being "synced", meaning they are either scheduled to
+     * be copied to S3 or are actively being copied to S3
+     * but the copy of these files has not yet completed.
+     *
+     * @return number of syncs in progress (active).
+     */
+    long getActiveSyncs();
+
+    /**
+     * Determines whether a file-like entity with the given name
+     * has been "synced" (completely copied) to S3.
+     *
+     * @param nodePathName - Path to the entity to check.  This is
+     *                       the repository node path, not an external file 
path.
+     * @return true if the file is synced to S3.
+     */
+    boolean isFileSynced(final String nodePathName);
+}

Propchange: 
jackrabbit/oak/trunk/oak-blob-cloud/src/main/java/org/apache/jackrabbit/oak/blob/cloud/aws/s3/stats/S3DataStoreStatsMBean.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreService.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreService.java?rev=1760661&r1=1760660&r2=1760661&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreService.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreService.java
 Wed Sep 14 09:03:33 2016
@@ -19,18 +19,25 @@
 
 package org.apache.jackrabbit.oak.plugins.blob.datastore;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
 import java.util.Map;
 import java.util.Properties;
 
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.ConfigurationPolicy;
 import org.apache.jackrabbit.core.data.DataStore;
-import org.apache.jackrabbit.oak.blob.cloud.aws.s3.S3DataStore;
+import org.apache.jackrabbit.core.data.DataStoreException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.component.ComponentContext;
 
 @Component(policy = ConfigurationPolicy.REQUIRE, name = 
S3DataStoreService.NAME)
 public class S3DataStoreService extends AbstractDataStoreService{
     public static final String NAME = 
"org.apache.jackrabbit.oak.plugins.blob.datastore.S3DataStore";
+    private static final String DESCRIPTION = "oak.datastore.description";
+
+    private ServiceRegistration delegateReg;
 
     @Override
     protected DataStore createDataStore(ComponentContext context, Map<String, 
Object> config) {
@@ -40,9 +47,26 @@ public class S3DataStoreService extends
         properties.putAll(config);
 
         dataStore.setProperties(properties);
+
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(Constants.SERVICE_PID, dataStore.getClass().getName());
+        props.put(DESCRIPTION, getDescription());
+
+        delegateReg = context.getBundleContext().registerService(new String[] {
+            SharedS3DataStore.class.getName(),
+            SharedS3DataStore.class.getName()
+        }, dataStore , props);
+
         return dataStore;
     }
 
+    protected void deactivate() throws DataStoreException {
+        if (delegateReg != null) {
+            delegateReg.unregister();
+        }
+        super.deactivate();
+    }
+
     @Override
     protected String[] getDescription() {
         return new String[] {"type=S3"};

Added: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStats.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStats.java?rev=1760661&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStats.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStats.java
 Wed Sep 14 09:03:33 2016
@@ -0,0 +1,174 @@
+/*
+ * 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.oak.plugins.blob.datastore;
+
+import com.google.common.base.Strings;
+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.Reference;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.blob.cloud.aws.s3.stats.S3DataStoreStatsMBean;
+import org.apache.jackrabbit.oak.commons.PathUtils;
+import org.apache.jackrabbit.oak.osgi.OsgiWhiteboard;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.whiteboard.Registration;
+import org.apache.jackrabbit.oak.spi.whiteboard.Whiteboard;
+import org.osgi.framework.BundleContext;
+
+import java.util.List;
+
+import static 
org.apache.jackrabbit.oak.spi.whiteboard.WhiteboardUtils.registerMBean;
+
+@Component
+public class S3DataStoreStats implements S3DataStoreStatsMBean {
+
+    private Registration mbeanReg;
+
+    @Reference
+    protected SharedS3DataStore s3ds;
+
+    @Reference
+    protected NodeStore nodeStore;
+
+    @Activate
+    private void activate(BundleContext context){
+        Whiteboard wb = new OsgiWhiteboard(context);
+        mbeanReg = registerMBean(wb,
+            S3DataStoreStatsMBean.class,
+            this,
+            S3DataStoreStatsMBean.TYPE,
+            "S3 DataStore statistics");
+    }
+
+    @Deactivate
+    private void deactivate(){
+        if(mbeanReg != null){
+            mbeanReg.unregister();
+        }
+    }
+
+    /**
+     * Obtains the number of records that are in the process
+     * of being "synced", meaning they are either scheduled to
+     * be copied to S3 or are actively being copied to S3
+     * but the copy of these files has not yet completed.
+     *
+     * @return number of syncs in progress (active).
+     */
+    @Override
+    public long getActiveSyncs() {
+        return s3ds.getPendingUploads().size();
+    }
+
+    /**
+     * Determines whether a file-like entity with the given name
+     * has been "synced" (completely copied) to S3.
+     *
+     * Determination of "synced":
+     * - A nodeName of null or "" is always "not synced".
+     * - A nodeName that does not map to a valid node is always "not synced".
+     * - If the node for this nodeName does not have a binary property,
+     * this node is always "not synced" since such a node would never be
+     * copied to S3.
+     * - If the node for this nodeName is not in the nodeStore, this node is
+     * always "not synced".
+     * - Otherwise, the state is "synced" if the corresponding blob is
+     * completely stored in S3.
+     *
+     * @param nodePathName - Path to the entity to check.  This is
+     *                       a node path, not an external file path.
+     * @return true if the file is synced to S3.
+     */
+    @Override
+    public boolean isFileSynced(final String nodePathName) {
+        if (Strings.isNullOrEmpty(nodePathName)) {
+            return false;
+        }
+
+        if (null == nodeStore) {
+            return false;
+        }
+
+        final NodeState leafNode = findLeafNode(nodePathName);
+        if (!leafNode.exists()) {
+            return false;
+        }
+
+        boolean nodeHasBinaryProperties = false;
+        for (final PropertyState propertyState : leafNode.getProperties()) {
+            nodeHasBinaryProperties |= (propertyState.getType() == Type.BINARY 
|| propertyState.getType() == Type.BINARIES);
+            try {
+                if (propertyState.getType() == Type.BINARY) {
+                    final Blob blob = (Blob) 
propertyState.getValue(propertyState.getType());
+                    if (null == blob || !haveRecordForBlob(blob)) {
+                        return false;
+                    }
+                } else if (propertyState.getType() == Type.BINARIES) {
+                    final List<Blob> blobs = (List<Blob>) 
propertyState.getValue(propertyState.getType());
+                    if (null == blobs) {
+                        return false;
+                    }
+                    for (final Blob blob : blobs) {
+                        if (!haveRecordForBlob(blob)) {
+                            return false;
+                        }
+                    }
+                }
+            }
+            catch (ClassCastException e) {
+                return false;
+            }
+        }
+
+        // If we got here and nodeHasBinaryProperties is true,
+        // it means at least one binary property was found for
+        // the leaf node and that we were able to locate a
+        // records for binaries found.
+        return nodeHasBinaryProperties;
+    }
+
+    private NodeState findLeafNode(final String nodePathName) {
+        final Iterable<String> pathNodes = 
PathUtils.elements(PathUtils.getParentPath(nodePathName));
+        final String leafNodeName = PathUtils.getName(nodePathName);
+
+        NodeState currentNode = nodeStore.getRoot();
+        for (String pathNodeName : pathNodes) {
+            if (pathNodeName.length() > 0) {
+                NodeState childNode = currentNode.getChildNode(pathNodeName);
+                if (!childNode.exists()) {
+                    break;
+                }
+                currentNode = childNode;
+            }
+        }
+        return currentNode.getChildNode(leafNodeName);
+    }
+
+    private boolean haveRecordForBlob(final Blob blob) {
+        final String fullBlobId = blob.getContentIdentity();
+        if (!Strings.isNullOrEmpty(fullBlobId)
+            && !InMemoryDataRecord.isInstance(fullBlobId)) {
+            String blobId = DataStoreBlobStore.BlobId.of(fullBlobId).blobId;
+            return s3ds.haveRecordForIdentifier(blobId);
+        }
+        return false;
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStats.java
------------------------------------------------------------------------------
    svn:eol-style = native

Modified: 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/SharedS3DataStoreService.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/SharedS3DataStoreService.java?rev=1760661&r1=1760660&r2=1760661&view=diff
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/SharedS3DataStoreService.java
 (original)
+++ 
jackrabbit/oak/trunk/oak-core/src/main/java/org/apache/jackrabbit/oak/plugins/blob/datastore/SharedS3DataStoreService.java
 Wed Sep 14 09:03:33 2016
@@ -22,14 +22,22 @@ package org.apache.jackrabbit.oak.plugin
 import org.apache.felix.scr.annotations.Component;
 import org.apache.felix.scr.annotations.ConfigurationPolicy;
 import org.apache.jackrabbit.core.data.DataStore;
+import org.apache.jackrabbit.core.data.DataStoreException;
+import org.osgi.framework.Constants;
+import org.osgi.framework.ServiceRegistration;
 import org.osgi.service.component.ComponentContext;
 
+import java.util.Dictionary;
+import java.util.Hashtable;
 import java.util.Map;
 import java.util.Properties;
 
 @Component(policy = ConfigurationPolicy.REQUIRE, name = 
SharedS3DataStoreService.NAME)
 public class SharedS3DataStoreService extends AbstractDataStoreService{
     public static final String NAME = 
"org.apache.jackrabbit.oak.plugins.blob.datastore.SharedS3DataStore";
+    private static final String DESCRIPTION = "oak.datastore.description";
+
+    private ServiceRegistration delegateReg;
 
     @Override
     protected DataStore createDataStore(ComponentContext context, Map<String, 
Object> config) {
@@ -39,9 +47,26 @@ public class SharedS3DataStoreService ex
         properties.putAll(config);
 
         dataStore.setProperties(properties);
+
+        Dictionary<String, Object> props = new Hashtable<String, Object>();
+        props.put(Constants.SERVICE_PID, dataStore.getClass().getName());
+        props.put(DESCRIPTION, getDescription());
+
+        delegateReg = context.getBundleContext().registerService(new String[] {
+            SharedS3DataStore.class.getName(),
+            SharedS3DataStore.class.getName()
+        }, dataStore , props);
+
         return dataStore;
     }
 
+    protected void deactivate() throws DataStoreException {
+        if (delegateReg != null) {
+            delegateReg.unregister();
+        }
+        super.deactivate();
+    }
+
     @Override
     protected String[] getDescription() {
         return new String[] {"type=SharedS3"};

Added: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStatsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStatsTest.java?rev=1760661&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStatsTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStatsTest.java
 Wed Sep 14 09:03:33 2016
@@ -0,0 +1,438 @@
+/*
+ * 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.oak.plugins.blob.datastore;
+
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.FileOutputStream;
+import java.io.InputStream;
+import java.io.OutputStream;
+import java.security.DigestOutputStream;
+import java.security.MessageDigest;
+import java.util.Iterator;
+import java.util.Map;
+import java.util.Random;
+import java.util.Set;
+
+import com.amazonaws.util.StringInputStream;
+import com.google.common.collect.Lists;
+import com.google.common.collect.Maps;
+import com.google.common.collect.Sets;
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.core.data.AsyncTouchCallback;
+import org.apache.jackrabbit.core.data.AsyncTouchResult;
+import org.apache.jackrabbit.core.data.AsyncUploadCallback;
+import org.apache.jackrabbit.core.data.AsyncUploadResult;
+import org.apache.jackrabbit.core.data.Backend;
+import org.apache.jackrabbit.core.data.CachingDataStore;
+import org.apache.jackrabbit.core.data.DataIdentifier;
+import org.apache.jackrabbit.core.data.DataRecord;
+import org.apache.jackrabbit.core.data.DataStoreException;
+import org.apache.jackrabbit.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.PropertyState;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.spi.state.NodeState;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.joda.time.DateTime;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.TemporaryFolder;
+
+import static org.apache.commons.codec.binary.Hex.encodeHexString;
+import static org.apache.commons.io.FileUtils.copyInputStreamToFile;
+import static org.junit.Assert.assertFalse;
+import static org.junit.Assert.assertTrue;
+import static org.mockito.Matchers.anyString;
+import static org.mockito.Mockito.doReturn;
+import static org.mockito.Mockito.mock;
+import static org.mockito.Mockito.when;
+
+public class S3DataStoreStatsTest {
+    @Rule public TemporaryFolder folder = new TemporaryFolder(new 
File("target")) {
+        @Override public void delete() {
+        }
+    };
+
+    private NodeStore mockNodeStore;
+
+    private static String testNodePathName = "test/node/path/name";
+    private File testFile;
+
+    private SharedS3DataStore defaultS3ds;
+    private SharedS3DataStore autoSyncMockS3ds;
+    private SharedS3DataStore manualSyncMockS3ds;
+    private S3DataStoreStats mBean;
+
+    @Before
+    public void setup() throws Exception {
+        testFile = folder.newFile();
+        copyInputStreamToFile(randomStream(0, 16384), testFile);
+        mockNodeStore = mock(NodeStore.class);
+        MessageDigest digest = MessageDigest.getInstance("SHA-1");
+        OutputStream output = new DigestOutputStream(new 
FileOutputStream(testFile), digest);
+        FileInputStream inputStream = null;
+        try {
+            inputStream = new FileInputStream(testFile);
+            IOUtils.copyLarge(inputStream, output);
+        } finally {
+            IOUtils.closeQuietly(output);
+            IOUtils.closeQuietly(inputStream);
+        }
+        String testNodeId = encodeHexString(digest.digest());
+
+        mockNodeStore = mock(NodeStore.class);
+        final NodeState mockRootState = mock(NodeState.class);
+        final NodeState mockLeafState = mock(NodeState.class);
+        final PropertyState mockLeafPropertyState = mock(PropertyState.class);
+        final Blob mockBlob = mock(Blob.class);
+        when(mockNodeStore.getRoot()).thenReturn(mockRootState);
+        
when(mockRootState.getChildNode(anyString())).thenReturn(mockLeafState);
+        
when(mockLeafState.getChildNode(anyString())).thenReturn(mockLeafState);
+        when(mockLeafState.exists()).thenReturn(true);
+        
when(mockLeafState.getProperty(anyString())).thenReturn(mockLeafPropertyState);
+        
doReturn(Lists.newArrayList(mockLeafPropertyState)).when(mockLeafState).getProperties();
+        doReturn(Type.BINARY).when(mockLeafPropertyState).getType();
+        when(mockLeafPropertyState.getValue(Type.BINARY)).thenReturn(mockBlob);
+        when(mockBlob.getContentIdentity()).thenReturn(testNodeId);
+
+        defaultS3ds = mock(SharedS3DataStore.class);
+        defaultS3ds.init(folder.newFolder().getAbsolutePath());
+        autoSyncMockS3ds = new CustomBackendS3DataStore(new 
TestMemoryBackend());
+        autoSyncMockS3ds.init(folder.newFolder().getAbsolutePath());
+        manualSyncMockS3ds = new CustomBackendS3DataStore(new 
ManuallySyncingInMemoryBackend());
+        manualSyncMockS3ds.init(folder.newFolder().getAbsolutePath());
+    }
+
+    @After
+    public void teardown() throws Exception {
+    }
+
+    @Test public void testGetActiveS3FileSyncMetricExists()
+        throws Exception {
+
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = defaultS3ds;
+
+        assertTrue(0 == mBean.getActiveSyncs());
+    }
+
+    @Test
+    public void testGetSingleActiveS3FileSyncMetric()
+        throws Exception {
+
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = manualSyncMockS3ds;
+
+        DataRecord record = null;
+        try {
+            record = manualSyncMockS3ds.addRecord(new 
StringInputStream("test"));
+            assertTrue(1 == mBean.getActiveSyncs());
+        } finally {
+            if (null != record) {
+                manualSyncMockS3ds.deleteRecord(record.getIdentifier());
+            }
+        }
+
+        ((ManuallySyncingInMemoryBackend) 
manualSyncMockS3ds.getBackend()).clearInProgressWrites();
+
+        assertTrue(0 == mBean.getActiveSyncs());
+    }
+
+    @Test
+    public void testGetMultilpleActiveS3FileSyncMetric()
+        throws Exception {
+
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = manualSyncMockS3ds;
+
+        final Set<DataRecord> records = Sets.newHashSet();
+        try {
+            records.add(manualSyncMockS3ds.addRecord(new 
StringInputStream("test1")));
+            records.add(manualSyncMockS3ds.addRecord(new 
StringInputStream("test2")));
+            records.add(manualSyncMockS3ds.addRecord(new 
StringInputStream("test3")));
+
+            assertTrue(3 == mBean.getActiveSyncs());
+        } finally {
+            for (final DataRecord record : records) {
+                manualSyncMockS3ds.deleteRecord(record.getIdentifier());
+            }
+        }
+
+        ((ManuallySyncingInMemoryBackend) 
manualSyncMockS3ds.getBackend()).clearInProgressWrites();
+
+        assertTrue(0 == mBean.getActiveSyncs());
+    }
+
+    @Test
+    public void testIsFileSyncedMetricExists()
+        throws Exception {
+
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = defaultS3ds;
+
+        assertFalse(mBean.isFileSynced(testNodePathName));
+    }
+
+    @Test
+    public void testIsFileSyncedNullFileReturnsFalse()
+        throws Exception {
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = defaultS3ds;
+
+        assertFalse(mBean.isFileSynced(null));
+    }
+
+    @Test
+    public void testIsFileSyncedEmptyStringReturnsFalse()
+        throws Exception {
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = defaultS3ds;
+
+        assertFalse(mBean.isFileSynced(""));
+    }
+
+    @Test
+    public void testIsFileSyncedInvalidFilenameReturnsFalse()
+        throws Exception {
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = defaultS3ds;
+
+        assertFalse(mBean.isFileSynced("invalid"));
+    }
+
+    @Test
+    public void testIsFileSyncedFileNotAddedReturnsFalse()
+        throws Exception {
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = autoSyncMockS3ds;
+
+        assertFalse(mBean.isFileSynced(testNodePathName));
+    }
+
+    @Test
+    public void testIsFileSyncedSyncIncompleteReturnsFalse()
+        throws Exception {
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = manualSyncMockS3ds;
+
+        DataRecord record = null;
+        FileInputStream stream = null;
+        try {
+            stream = new FileInputStream(testFile);
+            record = manualSyncMockS3ds.addRecord(stream);
+            assertFalse(mBean.isFileSynced(testNodePathName));
+        } finally {
+            IOUtils.closeQuietly(stream);
+            if (null != record) {
+                manualSyncMockS3ds.deleteRecord(record.getIdentifier());
+            }
+        }
+    }
+
+    @Test
+    public void testIsFileSyncedSyncCompleteReturnsTrue()
+        throws Exception {
+
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = autoSyncMockS3ds;
+
+        DataRecord record = null;
+        FileInputStream stream = null;
+        try {
+            stream = new FileInputStream(testFile);
+            record = autoSyncMockS3ds.addRecord(stream);
+            assertTrue(mBean.isFileSynced(testNodePathName));
+        } finally {
+            IOUtils.closeQuietly(stream);
+            if (null != record) {
+                autoSyncMockS3ds.deleteRecord(record.getIdentifier());
+            }
+        }
+    }
+
+    @Test
+    public void testIsFileSyncedFileDeletedReturnsFalse()
+        throws Exception {
+        mBean = new S3DataStoreStats();
+        mBean.nodeStore = mockNodeStore;
+        mBean.s3ds = autoSyncMockS3ds;
+
+        DataRecord record = null;
+        FileInputStream stream = null;
+        try {
+            stream = new FileInputStream(testFile);
+            record = autoSyncMockS3ds.addRecord(stream);
+        } finally {
+            IOUtils.closeQuietly(stream);
+            if (null != record) {
+                autoSyncMockS3ds.deleteRecord(record.getIdentifier());
+            }
+        }
+
+        assertFalse(mBean.isFileSynced(testNodePathName));
+    }
+
+
+    // A mock S3DataStore that allows us to replace the default
+    // S3Backend with our own backend, for test purposes only.
+    private class CustomBackendS3DataStore extends SharedS3DataStore {
+        private Backend _localBackend;
+
+        CustomBackendS3DataStore(final Backend backend) {
+            _localBackend = backend;
+        }
+
+        @Override protected Backend createBackend() {
+            return _localBackend;
+        }
+    }
+
+
+    // A mock Backend implementation that uses a Map to keep track of what
+    // records have been added and removed, for test purposes only.
+    private class TestMemoryBackend implements Backend {
+        final Map<DataIdentifier, File> _backend = Maps.newHashMap();
+
+        @Override public void init(CachingDataStore store, String homeDir, 
String config)
+            throws DataStoreException {
+
+        }
+
+        @Override public InputStream read(DataIdentifier identifier) throws 
DataStoreException {
+            try {
+                return new FileInputStream(_backend.get(identifier));
+            } catch (FileNotFoundException e) {
+                throw new DataStoreException(e);
+            }
+        }
+
+        @Override public long getLength(DataIdentifier identifier) throws 
DataStoreException {
+            return _backend.get(identifier).length();
+        }
+
+        @Override public long getLastModified(DataIdentifier identifier) 
throws DataStoreException {
+            return _backend.get(identifier).lastModified();
+        }
+
+        @Override public void write(DataIdentifier identifier, File file)
+            throws DataStoreException {
+            _backend.put(identifier, file);
+        }
+
+        @Override public void writeAsync(final DataIdentifier identifier, 
final File file,
+            AsyncUploadCallback callback) throws DataStoreException {
+            write(identifier, file);
+            callback.onSuccess(new AsyncUploadResult(identifier, file));
+        }
+
+        @Override public Iterator<DataIdentifier> getAllIdentifiers() throws 
DataStoreException {
+            return _backend.keySet().iterator();
+        }
+
+        @Override public boolean exists(DataIdentifier identifier, boolean 
touch)
+            throws DataStoreException {
+            if (_backend.containsKey(identifier) && touch) {
+                touch(identifier, new DateTime().getMillis());
+            }
+            return exists(identifier);
+        }
+
+        @Override public boolean exists(DataIdentifier identifier) throws 
DataStoreException {
+            return _backend.containsKey(identifier);
+        }
+
+        @Override public void touch(DataIdentifier identifier, long 
minModifiedDate)
+            throws DataStoreException {
+
+        }
+
+        @Override public void touchAsync(DataIdentifier identifier, long 
minModifiedDate,
+            AsyncTouchCallback callback) throws DataStoreException {
+            callback.onSuccess(new AsyncTouchResult(identifier));
+        }
+
+        @Override public void close() throws DataStoreException {
+
+        }
+
+        @Override public Set<DataIdentifier> deleteAllOlderThan(long timestamp)
+            throws DataStoreException {
+            final Set<DataIdentifier> toDelete = Sets.newHashSet();
+            for (final DataIdentifier identifier : _backend.keySet()) {
+                if (_backend.get(identifier).lastModified() < timestamp) {
+                    toDelete.add(identifier);
+                }
+            }
+            for (final DataIdentifier identifier : toDelete) {
+                _backend.remove(identifier);
+            }
+            return toDelete;
+        }
+
+        @Override public void deleteRecord(DataIdentifier identifier) throws 
DataStoreException {
+            if (_backend.containsKey(identifier)) {
+                _backend.remove(identifier);
+            }
+        }
+    }
+
+
+    // A modified InMemoryBackend that, when writeAsync() is called, does not
+    // actually store the record but keeps track that it was intended to be
+    // stored, and allows the test to tell it when it expects the record
+    // to be "synced".
+    private class ManuallySyncingInMemoryBackend extends TestMemoryBackend {
+        final Map<DataIdentifier, File> inProgessWrites = Maps.newHashMap();
+        final Map<DataIdentifier, AsyncUploadCallback> asyncCallbacks = 
Maps.newHashMap();
+
+        @Override public void writeAsync(final DataIdentifier identifier, 
final File file,
+            AsyncUploadCallback callback) throws DataStoreException {
+            inProgessWrites.put(identifier, file);
+            asyncCallbacks.put(identifier, callback);
+        }
+
+        void clearInProgressWrites() throws DataStoreException {
+            for (final DataIdentifier identifier : inProgessWrites.keySet()) {
+                final File file = inProgessWrites.get(identifier);
+                asyncCallbacks.get(identifier).onSuccess(new 
AsyncUploadResult(identifier, file));
+                write(identifier, file);
+            }
+            inProgessWrites.clear();
+        }
+    }
+
+    private static InputStream randomStream(int seed, int size) {
+        Random r = new Random(seed);
+        byte[] data = new byte[size];
+        r.nextBytes(data);
+        return new ByteArrayInputStream(data);
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/blob/datastore/S3DataStoreStatsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentS3DataStoreStatsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentS3DataStoreStatsTest.java?rev=1760661&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentS3DataStoreStatsTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentS3DataStoreStatsTest.java
 Wed Sep 14 09:03:33 2016
@@ -0,0 +1,134 @@
+/*
+ * 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.oak.plugins.document;
+
+import java.util.Map;
+
+import org.apache.jackrabbit.oak.blob.cloud.aws.s3.stats.S3DataStoreStatsMBean;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.S3DataStoreStats;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedS3DataStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.sling.testing.mock.osgi.ReferenceViolationException;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Assume;
+import org.junit.Before;
+import org.junit.BeforeClass;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.osgi.framework.ServiceRegistration;
+
+import static com.google.common.collect.Maps.newHashMap;
+import static org.apache.sling.testing.mock.osgi.MockOsgi.deactivate;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Tests the registration of the S3DataStoreStatsMbean.
+ */
+public class DocumentS3DataStoreStatsTest {
+
+    @Rule
+    public OsgiContext context = new OsgiContext();
+
+    @Rule
+    public ExpectedException expectedEx = ExpectedException.none();
+
+    @BeforeClass
+    public static void checkMongoDbAvailable() {
+        Assume.assumeTrue(MongoUtils.isAvailable());
+    }
+
+    @Before
+    public void setUp() {
+        context.registerService(StatisticsProvider.class, 
StatisticsProvider.NOOP);
+    }
+
+    @Test
+    public void testUseS3BlobStore() {
+        ServiceRegistration delegateReg =
+            
context.bundleContext().registerService(SharedS3DataStore.class.getName(),
+                mock(SharedS3DataStore.class), null);
+        assertNotNull(context.getService(SharedS3DataStore.class));
+        registerBlobStore();
+
+        registerDocumentNodeStoreService(true);
+        assertServiceActivated();
+
+        S3DataStoreStats s3DataStoreStats =
+            context.registerInjectActivateService(new S3DataStoreStats(), 
null);
+        assertNotNull(context.getService(S3DataStoreStatsMBean.class));
+
+        deactivate(s3DataStoreStats);
+        unregisterDocumentNodeStoreService();
+        unregisterBlobStore();
+        delegateReg.unregister();
+    }
+
+    @Test
+    public void testNoS3BlobStore() {
+        expectedEx.expect(ReferenceViolationException.class);
+
+        registerBlobStore();
+
+        registerDocumentNodeStoreService(true);
+        assertServiceActivated();
+
+        S3DataStoreStats s3DataStoreStats =
+            context.registerInjectActivateService(new S3DataStoreStats(), 
null);
+        assertNull(context.getService(S3DataStoreStatsMBean.class));
+
+        unregisterDocumentNodeStoreService();
+        unregisterBlobStore();
+    }
+
+    private DocumentNodeStoreService documentNodeStoreService;
+
+    private void registerDocumentNodeStoreService(boolean customBlobStore) {
+        Map<String, Object> properties = newHashMap();
+
+        properties.put("mongouri", MongoUtils.URL);
+        properties.put("db", MongoUtils.DB);
+        properties.put(DocumentNodeStoreService.CUSTOM_BLOB_STORE, 
customBlobStore);
+        documentNodeStoreService =
+            context.registerInjectActivateService(new 
DocumentNodeStoreService(), properties);
+    }
+
+    private void unregisterDocumentNodeStoreService() {
+        deactivate(documentNodeStoreService);
+    }
+
+    private ServiceRegistration blobStore;
+
+    private void registerBlobStore() {
+        blobStore = context.bundleContext()
+            .registerService(BlobStore.class.getName(), mock(BlobStore.class), 
null);
+    }
+
+    private void unregisterBlobStore() {
+        blobStore.unregister();
+    }
+
+    private void assertServiceActivated() {
+        assertNotNull(context.getService(NodeStore.class));
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-core/src/test/java/org/apache/jackrabbit/oak/plugins/document/DocumentS3DataStoreStatsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: 
jackrabbit/oak/trunk/oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentS3DataStoreStatsTest.java
URL: 
http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentS3DataStoreStatsTest.java?rev=1760661&view=auto
==============================================================================
--- 
jackrabbit/oak/trunk/oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentS3DataStoreStatsTest.java
 (added)
+++ 
jackrabbit/oak/trunk/oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentS3DataStoreStatsTest.java
 Wed Sep 14 09:03:33 2016
@@ -0,0 +1,131 @@
+/*
+ * 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.oak.plugins.segment;
+
+import java.io.File;
+import java.util.Map;
+
+import org.apache.jackrabbit.oak.blob.cloud.aws.s3.stats.S3DataStoreStatsMBean;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.S3DataStoreStats;
+import org.apache.jackrabbit.oak.plugins.blob.datastore.SharedS3DataStore;
+import org.apache.jackrabbit.oak.spi.blob.BlobStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.stats.StatisticsProvider;
+import org.apache.sling.testing.mock.osgi.ReferenceViolationException;
+import org.apache.sling.testing.mock.osgi.junit.OsgiContext;
+import org.junit.Before;
+import org.junit.Rule;
+import org.junit.Test;
+import org.junit.rules.ExpectedException;
+import org.junit.rules.TemporaryFolder;
+import org.osgi.framework.ServiceRegistration;
+
+import static com.google.common.collect.Maps.newHashMap;
+import static org.apache.sling.testing.mock.osgi.MockOsgi.deactivate;
+import static org.junit.Assert.assertNotNull;
+import static org.junit.Assert.assertNull;
+import static org.mockito.Mockito.mock;
+
+/**
+ * Tests the registration of the S3DataStoreStatsMbean.
+ */
+public class SegmentS3DataStoreStatsTest {
+
+    @Rule
+    public OsgiContext context = new OsgiContext();
+
+    @Rule
+    public TemporaryFolder folder = new TemporaryFolder(new File("target"));
+
+    @Rule
+    public ExpectedException expectedEx = ExpectedException.none();
+
+    @Before
+    public void setUp() {
+        context.registerService(StatisticsProvider.class, 
StatisticsProvider.NOOP);
+    }
+
+    @Test
+    public void testUseS3BlobStore() {
+        ServiceRegistration delegateReg =
+            
context.bundleContext().registerService(SharedS3DataStore.class.getName(),
+                mock(SharedS3DataStore.class), null);
+        assertNotNull(context.getService(SharedS3DataStore.class));
+        registerBlobStore();
+
+        registerSegmentNodeStoreService(true);
+        assertServiceActivated();
+
+        S3DataStoreStats s3DataStoreStats =
+            context.registerInjectActivateService(new S3DataStoreStats(), 
null);
+        assertNotNull(context.getService(S3DataStoreStatsMBean.class));
+
+        deactivate(s3DataStoreStats);
+        unregisterSegmentNodeStoreService();
+        unregisterBlobStore();
+        delegateReg.unregister();
+    }
+
+    @Test
+    public void testNoS3BlobStore() {
+        expectedEx.expect(ReferenceViolationException.class);
+
+        registerBlobStore();
+
+        registerSegmentNodeStoreService(true);
+        assertServiceActivated();
+
+        S3DataStoreStats s3DataStoreStats =
+            context.registerInjectActivateService(new S3DataStoreStats(), 
null);
+        assertNull(context.getService(S3DataStoreStatsMBean.class));
+
+        unregisterSegmentNodeStoreService();
+        unregisterBlobStore();
+    }
+
+    private SegmentNodeStoreService segmentNodeStoreService;
+
+    private void registerSegmentNodeStoreService(boolean customBlobStore) {
+        Map<String, Object> properties = newHashMap();
+
+        properties.put(SegmentNodeStoreService.CUSTOM_BLOB_STORE, 
customBlobStore);
+        properties.put(SegmentNodeStoreService.DIRECTORY, 
folder.getRoot().getAbsolutePath());
+
+        segmentNodeStoreService = context.registerInjectActivateService(new 
SegmentNodeStoreService(), properties);
+    }
+
+    private void unregisterSegmentNodeStoreService() {
+        deactivate(segmentNodeStoreService);
+    }
+
+    private ServiceRegistration blobStore;
+
+    private void registerBlobStore() {
+        blobStore = 
context.bundleContext().registerService(BlobStore.class.getName(), 
mock(BlobStore.class), null);
+    }
+
+    private void unregisterBlobStore() {
+        blobStore.unregister();
+    }
+
+    private void assertServiceActivated() {
+        assertNotNull(context.getService(NodeStore.class));
+        assertNotNull(context.getService(SegmentStoreProvider.class));
+    }
+}

Propchange: 
jackrabbit/oak/trunk/oak-segment/src/test/java/org/apache/jackrabbit/oak/plugins/segment/SegmentS3DataStoreStatsTest.java
------------------------------------------------------------------------------
    svn:eol-style = native


Reply via email to