| /* |
| * 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.index.lucene.directory; |
| |
| import java.io.File; |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.Collections; |
| import java.util.Iterator; |
| import java.util.List; |
| import java.util.Set; |
| import java.util.concurrent.ExecutorService; |
| |
| import com.google.common.collect.Iterators; |
| import com.google.common.util.concurrent.MoreExecutors; |
| import org.apache.jackrabbit.JcrConstants; |
| 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.CommitFailedException; |
| import org.apache.jackrabbit.oak.api.Tree; |
| import org.apache.jackrabbit.oak.api.Type; |
| import org.apache.jackrabbit.oak.commons.concurrent.ExecutorCloser; |
| import org.apache.jackrabbit.oak.plugins.blob.BlobTrackingStore; |
| import org.apache.jackrabbit.oak.plugins.blob.datastore.BlobTracker; |
| import org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate; |
| import org.apache.jackrabbit.oak.plugins.index.lucene.IndexCopier; |
| import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexConstants; |
| import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexEditorProvider; |
| import org.apache.jackrabbit.oak.plugins.index.lucene.LuceneIndexProvider; |
| import org.apache.jackrabbit.oak.plugins.index.search.FulltextIndexConstants; |
| import org.apache.jackrabbit.oak.plugins.index.search.IndexDefinition; |
| import org.apache.jackrabbit.oak.plugins.memory.PropertyStates; |
| import org.apache.jackrabbit.oak.query.AbstractQueryTest; |
| import org.apache.jackrabbit.oak.spi.blob.BlobOptions; |
| import org.apache.jackrabbit.oak.spi.blob.GarbageCollectableBlobStore; |
| import org.apache.jackrabbit.oak.spi.state.NodeStore; |
| import org.apache.jackrabbit.oak.stats.Clock; |
| import org.jetbrains.annotations.NotNull; |
| import org.jetbrains.annotations.Nullable; |
| import org.junit.After; |
| import org.junit.Rule; |
| import org.junit.rules.TemporaryFolder; |
| |
| import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ASYNC_PROPERTY_NAME; |
| import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME; |
| import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NODE_TYPE; |
| import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.REINDEX_PROPERTY_NAME; |
| import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME; |
| |
| public abstract class AbstractActiveDeletedBlobTest extends AbstractQueryTest { |
| @Rule |
| public TemporaryFolder temporaryFolder = new TemporaryFolder(new File("target")); |
| @Rule |
| public TemporaryFolder blobCollectionRoot = new TemporaryFolder(new File("target")); |
| @Rule |
| public TemporaryFolder fileDataStoreRoot = new TemporaryFolder(new File("target")); |
| |
| protected ExecutorService executorService = MoreExecutors.sameThreadExecutor(); |
| |
| protected CountingBlobStore blobStore = null; |
| |
| protected Clock clock = new Clock.Virtual(); |
| |
| protected ActiveDeletedBlobCollectorFactory.ActiveDeletedBlobCollectorImpl adbc = null; |
| |
| protected AsyncIndexUpdate asyncIndexUpdate; |
| |
| protected LuceneIndexEditorProvider editorProvider; |
| |
| protected NodeStore nodeStore; |
| |
| protected LuceneIndexProvider provider; |
| |
| @After |
| public void after() { |
| new ExecutorCloser(executorService).close(); |
| executorService.shutdown(); |
| IndexDefinition.setDisableStoredIndexDefinition(false); |
| } |
| |
| public static Tree createIndex(Tree index, String name, Set<String> propNames) throws CommitFailedException { |
| Tree def = index.addChild(INDEX_DEFINITIONS_NAME).addChild(name); |
| def.setProperty(JcrConstants.JCR_PRIMARYTYPE, |
| INDEX_DEFINITIONS_NODE_TYPE, Type.NAME); |
| def.setProperty(TYPE_PROPERTY_NAME, LuceneIndexConstants.TYPE_LUCENE); |
| def.setProperty(REINDEX_PROPERTY_NAME, true); |
| def.setProperty(ASYNC_PROPERTY_NAME, "async"); |
| def.setProperty(FulltextIndexConstants.FULL_TEXT_ENABLED, false); |
| def.setProperty(PropertyStates.createProperty(FulltextIndexConstants.INCLUDE_PROPERTY_NAMES, propNames, Type.STRINGS)); |
| def.setProperty(LuceneIndexConstants.SAVE_DIR_LISTING, true); |
| return index.getChild(INDEX_DEFINITIONS_NAME).getChild(name); |
| } |
| |
| @Override |
| protected void createTestIndexNode() throws Exception { |
| setTraversalEnabled(false); |
| } |
| |
| protected IndexCopier createIndexCopier() { |
| try { |
| return new IndexCopier(executorService, temporaryFolder.getRoot()); |
| } catch (IOException e) { |
| throw new RuntimeException(e); |
| } |
| } |
| |
| protected Tree createIndex(String name, Set<String> propNames) throws CommitFailedException { |
| Tree index = root.getTree("/"); |
| return createIndex(index, name, propNames); |
| } |
| |
| class CountingBlobStore implements GarbageCollectableBlobStore, BlobTrackingStore { |
| |
| private final GarbageCollectableBlobStore delegate; |
| protected long numChunks = 0; |
| |
| CountingBlobStore(GarbageCollectableBlobStore delegate) { |
| this.delegate = delegate; |
| } |
| |
| @Override public String writeBlob(InputStream in) throws IOException { |
| String blobId = delegate.writeBlob(in); |
| numChunks += Iterators.size(delegate.resolveChunks(blobId)); |
| return blobId; |
| } |
| |
| @Override public void setBlockSize(int x) { |
| delegate.setBlockSize(x); |
| } |
| |
| @Override public String writeBlob(InputStream in, BlobOptions options) throws IOException { |
| String blobId = delegate.writeBlob(in, options); |
| numChunks += Iterators.size(delegate.resolveChunks(blobId)); |
| return blobId; |
| } |
| |
| @Override public String writeBlob(String tempFileName) throws IOException { |
| String blobId = delegate.writeBlob(tempFileName); |
| numChunks += Iterators.size(delegate.resolveChunks(blobId)); |
| return blobId; |
| } |
| |
| @Override public int sweep() throws IOException { |
| return delegate.sweep(); |
| } |
| |
| @Override public void startMark() throws IOException { |
| delegate.startMark(); |
| } |
| |
| @Override public int readBlob(String blobId, long pos, byte[] buff, int off, int length) throws IOException { |
| return delegate.readBlob(blobId, pos, buff, off, length); |
| } |
| |
| @Override public void clearInUse() { |
| delegate.clearInUse(); |
| } |
| |
| @Override public void clearCache() { |
| delegate.clearCache(); |
| } |
| |
| @Override public long getBlobLength(String blobId) throws IOException { |
| return delegate.getBlobLength(blobId); |
| } |
| |
| @Override public long getBlockSizeMin() { |
| return delegate.getBlockSizeMin(); |
| } |
| |
| @Override public Iterator<String> getAllChunkIds(long maxLastModifiedTime) throws Exception { |
| return delegate.getAllChunkIds(maxLastModifiedTime); |
| } |
| |
| @Override public InputStream getInputStream(String blobId) throws IOException { |
| return delegate.getInputStream(blobId); |
| } |
| |
| @Override @Deprecated public boolean deleteChunks(List<String> chunkIds, long maxLastModifiedTime) |
| throws Exception { |
| numChunks -= chunkIds.size(); |
| return delegate.deleteChunks(chunkIds, maxLastModifiedTime); |
| } |
| |
| @Override @Nullable public String getBlobId(@NotNull String reference) { |
| return delegate.getBlobId(reference); |
| } |
| |
| @Override @Nullable public String getReference(@NotNull String blobId) { |
| return delegate.getReference(blobId); |
| } |
| |
| @Override public long countDeleteChunks(List<String> chunkIds, long maxLastModifiedTime) throws Exception { |
| long numDeleted = delegate.countDeleteChunks(chunkIds, maxLastModifiedTime); |
| numChunks -= numDeleted; |
| return numDeleted; |
| } |
| |
| @Override public Iterator<String> resolveChunks(String blobId) throws IOException { |
| return delegate.resolveChunks(blobId); |
| } |
| |
| @Override public void addTracker(BlobTracker tracker) { |
| if (delegate instanceof BlobTrackingStore) { |
| ((BlobTrackingStore) delegate).addTracker(tracker); |
| } |
| } |
| |
| @Override public BlobTracker getTracker() { |
| if (delegate instanceof BlobTrackingStore) { |
| return ((BlobTrackingStore) delegate).getTracker(); |
| } |
| return null; |
| } |
| |
| @Override public void addMetadataRecord(InputStream stream, String name) throws DataStoreException { |
| if (delegate instanceof BlobTrackingStore) { |
| ((BlobTrackingStore) delegate).addMetadataRecord(stream, name); |
| } |
| } |
| |
| @Override public void addMetadataRecord(File f, String name) throws DataStoreException { |
| if (delegate instanceof BlobTrackingStore) { |
| ((BlobTrackingStore) delegate).addMetadataRecord(f, name); |
| } |
| } |
| |
| @Override public DataRecord getMetadataRecord(String name) { |
| if (delegate instanceof BlobTrackingStore) { |
| return ((BlobTrackingStore) delegate).getMetadataRecord(name); |
| } |
| return null; |
| } |
| |
| @Override public boolean metadataRecordExists(String name) { |
| if (delegate instanceof BlobTrackingStore) { |
| return ((BlobTrackingStore) delegate).metadataRecordExists(name); |
| } |
| return false; |
| } |
| |
| @Override public List<DataRecord> getAllMetadataRecords(String prefix) { |
| if (delegate instanceof BlobTrackingStore) { |
| return ((BlobTrackingStore) delegate).getAllMetadataRecords(prefix); |
| } |
| return null; |
| } |
| |
| @Override public boolean deleteMetadataRecord(String name) { |
| if (delegate instanceof BlobTrackingStore) { |
| ((BlobTrackingStore) delegate).deleteMetadataRecord(name); |
| } |
| return false; |
| } |
| |
| @Override public void deleteAllMetadataRecords(String prefix) { |
| if (delegate instanceof BlobTrackingStore) { |
| ((BlobTrackingStore) delegate).deleteAllMetadataRecords(prefix); |
| } |
| } |
| |
| @Override public Iterator<DataRecord> getAllRecords() throws DataStoreException { |
| if (delegate instanceof BlobTrackingStore) { |
| return ((BlobTrackingStore) delegate).getAllRecords(); |
| } |
| return Collections.emptyIterator(); |
| } |
| |
| @Override public DataRecord getRecordForId(DataIdentifier id) throws DataStoreException { |
| if (delegate instanceof BlobTrackingStore) { |
| return ((BlobTrackingStore) delegate).getRecordForId(id); |
| } |
| return null; |
| } |
| |
| @Override public Type getType() { |
| if (delegate instanceof BlobTrackingStore) { |
| ((BlobTrackingStore) delegate).getType(); |
| } |
| return Type.DEFAULT; |
| } |
| } |
| } |