| /* |
| * 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.IOException; |
| import java.io.InputStream; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Random; |
| import java.util.Set; |
| import java.util.UUID; |
| |
| import com.google.common.base.Function; |
| import com.google.common.collect.ImmutableList; |
| import com.google.common.collect.Iterables; |
| import com.google.common.collect.Lists; |
| import com.google.common.collect.Sets; |
| import org.apache.commons.io.FileUtils; |
| import org.apache.commons.io.IOUtils; |
| import org.apache.jackrabbit.core.data.DataIdentifier; |
| import org.apache.jackrabbit.core.data.DataRecord; |
| import org.apache.jackrabbit.core.data.DataStore; |
| import org.apache.jackrabbit.core.data.DataStoreException; |
| import org.apache.jackrabbit.oak.plugins.blob.BlobStoreBlob; |
| import org.apache.jackrabbit.oak.spi.blob.AbstractBlobStoreTest; |
| import org.apache.jackrabbit.oak.spi.blob.BlobStoreInputStream; |
| import org.apache.jackrabbit.oak.spi.blob.stats.BlobStatsCollector; |
| import org.jetbrains.annotations.Nullable; |
| import org.junit.After; |
| import org.junit.Before; |
| import org.junit.Test; |
| |
| import static org.apache.jackrabbit.oak.plugins.blob.datastore.DataStoreBlobStore.BlobId; |
| import static org.junit.Assert.assertEquals; |
| import static org.junit.Assert.assertFalse; |
| import static org.junit.Assert.assertNull; |
| import static org.junit.Assert.assertTrue; |
| import static org.mockito.Matchers.any; |
| import static org.mockito.Mockito.doThrow; |
| import static org.mockito.Mockito.mock; |
| import static org.mockito.Mockito.when; |
| |
| public class DataStoreBlobStoreTest extends AbstractBlobStoreTest { |
| @Before |
| @Override |
| public void setUp() throws Exception { |
| store = DataStoreUtils.getBlobStore(); |
| } |
| |
| @Override |
| protected void setupCollector(BlobStatsCollector statsCollector) { |
| if (store instanceof DataStoreBlobStore){ |
| ((DataStoreBlobStore) store).setBlobStatsCollector(statsCollector); |
| } |
| } |
| |
| @Override |
| protected boolean supportsStatsCollection() { |
| return true; |
| } |
| |
| @Test |
| public void testInlineBinary() throws DataStoreException, IOException { |
| int maxInlineSize = 300; |
| |
| DataStore mockedDS = mock(DataStore.class); |
| when(mockedDS.getMinRecordLength()).thenReturn(maxInlineSize); |
| DataStoreBlobStore ds = new DataStoreBlobStore(mockedDS); |
| |
| byte[] data = new byte[maxInlineSize]; |
| new Random().nextBytes(data); |
| |
| DataRecord dr = ds.addRecord(new ByteArrayInputStream(data)); |
| assertTrue(InMemoryDataRecord.isInstance(dr.getIdentifier().toString())); |
| assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(data), dr.getStream())); |
| assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(data), |
| new BlobStoreInputStream(ds, dr.getIdentifier().toString(), 0))); |
| |
| assertEquals(dr, ds.getRecordIfStored(dr.getIdentifier())); |
| assertEquals(dr, ds.getRecord(dr.getIdentifier())); |
| |
| //Check for BlobStore methods |
| assertEquals(maxInlineSize, ds.getBlobLength(dr.getIdentifier().toString())); |
| assertEquals(dr.getIdentifier().toString(), BlobId.of(ds.writeBlob(new ByteArrayInputStream(data))).blobId); |
| |
| BlobStoreBlob blob = new BlobStoreBlob(ds, dr.getIdentifier().toString()); |
| assertTrue(blob.isInlined()); |
| } |
| |
| @Test |
| public void testExternalBinary() throws DataStoreException, IOException { |
| int maxInlineSize = 300; |
| int actualSize = maxInlineSize + 10; |
| |
| byte[] data = new byte[actualSize]; |
| new Random().nextBytes(data); |
| |
| DataIdentifier testDI = new DataIdentifier("test"); |
| DataRecord testDR = new ByteArrayDataRecord(data, testDI, "testReference"); |
| |
| DataStore mockedDS = mock(DataStore.class); |
| when(mockedDS.getMinRecordLength()).thenReturn(maxInlineSize); |
| when(mockedDS.getRecord(testDI)).thenReturn(testDR); |
| when(mockedDS.getRecordIfStored(testDI)).thenReturn(testDR); |
| when(mockedDS.addRecord(any(InputStream.class))).thenReturn(testDR); |
| DataStoreBlobStore ds = new DataStoreBlobStore(mockedDS); |
| |
| |
| DataRecord dr = ds.addRecord(new ByteArrayInputStream(data)); |
| assertFalse(InMemoryDataRecord.isInstance(dr.getIdentifier().toString())); |
| assertEquals(testDI, dr.getIdentifier()); |
| assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(data), dr.getStream())); |
| assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(data), |
| new BlobStoreInputStream(ds, dr.getIdentifier().toString(), 0))); |
| |
| assertEquals(dr, ds.getRecordIfStored(dr.getIdentifier())); |
| assertEquals(dr, ds.getRecord(dr.getIdentifier())); |
| |
| // assertTrue(ds.getInputStream(dr.getIdentifier().toString()) instanceof BufferedInputStream); |
| assertEquals(actualSize, ds.getBlobLength(dr.getIdentifier().toString())); |
| assertEquals(testDI.toString(), BlobId.of(ds.writeBlob(new ByteArrayInputStream(data))).blobId); |
| |
| BlobStoreBlob blob = new BlobStoreBlob(ds, dr.getIdentifier().toString()); |
| assertFalse(blob.isInlined()); |
| } |
| |
| @Test |
| public void testReference() throws DataStoreException, IOException { |
| String reference = "testReference"; |
| String blobId = "test"; |
| DataIdentifier testDI = new DataIdentifier(blobId); |
| DataRecord testDR = new ByteArrayDataRecord("foo".getBytes(), testDI, reference); |
| |
| DataStore mockedDS = mock(DataStore.class); |
| when(mockedDS.getRecordFromReference(reference)).thenReturn(testDR); |
| when(mockedDS.getRecord(testDI)).thenReturn(testDR); |
| when(mockedDS.getRecordIfStored(testDI)).thenReturn(testDR); |
| DataStoreBlobStore ds = new DataStoreBlobStore(mockedDS); |
| |
| assertEquals(reference,ds.getReference(blobId)); |
| assertEquals(blobId, BlobId.of(ds.getBlobId(reference)).blobId); |
| assertEquals(BlobId.of(testDR).encodedValue(),ds.getBlobId(reference)); |
| |
| String inMemBlobId = InMemoryDataRecord.getInstance("foo".getBytes()) |
| .getIdentifier().toString(); |
| |
| //For in memory record the reference should be null |
| assertNull(ds.getReference(inMemBlobId)); |
| } |
| |
| @Test |
| public void testGetAllChunks() throws Exception{ |
| DataIdentifier d10 = new DataIdentifier("d-10"); |
| DataIdentifier d20 = new DataIdentifier("d-20"); |
| DataIdentifier d30 = new DataIdentifier("d-30"); |
| List<DataIdentifier> dis = ImmutableList.of(d10, d20, d30); |
| List<DataRecord> recs = Lists.newArrayList( |
| Iterables.transform(dis, new Function<DataIdentifier, DataRecord>() { |
| @Nullable |
| @Override |
| public DataRecord apply(@Nullable DataIdentifier input) { |
| return new TimeDataRecord(input); |
| } |
| })); |
| OakFileDataStore mockedDS = mock(OakFileDataStore.class); |
| when(mockedDS.getAllRecords()).thenReturn(recs.iterator()); |
| when(mockedDS.getRecord(new DataIdentifier("d-10"))).thenReturn(new TimeDataRecord(d10)); |
| when(mockedDS.getRecord(new DataIdentifier("d-20"))).thenReturn(new TimeDataRecord(d20)); |
| when(mockedDS.getRecord(new DataIdentifier("d-30"))).thenReturn(new TimeDataRecord(d30)); |
| DataStoreBlobStore ds = new DataStoreBlobStore(mockedDS); |
| |
| Iterator<String> chunks = ds.getAllChunkIds(25); |
| Set<String> expected = Sets.newHashSet("d-10","d-20"); |
| assertEquals(expected, Sets.newHashSet(chunks)); |
| } |
| |
| @Test |
| public void testEncodedBlobId() throws Exception{ |
| BlobId blobId = new BlobId("abc"+BlobId.SEP+"123"); |
| assertEquals("abc", blobId.blobId); |
| assertEquals(123, blobId.length); |
| |
| blobId = new BlobId("abc"+BlobId.SEP+"abc"+BlobId.SEP+"123"); |
| assertEquals("abc"+BlobId.SEP+"abc", blobId.blobId); |
| assertEquals(123, blobId.length); |
| |
| blobId = new BlobId("abc",123); |
| assertEquals("abc"+BlobId.SEP+"123", blobId.encodedValue()); |
| |
| assertTrue(BlobId.isEncoded("abc"+BlobId.SEP+"123")); |
| assertFalse(BlobId.isEncoded("abc")); |
| } |
| |
| @Test |
| public void testAddOnTrackError() throws Exception { |
| int maxInlineSize = 300; |
| byte[] data = new byte[maxInlineSize]; |
| new Random().nextBytes(data); |
| |
| DataStore mockedDS = mock(DataStore.class); |
| when(mockedDS.getMinRecordLength()).thenReturn(maxInlineSize); |
| DataStoreBlobStore ds = new DataStoreBlobStore(mockedDS); |
| |
| BlobIdTracker mockedTracker = mock(BlobIdTracker.class); |
| doThrow(new IOException("Mocking tracking error")).when(mockedTracker).add(any(String.class)); |
| ds.addTracker(mockedTracker); |
| |
| String id = ds.writeBlob(new ByteArrayInputStream(data)); |
| assertTrue(IOUtils.contentEquals(new ByteArrayInputStream(data), ds.getInputStream(id))); |
| } |
| |
| @Test |
| public void testAddRepositoryId() throws DataStoreException { |
| String repoId = UUID.randomUUID().toString(); |
| ((DataStoreBlobStore) store).setRepositoryId(repoId); |
| assertEquals(repoId, ((DataStoreBlobStore) store).getRepositoryId()); |
| DataRecord metadataRecord = ((DataStoreBlobStore) store) |
| .getMetadataRecord(SharedDataStoreUtils.SharedStoreRecordType.REPOSITORY.getNameFromId(repoId)); |
| |
| assertEquals(repoId, SharedDataStoreUtils.SharedStoreRecordType.REPOSITORY |
| .getIdFromName(metadataRecord.getIdentifier().toString())); |
| } |
| |
| @Override |
| @Test |
| public void testCombinedIdentifier() throws Exception { |
| } |
| |
| @Override |
| public void testEmptyIdentifier() throws Exception { |
| } |
| |
| @Override |
| @Test |
| public void testGarbageCollection() throws Exception { |
| } |
| |
| @After |
| @Override |
| public void tearDown() throws Exception { |
| FileUtils.deleteDirectory(new File(DataStoreUtils.getHomeDir())); |
| super.tearDown(); |
| } |
| |
| private static class ByteArrayDataRecord implements DataRecord { |
| private final byte[] data; |
| private final DataIdentifier identifier; |
| private final String reference; |
| |
| private ByteArrayDataRecord(byte[] data, DataIdentifier di, String reference) { |
| this.data = data; |
| this.identifier = di; |
| this.reference = reference; |
| } |
| |
| @Override |
| public DataIdentifier getIdentifier() { |
| return identifier; |
| } |
| |
| @Override |
| public String getReference() { |
| return reference; |
| } |
| |
| @Override |
| public long getLength() throws DataStoreException { |
| return data.length; |
| } |
| |
| @Override |
| public InputStream getStream() throws DataStoreException { |
| return new ByteArrayInputStream(data); |
| } |
| |
| @Override |
| public long getLastModified() { |
| return 0; |
| } |
| } |
| |
| private static class TimeDataRecord implements DataRecord { |
| private final DataIdentifier id; |
| |
| private TimeDataRecord(DataIdentifier id) { |
| this.id = id; |
| } |
| |
| @Override |
| public DataIdentifier getIdentifier() { |
| return id; |
| } |
| |
| @Override |
| public String getReference() { |
| return id.toString(); |
| } |
| |
| @Override |
| public long getLength() throws DataStoreException { |
| throw new DataStoreException(new UnsupportedOperationException()); |
| } |
| |
| @Override |
| public InputStream getStream() throws DataStoreException { |
| throw new UnsupportedOperationException(); |
| } |
| |
| @Override |
| public long getLastModified() { |
| return Long.valueOf(id.toString().substring(id.toString().indexOf('-')+1)); |
| } |
| } |
| } |