1 package org.kuali.ole.docstore.repository;
2
3 import org.apache.commons.io.FileUtils;
4 import org.kuali.ole.RepositoryManager;
5 import org.kuali.ole.docstore.OleDocStoreException;
6 import org.kuali.ole.docstore.model.enums.DocFormat;
7 import org.kuali.ole.docstore.model.xmlpojo.ingest.AdditionalAttributes;
8 import org.kuali.ole.docstore.model.xmlpojo.ingest.RequestDocument;
9 import org.kuali.ole.pojo.OleException;
10 import org.kuali.ole.repository.NodeHandler;
11 import org.slf4j.Logger;
12 import org.slf4j.LoggerFactory;
13
14 import javax.jcr.*;
15 import java.io.*;
16 import java.util.Calendar;
17 import java.util.Collection;
18 import java.util.Date;
19 import java.util.Iterator;
20
21 import static org.kuali.ole.docstore.process.ProcessParameters.*;
22
23
24
25
26
27
28
29
30 public class CustomNodeManager
31 implements NodeManager {
32 private static final Logger logger = LoggerFactory.getLogger(NodeHandler.class);
33 private static CustomNodeManager ourInstance = new CustomNodeManager();
34
35 protected int numLevels = 3;
36
37 public static CustomNodeManager getInstance() {
38 return ourInstance;
39 }
40
41 public CustomNodeManager() {
42 }
43
44 public Node getParentNode(RequestDocument requestDocument, Session session) throws OleDocStoreException {
45 Node levelNode = null;
46 try {
47 Node formatNode = getStaticFormatNode(requestDocument, session);
48 synchronized (this.getClass()) {
49 for (int i = 1; i <= numLevels; i++) {
50 levelNode = initLevelNode("l" + i, formatNode, false, session);
51 formatNode = levelNode;
52 }
53
54
55
56 }
57 }
58 catch (Exception e) {
59 throw new OleDocStoreException(e);
60 }
61 return levelNode;
62 }
63
64
65 @Override
66 public void linkNodes(Node node, Node linkedDocumentNode, Session session) throws OleDocStoreException {
67 }
68
69 public Node getStaticFormatNode(RequestDocument doc, Session session) throws RepositoryException {
70 Node root = session.getRootNode();
71 Node categoryNode = initStaticNode(doc.getCategory(), root, session);
72 Node typeNode = initStaticNode(doc.getType(), categoryNode, session);
73 Node formatNode = initStaticNode(doc.getFormat(), typeNode, session);
74 return formatNode;
75 }
76
77 public Node initStaticNode(String nodeName, Node parentNode, Session session) throws RepositoryException {
78 Node node;
79 if (!parentNode.hasNode(nodeName)) {
80 synchronized (session) {
81 node = parentNode.addNode(nodeName, "nt:unstructured");
82 node.setProperty("nodeType", "folder");
83 node.addMixin("mix:referenceable");
84 session.save();
85 }
86 }
87 else {
88 node = parentNode.getNode(nodeName);
89 }
90 return node;
91 }
92
93 public Node initNonStaticNode(String nodeName, Node parentNode) throws RepositoryException {
94 Node node;
95 node = parentNode.addNode(nodeName, "nt:unstructured");
96 node.setProperty("nodeType", "folder");
97 node.addMixin("mix:referenceable");
98 return node;
99 }
100
101 protected void modifyAdditionalAttributes(AdditionalAttributes additionalAttributes,
102 RequestDocument requestDocument) {
103 }
104
105 public synchronized Node initLevelNode(String name, Node parent, boolean isRecursiveCall, Session session)
106 throws Exception {
107 long existing = 0;
108 try {
109 long bucketSize = BUCKET_SIZES.get(name);
110 boolean hasRepeatedChild = HAS_REPEATED_CHILD.get(name);
111 if (parent.hasNode(name)) {
112 NodeIterator existingNodes = parent.getNodes(name);
113 existing = existingNodes.getSize();
114 if (existing <= bucketSize && !((isRecursiveCall && existing == bucketSize) || (!hasRepeatedChild
115 && existing
116 == bucketSize))) {
117 if (hasRepeatedChild && !isRecursiveCall) {
118 existingNodes.skip(existing - 1);
119 return existingNodes.nextNode();
120 }
121 else {
122 Node levelNode = initNonStaticNode(name, parent);
123 if (existing == 0) {
124 session.save();
125 }
126 return levelNode;
127 }
128 }
129 else {
130 if (!STATIC_NODES.contains(parent.getPath())) {
131 parent = initLevelNode(parent.getName(), parent.getParent(), true, session);
132 return initNonStaticNode(name, parent);
133 }
134 else {
135 throw new Exception("Node [" + parent.getName() + "/" + name + "[" + (existing + 1)
136 + "]] Cannot Be Created. CAUSE: TREE [" + bucketSize + "] FULL ");
137 }
138 }
139 }
140 else {
141 return initNonStaticNode(name, parent);
142 }
143 }
144 catch (Exception e) {
145 try {
146 logger.error(
147 "Exception While initializing Node: " + parent.getName() + "/" + name + "[" + (existing + 1)
148 + "] \t to Parent: " + parent.getName(), e);
149 }
150 catch (RepositoryException e1) {
151 }
152 throw e;
153 }
154 }
155
156 public synchronized Node initFileNode(RequestDocument document, String name, Node parentNode, Session session)
157 throws Exception {
158 String uuid = null;
159 Node fileNode = createFileNode(document, name, parentNode, session);
160
161 return fileNode;
162 }
163
164 public synchronized Node createFileNode(RequestDocument document, String name, Node parentNode, Session session)
165 throws OleDocStoreException {
166 Node fileNode = null;
167 try {
168 NodeIterator nodes = parentNode.getNodes(name);
169 if (nodes.getSize() >= BUCKET_SIZE_FILE_NODES) {
170 if (document != null && DocFormat.OLEML.isEqualTo(document.getFormat())) {
171 throw new RuntimeException("FileNode creation failed as the BUCKET_SIZE[" + BUCKET_SIZE_FILE_NODES
172 + "] is FULL: for the doc: " + document.getFormat() + "\n@ level: "
173 + parentNode.getPath() + "/" + name + "[" + (nodes.getSize() + 1) + "]");
174 }
175 else {
176 parentNode = initLevelNode(parentNode.getName(), parentNode.getParent(), true, session);
177 }
178 }
179
180 fileNode = parentNode.addNode(name, "olefile");
181 fileNode.addMixin("mix:referenceable");
182
183
184
185
186
187
188
189 AdditionalAttributes additionalAttributes = document.getAdditionalAttributes();
190
191
192
193
194
195
196
197
198
199 if (additionalAttributes != null) {
200 Collection<String> attributeNames = additionalAttributes.getAttributeNames();
201 if (attributeNames != null && attributeNames.size() > 0) {
202 for (Iterator<String> iterator = attributeNames.iterator(); iterator.hasNext(); ) {
203 String attributeName = iterator.next();
204 String attributeValue = additionalAttributes.getAttribute(attributeName);
205 fileNode.setProperty(attributeName, attributeValue);
206 }
207 }
208
209 }
210
211
212
213
214
215 if (document != null) {
216 document.setUuid(fileNode.getIdentifier());
217 }
218 }
219 catch (Exception e) {
220 throw new OleDocStoreException("File node cannot be created.", e);
221 }
222 return fileNode;
223 }
224
225 public synchronized Node createContentNode(Node fileNode, RequestDocument document, Node parentNode,
226 Session session) throws OleDocStoreException {
227 Node resNode = null;
228 try {
229
230 resNode = fileNode.addNode("jcr:content", "nt:resource");
231 resNode.setProperty("jcr:mimeType", "application/xml");
232 resNode.setProperty("jcr:encoding", "");
233
234 String charset = "UTF-8";
235 byte[] documentBytes = null;
236 try {
237
238
239
240
241
242
243
244
245
246
247
248
249 if (document.getContent() != null && document.getContent().getContent() != null) {
250 documentBytes = document.getContent().getContent().getBytes();
251 }
252
253 else if (document.getDocumentName() != null) {
254 File file = new File(document.getDocumentName());
255 if (file.exists()) {
256 documentBytes = FileUtils.readFileToByteArray(file);
257 }
258 }
259 }
260 catch (Exception e) {
261 logger.error("Failed to convert document string to byte[] with charset " + charset, e);
262 }
263 InputStream docInputStream = new ByteArrayInputStream(documentBytes);
264 Binary binary = session.getValueFactory().createBinary(docInputStream);
265 resNode.setProperty("jcr:data", binary);
266 Calendar lastModified = Calendar.getInstance();
267 lastModified.setTimeInMillis(new Date().getTime());
268 resNode.setProperty("jcr:lastModified", lastModified);
269 }
270 catch (RepositoryException e) {
271 throw new OleDocStoreException(e);
272 }
273 return resNode;
274 }
275
276 public void enableVersioning(Node node) throws OleDocStoreException {
277 try {
278 node.addMixin("mix:versionable");
279 }
280 catch (Exception e) {
281 throw new OleDocStoreException(e);
282 }
283 }
284
285 public Node getNodeByUUID(Session session, String uuid) throws OleDocStoreException {
286 logger.debug("Started getting node for UUID:" + uuid);
287 try {
288 return session.getNodeByIdentifier(uuid);
289 }
290 catch (RepositoryException e) {
291 throw new OleDocStoreException("getNodeByUUID failed", e);
292 }
293 }
294
295 public String getData(Node nodeByUUID) throws OleDocStoreException, RepositoryException, FileNotFoundException {
296 StringBuffer stringBuffer = new StringBuffer();
297 if (null != nodeByUUID) {
298 Node jcrContent = nodeByUUID.getNode("jcr:content");
299 Binary binary = jcrContent.getProperty("jcr:data").getBinary();
300 InputStream content = binary.getStream();
301
302 Writer writer;
303 try {
304 writer = new StringWriter();
305 char[] buffer = new char[1024];
306 try {
307 Reader reader = new BufferedReader(new InputStreamReader(content, "UTF-8"));
308 int n;
309 while ((n = reader.read(buffer)) != -1) {
310 writer.write(buffer, 0, n);
311 }
312 }
313 finally {
314 stringBuffer.append(writer.toString());
315 content.close();
316 }
317
318 }
319 catch (IOException e) {
320 logger.info("failure during checkOut of ", e);
321 }
322
323 }
324
325 return stringBuffer.toString();
326 }
327
328 public byte[] getBinaryData(Node nodeByUUID) throws RepositoryException, IOException {
329 byte[] bytes = null;
330 if (null != nodeByUUID) {
331 Node jcrContent = nodeByUUID.getNode("jcr:content");
332 Binary binary = jcrContent.getProperty("jcr:data").getBinary();
333 InputStream inputStream = binary.getStream();
334 bytes = getBytesFromInputStream(inputStream);
335 }
336 return bytes;
337 }
338
339 public byte[] getBytesFromInputStream(InputStream is) throws IOException {
340 long length = is.available();
341 if (length > Integer.MAX_VALUE) {
342
343 }
344 byte[] bytes = new byte[(int) length];
345
346 int offset = 0;
347 int numRead = 0;
348 while (offset < bytes.length && (numRead = is.read(bytes, offset, bytes.length - offset)) >= 0) {
349 offset += numRead;
350 }
351
352 if (offset < bytes.length) {
353 throw new IOException("Could not completely read file ");
354 }
355
356 is.close();
357 return bytes;
358 }
359
360 }