Skip to content

Commit 0480f8a

Browse files
committed
MCR-3126 try to reduce usage of toPhysicalPath
1 parent 17a02fa commit 0480f8a

File tree

8 files changed

+167
-53
lines changed

8 files changed

+167
-53
lines changed

mycore-ocfl/src/main/java/org/mycore/ocfl/niofs/MCROCFLFileTypeDetector.java

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -61,8 +61,7 @@ public String probeContentType(Path path) throws IOException {
6161
if (virtualObject == null) {
6262
throw new NoSuchFileException(path.toString());
6363
}
64-
Path physicalPath = virtualObject.toPhysicalPath(versionedPath);
65-
return Files.probeContentType(physicalPath);
64+
return virtualObject.probeContentType(versionedPath);
6665
}
6766

6867
}

mycore-ocfl/src/main/java/org/mycore/ocfl/niofs/MCROCFLLocalVirtualObject.java

Lines changed: 43 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,6 @@
5353
* It handles file operations such as copying, moving, and deleting files within the local file system,
5454
* and ensures consistency with the OCFL repository. This class also manages local modifications and
5555
* synchronizes changes with the OCFL repository.
56-
* </p>
5756
*/
5857
public class MCROCFLLocalVirtualObject extends MCROCFLVirtualObject {
5958

@@ -111,40 +110,46 @@ public void copy(MCRVersionedPath source, MCRVersionedPath target, CopyOption...
111110
MCRVersionedPath lockedTarget = lockVersion(target);
112111
checkPurged(lockedSource);
113112
checkReadOnly();
114-
boolean targetExists = exists(lockedTarget);
115113
if (this.localStorage.exists(lockedSource)) {
116114
this.localStorage.copy(lockedSource, lockedTarget, options);
117115
} else {
118-
Path localSourcePath = toPhysicalPath(lockedSource);
116+
Path localSourcePath = toPhysicalOcflPath(lockedSource);
119117
try (InputStream inputStream = Files.newInputStream(localSourcePath)) {
120118
this.localStorage.copy(inputStream, lockedTarget, options);
121119
}
122120
}
121+
boolean targetExists = exists(lockedTarget);
123122
trackFileWrite(lockedTarget, targetExists ? MCREvent.EventType.UPDATE : MCREvent.EventType.CREATE);
124123
}
125124

126125
/**
127126
* {@inheritDoc}
128127
*/
129128
@Override
130-
public void externalCopy(MCROCFLVirtualObject virtualTarget, MCRVersionedPath source,
131-
MCRVersionedPath target, CopyOption... options) throws IOException {
129+
public void externalCopy(MCROCFLVirtualObject virtualTarget, MCRVersionedPath source, MCRVersionedPath target,
130+
CopyOption... options) throws IOException {
132131
MCRVersionedPath lockedSource = lockVersion(source);
133132
checkPurged(lockedSource);
134133
virtualTarget.checkReadOnly();
135-
boolean targetExists = virtualTarget.exists(target);
136-
Path localSourcePath = toPhysicalPath(lockedSource);
137-
try (InputStream is = Files.newInputStream(localSourcePath)) {
138-
this.localStorage.copy(is, target, options);
134+
if (this.localStorage.exists(lockedSource)) {
135+
this.localStorage.copy(lockedSource, target, options);
136+
} else {
137+
Path localOcflPath = toPhysicalOcflPath(lockedSource);
138+
try (InputStream is = Files.newInputStream(localOcflPath)) {
139+
this.localStorage.copy(is, target, options);
140+
}
139141
}
142+
boolean targetExists = virtualTarget.exists(target);
140143
virtualTarget.trackFileWrite(target, targetExists ? MCREvent.EventType.UPDATE : MCREvent.EventType.CREATE);
141144
}
142145

143146
@Override
144147
protected SeekableByteChannel readByteChannel(MCRVersionedPath path, Set<? extends OpenOption> options,
145148
FileAttribute<?>... fileAttributes) throws IOException {
146-
return this.localStorage.exists(path) ? this.localStorage.newByteChannel(path, options, fileAttributes)
147-
: Files.newByteChannel(toPhysicalPath(path), options, fileAttributes);
149+
if (this.localStorage.exists(path)) {
150+
return this.localStorage.newByteChannel(path, options, fileAttributes);
151+
}
152+
return Files.newByteChannel(toPhysicalOcflPath(path), options, fileAttributes);
148153
}
149154

150155
@Override
@@ -173,18 +178,38 @@ protected SeekableByteChannel writeByteChannel(MCRVersionedPath path, Set<? exte
173178
public FileTime getModifiedTime(MCRVersionedPath path) throws IOException {
174179
MCRVersionedPath lockedPath = lockVersion(path);
175180
checkExists(lockedPath);
176-
Path physicalPath = toPhysicalPath(lockedPath);
181+
if (this.localStorage.exists(lockedPath)) {
182+
return this.localStorage.readAttributes(lockedPath, BasicFileAttributes.class).lastModifiedTime();
183+
}
184+
Path physicalPath = toPhysicalOcflPath(lockedPath);
177185
return Files.readAttributes(physicalPath, BasicFileAttributes.class).lastModifiedTime();
178186
}
179187

180188
@Override
181189
public FileTime getAccessTime(MCRVersionedPath path) throws IOException {
182190
MCRVersionedPath lockedPath = lockVersion(path);
183191
checkExists(lockedPath);
184-
Path physicalPath = toPhysicalPath(lockedPath);
192+
if (this.localStorage.exists(lockedPath)) {
193+
return this.localStorage.readAttributes(lockedPath, BasicFileAttributes.class).lastAccessTime();
194+
}
195+
Path physicalPath = toPhysicalOcflPath(lockedPath);
185196
return Files.readAttributes(physicalPath, BasicFileAttributes.class).lastAccessTime();
186197
}
187198

199+
/**
200+
* {@inheritDoc}
201+
*/
202+
@Override
203+
public Object getFileKey(MCRVersionedPath path) throws IOException {
204+
MCRVersionedPath lockedPath = lockVersion(path);
205+
checkExists(lockedPath);
206+
if (this.localStorage.exists(lockedPath)) {
207+
return this.localStorage.readAttributes(lockedPath, BasicFileAttributes.class).fileKey();
208+
}
209+
Path physicalPath = toPhysicalOcflPath(lockedPath);
210+
return Files.readAttributes(physicalPath, BasicFileAttributes.class).fileKey();
211+
}
212+
188213
/**
189214
* {@inheritDoc}
190215
*/
@@ -195,6 +220,11 @@ public Path toPhysicalPath(MCRVersionedPath path) throws IOException {
195220
if (this.localStorage.exists(lockedPath)) {
196221
return this.localStorage.toPhysicalPath(lockedPath);
197222
}
223+
return toPhysicalOcflPath(path);
224+
}
225+
226+
private Path toPhysicalOcflPath(MCRVersionedPath path) throws IOException {
227+
MCRVersionedPath lockedPath = lockVersion(path);
198228
FileChangeHistory changeHistory = getChangeHistory(lockedPath);
199229
String storageRelativePath = changeHistory.getMostRecent().getStorageRelativePath();
200230
return getLocalRepositoryPath().resolve(storageRelativePath);

mycore-ocfl/src/main/java/org/mycore/ocfl/niofs/MCROCFLRemoteVirtualObject.java

Lines changed: 14 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -157,8 +157,7 @@ public FileTime getModifiedTime(MCRVersionedPath path) throws IOException {
157157
MCRVersionedPath lockedPath = lockVersion(path);
158158
checkExists(lockedPath);
159159
if (this.localStorage.exists(lockedPath)) {
160-
Path physicalPath = this.localStorage.toPhysicalPath(lockedPath);
161-
return Files.readAttributes(physicalPath, BasicFileAttributes.class).lastModifiedTime();
160+
return this.localStorage.readAttributes(lockedPath, BasicFileAttributes.class).lastModifiedTime();
162161
}
163162
FileChangeHistory changeHistory = getChangeHistory(lockedPath);
164163
return FileTime.from(changeHistory.getMostRecent().getTimestamp().toInstant());
@@ -172,8 +171,7 @@ public FileTime getAccessTime(MCRVersionedPath path) throws IOException {
172171
MCRVersionedPath lockedPath = lockVersion(path);
173172
checkExists(lockedPath);
174173
if (this.localStorage.exists(lockedPath)) {
175-
Path physicalPath = this.localStorage.toPhysicalPath(lockedPath);
176-
return Files.readAttributes(physicalPath, BasicFileAttributes.class).lastAccessTime();
174+
return this.localStorage.readAttributes(lockedPath, BasicFileAttributes.class).lastAccessTime();
177175
}
178176
FileChangeHistory changeHistory = getChangeHistory(lockedPath);
179177
return FileTime.from(changeHistory.getMostRecent().getTimestamp().toInstant());
@@ -211,17 +209,26 @@ public void externalCopy(MCROCFLVirtualObject virtualTarget, MCRVersionedPath so
211209

212210
/**
213211
* {@inheritDoc}
214-
* This implementation always creates a copy before returning the physical path, guaranteeing
215-
* that the file exists in the local temporary storage.
216212
*/
217213
@Override
218-
public Path toPhysicalPath(MCRVersionedPath path) throws IOException {
214+
protected Path toPhysicalPath(MCRVersionedPath path) throws IOException {
219215
MCRVersionedPath lockedPath = lockVersion(path);
220216
checkExists(lockedPath);
221217
localCopy(lockedPath);
222218
return this.localStorage.toPhysicalPath(lockedPath);
223219
}
224220

221+
/**
222+
* {@inheritDoc}
223+
* This implementation always creates a copy before returning the physical path, guaranteeing
224+
* that the file exists in the local temporary storage.
225+
*/
226+
@Override
227+
public Object getFileKey(MCRVersionedPath path) throws IOException {
228+
Path physicalPath = toPhysicalPath(path);
229+
return Files.readAttributes(physicalPath, BasicFileAttributes.class).fileKey();
230+
}
231+
225232
/**
226233
* Creates a deep clone of this remote virtual object.
227234
*

mycore-ocfl/src/main/java/org/mycore/ocfl/niofs/MCROCFLVirtualObject.java

Lines changed: 33 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -636,8 +636,7 @@ public FileTime getCreationTime(MCRVersionedPath path) throws IOException {
636636
boolean added = this.isAdded(lockedPath);
637637
boolean inLocalStorage = this.isLocal(lockedPath);
638638
if (added && inLocalStorage) {
639-
Path physicalPath = this.localStorage.toPhysicalPath(lockedPath);
640-
return Files.readAttributes(physicalPath, BasicFileAttributes.class).creationTime();
639+
return this.localStorage.readAttributes(lockedPath, BasicFileAttributes.class).creationTime();
641640
}
642641
FileChangeHistory changeHistory = getChangeHistory(lockedPath);
643642
return FileTime.from(changeHistory.getOldest().getTimestamp().toInstant());
@@ -676,8 +675,7 @@ public long getSize(MCRVersionedPath path) throws IOException {
676675
return 0;
677676
}
678677
if (this.localStorage.exists(lockedPath)) {
679-
Path physicalPath = this.localStorage.toPhysicalPath(lockedPath);
680-
return Files.size(physicalPath);
678+
return this.localStorage.size(lockedPath);
681679
}
682680
OcflObjectVersionFile ocflObjectVersionFile = fromOcfl(lockedPath);
683681
String sizeAsString = ocflObjectVersionFile.getFixity().get(new SizeDigestAlgorithm());
@@ -687,48 +685,56 @@ public long getSize(MCRVersionedPath path) throws IOException {
687685
/**
688686
* Returns the `filekey` of the given path.
689687
* <p>
690-
* Because OCFL is a version file system and the mycore implementation uses transactions this `filekey`
691-
* implementation differs from a Unix like system.
692-
* </p>
688+
* Because OCFL is a version file system and the mycore implementation uses transactions this `filekey`
689+
* implementation differs from a Unix like system.
693690
* <p>
694-
* The Unix `filekey` is typically derived from the inode and device ID, ensuring uniqueness within the
695-
* filesystem. When a file is modified (written, moved...), the Unix `filekey` remains unchanged as long as the
696-
* inode remains the same.
697-
* </p>
691+
* The Unix `filekey` is typically derived from the inode and device ID, ensuring uniqueness within the
692+
* filesystem. When a file is modified (written, moved...), the Unix `filekey` remains unchanged as long as the
693+
* inode remains the same.
698694
* <p>
699-
* In contrast this implementation returns a new `filekey` as soon as a file is written or moved. The `filekey`
700-
* then remains constant as long as the transaction is open. After the transaction is committed the `filekey`
701-
* may change again.
702-
* </p>
695+
* In contrast, this implementation returns a new `filekey` as soon as a file is written or moved. The `filekey`
696+
* then remains constant as long as the transaction is open. After the transaction is committed the `filekey`
697+
* may change again.
703698
* <p>
704-
* Implementation detail: Be aware that this method calls {@link #toPhysicalPath(MCRVersionedPath)}. For remote
705-
* virtual objects this means that the whole file is copied to the local storage. Because the fileKey is not
706-
* accessed frequently this should be acceptable. If this assumption proves to be wrong a special implementation
707-
* for remote virtual objects is required!
708-
* </p>
699+
* Implementation detail: Be aware that for remote virtual objects the whole file is copied to the local
700+
* storage. Because the fileKey is not accessed frequently this should be acceptable. If this assumption proves
701+
* to be wrong a special implementation for remote virtual objects is required!
709702
*
710703
* @param path versioned path
711704
* @return fileKey
712705
* @throws IOException if an I/O error occurs.
713706
*/
714-
public Object getFileKey(MCRVersionedPath path) throws IOException {
707+
public abstract Object getFileKey(MCRVersionedPath path) throws IOException;
708+
709+
/**
710+
* Identifies the MIME type of a give path.
711+
* <p>
712+
* Implementation detail: Be aware that for remote virtual objects the whole file is copied to the local
713+
* storage. Because the MIME type is not accessed frequently this should be acceptable. If this assumption proves
714+
* to be wrong a special implementation for remote virtual objects is required!
715+
*
716+
* @param path versioned path
717+
* @return the mime type
718+
* @throws IOException if an I/O error occurs.
719+
*/
720+
public String probeContentType(MCRVersionedPath path) throws IOException {
715721
MCRVersionedPath lockedPath = lockVersion(path);
716-
Path physicalPath = toPhysicalPath(lockedPath);
717-
return Files.readAttributes(physicalPath, BasicFileAttributes.class).fileKey();
722+
checkExists(lockedPath);
723+
return Files.probeContentType(toPhysicalPath(lockedPath));
718724
}
719725

720726
/**
721727
* Converts the specified versioned path to a local file system path. The path can either point at the local
722728
* temporary storage (if it exists) or at the original OCFL file or directory.
723729
* <p>
724-
* Use the returned path ONLY for read operations. It's not allowed to write/move or remove the returned path.
730+
* Use the returned path ONLY for read operations. It's not allowed to write/move or remove the returned path.
725731
* Because this would create inconsistencies in the OCFL repository or the local storage.
726732
*
727733
* @param path the virtual path.
728734
* @return the physical path.
729735
* @throws IOException if an I/O error occurs.
730736
*/
731-
public abstract Path toPhysicalPath(MCRVersionedPath path) throws IOException;
737+
protected abstract Path toPhysicalPath(MCRVersionedPath path) throws IOException;
732738

733739
/**
734740
* Returns the change history of a path.
@@ -1001,7 +1007,7 @@ protected MCRVersionedPath lockVersion(MCRVersionedPath versionedPath) {
10011007
}
10021008

10031009
/**
1004-
* Releases the version on a given {@link MCRVersionedPath}, returning the path
1010+
* Releases the version on a given {@link MCRVersionedPath}, returning the path
10051011
* that points to the latest (head) version of the virtual object.
10061012
* <p>
10071013
* This is necessary for the mycore event system. Due to the fact that the metadata is not yet
@@ -1012,7 +1018,7 @@ protected MCRVersionedPath lockVersion(MCRVersionedPath versionedPath) {
10121018
* TODO: This should be removed when the metadata is also included in the transaction system!
10131019
*
10141020
* @param versionedPath the {@link MCRVersionedPath} to release, retaining the same owner.
1015-
* @return a {@link MCRVersionedPath} that points to the head (latest) version of
1021+
* @return a {@link MCRVersionedPath} that points to the head (latest) version of
10161022
* the path for the specified owner.
10171023
*/
10181024
protected MCRVersionedPath releaseVersion(MCRVersionedPath versionedPath) {
@@ -1162,5 +1168,4 @@ protected MCRDigest calculateDigest(MCRVersionedPath path) {
11621168
throw new UncheckedIOException("Unable to calculate digest for path '" + path + "'.", ioException);
11631169
}
11641170
}
1165-
11661171
}

mycore-ocfl/src/main/java/org/mycore/ocfl/niofs/storage/MCROCFLRollingCacheStorage.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ public MCROCFLRollingCacheStorage(Path root, MCROCFLEvictionStrategy evictionStr
9191

9292
private void init() throws IOException {
9393
final List<Path> toRemoveList = new ArrayList<>();
94+
if (!Files.exists(root)) {
95+
return;
96+
}
9497
// update cache
9598
try (Stream<Path> pathStream = Files.walk(root)) {
9699
pathStream.filter(Files::isRegularFile)
@@ -108,7 +111,7 @@ private void init() throws IOException {
108111
try {
109112
Files.delete(toRemove);
110113
} catch (IOException removeException) {
111-
LOGGER.error(() -> "Unable to remove path " + toRemove + "from rolling cache.", removeException);
114+
LOGGER.error(() -> "Unable to remove path " + toRemove + " from rolling cache.", removeException);
112115
}
113116
}
114117
}

mycore-ocfl/src/main/java/org/mycore/ocfl/niofs/storage/MCROCFLTempFileStorage.java

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,9 +23,11 @@
2323
import java.nio.channels.SeekableByteChannel;
2424
import java.nio.file.CopyOption;
2525
import java.nio.file.Files;
26+
import java.nio.file.LinkOption;
2627
import java.nio.file.OpenOption;
2728
import java.nio.file.Path;
2829
import java.nio.file.StandardOpenOption;
30+
import java.nio.file.attribute.BasicFileAttributes;
2931
import java.nio.file.attribute.FileAttribute;
3032
import java.util.Set;
3133

@@ -139,6 +141,32 @@ default void createDirectories(MCRVersionedPath directoryPath, FileAttribute<?>.
139141
Files.createDirectories(toPhysicalPath(directoryPath), attrs);
140142
}
141143

144+
/**
145+
* Returns the size of a file (in bytes).
146+
*
147+
* @param path the path to the file
148+
* @return the size of a file in bytes
149+
* @throws IOException if an I/O error occurs.
150+
*/
151+
default long size(MCRVersionedPath path) throws IOException {
152+
// TODO implement
153+
return Files.size(toPhysicalPath(path));
154+
}
155+
156+
/**
157+
* Reads a file's attributes as a bulk operation.
158+
*
159+
* @param path the path to the file
160+
* @param type the Class of the file attributes required to read
161+
* @param options options indicating how symbolic links are handled
162+
* @return the file attributes
163+
* @throws IOException if an I/O error occurs.
164+
*/
165+
default <A extends BasicFileAttributes> A readAttributes(MCRVersionedPath path, Class<A> type,
166+
LinkOption... options) throws IOException {
167+
return Files.readAttributes(toPhysicalPath(path), type, options);
168+
}
169+
142170
/**
143171
* Clears the storage by deleting all files and directories under the root path.
144172
*

0 commit comments

Comments
 (0)