package org.wyona.yarep.util; import org.apache.logging.log4j.Logger; import org.apache.logging.log4j.LogManager; import org.wyona.yarep.core.Node; import org.wyona.yarep.core.Path; import org.wyona.yarep.core.Repository; import org.wyona.yarep.core.RepositoryException; import org.wyona.yarep.core.RepositoryFactory; import org.wyona.yarep.core.Revision; import java.util.Date; /** * Various yarep utility methods */ public class YarepUtil { private static Logger log = LogManager.getLogger(YarepUtil.class); /** * Get revision of a specific node for a specific date (or just before) * (also see http://en.wikipedia.org/wiki/Point-in-time_recovery) * * @param node Yarep node for which a specific revision shall be found * @param pointInTime Date for which a revision shall be found, whereas the creation date of the revision is equals or older */ public static Revision getRevision(Node node, Date pointInTime) throws RepositoryException { if (hasInterfaceImplemented(node, "Versionable", "1")) { try { //log.debug("Get revision of node '" + node.getPath() + "' for point in time '" + pointInTime + "'..."); return ((org.wyona.yarep.core.attributes.VersionableV1) node).getRevision(pointInTime); } catch(Exception e) { log.error(e, e); throw new RepositoryException(e.getMessage()); } } log.warn("Use SLOW implementation!"); String path = null; try { path = node.getPath(); // INFO: Find the revision which was the current revision at (or before) the given date // IMPORTANT TODO: Improve this algorithm re performance/scalability Revision[] revisions = node.getRevisions(); if (log.isDebugEnabled()) log.debug("Trying to find revision for node " + node.getPath() + " at time " + pointInTime); for (int i = revisions.length - 1; i >= 0; i--) { //if (log.isDebugEnabled()) log.debug("Checking revision: " + revisions[i].getName() + " " + revisions[i].getCreationDate()); Date creationDate = revisions[i].getCreationDate(); if (creationDate.before(pointInTime) || creationDate.equals(pointInTime)) { if (log.isDebugEnabled()) log.debug("Revision found: " + revisions[i].getRevisionName()); return revisions[i]; } } // TODO: what should happen in this case? log.warn("No revision found for node " + path + " and date " + pointInTime); return null; } catch (Exception e) { log.error(e, e); throw new RepositoryException("No revision found for node " + path + " and date " + pointInTime + ": " + e.getMessage(), e); } } /** * Get revision of a specific node for a specific date (or just before) * (also see http://en.wikipedia.org/wiki/Point-in-time_recovery) * * @param repo Repository containing node or which used to contain node * @param path Absolute repository path of node for which a specific revision shall be found * @param pointInTime Date for which a revision shall be found, whereas the creation date of the revision is equals or older */ public static Revision getRevision(Repository repo, String path, Date pointInTime) throws RepositoryException { if (hasInterfaceImplemented(repo, "VersionableRepository", "1")) { try { return ((org.wyona.yarep.core.attributes.VersionableRepositoryV1) repo).getRevision(path, pointInTime); } catch(Exception e) { log.error(e, e); throw new RepositoryException(e.getMessage()); } } else { throw new RepositoryException("Repository '" + repo + "' has interface VersionableRepositoryV1 not implemented!"); } } /** * */ public RepoPath getRepositoryPath(Path path, RepositoryFactory repoFactory) throws RepositoryException { Repository repo = null; // Determine possible Repository ID. If such a repo ID doesn't exist, then use ROOT repository String[] splittedPath = path.toString().split("/"); if (splittedPath != null) { if (splittedPath.length < 2) { log.debug("Length = " + splittedPath.length + ". Use ROOT repository."); } else { if (repoFactory.exists(splittedPath[1])) { repo = repoFactory.newRepository(splittedPath[1]); log.debug("New Repository: " + repo.getID() + " - " + repo.getName()); log.debug("Repo ID length: " + repo.getID().length()); path = new Path(path.toString().substring(repo.getID().length() + 1)); log.debug("New Path: " + path); return new RepoPath(repo, path); } else { log.debug("No such repository \"" + splittedPath[1] + "\". Use ROOT repository."); } } } else { log.debug("Path could not be split. Use ROOT repository."); } // First repository shall be ROOT repository repo = repoFactory.firstRepository(); log.debug("ROOT Repository: " + repo.getID() + " - " + repo.getName()); log.debug("Path (still original): " + path); return new RepoPath(repo, path); } /** * Copies the content of one repository into another repository. * Currently copies nodes and properties, but no revisions. * @param srcRepo repository to be copied * @param destRepo assumed to be empty * @throws RepositoryException */ public static void copyRepository(Repository srcRepo, Repository destRepo) throws RepositoryException { Node srcRootNode = srcRepo.getRootNode(); Node[] childNodes = srcRootNode.getNodes(); for (int i = 0; i < childNodes.length; i++) { importNodeRec(childNodes[i], srcRepo, destRepo); } } /** * Imports nodes recursively * @param srcNode Source node * @param srcRepo Source repository * @param destRepo Destination repository * @throws RepositoryException */ protected static void importNodeRec(Node srcNode, Repository srcRepo, Repository destRepo) throws RepositoryException { try { String destPath = srcNode.getPath(); if (log.isInfoEnabled()) log.info("Importing node to "+destPath+"..."); destRepo.importNode(destPath, srcNode.getPath(), srcRepo); // recursively import children Node[] childNodes = srcNode.getNodes(); for (int i = 0; i < childNodes.length; i++) { importNodeRec(childNodes[i], srcRepo, destRepo); } } catch (Exception e) { //throw new RepositoryException(e.getMessage(), e); log.error("Could not import node: " + srcNode.getPath() + ": " + e.getMessage(), e); } } /** * Creates the node named by this abstract pathname, including any necessary but nonexistent parent nodes (similar to java.io.File.mkdirs()). * @param repo Repository within the node shall be created * @param path Node path * @param nodeType Type of node (e.g. resource or collection) * @return node if it has been created successfully or if it already exists */ public static Node addNodes(Repository repo, String path, int nodeType) throws RepositoryException { if (repo.existsNode(path)) { log.warn("Node '" + path + "' already exists, hence ignore creation, but return existing node instance."); return repo.getNode(path); } else { log.debug("Try to create node: " + path); org.wyona.commons.io.Path parentPath = new org.wyona.commons.io.Path(path).getParent(); if (parentPath != null) { Node parentNode = null; if (repo.existsNode(parentPath.toString())) { parentNode = repo.getNode(parentPath.toString()); } else { parentNode = addNodes(repo, parentPath.toString(), org.wyona.yarep.core.NodeType.COLLECTION); } log.debug("Parent node: " + parentNode.getPath()); Node childNode = parentNode.addNode(new Path(path).getName().toString(), nodeType); log.info("Child node has been created: " + childNode.getPath()); return childNode; } else { throw new RepositoryException("Root node does not have a parent!"); } } } /** * Copy yarep node * @param repo Data repository * @param source Path of source node (to be copied) * @param destination Path of destination node (copy of orginial node) * @return Destination (copy of) node */ public static Node copyNode(Repository repo, String source, String destination) throws RepositoryException, java.io.IOException { log.warn("DEBUG: Try to copy node from '" + source + "' to '" + destination + "'..."); if (!repo.existsNode(source)) throw new RepositoryException("No such source node: " + source); Node src = repo.getNode(source); if (src.getType() == org.wyona.yarep.core.NodeType.RESOURCE) { Node dest = YarepUtil.addNodes(repo, destination, org.wyona.yarep.core.NodeType.RESOURCE); byte buffer[] = new byte[8192]; int bytesRead; java.io.InputStream in = src.getInputStream(); java.io.OutputStream out = dest.getOutputStream(); while ((bytesRead = in.read(buffer)) != -1) { out.write(buffer, 0, bytesRead); } in.close(); out.close(); return dest; } else if (src.getType() == org.wyona.yarep.core.NodeType.COLLECTION) { Node dest = YarepUtil.addNodes(repo, destination, org.wyona.yarep.core.NodeType.COLLECTION); Node[] sourceChildren = src.getNodes(); for (int i = 0; i < sourceChildren.length; i++) { String childSourcePath = source + "/" + sourceChildren[i].getName(); String childDestPath = destination + "/" + sourceChildren[i].getName(); if(copyNode(repo, childSourcePath, childDestPath) != null) { log.warn("DEBUG: Child node '" + childSourcePath + "' has been copied to '" + childDestPath + "'."); } else { log.warn("Child node '" + childSourcePath + "' did not get copied to '" + childDestPath + "'!"); } } return dest; } else { log.error("No such type '" + src.getType() + "' supported, hence to not copy node with path '" + source + "'!"); } return null; } /** * Check if a class/object has an interface with a specific version implemented */ static private boolean hasInterfaceImplemented(Object object, String attribute, String version) { boolean implemented = false; Class clazz = object.getClass(); while (!clazz.getName().equals("java.lang.Object") && !implemented) { Class[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (interfaces[i].getName().equals("org.wyona.yarep.core.attributes." + attribute + "V" + version)) { implemented = true; break; } // TODO: Why does this not work? //if (interfaces[i].isInstance(iface)) implemented = true; } clazz = clazz.getSuperclass(); } if (implemented) { if (log.isDebugEnabled()) log.debug(clazz.getName() + " does implement " + attribute + "V" + version + " interface!"); } else { if (log.isDebugEnabled()) log.debug(clazz.getName() + " does NOT implement " + attribute + "V" + version + " interface!"); } return implemented; } /** * (Re-)Index repository. * @param repo Repository to be (re-)indexed * @throws RepositoryException */ public static void indexRepository(Repository repo) throws RepositoryException { indexRepository(repo, "/"); } /** * (Re-)Index a particular part/sub-tree of a repository. * @param repo Repository to be (re-)indexed * @param path Path from where (re-)indexing shall be started, which means the node of this particular path and all its children will be (re-)indexed * @throws RepositoryException */ public static void indexRepository(Repository repo, String path) throws RepositoryException { log.warn("DEBUG: Start indexing repository: " + repo.getName()); Node startNode = null; if ("/".equals(path)) { log.warn("DEBUG: Index the whole repository, which might take a while..."); startNode = repo.getRootNode(); } else if (repo.existsNode(path)) { startNode = repo.getNode(path); } else { throw new RepositoryException("No such node: " + path); } indexNodeRecursively(startNode, repo.getIndexer()); log.warn("DEBUG: End indexing repository: " + repo.getName()); } /** * Index nodes recursively * @param node Node to be indexed * @param indexer Indexer of repository * @throws RepositoryException */ protected static void indexNodeRecursively(Node node, org.wyona.yarep.core.search.Indexer indexer) throws RepositoryException { try { // TODO: Make the ignore configurable. Actually the ignore should already be configurable by the repository (see for example src/test/repository/new-vfs-example/repository.xml)! if (node.getName().equals(".svn")) { log.warn("Ignore .svn directories."); return; } log.warn("DEBUG: Index node (inluding properties, but no revisions): " + node.getPath()); indexer.index(node); org.wyona.yarep.core.Property[] properties = node.getProperties(); if (properties != null) { for (org.wyona.yarep.core.Property property : properties) { indexer.index(node, property); } } // TODO: Index also revisions ... (Use iterator if versioning interface available) /* if (VersionableV1) { Iterator it = (VersionableV1).getRevisions(false); } else { Revision[] revisions = node.getRevisions(); } */ // INFO: Index children recursively Node[] childNodes = node.getNodes(); for (int i = 0; i < childNodes.length; i++) { indexNodeRecursively(childNodes[i], indexer); } } catch (Exception e) { //throw new RepositoryException(e.getMessage(), e); log.error("Could not index node: " + node.getPath() + ": " + e.getMessage(), e); } } /** * Check if a node is actually a revision */ static public boolean isRevision(Node node) { boolean implemented = false; Class clazz = node.getClass(); while (!clazz.getName().equals("java.lang.Object") && !implemented) { Class[] interfaces = clazz.getInterfaces(); for (int i = 0; i < interfaces.length; i++) { if (interfaces[i].getName().equals("org.wyona.yarep.core.Revision")) { implemented = true; break; } // TODO: Why does this not work? //if (interfaces[i].isInstance(iface)) implemented = true; } clazz = clazz.getSuperclass(); } if (implemented) { if (log.isDebugEnabled()) log.debug(node.getClass().getName() + " is a Revision!"); } else { if (log.isDebugEnabled()) log.debug(node.getClass().getName() + " is NOT a Revision!"); } return implemented; } }