-
Notifications
You must be signed in to change notification settings - Fork 615
/
DirectoryImplementor.java
177 lines (152 loc) · 6.34 KB
/
DirectoryImplementor.java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
package org.infinispan.lucene.impl;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.util.Set;
import org.apache.lucene.store.IndexOutput;
import org.infinispan.AdvancedCache;
import org.infinispan.Cache;
import org.infinispan.context.Flag;
import org.infinispan.lucene.ChunkCacheKey;
import org.infinispan.lucene.FileCacheKey;
import org.infinispan.lucene.FileMetadata;
import org.infinispan.lucene.readlocks.SegmentReadLocker;
import org.infinispan.util.logging.Log;
import org.infinispan.util.logging.LogFactory;
/**
* Common code for different Directory implementations. Extracted to accommodate support
* for both Apache Lucene v.3.x and v.4
*
* @author Sanne Grinovero
* @since 5.2
*/
final class DirectoryImplementor {
private static final Log log = LogFactory.getLog(DirectoryImplementor.class);
private final AdvancedCache<FileCacheKey, FileMetadata> metadataCache;
private final AdvancedCache<ChunkCacheKey, Object> chunksCache;
// indexName is used to be able to store multiple named indexes in the same caches
private final String indexName;
// chunk size used for this Directory
private final int chunkSize;
private final FileListOperations fileOps;
private final SegmentReadLocker readLocks;
public DirectoryImplementor(Cache<?, ?> metadataCache, Cache<?, ?> chunksCache, String indexName, int chunkSize, SegmentReadLocker readLocker) {
if (chunkSize <= 0)
throw new IllegalArgumentException("chunkSize must be a positive integer");
this.metadataCache = (AdvancedCache<FileCacheKey, FileMetadata>) metadataCache.getAdvancedCache().withFlags(Flag.SKIP_INDEXING);
this.chunksCache = (AdvancedCache<ChunkCacheKey, Object>) chunksCache.getAdvancedCache().withFlags(Flag.SKIP_INDEXING);
this.indexName = indexName;
this.chunkSize = chunkSize;
this.fileOps = new FileListOperations(this.metadataCache, indexName);
this.readLocks = readLocker;
}
String[] list() {
final Set<String> files = fileOps.getFileList();
//Careful! if you think you can optimize this array allocation, think again.
//The _files_ are a concurrent structure, its size could vary in parallel:
//the array population and dimensioning need to be performed atomically
//to avoid trailing null elements in the returned array.
final String[] array = files.toArray(new String[0]);
return array;
}
boolean fileExists(final String name) {
return fileOps.getFileList().contains(name);
}
/**
* Used by Lucene v3.x only
*/
long fileModified(final String name) {
final FileMetadata fileMetadata = fileOps.getFileMetadata(name);
if (fileMetadata == null) {
return 0L;
}
else {
return fileMetadata.getLastModified();
}
}
/**
* Used by Lucene v3.x only
*/
void touchFile(final String fileName) {
final FileMetadata file = fileOps.getFileMetadata(fileName);
if (file != null) {
final FileCacheKey key = new FileCacheKey(indexName, fileName);
file.touch();
metadataCache.put(key, file);
}
}
void deleteFile(final String name) {
fileOps.deleteFileName(name);
readLocks.deleteOrReleaseReadLock(name);
if (log.isDebugEnabled()) {
log.debugf("Removed file: %s from index: %s", name, indexName);
}
}
void renameFile(final String from, final String to) {
final FileCacheKey fromKey = new FileCacheKey(indexName, from);
final FileMetadata metadata = metadataCache.get(fromKey);
final int bufferSize = metadata.getBufferSize();
// preparation: copy all chunks to new keys
int i = -1;
Object ob;
do {
final ChunkCacheKey fromChunkKey = new ChunkCacheKey(indexName, from, ++i, bufferSize);
ob = chunksCache.get(fromChunkKey);
if (ob == null) {
break;
}
final ChunkCacheKey toChunkKey = new ChunkCacheKey(indexName, to, i, bufferSize);
chunksCache.withFlags(Flag.IGNORE_RETURN_VALUES).put(toChunkKey, ob);
} while (true);
// rename metadata first
metadataCache.put(new FileCacheKey(indexName, to), metadata);
fileOps.removeAndAdd(from, to);
// now trigger deletion of old file chunks:
readLocks.deleteOrReleaseReadLock(from);
if (log.isTraceEnabled()) {
log.tracef("Renamed file from: %s to: %s in index %s", from, to, indexName);
}
}
long fileLength(final String name) {
final FileMetadata fileMetadata = fileOps.getFileMetadata(name);
if (fileMetadata == null) {
return 0L; //as in FSDirectory (RAMDirectory throws an exception instead)
}
else {
return fileMetadata.getSize();
}
}
IndexOutput createOutput(final String name) {
final FileCacheKey key = new FileCacheKey(indexName, name);
// creating new file, metadata is added on flush() or close() of IndexOutPut
return new InfinispanIndexOutput(metadataCache, chunksCache, key, chunkSize, fileOps);
}
IndexInputContext openInput(final String name) throws IOException {
final FileCacheKey fileKey = new FileCacheKey(indexName, name);
final FileMetadata fileMetadata = metadataCache.get(fileKey);
if (fileMetadata == null) {
throw new FileNotFoundException("Error loading metadata for index file: " + fileKey);
}
else if (fileMetadata.getSize() <= fileMetadata.getBufferSize()) {
//files smaller than chunkSize don't need a readLock
return new IndexInputContext(chunksCache, fileKey, fileMetadata, null);
}
else {
boolean locked = readLocks.acquireReadLock(name);
if (!locked) {
// safest reaction is to tell this file doesn't exist anymore.
throw new FileNotFoundException("Error loading metadata for index file: " + fileKey);
}
return new IndexInputContext(chunksCache, fileKey, fileMetadata, readLocks);
}
}
/**
* @return The value of indexName, same constant as provided to the constructor.
*/
public String getIndexName() {
return indexName;
}
@Override
public String toString() {
return "DirectoryImplementor{indexName=\'" + indexName + "\'}";
}
}