package org.wyona.yarep.impl.repo.treefs; import java.io.File; import org.apache.avalon.framework.configuration.Configuration; import org.apache.avalon.framework.configuration.DefaultConfigurationBuilder; import org.apache.log4j.Category; import org.wyona.commons.io.FileUtil; import org.wyona.yarep.core.Map; import org.wyona.yarep.core.NoSuchNodeException; import org.wyona.yarep.core.Node; import org.wyona.yarep.core.Path; import org.wyona.yarep.core.RepositoryException; import org.wyona.yarep.core.UID; import org.wyona.yarep.impl.repo.fs.FileSystemRepository; import org.wyona.yarep.impl.repo.treefs.map.TreeFileSystemMap; /** * * This implementation extends the FileSystemRepository and should provide better scaling * with a large number of documents. * * To avoid a large number of directories on the same level, this implementation splits * the uuid and creates a tree as follows: * * Example: * uuids: 123456, 123457, 456789 * directories: * /content-root/12/34/56 * /content-root/12/34/57 * /content-root/45/67/89 * * The splitting can be configured by two parameters: * split-interval: number of characters on each level (two in the above example) * max-splits: maximum number of splittings * */ public class TreeFileSystemRepository extends FileSystemRepository { private static Category log = Category.getInstance(TreeFileSystemRepository.class); protected int splitInterval; protected int maxSplits; /** * */ public TreeFileSystemRepository() { } /** * */ public TreeFileSystemRepository(String id, File configFile) throws RepositoryException { setID(id); readConfiguration(configFile); } /** * Read respectively load repository configuration */ public void readConfiguration(File configFile) throws RepositoryException { this.configFile = configFile; DefaultConfigurationBuilder builder = new DefaultConfigurationBuilder(); Configuration config; try { config = builder.buildFromFile(configFile); name = config.getChild("name", false).getValue(); Configuration pathConfig = config.getChild("paths", false); String pathsClassname = pathConfig.getAttribute("class", null); if (pathsClassname != null) { log.debug(pathsClassname); Class pathsClass = Class.forName(pathsClassname); map = (Map) pathsClass.newInstance(); } else { map = (Map) Class.forName("org.wyona.yarep.impl.DefaultMapImpl").newInstance(); //map = new org.wyona.yarep.impl.DefaultMapImpl(); } map.readConfig(pathConfig, configFile); this.contentDir = new File(config.getChild("content", false).getAttribute("src")); if (!this.contentDir.isAbsolute()) { this.contentDir = FileUtil.file(configFile.getParent(), this.contentDir.toString()); } splitInterval = config.getChild("split-interval", false).getValueAsInteger(); maxSplits = config.getChild("max-splits", false).getValueAsInteger(); if (map instanceof TreeFileSystemMap) { ((TreeFileSystemMap)map).setSplitInterval(splitInterval); ((TreeFileSystemMap)map).setMaxSplits(maxSplits); } log.debug("content dir: " + this.contentDir); } catch (Exception e) { log.error(e.toString()); throw new RepositoryException("Could not read repository configuration: " + e.getMessage(), e); } } /** * @see org.wyona.yarep.core.Repository#existsNode(java.lang.String) */ public boolean existsNode(String path) throws RepositoryException { // strip trailing slash: if (path.length() > 1 && path.endsWith("/")) { path = path.substring(0, path.length() - 1); } if (map.exists(new Path(path))) { return true; } else { return false; } } /** * @see org.wyona.yarep.core.Repository#getNode(java.lang.String) */ public Node getNode(String path) throws NoSuchNodeException, RepositoryException { // strip trailing slash: if (path.length() > 1 && path.endsWith("/")) { path = path.substring(0, path.length() - 1); } String uuid; if (!map.exists(new Path(path))) { throw new NoSuchNodeException(path, this); } else { UID uid = map.getUID(new Path(path)); uuid = (uid == null) ? path : uid.toString(); } return new TreeFileSystemNode(this, path, uuid); } public int getSplitInterval() { return this.splitInterval; } public int getMaxSplits() { return this.maxSplits; } }