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;
20  
21  import static org.mycore.datamodel.niofs.MCRAbstractFileSystem.SEPARATOR;
22  import static org.mycore.datamodel.niofs.MCRAbstractFileSystem.SEPARATOR_STRING;
23  
24  import java.io.IOException;
25  import java.io.UncheckedIOException;
26  import java.nio.file.DirectoryStream;
27  import java.nio.file.Files;
28  import java.nio.file.Path;
29  import java.nio.file.SecureDirectoryStream;
30  import java.nio.file.attribute.BasicFileAttributes;
31  import java.nio.file.attribute.FileTime;
32  import java.util.Collection;
33  import java.util.Map;
34  import java.util.SortedMap;
35  import java.util.TreeMap;
36  import java.util.concurrent.TimeUnit;
37  import java.util.function.Function;
38  
39  import org.apache.logging.log4j.LogManager;
40  import org.apache.logging.log4j.Logger;
41  import org.jdom2.Document;
42  import org.jdom2.Element;
43  import org.mycore.common.MCRConstants;
44  import org.mycore.datamodel.classifications2.MCRCategLinkReference;
45  import org.mycore.datamodel.classifications2.MCRCategLinkServiceFactory;
46  import org.mycore.datamodel.classifications2.MCRCategoryID;
47  import org.mycore.datamodel.metadata.MCRMetadataManager;
48  import org.mycore.datamodel.metadata.MCRObjectID;
49  
50  /**
51   * @author Thomas Scheffler (yagee)
52   * @version $Revision: 28688 $ $Date: 2013-12-18 15:27:20 +0100 (Wed, 18 Dec 2013) $
53   */
54  public class MCRPathXML {
55  
56      static Logger LOGGER = LogManager.getLogger(MCRPathXML.class);
57  
58      private MCRPathXML() {
59      }
60  
61      public static Document getDirectoryXML(MCRPath path) throws IOException {
62          BasicFileAttributes attr = path.getFileSystem().provider().readAttributes(path, BasicFileAttributes.class);
63          return getDirectoryXML(path, attr);
64      }
65  
66      /**
67       * Sends the contents of an MCRDirectory as XML data to the client
68       */
69      public static Document getDirectoryXML(MCRPath path, BasicFileAttributes attr) throws IOException {
70          LOGGER.debug("MCRDirectoryXML: start listing of directory {}", path);
71  
72          Element root = new Element("mcr_directory");
73          Document doc = new Document(root);
74  
75          addString(root, "uri", path.toUri().toString(), false);
76          addString(root, "ownerID", path.getOwner(), false);
77          MCRPath relativePath = path.getRoot().relativize(path);
78          boolean isRoot = relativePath.toString().isEmpty();
79          addString(root, "name", (isRoot ? "" : relativePath.getFileName().toString()), true);
80          addString(root, "path", toStringValue(relativePath), true);
81          if (!isRoot) {
82              addString(root, "parentPath", toStringValue(relativePath.getParent()), true);
83          }
84          addBasicAttributes(root, attr, path);
85          Element numChildren = new Element("numChildren");
86          Element here = new Element("here");
87          root.addContent(numChildren);
88          numChildren.addContent(here);
89  
90          Element nodes = new Element("children");
91          root.addContent(nodes);
92          SortedMap<MCRPath, MCRFileAttributes<?>> directories = new TreeMap<>();
93          SortedMap<MCRPath, MCRFileAttributes<?>> files = new TreeMap<>();
94          try (DirectoryStream<Path> dirStream = Files.newDirectoryStream(path)) {
95              LOGGER.debug(() -> "Opened DirectoryStream for " + path);
96              Function<Path, MCRFileAttributes<?>> attrResolver = p -> {
97                  try {
98                      return Files.readAttributes(p, MCRFileAttributes.class);
99                  } catch (IOException e) {
100                     throw new UncheckedIOException(e);
101                 }
102             };
103             if (dirStream instanceof SecureDirectoryStream) {
104                 //fast path
105                 LOGGER.debug(() -> "Using SecureDirectoryStream code path for " + path);
106                 attrResolver = p -> {
107                     try {
108                         MCRMD5AttributeView attributeView = ((SecureDirectoryStream<Path>) dirStream)
109                             .getFileAttributeView(p.getFileName(), MCRMD5AttributeView.class);
110                         return attributeView.readAllAttributes();
111                     } catch (IOException e) {
112                         throw new UncheckedIOException(e);
113                     }
114                 };
115 
116             }
117             for (Path child : dirStream) {
118                 MCRFileAttributes<?> childAttrs;
119                 try {
120                     childAttrs = attrResolver.apply(child);
121                 } catch (UncheckedIOException e) {
122                     throw e.getCause();
123                 }
124                 if (childAttrs.isDirectory()) {
125                     directories.put(MCRPath.toMCRPath(child), childAttrs);
126                 } else {
127                     files.put(MCRPath.toMCRPath(child), childAttrs);
128                 }
129             }
130         }
131         //store current directory statistics
132         addString(here, "directories", Integer.toString(directories.size()), false);
133         addString(here, "files", Integer.toString(files.size()), false);
134         for (Map.Entry<MCRPath, MCRFileAttributes<?>> dirEntry : directories.entrySet()) {
135             Element child = new Element("child");
136             child.setAttribute("type", "directory");
137             addString(child, "name", dirEntry.getKey().getFileName().toString(), true);
138             addString(child, "uri", dirEntry.getKey().toUri().toString(), false);
139             nodes.addContent(child);
140             addBasicAttributes(child, dirEntry.getValue(), dirEntry.getKey());
141         }
142         for (Map.Entry<MCRPath, MCRFileAttributes<?>> fileEntry : files.entrySet()) {
143             Element child = new Element("child");
144             child.setAttribute("type", "file");
145             addString(child, "name", fileEntry.getKey().getFileName().toString(), true);
146             addString(child, "uri", fileEntry.getKey().toUri().toString(), false);
147             nodes.addContent(child);
148             addAttributes(child, fileEntry.getValue(), fileEntry.getKey());
149         }
150 
151         LOGGER.debug("MCRDirectoryXML: end listing of directory {}", path);
152 
153         return doc;
154 
155     }
156 
157     /**
158      * Returns metadata of the file retrievable by 'path' in XML form. Same as
159      * {@link #getFileXML(MCRPath, BasicFileAttributes)}, but attributes are retrieved first.
160      */
161     public static Document getFileXML(MCRPath path) throws IOException {
162         MCRFileAttributes<?> attrs = Files.readAttributes(path, MCRFileAttributes.class);
163         return getFileXML(path, attrs);
164     }
165 
166     /**
167      * Returns metadata of the file retrievable by 'path' in XML form.
168      *
169      * @param path
170      *            Path to File
171      * @param attrs
172      *            file attributes of given file
173      */
174     public static Document getFileXML(MCRPath path, BasicFileAttributes attrs) throws IOException {
175         Element root = new Element("file");
176         root.setAttribute("uri", path.toUri().toString());
177         root.setAttribute("ownerID", path.getOwner());
178         String fileName = path.getFileName().toString();
179         root.setAttribute("name", fileName);
180         String absolutePath = path.getOwnerRelativePath();
181         root.setAttribute("path", absolutePath);
182         root.setAttribute("extension", getFileExtension(fileName));
183         root.setAttribute("returnId",
184             MCRMetadataManager.getObjectId(MCRObjectID.getInstance(path.getOwner()), 10, TimeUnit.SECONDS).toString());
185         Collection<MCRCategoryID> linksFromReference = MCRCategLinkServiceFactory.getInstance().getLinksFromReference(
186             new MCRCategLinkReference(path));
187         for (MCRCategoryID category : linksFromReference) {
188             Element catEl = new Element("category");
189             catEl.setAttribute("id", category.toString());
190             root.addContent(catEl);
191         }
192         if (!attrs.isDirectory() && attrs instanceof MCRFileAttributes<?>) {
193             addAttributes(root, (MCRFileAttributes<?>) attrs, path);
194         } else {
195             addBasicAttributes(root, attrs, path);
196         }
197         return new Document(root);
198     }
199 
200     private static String getFileExtension(String fileName) {
201         if (fileName.endsWith(".")) {
202             return "";
203         }
204         int pos = fileName.lastIndexOf(".");
205         return pos == -1 ? "" : fileName.substring(pos + 1);
206     }
207 
208     private static String toStringValue(MCRPath relativePath) {
209         if (relativePath == null) {
210             return SEPARATOR_STRING;
211         }
212         String pathString = relativePath.toString();
213         if (pathString.isEmpty()) {
214             return SEPARATOR_STRING;
215         }
216         if (pathString.equals(SEPARATOR_STRING)) {
217             return pathString;
218         }
219         return SEPARATOR + pathString + SEPARATOR;
220     }
221 
222     private static void addBasicAttributes(Element root, BasicFileAttributes attr, MCRPath path) throws IOException {
223         addString(root, "size", String.valueOf(attr.size()), false);
224         addDate(root, "created", attr.creationTime());
225         addDate(root, "lastModified", attr.lastModifiedTime());
226         addDate(root, "lastAccessed", attr.lastAccessTime());
227         if (attr.isRegularFile()) {
228             addString(root, "contentType", MCRContentTypes.probeContentType(path), false);
229         }
230     }
231 
232     private static void addAttributes(Element root, MCRFileAttributes<?> attr, MCRPath path) throws IOException {
233         addBasicAttributes(root, attr, path);
234         addString(root, "md5", attr.md5sum(), false);
235     }
236 
237     private static void addDate(Element parent, String type, FileTime date) {
238         Element xDate = new Element("date");
239         parent.addContent(xDate);
240         xDate.setAttribute("type", type);
241         xDate.addContent(date.toString());
242     }
243 
244     private static void addString(Element parent, String itemName, String content, boolean preserve) {
245         if (content == null) {
246             return;
247         }
248         Element child = new Element(itemName).addContent(content);
249         if (preserve) {
250             child.setAttribute("space", "preserve", MCRConstants.XML_NAMESPACE);
251         }
252         parent.addContent(child);
253     }
254 }