1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19 package org.mycore.datamodel.niofs.ifs2;
20
21 import java.io.File;
22 import java.io.IOException;
23 import java.nio.file.FileAlreadyExistsException;
24 import java.nio.file.FileSystem;
25 import java.nio.file.InvalidPathException;
26 import java.nio.file.NoSuchFileException;
27 import java.nio.file.Path;
28 import java.nio.file.ProviderMismatchException;
29 import java.util.Deque;
30 import java.util.LinkedList;
31 import java.util.Objects;
32 import java.util.Optional;
33
34 import org.apache.logging.log4j.LogManager;
35 import org.apache.logging.log4j.Logger;
36 import org.mycore.common.config.MCRConfiguration2;
37 import org.mycore.common.config.MCRConfigurationException;
38 import org.mycore.datamodel.ifs2.MCRDirectory;
39 import org.mycore.datamodel.ifs2.MCRFile;
40 import org.mycore.datamodel.ifs2.MCRFileCollection;
41 import org.mycore.datamodel.ifs2.MCRFileStore;
42 import org.mycore.datamodel.ifs2.MCRStoreManager;
43 import org.mycore.datamodel.ifs2.MCRStoredNode;
44 import org.mycore.datamodel.metadata.MCRObjectID;
45 import org.mycore.datamodel.niofs.MCRAbstractFileSystem;
46 import org.mycore.datamodel.niofs.MCRPath;
47
48
49
50
51
52 abstract class MCRFileSystemUtils {
53
54 private static final Logger LOGGER = LogManager.getLogger(MCRFileSystemUtils.class);
55
56 private static final String DEFAULT_CONFIG_PREFIX = "MCR.IFS.ContentStore.IFS2.";
57
58 public static final String STORE_ID_PREFIX = "IFS2_";
59
60 private static String getBaseDir() {
61 return MCRConfiguration2.getStringOrThrow(DEFAULT_CONFIG_PREFIX + "BaseDir");
62 }
63
64 private static String getDefaultSlotLayout() {
65 return MCRConfiguration2.getString(DEFAULT_CONFIG_PREFIX + "SlotLayout")
66 .orElseGet(() -> {
67 String baseID = "a_a";
68 String formatID = MCRObjectID.formatID(baseID, 1);
69 int patternLength = formatID.length() - baseID.length() - "_".length();
70 return patternLength - 4 + "-2-2";
71 });
72 }
73
74 static MCRFileCollection getFileCollection(String owner) throws IOException {
75 MCRObjectID derId = MCRObjectID.getInstance(owner);
76 MCRFileStore fileStore = getStore(derId.getBase());
77 MCRFileCollection fileCollection = fileStore
78 .retrieve(derId.getNumberAsInteger());
79 if (fileCollection == null) {
80 throw new NoSuchFileException(null, null,
81 "File collection " + owner + " is not available here: " + fileStore.getBaseDirectory());
82 }
83 return fileCollection;
84 }
85
86 static MCRFileStore getStore(String base) {
87 String storeBaseDir = getBaseDir();
88
89 String sid = STORE_ID_PREFIX + base;
90 storeBaseDir += File.separatorChar + base.replace("_", File.separator);
91
92 MCRFileStore store = MCRStoreManager.getStore(sid);
93 if (store == null) {
94 synchronized (MCRStoreManager.class) {
95 store = MCRStoreManager.getStore(sid);
96 if (store == null) {
97 store = createStore(sid, storeBaseDir, base);
98 }
99 }
100 }
101 return store;
102 }
103
104 private static MCRFileStore createStore(String sid, String storeBaseDir, String base) {
105 try {
106 configureStore(sid, storeBaseDir, base);
107 return MCRStoreManager.createStore(sid, MCRFileStore.class);
108 } catch (Exception ex) {
109 String msg = "Could not create IFS2 file store with ID " + sid;
110 throw new MCRConfigurationException(msg, ex);
111 }
112 }
113
114 private static void configureStore(String sid, String storeBaseDir, String base) {
115 String storeConfigPrefix = "MCR.IFS2.Store." + sid + ".";
116 configureIfNotSet(storeConfigPrefix + "Class", MCRFileStore.class.getName());
117 configureIfNotSet(storeConfigPrefix + "BaseDir", storeBaseDir);
118 configureIfNotSet(storeConfigPrefix + "Prefix", base + "_");
119 configureIfNotSet(storeConfigPrefix + "SlotLayout", getDefaultSlotLayout());
120 }
121
122 private static void configureIfNotSet(String property, String value) {
123 MCRConfiguration2.getString(property)
124 .ifPresentOrElse(s -> {
125
126 }, () -> {
127 MCRConfiguration2.set(property, value);
128 LOGGER.info("Configured {}={}", property, value);
129 });
130 }
131
132 static MCRPath checkPathAbsolute(Path path) {
133 MCRPath mcrPath = MCRPath.toMCRPath(path);
134 if (!(Objects.requireNonNull(mcrPath.getFileSystem(), "'path' requires a associated filesystem.")
135 .provider() instanceof MCRFileSystemProvider)) {
136 throw new ProviderMismatchException("Path does not match to this provider: " + path);
137 }
138 if (!mcrPath.isAbsolute()) {
139 throw new InvalidPathException(mcrPath.toString(), "'path' must be absolute.");
140 }
141 return mcrPath;
142 }
143
144 static String getOwnerID(MCRStoredNode node) {
145 MCRFileCollection collection = node.getRoot();
146 int intValue = collection.getID();
147 String storeId = collection.getStore().getID();
148 String baseId = storeId.substring(STORE_ID_PREFIX.length());
149 return MCRObjectID.formatID(baseId, intValue);
150 }
151
152 static MCRPath toPath(MCRStoredNode node) {
153 String ownerID = getOwnerID(node);
154 String path = node.getPath();
155 return MCRAbstractFileSystem.getPath(ownerID, path, MCRFileSystemProvider.getMCRIFSFileSystem());
156 }
157
158 static MCRFile getMCRFile(MCRPath ifsPath, boolean create, boolean createNew, boolean fireCreateEvent)
159 throws IOException {
160 if (!ifsPath.isAbsolute()) {
161 throw new IllegalArgumentException("'path' needs to be absolute.");
162 }
163 MCRFile file;
164 MCRDirectory root = null;
165 boolean rootCreated = false;
166 try {
167 try {
168 root = getFileCollection(ifsPath.getOwner());
169 } catch (NoSuchFileException e) {
170 if (create || createNew) {
171 MCRObjectID derId = MCRObjectID.getInstance(ifsPath.getOwner());
172 root = getStore(derId.getBase()).create(derId.getNumberAsInteger());
173 rootCreated = true;
174 } else {
175 throw e;
176 }
177 }
178 MCRPath relativePath = toPath(root).relativize(ifsPath);
179 file = getMCRFile(root, relativePath, create, createNew, fireCreateEvent);
180 } catch (Exception e) {
181 if (rootCreated) {
182 LOGGER.error("Exception while getting MCRFile {}. Removing created filesystem nodes.", ifsPath);
183 try {
184 root.delete();
185 } catch (Exception de) {
186 LOGGER.fatal("Error while deleting file system node: {}", root.getName(), de);
187 }
188 }
189 throw e;
190 }
191 return file;
192 }
193
194 private static MCRFile getMCRFile(MCRDirectory baseDir, MCRPath relativePath, boolean create, boolean createNew,
195 boolean fireEvent)
196 throws IOException {
197 MCRPath ifsPath = relativePath;
198 if (relativePath.isAbsolute()) {
199 if (getOwnerID(baseDir).equals(relativePath.getOwner())) {
200 ifsPath = toPath(baseDir).relativize(relativePath);
201 } else {
202 throw new IOException(relativePath + " is absolute does not fit to " + toPath(baseDir));
203 }
204 }
205 Deque<MCRStoredNode> created = new LinkedList<>();
206 MCRFile file;
207 try {
208 file = (MCRFile) baseDir.getNodeByPath(ifsPath.toString());
209 if (file != null && createNew) {
210 throw new FileAlreadyExistsException(toPath(baseDir).resolve(ifsPath).toString());
211 }
212 if (file == null & (create || createNew)) {
213 Path normalized = ifsPath.normalize();
214 MCRDirectory parent = baseDir;
215 int nameCount = normalized.getNameCount();
216 int directoryCount = nameCount - 1;
217 int i = 0;
218 while (i < directoryCount) {
219 String curName = normalized.getName(i).toString();
220 MCRDirectory curDir = (MCRDirectory) parent.getChild(curName);
221 if (curDir == null) {
222 curDir = parent.createDir(curName);
223 created.addFirst(curDir);
224 }
225 i++;
226 parent = curDir;
227 }
228 String fileName = normalized.getFileName().toString();
229 file = parent.createFile(fileName);
230 created.addFirst(file);
231 if (fireEvent) {
232 MCRPathEventHelper.fireFileCreateEvent(toPath(baseDir).resolve(relativePath),
233 file.getBasicFileAttributes());
234 }
235 }
236 } catch (Exception e) {
237 if (create || createNew) {
238 LOGGER.error("Exception while getting MCRFile {}. Removing created filesystem nodes.", ifsPath);
239 while (created.peekFirst() != null) {
240 MCRStoredNode node = created.pollFirst();
241 try {
242 node.delete();
243 } catch (Exception de) {
244 LOGGER.fatal("Error while deleting file system node: {}", node.getName(), de);
245 }
246 }
247 }
248 throw e;
249 }
250 return file;
251 }
252
253 @SuppressWarnings("unchecked")
254 static <T extends MCRStoredNode> T resolvePath(MCRPath path) throws IOException {
255 if (path.getNameCount() == 0) {
256 return (T) getFileCollection(path.getOwner());
257 }
258
259 String fileOrDir = path.getFileName().toString();
260 MCRDirectory parentDir = doResolveParent(path.getParent())
261 .map(MCRDirectory.class::cast)
262 .orElseThrow(() -> new NoSuchFileException(path.getParent().toString(), fileOrDir,
263 "parent directory does not exist"));
264
265 return (T) Optional.ofNullable(parentDir.getChild(fileOrDir))
266 .map(MCRStoredNode.class::cast)
267 .orElseThrow(
268 () -> new NoSuchFileException(path.getParent().toString(), fileOrDir, "file does not exist"));
269 }
270
271 static Path toNativePath(FileSystem fs, Path path) {
272 if (fs.equals(path.getFileSystem())) {
273 return path;
274 }
275 if (path.isAbsolute()) {
276 throw new IllegalArgumentException("path is absolute");
277 }
278 switch (path.getNameCount()) {
279 case 0:
280 return fs.getPath("");
281 case 1:
282 return fs.getPath(path.toString());
283 default:
284 String[] pathComp = new String[path.getNameCount() - 1];
285 for (int i = 1; i < pathComp.length; i++) {
286 pathComp[i] = path.getName(i).toString();
287 }
288 return fs.getPath(path.getName(0).toString(), pathComp);
289 }
290 }
291
292 private static Optional<MCRDirectory> doResolveParent(MCRPath parent) {
293 if (parent.getNameCount() == 0) {
294 MCRObjectID derId = MCRObjectID.getInstance(parent.getOwner());
295 return Optional.ofNullable(getStore(derId.getBase()))
296 .map(s -> {
297 try {
298 return s.retrieve(derId.getNumberAsInteger());
299 } catch (IOException e) {
300 LOGGER.warn("Exception while retrieving file collection " + derId, e);
301 return null;
302 }
303 });
304 }
305
306 String dirName = parent.getFileName().toString();
307 return doResolveParent(parent.getParent())
308 .map(p -> p.getChild(dirName))
309 .map(MCRDirectory.class::cast);
310 }
311
312 }