View Javadoc
1   /*
2    * This file is part of ***  M y C o R e  ***
3    * See http://www.mycore.de/ for details.
4    *
5    * MyCoRe is free software: you can redistribute it and/or modify
6    * it under the terms of the GNU General Public License as published by
7    * the Free Software Foundation, either version 3 of the License, or
8    * (at your option) any later version.
9    *
10   * MyCoRe is distributed in the hope that it will be useful,
11   * but WITHOUT ANY WARRANTY; without even the implied warranty of
12   * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13   * GNU General Public License for more details.
14   *
15   * You should have received a copy of the GNU General Public License
16   * along with MyCoRe.  If not, see <http://www.gnu.org/licenses/>.
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   * @author Thomas Scheffler
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."; //todo: rename
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                 //if set, do nothing
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         //recursive call
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         //recursive call
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 }