You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@jackrabbit.apache.org by tr...@apache.org on 2013/08/10 07:53:54 UTC
svn commit: r1512568 [12/39] - in /jackrabbit/commons/filevault/trunk: ./
parent/ vault-cli/ vault-cli/src/ vault-cli/src/main/
vault-cli/src/main/appassembler/ vault-cli/src/main/assembly/
vault-cli/src/main/java/ vault-cli/src/main/java/org/ vault-cl...
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/AggregatorProvider.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/AggregatorProvider.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/AggregatorProvider.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/AggregatorProvider.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,86 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.fs.impl;
+
+import java.util.Collections;
+import java.util.Iterator;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.Aggregator;
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+
+/**
+ * List of configured aggregators that selects one given a repository node.
+ */
+public class AggregatorProvider {
+
+ /**
+ * list of aggregators
+ */
+ private final List<Aggregator> aggregators;
+
+ /**
+ * Constructs a new aggregator provider with a given aggregator list.
+ * @param aggregators the list of aggregators.
+ */
+ public AggregatorProvider(List<Aggregator> aggregators) {
+ this.aggregators = Collections.unmodifiableList(aggregators);
+ }
+
+ /**
+ * Returns the list of aggregators
+ * @return the list of aggregators
+ */
+ public List<Aggregator> getAggregators() {
+ return aggregators;
+ }
+
+ /**
+ * Selects an aggregator that can handle the given node. If no aggregator can
+ * be found, <code>null</code> is returned. Although this is a very rare case
+ * because there should always be a default, catch-all aggregator.
+ *
+ * @param node the node to match
+ * @return an aggregator that handles the node or <code>null</code> if not found.
+ * @throws RepositoryException if a repository error occurs
+ */
+ public Aggregator getAggregator(Node node, String path) throws RepositoryException {
+ for (Aggregator a: aggregators) {
+ if (a.matches(node, path)) {
+ return a;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void dump(DumpContext ctx, boolean isLast) {
+ ctx.println(isLast, "aggregators");
+ ctx.indent(isLast);
+ for (Iterator<Aggregator> iter = aggregators.iterator(); iter.hasNext();) {
+ Aggregator a = iter.next();
+ a.dump(ctx, !iter.hasNext());
+ }
+ ctx.outdent();
+ }
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/ArtifactSetImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/ArtifactSetImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/ArtifactSetImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/ArtifactSetImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,328 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.fs.impl;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.EnumMap;
+import java.util.HashSet;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.PropertyValueArtifact;
+import org.apache.jackrabbit.vault.fs.SerializerArtifact;
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ArtifactIterator;
+import org.apache.jackrabbit.vault.fs.api.ArtifactSet;
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.fs.api.Dumpable;
+import org.apache.jackrabbit.vault.fs.api.ItemFilter;
+import org.apache.jackrabbit.vault.fs.api.ItemFilterSet;
+import org.apache.jackrabbit.vault.fs.io.Serializer;
+
+/**
+ * Implements a set of artifacts. The artifacts can be retrieved via an
+ * {@link ArtifactIterator}. Special artifacts like {@link ArtifactType#PRIMARY}
+ * and {@link ArtifactType#DIRECTORY} can be retrieved individually. This set
+ * only allows one of those artifacts per type.
+ *
+ * All modifications to this set have no influence on the persisted data.
+ *
+ */
+public class ArtifactSetImpl implements Dumpable, ArtifactSet {
+
+ /**
+ * the list of artifacts
+ */
+ private final List<Artifact> artifacts = new LinkedList<Artifact>();
+
+ /**
+ * the list of removed
+ */
+ private List<Artifact> removed;
+
+ /**
+ * Set of artifacts that are only allowed once
+ */
+ private final Map<ArtifactType, Artifact> single = new EnumMap<ArtifactType, Artifact>(ArtifactType.class);
+
+ /**
+ * list of hint artifacts
+ */
+ private Set<Artifact> hints;
+
+ /**
+ * The item filter set that defines the coverage of this artifact set
+ */
+ private ItemFilterSet coverageFilter = (ItemFilterSet) new ItemFilterSet().addInclude(ItemFilter.ALL);
+
+ public ItemFilterSet getCoverage() {
+ return coverageFilter;
+ }
+
+ /**
+ * Sets the item filter set that defines the coverage of the items in
+ * this artifact set.
+ * @param filter the item filter set.
+ */
+ public void setCoverage(ItemFilterSet filter) {
+ this.coverageFilter = filter;
+ }
+
+ public void addAll(Collection<? extends Artifact> artifacts) {
+ for (Artifact artifact: artifacts) {
+ add(artifact);
+ }
+ }
+
+ public void addAll(ArtifactSet artifacts) {
+ addAll(((ArtifactSetImpl) artifacts).artifacts);
+ }
+
+ public void add(Artifact artifact) {
+ ArtifactType type = artifact.getType();
+ if (type == ArtifactType.PRIMARY || type == ArtifactType.DIRECTORY) {
+ if (single.containsKey(type)) {
+ throw new IllegalArgumentException("Only 1 " + type + " artifact allowed.");
+ }
+ single.put(type, artifact);
+ artifacts.add(artifact);
+ } else if (type == ArtifactType.HINT) {
+ if (hints == null) {
+ hints = new HashSet<Artifact>();
+ }
+ hints.add(artifact);
+ } else {
+ artifacts.add(artifact);
+ }
+ }
+
+ /**
+ * Adds an {@link SerializerArtifact} with the given name, type and
+ * serializer.
+ *
+ * @param parent the parent artifact
+ * @param name the name of the artifact
+ * @param ext the extension of the artifact
+ * @param type the type of the artifact
+ * @param ser the serializer of the artifact
+ * @param lastModified the last modified date
+ * @return the added artifact
+ */
+ public SerializerArtifact add(Artifact parent, String name, String ext,
+ ArtifactType type, Serializer ser,
+ long lastModified) {
+ SerializerArtifact a = new SerializerArtifact(parent, name, ext, type, ser, lastModified);
+ add(a);
+ return a;
+ }
+
+ /**
+ * Adds an {@link PropertyValueArtifact} with the given name, type and
+ * serializer
+ *
+ * @param parent parent artifact
+ * @param relPath the name of the artifact
+ * @param ext the extension
+ * @param type the type of the artifact
+ * @param prop the property of the artifact
+ * @param lastModified the last modified date. can be 0.
+ *
+ * @return a collection of artifacts
+ * @throws RepositoryException if an error occurs.
+ */
+ public Collection<PropertyValueArtifact> add(Artifact parent, String relPath,
+ String ext, ArtifactType type, Property prop, long lastModified)
+ throws RepositoryException {
+ Collection<PropertyValueArtifact> a =
+ PropertyValueArtifact.create(parent, relPath, ext, type, prop, lastModified);
+ addAll(a);
+ return a;
+ }
+
+
+ public Artifact getPrimaryData() {
+ return single.get(ArtifactType.PRIMARY);
+ }
+
+ public Artifact getDirectory() {
+ return single.get(ArtifactType.DIRECTORY);
+ }
+
+ public boolean isEmpty() {
+ return artifacts.isEmpty();
+ }
+
+ public int size() {
+ return artifacts.size();
+ }
+
+ /**
+ * Removes the artifact from this set.
+ *
+ * @param a the artifact to remove
+ * @return <code>true</code> if the artifact was removed.
+ */
+ public boolean remove(Artifact a) {
+ if (artifacts.remove(a)) {
+ single.remove(a.getType());
+ if (removed == null) {
+ removed = new LinkedList<Artifact>();
+ removed.add(a);
+ }
+ return true;
+ } else {
+ return false;
+ }
+ }
+
+ /**
+ * Puts the given artifact to this set. In comparison to the add operations,
+ * an already existing artifact is replaced. If the type of the newly
+ * artifact is {@link ArtifactType#BINARY} it equivalent with the
+ * same name is replaced.
+ *
+ * @param a the artifact to put
+ * @return the previous artifact or <code>null</code>
+ */
+ public Artifact put(Artifact a) {
+ Artifact prev = null;
+ int idx = artifacts.indexOf(a);
+ if (idx >=0) {
+ prev = artifacts.remove(idx);
+ }
+ single.put(a.getType(), a);
+ artifacts.add(a);
+ return prev;
+ }
+
+ /**
+ * Returns the number of artifacts in this set that have the given type.
+ * @param type the artifact type
+ * @return the number of artifacts in this set that have the given type.
+ */
+ public int size(ArtifactType type) {
+ int num = 0;
+ if (type == ArtifactType.HINT) {
+ num = hints == null ? 0 : hints.size();
+ } else {
+ for (Artifact a: artifacts) {
+ if (a.getType() == type) {
+ num++;
+ }
+ }
+ }
+ return num;
+ }
+
+ /**
+ * Returns a collection of all artifacts that have the given type.
+ * @param type the type of the artifacts to return
+ * @return the artifacts
+ */
+ public Collection<Artifact> values(ArtifactType type) {
+ List<Artifact> ret = new LinkedList<Artifact>();
+ if (type == ArtifactType.HINT) {
+ if (hints != null) {
+ ret.addAll(hints);
+ }
+ } else {
+ for (Artifact a: artifacts) {
+ if (type == null || a.getType() == type) {
+ ret.add(a);
+ }
+ }
+ }
+ return ret;
+ }
+
+ /**
+ * Returns a collection of all artifacts
+ * @return the artifacts
+ */
+ public Collection<Artifact> values() {
+ return Collections.unmodifiableList(artifacts);
+ }
+
+ /**
+ * Returns the collection of removed artifacts
+ * @return the removed artifacts
+ */
+ public Collection<Artifact> removed() {
+ if (removed == null) {
+ return Collections.emptyList();
+ } else {
+ return Collections.unmodifiableList(removed);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String toString() {
+ StringBuilder buf = new StringBuilder("ArtifactSet={");
+ Iterator<Artifact> iter = artifacts.iterator();
+ while (iter.hasNext()) {
+ buf.append(iter.next());
+ if (iter.hasNext()) {
+ buf.append(", ");
+ }
+ }
+ buf.append('}');
+ return buf.toString();
+ }
+
+ /**
+ * Returns the artifact with the given path or <code>null</code> if it does
+ * not exist.
+ * @param path the name of the artifact.
+ * @return the desired artifact or <code>null</code>
+ */
+ public Artifact getArtifact(String path) {
+ for (Artifact a: artifacts) {
+ if (a.getRelativePath().equals(path)) {
+ return a;
+ }
+ }
+ return null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void dump(DumpContext ctx, boolean isLast) {
+ ctx.println(isLast, "Artifacts");
+ ctx.indent(isLast);
+ for (Artifact a: artifacts) {
+ a.dump(ctx, false);
+ }
+ ctx.println(true, "Coverage");
+ ctx.indent(true);
+ coverageFilter.dump(ctx, true);
+ ctx.outdent();
+ ctx.outdent();
+ }
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/TransactionImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/TransactionImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/TransactionImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/TransactionImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,770 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.fs.impl;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.HashMap;
+import java.util.Iterator;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+import java.util.TreeMap;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.DirectoryArtifact;
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.ImportArtifact;
+import org.apache.jackrabbit.vault.fs.api.ImportInfo;
+import org.apache.jackrabbit.vault.fs.api.SerializationType;
+import org.apache.jackrabbit.vault.fs.api.VaultFile;
+import org.apache.jackrabbit.vault.fs.api.VaultFileOutput;
+import org.apache.jackrabbit.vault.fs.api.VaultFileSystem;
+import org.apache.jackrabbit.vault.fs.api.VaultFsTransaction;
+import org.apache.jackrabbit.vault.fs.api.VaultInputSource;
+import org.apache.jackrabbit.vault.fs.impl.io.DocViewAnalyzer;
+import org.apache.jackrabbit.vault.fs.impl.io.ImportInfoImpl;
+import org.apache.jackrabbit.vault.fs.impl.io.InputSourceArtifact;
+import org.apache.jackrabbit.vault.fs.impl.io.XmlAnalyzer;
+import org.apache.jackrabbit.vault.fs.io.AutoSave;
+import org.apache.jackrabbit.vault.fs.io.DocViewAnalyzerListener;
+import org.apache.jackrabbit.vault.util.Constants;
+import org.apache.jackrabbit.vault.util.PathComparator;
+import org.apache.jackrabbit.vault.util.PathUtil;
+import org.apache.jackrabbit.vault.util.PlatformNameFormat;
+import org.apache.jackrabbit.vault.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Provides transactional brackets around a write back transaction to
+ * the Vault filesystem. a transaction is always needed due to the fact that
+ * several jcr files could belong to the same artifact.
+ *
+ * TODO: check all vault operations!
+ *
+ */
+public class TransactionImpl implements VaultFsTransaction {
+
+ /**
+ * default logger
+ */
+ private static final Logger log = LoggerFactory.getLogger(TransactionImpl.class);
+
+ private final VaultFileSystem fs;
+
+ private final List<Change> changes = new LinkedList<Change>();
+
+ private final Map<String, DotXmlInfo> dotXmlNodes = new HashMap<String, DotXmlInfo>();
+
+ private boolean verbose;
+
+ private final AutoSave autoSave = new AutoSave();
+
+ public TransactionImpl(VaultFileSystem fs) {
+ this.fs = fs;
+ }
+
+ public AutoSave getAutoSave() {
+ return autoSave;
+ }
+
+ public boolean isVerbose() {
+ return verbose;
+ }
+
+ public void setVerbose(boolean verbose) {
+ this.verbose = verbose;
+ }
+
+ public void delete(VaultFile file) throws IOException {
+ if (file != null) {
+ changes.add(new Change(Type.DELETED, file, null));
+ }
+ }
+
+ public void modify(VaultFile file, VaultInputSource input) throws IOException {
+ final Change change = new Change(Type.MODIFIED, file, input);
+ changes.add(change);
+
+ // special handling for .content.xml
+ if (file.getName().equals(Constants.DOT_CONTENT_XML)) {
+ String repoParentPath = file.getAggregate().getPath();
+ // analyze the xml to scan for added nodes
+ DocViewAnalyzer.analyze(
+ new DocViewAnalyzerListener(){
+ public void onNode(String path, boolean intermediate, String nodeType) {
+ if (!intermediate) {
+ dotXmlNodes.put(path, new DotXmlInfo(change, nodeType == null));
+ }
+ }
+ }, fs.getAggregateManager().getSession(), repoParentPath, input);
+ }
+ }
+
+ public VaultFileOutput add(String path, VaultInputSource input)
+ throws IOException, RepositoryException {
+
+ String repoPath = PlatformNameFormat.getRepositoryPath(path, true);
+ String repoName = Text.getName(repoPath);
+ String parentPath = Text.getRelativeParent(path, 1);
+ String repoParentPath = Text.getRelativeParent(repoPath, 1);
+
+ if (repoName.equals(Constants.DOT_CONTENT_XML)) {
+ // special handling for .content.xml
+ final Change change = new Change(Type.ADDED_X, repoParentPath, path, input);
+ changes.add(change);
+
+ // analyze the xml to scan for added nodes
+ DocViewAnalyzer.analyze(
+ new DocViewAnalyzerListener(){
+ public void onNode(String path, boolean intermediate, String nodeType) {
+ if (!intermediate) {
+ dotXmlNodes.put(path, new DotXmlInfo(change, nodeType == null));
+ }
+ }
+ }, fs.getAggregateManager().getSession(), repoParentPath, input);
+ // create artifact
+ String parentExt = parentPath.endsWith(".dir") ? ".dir" : "";
+ Artifact parent = new DirectoryArtifact(Text.getName(repoParentPath), parentExt);
+ InputSourceArtifact isa = new InputSourceArtifact(
+ parent,
+ Constants.DOT_CONTENT_XML,
+ "",
+ ArtifactType.PRIMARY, input,
+ SerializationType.XML_DOCVIEW);
+ isa.setContentType("text/xml");
+ // attach to change
+ change.isa = isa;
+ return new VaultFileOutputImpl(change);
+
+ } else {
+ // normal file, detect type
+ SerializationType serType = SerializationType.GENERIC;
+ ArtifactType aType = ArtifactType.FILE;
+ String extension = "";
+ int idx = repoName.lastIndexOf('.');
+ if (idx > 0) {
+ String base = repoName.substring(0, idx);
+ String ext = repoName.substring(idx);
+ if (ext.equals(".xml")) {
+ // this can either be an generic exported docview or
+ // a 'user-xml' that is imported as file
+ // btw: this only works for input sources that can
+ // refetch their input stream
+ serType = XmlAnalyzer.analyze(input);
+ if (serType == SerializationType.XML_DOCVIEW) {
+ // in this case, the extension was added by the exporter.
+ aType = ArtifactType.PRIMARY;
+ repoName = base;
+ extension = ext;
+ } else {
+ // force generic
+ serType = SerializationType.GENERIC;
+ }
+ } else if (ext.equals(".xcnd")) {
+ aType = ArtifactType.PRIMARY;
+ serType = SerializationType.CND;
+ repoName = base;
+ extension = ext;
+ } else if (ext.equals(".binary")) {
+ aType = ArtifactType.BINARY;
+ repoName = base;
+ extension = ext;
+ }
+ }
+ InputSourceArtifact isa = new InputSourceArtifact(null, repoName,
+ extension, aType, input, serType);
+
+ Change change = new Change(Type.ADDED, repoParentPath + "/" + repoName, path, input);
+ change.isa = isa;
+ changes.add(change);
+ return new VaultFileOutputImpl(change);
+ }
+ }
+
+ public void mkdir(String path) throws IOException, RepositoryException {
+ changes.add(new Change(Type.MKDIR, PlatformNameFormat.getRepositoryPath(path), path, null));
+ }
+
+ public Collection<Info> commit() throws RepositoryException, IOException {
+ Map<String, Info> infos = new HashMap<String, Info>();
+
+ // remember all nodes to checkin again
+ ImportInfoImpl allInfos = new ImportInfoImpl();
+
+ // first scan all changes for additions that need to be attached to a
+ // .content.xml change
+ if (!dotXmlNodes.isEmpty()) {
+ Iterator<Change> iter = changes.iterator();
+ while (iter.hasNext()) {
+ Change c = iter.next();
+ if (c.type == Type.ADDED) {
+ if (c.isa.getType() == ArtifactType.BINARY) {
+ DotXmlInfo dxi = dotXmlNodes.get(c.repoPath);
+ if (dxi != null) {
+ dxi.change.add(c);
+ iter.remove();
+ }
+ }
+ } else if (c.type == Type.MKDIR) {
+ DotXmlInfo dxi = dotXmlNodes.get(c.repoPath);
+ if (dxi != null) {
+ iter.remove();
+ }
+ }
+ }
+ }
+
+ // process the changes and group them by artifact path
+ Map<String, TxInfo> modified = new TreeMap<String, TxInfo>(new PathComparator());
+ boolean ignoreMP = true;
+ while (!changes.isEmpty()) {
+ int size = changes.size();
+ // process as many changes that have a parent file
+ Iterator<Change> iter = changes.iterator();
+ while (iter.hasNext()) {
+ Change change = iter.next();
+ if (processChange(change, modified, ignoreMP)) {
+ changes.remove(change);
+ iter = changes.iterator();
+ }
+ }
+ if (changes.size() == size) {
+ if (ignoreMP) {
+ ignoreMP = false;
+ } else {
+ for (Change c: changes) {
+ infos.put(c.filePath, new Info(Type.ERROR, c.filePath));
+ }
+ // abort iteration
+ changes.clear();
+ }
+ } else {
+ // write back the current collected modifications and generate a
+ // new modified info list
+ for (TxInfo info : modified.values()) {
+
+ // check if primary artifact is still present
+ if (info.out == null && info.aggregate == null) {
+ // this was an intermediate directory delete
+ for (String path: info.original.keySet()) {
+ infos.put(path, new Info(Type.DELETED, path));
+ if (verbose) {
+ log.info("...comitting DEL {}", path);
+ }
+ }
+ } else if (info.out.getArtifacts().isEmpty() && info.aggregate != null) {
+ // delete entire node if aggregate is still attached
+ if (info.aggregate.isAttached()) {
+ info.aggregate.remove(false);
+ }
+ // generate infos for the deleted ones
+ for (String path: info.original.keySet()) {
+ infos.put(path, new Info(Type.DELETED, path));
+ if (verbose) {
+ log.info("...comitting DEL {}", path);
+ }
+ }
+ // mark the primary artifact of the parent as modified
+ // TODO fix
+ String cXmlPath = info.parentFile.getPath();
+ if (cXmlPath.endsWith("/")) {
+ cXmlPath+= Constants.DOT_CONTENT_XML;
+ } else {
+ cXmlPath+= "/" + Constants.DOT_CONTENT_XML;
+ }
+ Info i = infos.get(cXmlPath);
+ if (i == null) {
+ infos.put(cXmlPath, new Info(Type.MODIFIED, cXmlPath));
+ }
+ } else if (info.aggregate == null) {
+ // this was and addition
+ // for now, just guess from the artifacts the new files
+ String parentPath = info.parentFile.getPath();
+ if (!parentPath.endsWith("/")) {
+ parentPath += "/";
+ }
+ for (Artifact a: info.out.getArtifacts().values()) {
+ if (a instanceof ImportArtifact) {
+ String path = parentPath + a.getPlatformPath();
+ infos.put(path, new Info(Type.ADDED, path));
+ }
+ }
+
+ ImportInfo ret = info.out.close();
+ if (ret != null) {
+ allInfos.merge(ret);
+ if (verbose) {
+ for (Map.Entry e: ret.getModifications().entrySet()) {
+ log.info("...comitting {} {}", e.getValue(), e.getKey());
+ }
+ }
+ }
+ // modify parent
+ infos.put(info.parentFile.getPath(), new Info(Type.MODIFIED, info.parentFile.getPath()));
+
+ } else {
+ // this was a modification
+ ImportInfo ret = info.out.close();
+ if (ret != null) {
+ allInfos.merge(ret);
+ }
+ for (VaultFile file: info.original.values()) {
+ infos.put(file.getPath(), new Info(Type.MODIFIED, file.getPath()));
+ if (verbose) {
+ log.info("...comitting UPD {}", file.getPath());
+ }
+ }
+ if (verbose && ret != null) {
+ for (Map.Entry e: ret.getModifications().entrySet()) {
+ log.info("...comitting {} {}", e.getValue(), e.getKey());
+ }
+ }
+ }
+ }
+ }
+ modified.clear();
+ fs.invalidate();
+ }
+ if (verbose) {
+ log.info("Persisting changes...");
+ }
+ if (allInfos.numErrors() > 0) {
+ try {
+ fs.getAggregateManager().getSession().refresh(false);
+ } catch (RepositoryException e) {
+ // ignore
+ }
+ throw new RepositoryException("There were errors during commit. Aborting transaction.");
+ }
+ fs.getAggregateManager().getSession().save();
+ allInfos.checkinNodes(fs.getAggregateManager().getSession());
+ fs.invalidate();
+ return infos.values();
+ }
+
+
+ private boolean processChange(Change change, Map<String, TxInfo> modified, boolean ignoreMP)
+ throws RepositoryException, IOException {
+ switch (change.type) {
+ case ADDED_X: {
+ // special handling for .content.xml
+ // filePath: /vltTest/foo/.content.xml
+ // repoPath: /vltTest/foo
+
+ // parentPath: /vltTest/foo
+ String parentPath = Text.getRelativeParent(change.filePath, 1);
+ VaultFile parent = fs.getFile(parentPath);
+ TxInfo txInfo;
+ String repoPath;
+ if (parent == null) {
+ // parentPath: /vltTest
+ parentPath = Text.getRelativeParent(parentPath, 1);
+ parent = fs.getFile(parentPath);
+ repoPath = change.repoPath;
+ String repoName = Text.getName(repoPath);
+ if (parent == null) {
+ // special case if parent is an intermediate directory
+ // that is not created yet. for example _jcr_content
+ // /header.png/_jcr_content/renditions
+
+ // check if parent node exists
+ if (!fs.getAggregateManager().getSession().nodeExists(Text.getRelativeParent(repoPath, 1))) {
+ return false;
+ }
+ while ((parent == null || parent.getAggregate() == null)
+ && parentPath.length() > 0) {
+ String parentName = Text.getName(parentPath);
+ if (parentName.endsWith(".dir")) {
+ parentName = parentName.substring(0, parentName.length() - 4);
+ }
+ repoName = PlatformNameFormat.getRepositoryName(parentName) + "/" + repoName;
+ parentPath = Text.getRelativeParent(parentPath, 1);
+ parent = fs.getFile(parentPath);
+ }
+ if (parent == null || parent.getAggregate() == null) {
+ return false;
+ }
+ String repoRelPath = Text.getName(parentPath) + "/" + repoName;
+ txInfo = modified.get(parent.getAggregate().getPath());
+ if (txInfo == null) {
+ txInfo = new TxInfo(parent.getAggregate());
+ modified.put(parent.getAggregate().getPath(), txInfo);
+ }
+ txInfo.out.getArtifacts().add(new InputSourceArtifact(null,
+ repoRelPath, change.isa.getExtension(),
+ ArtifactType.FILE,
+ change.isa.getInputSource(), change.isa.getSerializationType()
+ ));
+ } else {
+ // repoPath: /vltTest.foo
+ assertInFilter(repoPath);
+ txInfo = modified.get(repoPath);
+ if (txInfo == null) {
+ txInfo = new TxInfo(repoPath,
+ ((AggregateImpl) parent.getAggregate()).create(repoName));
+ txInfo.parentFile = parent;
+ modified.put(repoPath, txInfo);
+ }
+ txInfo.out.getArtifacts().add(change.isa);
+ }
+ } else {
+ // repoPath: /vltTest/foo
+ repoPath = parent.getAggregate().getPath();
+ assertInFilter(repoPath);
+ txInfo = modified.get(repoPath);
+ if (txInfo == null) {
+ txInfo = new TxInfo(parent.getAggregate());
+ txInfo.parentFile = parent;
+ modified.put(repoPath, txInfo);
+ }
+ txInfo.out.getArtifacts().add(change.isa);
+ }
+ if (txInfo == null) {
+ return false;
+ }
+ // add sub changes
+ if (change.subChanges != null) {
+ for (Change sc: change.subChanges) {
+ // need to adjust relative path of input
+ // repoPath = /vltTest/foo
+ // sc.repoPath = /vltTest/foo/text/file
+ // relPath = foo/text/file
+ String relPath = PathUtil.getRelativePath(repoPath, sc.repoPath);
+ relPath = Text.getName(repoPath) + "/" + relPath;
+ if (!relPath.equals(sc.isa.getRelativePath())) {
+ // todo: check if correct platform path
+ sc.isa = new InputSourceArtifact(
+ null,
+ relPath,
+ "",
+ sc.isa.getType(),
+ sc.isa.getInputSource(),
+ sc.isa.getSerializationType()
+ );
+ }
+ txInfo.out.getArtifacts().add(sc.isa);
+ }
+ }
+ if (verbose) {
+ log.info("...scheduling ADD {}/{}", parent.getPath(), Constants.DOT_CONTENT_XML);
+ }
+ } break;
+ case ADDED: {
+ // get parent file
+ String parentPath = Text.getRelativeParent(change.filePath, 1);
+
+ // stop processing if parent file does not exist.
+ VaultFile parent = fs.getFile(parentPath);
+ String repoName = change.isa.getRelativePath();
+ if (parent == null || parent.getAggregate() == null) {
+ if (ignoreMP) {
+ return false;
+ }
+ // Hack: if parent is intermediate directory, search
+ // next valid parent and modify its artifact set.
+ // since we cannot easily determine if the parent is an
+ // intermediate directory, we just process all failing ones
+ // at the end.
+ while ((parent == null || parent.getAggregate() == null)
+ && parentPath.length() > 0) {
+ String parentName = Text.getName(parentPath);
+ if (parentName.endsWith(".dir")) {
+ parentName = parentName.substring(0, parentName.length() - 4);
+ }
+ repoName = PlatformNameFormat.getRepositoryName(parentName) + "/" + repoName;
+ parentPath = Text.getRelativeParent(parentPath, 1);
+ parent = fs.getFile(parentPath);
+ }
+ if (parent == null) {
+ // no parent found ?
+ return false;
+ }
+ String repoPath = parent.getAggregate().getPath();
+ String repoRelPath = Text.getName(repoPath) + "/" + repoName;
+ if (!repoPath.endsWith("/")) {
+ repoPath += "/";
+ }
+ repoPath += repoName;
+ assertInFilter(repoPath);
+ if (false && change.isa.getSerializationType() == SerializationType.XML_DOCVIEW) {
+ // special case that full coverage is below a intermediate
+ // ignore and wait for next cycle
+ } else {
+ TxInfo txInfo = modified.get(parent.getAggregate().getPath());
+ if (txInfo == null) {
+ txInfo = new TxInfo(parent.getAggregate());
+ modified.put(parent.getAggregate().getPath(), txInfo);
+ }
+ txInfo.out.getArtifacts().add(new InputSourceArtifact(null,
+ repoRelPath, change.isa.getExtension(),
+ ArtifactType.FILE,
+ change.isa.getInputSource(), change.isa.getSerializationType()
+ ));
+ }
+ } else {
+ String repoPath = parent.getAggregate().getPath();
+
+ if (!repoPath.endsWith("/")) {
+ repoPath += "/";
+ }
+ repoPath += repoName;
+ assertInFilter(repoPath);
+ TxInfo txInfo = modified.get(repoPath);
+ if (txInfo == null) {
+ txInfo = new TxInfo(repoPath, ((AggregateImpl) parent.getAggregate()).create(repoName));
+ txInfo.setParentFile(parent);
+ modified.put(repoPath, txInfo);
+ }
+ txInfo.out.getArtifacts().add(change.isa);
+
+ }
+ if (verbose) {
+ log.info("...scheduling ADD {}/{}", parent.getPath(), repoName);
+ }
+ } break;
+ case MKDIR:{
+ // get parent file
+ String parentPath = Text.getRelativeParent(change.filePath, 1);
+ String name = Text.getName(change.filePath);
+ VaultFile parent = fs.getFile(parentPath);
+ if (parent == null || parent.isTransient()) {
+ return false;
+ }
+ String repoName = PlatformNameFormat.getRepositoryName(name);
+ int idx = repoName.lastIndexOf('.');
+ if (idx > 0) {
+ String base = repoName.substring(0, idx);
+ String ext = repoName.substring(idx);
+ if (ext.equals(".dir")) {
+ // assume no directories with .dir extension
+ repoName = base;
+ }
+ }
+ String repoPath = parent.getAggregate().getPath();
+ if (!repoPath.endsWith("/")) {
+ repoPath += "/";
+ }
+ repoPath += repoName;
+ assertInFilter(repoPath);
+ TxInfo txInfo = modified.get(repoPath);
+ if (txInfo == null) {
+ txInfo = new TxInfo(repoPath, ((AggregateImpl) parent.getAggregate()).create(repoName));
+ txInfo.setParentFile(parent);
+ modified.put(repoPath, txInfo);
+ }
+ txInfo.out.addArtifact(new DirectoryArtifact(repoName));
+ if (verbose) {
+ log.info("...scheduling MKD {}/{}", parent.getPath(), repoName);
+ }
+ } break;
+ case DELETED: {
+ Aggregate an = change.file.getAggregate();
+ if (an == null) {
+ // intermediate directory
+ // can't handle here
+ assertInFilter(change.repoPath);
+ TxInfo txInfo = new TxInfo(change.repoPath, null);
+ txInfo.original.put(change.file.getPath(), change.file);
+ modified.put(txInfo.artifactsPath, txInfo);
+ } else {
+ assertInFilter(an.getPath());
+ TxInfo txInfo = modified.get(an.getPath());
+ if (txInfo == null) {
+ txInfo = new TxInfo(an);
+ VaultFile dir = null;
+ for (VaultFile rel : change.file.getRelated()) {
+ txInfo.original.put(rel.getPath(), rel);
+ if (rel.isDirectory()) {
+ dir = rel;
+ }
+ }
+ modified.put(txInfo.artifactsPath, txInfo);
+ // set parent file
+ if (dir == null) {
+ txInfo.parentFile = change.file.getParent();
+ } else {
+ txInfo.parentFile = dir.getParent();
+ }
+ }
+ txInfo.out.getArtifacts().remove(change.file.getArtifact());
+ if (verbose) {
+ log.info("...scheduling DEL {}", an.getPath());
+ }
+ }
+ } break;
+ case MODIFIED: {
+ Aggregate an = change.file.getAggregate();
+ TxInfo txInfo = modified.get(an.getPath());
+ if (txInfo == null) {
+ txInfo = new TxInfo(an);
+ VaultFile dir = null;
+ for (VaultFile rel: change.file.getRelated()) {
+ txInfo.original.put(rel.getPath(), rel);
+ if (rel.isDirectory()) {
+ dir = rel;
+ }
+ }
+ modified.put(txInfo.artifactsPath, txInfo);
+ // set parent file
+ if (dir == null) {
+ txInfo.parentFile = change.file.getParent();
+ } else {
+ txInfo.parentFile = dir.getParent();
+ }
+ }
+ InputSourceArtifact isa = new InputSourceArtifact(change.file.getArtifact(), change.input);
+ txInfo.out.getArtifacts().put(isa);
+ // add sub changes
+ if (change.subChanges != null) {
+ for (Change sc: change.subChanges) {
+ // need to adjust relative path of input
+ // repoPath = /vltTest/foo
+ // sc.repoPath = /vltTest/foo/text/file
+ // relPath = foo/text/file
+ String relPath = PathUtil.getRelativePath(change.repoPath, sc.repoPath);
+ relPath = Text.getName(change.repoPath) + "/" + relPath;
+ if (!relPath.equals(sc.isa.getRelativePath())) {
+ // todo: check if correct platform path
+ sc.isa = new InputSourceArtifact(
+ null,
+ relPath,
+ sc.isa.getExtension(),
+ sc.isa.getType(),
+ sc.isa.getInputSource(),
+ sc.isa.getSerializationType()
+ );
+ }
+ txInfo.out.getArtifacts().add(sc.isa);
+ }
+ }
+
+ if (verbose) {
+ log.info("...scheduling UPD {}/{}", isa.getRelativePath());
+ }
+ } break;
+ case MOVED:
+ case ERROR:
+ break;
+ }
+ return true;
+ }
+
+ private void assertInFilter(String repoPath) {
+ if (!fs.getWorkspaceFilter().contains(repoPath)) {
+ log.warn("{} is excluded by the workspace filter. continuing with unknown results.", repoPath);
+ }
+ }
+
+ protected static class Change {
+
+ private final Type type;
+
+ private VaultFile file;
+
+ private VaultInputSource input;
+
+ private String filePath;
+
+ private String repoPath;
+
+ private InputSourceArtifact isa;
+
+ private LinkedList<Change> subChanges;
+
+ public Change(Type type, VaultFile file, VaultInputSource input) {
+ this.type = type;
+ this.file = file;
+ this.repoPath = file.getAggregatePath();
+ String relPath = file.getRepoRelPath();
+ if (relPath != null && relPath.length() > 0) {
+ this.repoPath += "/" + relPath;
+ }
+ this.input = input;
+ }
+
+ public Change(Type type, String repoPath, String filePath, VaultInputSource input) {
+ this.type = type;
+ this.repoPath = repoPath;
+ this.input = input;
+ this.filePath = filePath;
+ }
+
+ public void setInputSource(VaultInputSource input) {
+ this.input = input;
+ }
+
+ public void setContentType(String contentType) {
+ if (isa != null) {
+ isa.setContentType(contentType);
+ }
+ }
+
+ public void add(Change c) {
+ if (subChanges == null) {
+ subChanges = new LinkedList<Change>();
+ }
+ subChanges.add(c);
+ }
+ }
+
+ private static class DotXmlInfo {
+
+ private final Change change;
+
+ private final boolean intermediate;
+
+ private DotXmlInfo(Change change, boolean intermediate) {
+ this.change = change;
+ this.intermediate = intermediate;
+ }
+ }
+ private static class TxInfo {
+
+ private final AggregateImpl aggregate;
+
+ private final String artifactsPath;
+
+ private final AggregateBuilder out;
+
+ private final Map<String, VaultFile> original = new HashMap<String, VaultFile>();
+
+ private VaultFile parentFile;
+
+ public TxInfo(Aggregate a) throws RepositoryException {
+ aggregate = (AggregateImpl) a;
+ artifactsPath = a.getPath();
+ out = aggregate.getBuilder();
+ }
+
+ public TxInfo(String artifactsPath, AggregateBuilder out) {
+ aggregate = null;
+ this.artifactsPath = artifactsPath;
+ this.out = out;
+ }
+
+ public void setParentFile(VaultFile parentFile) {
+ this.parentFile = parentFile;
+ }
+ }
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,460 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.fs.impl;
+
+import java.io.IOException;
+import java.util.Collection;
+import java.util.Iterator;
+import java.util.LinkedHashMap;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.AccessType;
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+import org.apache.jackrabbit.vault.fs.api.Artifact;
+import org.apache.jackrabbit.vault.fs.api.ArtifactType;
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.fs.api.VaultFile;
+import org.apache.jackrabbit.vault.fs.api.VaultFileSystem;
+import org.apache.jackrabbit.vault.util.PlatformNameFormat;
+import org.apache.jackrabbit.vault.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implements the bridge between the repository based artifacts nodes and their
+ * file system representation as a collection of artifacts.
+ *
+ */
+public class VaultFileImpl implements VaultFile {
+
+ /**
+ * The default logger
+ */
+ protected static Logger log = LoggerFactory.getLogger(VaultFileImpl.class);
+
+ /**
+ * Vault filesystem of this file
+ */
+ private final VaultFileSystem fs;
+
+ /**
+ * the name of this entry
+ */
+ private final String name;
+
+ /**
+ * The artifact this represents or <code>null</code> if not attached
+ */
+ private Artifact artifact;
+
+ /**
+ * the jcr file node that this file belongs to
+ */
+ private VaultFileNode node;
+
+ /**
+ * the parent file
+ */
+ private VaultFileImpl parent;
+
+ /**
+ * a map of child nodes that weren't loaded yet
+ */
+ private LinkedHashMap<String, VaultFileNode> pendingChildNodes;
+
+ /**
+ * The map of child entries.
+ */
+ private LinkedHashMap<String, VaultFileImpl> children;
+
+ /**
+ * Internal constructor for the root file
+ *
+ * @param fs the file system
+ * @param rootPath path of the root node
+ * @param node the node
+ * @throws RepositoryException if an error occurs
+ */
+ protected VaultFileImpl(VaultFileSystem fs, String rootPath, VaultFileNode node)
+ throws RepositoryException {
+ this.fs = fs;
+ this.node = node;
+ if (rootPath.equals("")) {
+ this.name = rootPath;
+ // bit of a hack since we know how the root artifacts look like
+ for (Artifact a: node.getAggregate().getArtifacts().values()) {
+ if (a.getType() == ArtifactType.DIRECTORY) {
+ this.artifact = a;
+ node.getFiles().add(this);
+ } else {
+ VaultFileImpl child = new VaultFileImpl(fs, a.getPlatformPath(), node, a);
+ node.getFiles().add(child);
+ this.addChild(child);
+ }
+ }
+ } else {
+ this.name = rootPath;
+ // special case when mounted deeply
+ for (Artifact a: node.getAggregate().getArtifacts().values()) {
+ if (a.getType() == ArtifactType.DIRECTORY) {
+ this.artifact = a;
+ node.getFiles().add(this);
+ } else {
+ String p[] = Text.explode(a.getPlatformPath(), '/');
+ VaultFileImpl entry = null;
+ for (String cName: p) {
+ if (entry == null) {
+ entry = this;
+ } else {
+ entry = entry.getOrAddChild(cName);
+ }
+ }
+ if (entry != null && entry != this) {
+ entry.init(node, a);
+ }
+ // add to set of related files
+ node.getFiles().add(entry);
+ }
+ }
+
+ }
+ for (VaultFileNode child: node.getChildren()) {
+ addPendingNode(child);
+ }
+ }
+
+
+ /**
+ * Internal constructor
+ *
+ * @param fs the file system
+ * @param name the file entry name
+ * @param node the node
+ * @param artifact the underlying artifact. can be <code>null</code>
+ * @throws RepositoryException if an error occurs
+ */
+ protected VaultFileImpl(VaultFileSystem fs, String name, VaultFileNode node,
+ Artifact artifact)
+ throws RepositoryException {
+ this.fs = fs;
+ this.name = name;
+ init(node, artifact);
+ }
+
+ /**
+ * (re)initializes this file
+ * @param node the artifacts node
+ * @param a the artifact
+ * @throws RepositoryException if an error occurs
+ */
+ protected void init(VaultFileNode node, Artifact a) throws RepositoryException {
+ children = null;
+ pendingChildNodes = null;
+ this.node = node;
+ this.artifact = a;
+ if (node != null && a != null && a.getType() == ArtifactType.DIRECTORY) {
+ for (VaultFileNode child: node.getChildren()) {
+ addPendingNode(child);
+ }
+ }
+
+ }
+
+ protected void attach(VaultFileNode node, Artifact a) {
+ this.node = node;
+ this.artifact = a;
+ }
+
+ public String getPath() {
+ if (parent == null) {
+ return name.length() == 0 ? "/" : name;
+ } else {
+ return internalGetPath().toString();
+ }
+ }
+
+ public String getRepoRelPath() {
+ if (artifact == null) {
+ return null;
+ } else {
+ String relPath = artifact.getRelativePath();
+ int idx = relPath.indexOf('/');
+ if (idx > 0 && idx < relPath.length() - 1) {
+ return relPath.substring(idx + 1);
+ }
+ return "";
+ }
+ }
+
+ public String getAggregatePath() {
+ return node == null
+ ? parent.getAggregatePath()
+ : node.getPath();
+ }
+
+ /**
+ * Internal builder for the path
+ * @return the string buffer.
+ */
+ private StringBuffer internalGetPath() {
+ if (parent == null) {
+ return new StringBuffer(name);
+ } else {
+ return parent.internalGetPath().append('/').append(name);
+ }
+ }
+
+ public String getName() {
+ return name;
+ }
+
+ public Artifact getArtifact() {
+ return artifact;
+ }
+
+ public boolean isDirectory() {
+ return artifact == null || artifact.getType() == ArtifactType.DIRECTORY;
+ }
+
+ public boolean isTransient() {
+ return node == null;
+ }
+
+ public VaultFileImpl getParent() throws IOException, RepositoryException {
+ return parent;
+ }
+
+ public Aggregate getAggregate() {
+ return node == null ? null : node.getAggregate();
+ }
+
+ public Aggregate getControllingAggregate() {
+ if (node == null && parent != null) {
+ return parent.getControllingAggregate();
+ } else {
+ return node == null ? null : node.getAggregate();
+ }
+ }
+
+ public VaultFileImpl getChild(String name) throws RepositoryException {
+ VaultFileImpl child = children == null ? null : children.get(name);
+ if (child == null) {
+ // try to load child
+ String repoName = PlatformNameFormat.getRepositoryName(name);
+ loadPendingNode(repoName);
+ child = children == null ? null : children.get(name);
+ // if still not present, load all
+ if (child == null) {
+ loadChildren();
+ }
+ child = children == null ? null : children.get(name);
+ }
+ return child;
+ }
+
+ public Collection<? extends VaultFile> getChildren() throws RepositoryException {
+ loadChildren();
+ return children.values();
+ }
+
+ /**
+ * Internally loads all children out of the map of pending node.
+ * @throws RepositoryException if an error occurs
+ */
+ private void loadChildren() throws RepositoryException {
+ if (children == null) {
+ children = new LinkedHashMap<String, VaultFileImpl>();
+ }
+ if (pendingChildNodes != null) {
+ Iterator<String> iter = pendingChildNodes.keySet().iterator();
+ while (iter.hasNext()) {
+ loadPendingNode(iter.next());
+ iter = pendingChildNodes.keySet().iterator();
+ }
+ }
+ }
+
+ /**
+ * Adds a node to the map of pending ones
+ * @param n the node
+ * @throws RepositoryException if an error occurs
+ */
+ protected void addPendingNode(VaultFileNode n) throws RepositoryException {
+ // we would need to assert that (n.getParent() == this.node) but this
+ // is not possible since this.node might not be loaded yet
+ if (pendingChildNodes == null) {
+ pendingChildNodes = new LinkedHashMap<String, VaultFileNode>();
+ }
+ String name = n.getName();
+ pendingChildNodes.put(name, n);
+ // if pending node is deep, we need to load it. otherwise it might not
+ // be found afterwards
+ if (name.indexOf('/') > 0) {
+ loadPendingNode(name);
+ }
+ }
+
+ /**
+ * Loads a pending node
+ * @param repoName the repository name of the node
+ * @throws RepositoryException if an error occurs
+ */
+ private void loadPendingNode(String repoName) throws RepositoryException {
+ // get node from pending ones
+ VaultFileNode n = pendingChildNodes == null ? null : pendingChildNodes.remove(repoName);
+ if (n == null) {
+ return;
+ }
+ VaultFileImpl parent = this;
+ // if pending node has a deep aggregate, we create intermediate
+ // "directories"
+ String aggName = n.getAggregate().getRelPath();
+ if (aggName.indexOf('/') > 0) {
+ String[] p = Text.explode(aggName, '/');
+ for (int i=0; i<p.length-1; i++) {
+ parent = parent.getOrAddChild(PlatformNameFormat.getPlatformName(p[i]));
+ }
+ }
+
+ for (Artifact a: n.getAggregate().getArtifacts().values()) {
+ String p[] = Text.explode(a.getPlatformPath(), '/');
+ VaultFileImpl entry = parent;
+ for (String cName: p) {
+ entry = entry.getOrAddChild(cName);
+ }
+ entry.init(n, a);
+ // add to set of related files
+ n.getFiles().add(entry);
+ }
+ }
+
+ /**
+ * Attaches a child file
+ * @param child the child
+ */
+ private void addChild(VaultFileImpl child) {
+ child.parent = this;
+ if (children == null) {
+ children = new LinkedHashMap<String, VaultFileImpl>();
+ }
+ children.put(child.name, child);
+ }
+
+ /**
+ * Returns the child of the given name or creates and adds a new one.
+ * @param name the name of the file
+ * @return the child
+ * @throws RepositoryException if an error occurs
+ */
+ protected VaultFileImpl getOrAddChild(String name) throws RepositoryException {
+ VaultFileImpl c = children == null ? null : children.get(name);
+ if (c == null) {
+ c = new VaultFileImpl(fs, name, null, null);
+ addChild(c);
+ }
+ return c;
+ }
+
+ public Collection<? extends VaultFile> getRelated() throws RepositoryException {
+ if (node == null) {
+ return null;
+ }
+ return node.getFiles();
+ }
+
+ public boolean canRead() {
+ return artifact != null
+ && artifact.getPreferredAccess() != AccessType.NONE;
+ }
+
+ public long lastModified() {
+ return artifact == null ? 0 : artifact.getLastModified();
+ }
+
+ public long length() {
+ return artifact == null ? -1 : artifact.getContentLength();
+ }
+
+ public String getContentType() {
+ return artifact == null ? null : artifact.getContentType();
+ }
+
+ public VaultFileSystem getFileSystem() {
+ return fs;
+ }
+
+ public void invalidate() throws RepositoryException {
+ log.info("invalidating file {}", getPath());
+
+ // special handling for root node
+ if (parent == null) {
+ // bit of a hack since we know how the root artifacts look like
+ node.invalidate();
+ for (Artifact a: node.getAggregate().getArtifacts().values()) {
+ if (a.getType() == ArtifactType.DIRECTORY) {
+ this.artifact = a;
+ node.getFiles().add(this);
+ } else {
+ VaultFileImpl child = new VaultFileImpl(fs, a.getPlatformPath(), node, a);
+ node.getFiles().add(child);
+ this.addChild(child);
+ }
+ }
+ for (VaultFileNode child: node.getChildren()) {
+ addPendingNode(child);
+ }
+ } else {
+ // get the directory artifact of this file
+ for (VaultFileImpl f: node.getFiles()) {
+ if (f.parent != null && f.parent.artifact.getType() == ArtifactType.DIRECTORY) {
+ f.parent.node.invalidate();
+ f.parent.init(f.parent.node, f.parent.artifact);
+ break;
+ }
+ }
+ //node.invalidate();
+ //init(node, artifact);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void dump(DumpContext ctx, boolean isLast) {
+ ctx.println(isLast, "Vault file");
+ ctx.indent(isLast);
+ ctx.printf(false, "name: %s", name);
+ ctx.printf(false, "path: %s", getPath());
+ ctx.printf(false, "# pending: %d", pendingChildNodes == null ? -1 : pendingChildNodes.size());
+ ctx.printf(false, "# children: %d", children == null ? -1 : children.size());
+ if (artifact != null) {
+ artifact.dump(ctx, false);
+ } else {
+ ctx.println(false, "Artifact: (null)");
+ }
+ if (node != null) {
+ node.dump(ctx, true);
+ } else {
+ ctx.println(true, "ArtifactsNode: (null)");
+ }
+ ctx.outdent();
+ }
+
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileNode.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileNode.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileNode.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileNode.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,113 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.fs.impl;
+
+import java.util.Collection;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+import org.apache.jackrabbit.vault.fs.api.DumpContext;
+import org.apache.jackrabbit.vault.fs.api.Dumpable;
+
+/**
+ * The jcr file node combines the aggregates with the Vault file. each file
+ * node produces a list of jcr files, composed out of the artifacts on the
+ * artifacts node. the Vault files are the inserted into the overall hierarchy
+ * of the Vault filesystem.
+ *
+ */
+public class VaultFileNode implements Dumpable {
+
+ /**
+ * the artifacts node
+ */
+ private final AggregateImpl aggregate;
+
+ /**
+ * the files of this node
+ */
+ private List<VaultFileImpl> files = new LinkedList<VaultFileImpl>();
+
+ private Collection<VaultFileNode> children;
+
+ private final VaultFileNode parent;
+
+ public VaultFileNode(VaultFileNode parent, AggregateImpl node) throws RepositoryException {
+ this.parent = parent;
+ this.aggregate = node;
+ }
+
+ public String getName() {
+ return aggregate.getRelPath();
+ }
+
+ public String getPath() {
+ return aggregate.getPath();
+ }
+
+ public Collection<VaultFileNode> getChildren() throws RepositoryException {
+ if (children == null) {
+ children = new LinkedList<VaultFileNode>();
+ List<? extends Aggregate> leaves = aggregate.getLeaves();
+ if (leaves != null && !leaves.isEmpty()) {
+ for (Aggregate child: leaves) {
+ children.add(new VaultFileNode(this, (AggregateImpl) child));
+ }
+ }
+ }
+ return children;
+ }
+
+ public Aggregate getAggregate() {
+ return aggregate;
+ }
+
+ public void invalidate() {
+ files.clear();
+ aggregate.invalidate();
+ children = null;
+ }
+
+ public VaultFileNode getParent() {
+ return parent;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void dump(DumpContext ctx, boolean isLast) {
+ ctx.println(isLast, "VaultFileNode");
+ ctx.indent(isLast);
+ //ctx.printf(false, "# pending: %d", pendingChildNodes == null ? -1 : pendingChildNodes.size());
+ //ctx.printf(false, "# children: %d", children == null ? -1 : children.size());
+ if (aggregate != null) {
+ aggregate.dump(ctx, true);
+ } else {
+ ctx.println(true, "ArtifactsNode: (null)");
+ }
+ ctx.outdent();
+ }
+
+
+ protected List<VaultFileImpl> getFiles() {
+ return files;
+ }
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileOutputImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileOutputImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileOutputImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileOutputImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,84 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.fs.impl;
+
+import java.io.File;
+import java.io.FileOutputStream;
+import java.io.IOException;
+import java.io.OutputStream;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.VaultFileOutput;
+import org.apache.jackrabbit.vault.fs.api.VaultInputSource;
+import org.apache.jackrabbit.vault.util.FileInputSource;
+
+/**
+ * Provides methods for writing jcr files. This can either be done by providing
+ * an input source or by fetching an output stream. this output stream can be
+ * acquired via a {@link TransactionImpl}.
+ *
+ */
+public class VaultFileOutputImpl implements VaultFileOutput {
+
+ private final TransactionImpl.Change tx;
+
+ private OutputStream out;
+
+ private File tmpFile;
+
+ private VaultInputSource is;
+
+ protected VaultFileOutputImpl(TransactionImpl.Change tx) {
+ this.tx = tx;
+ }
+
+ protected VaultFileOutputImpl(TransactionImpl.Change tx, VaultInputSource input) {
+ this.tx = tx;
+ this.is = input;
+ }
+
+ public OutputStream getOutputStream() throws IOException {
+ if (out != null) {
+ throw new IOException("Output stream already obtained.");
+ }
+ tmpFile = File.createTempFile("vltfs", ".tmp");
+ tmpFile.deleteOnExit();
+ out = new FileOutputStream(tmpFile);
+ return out;
+ }
+
+ /*
+ public void setArtfiactType(ArtifactType artfiactType) {
+ this.artfiactType = artfiactType;
+ }
+ */
+
+ public void setContentType(String contentType) {
+ tx.setContentType(contentType);
+ }
+
+ public void close() throws IOException, RepositoryException {
+ if (out != null) {
+ out.close();
+ is = new FileInputSource(tmpFile);
+ tx.setInputSource(is);
+ }
+ }
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileSystemImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileSystemImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileSystemImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/fs/impl/VaultFileSystemImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,186 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements. See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.jackrabbit.vault.fs.impl;
+
+import java.io.IOException;
+
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.vault.fs.api.Aggregate;
+import org.apache.jackrabbit.vault.fs.api.AggregateManager;
+import org.apache.jackrabbit.vault.fs.api.VaultFile;
+import org.apache.jackrabbit.vault.fs.api.VaultFileSystem;
+import org.apache.jackrabbit.vault.fs.api.VaultFsConfig;
+import org.apache.jackrabbit.vault.fs.api.VaultFsTransaction;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.util.PathUtil;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * The Vault filesystem provides an additional abstraction layer on top of the
+ * artifacts manager tree. It is used to map the artifacts node artifacts to
+ * individual java.io like files.
+ *
+ */
+public class VaultFileSystemImpl implements VaultFileSystem {
+
+ /**
+ * default log
+ */
+ private static Logger log = LoggerFactory.getLogger(VaultFileSystemImpl.class);
+
+ /**
+ * The underlying artifacts manager
+ */
+ private AggregateManager mgr;
+
+ /**
+ * Indicates if this is our own manager and we need to release it
+ */
+ private boolean isOwnManager;
+
+ /**
+ * The os file root
+ */
+ private VaultFileImpl root;
+
+ /**
+ * the root path if mounted not a /
+ */
+ private final String rootPath;
+
+ /**
+ * Pattern that matches the root path
+ */
+ private final String rootPattern;
+
+
+ public void unmount() throws RepositoryException {
+ assertMounted();
+ if (isOwnManager) {
+ mgr.unmount();
+ }
+ mgr = null;
+ root = null;
+ }
+
+ /**
+ * Checks if this tree is still mounted and if the attached session
+ * is still live.
+ *
+ * @throws RepositoryException if not mounted or not live.
+ */
+ private void assertMounted() throws RepositoryException {
+ if (!isMounted()) {
+ throw new RepositoryException("JcrFS is not mounted anymore.");
+ }
+ }
+
+ public boolean isMounted() {
+ return mgr != null && mgr.isMounted();
+ }
+
+ /**
+ * Creates a new os file system that uses the given manager.
+ *
+ * @param rootAggregate the root artifacts node
+ * @param rootPath path of root file. used for remapping
+ * @param ownMgr <code>true</code> if it's own manager
+ * @throws IOException if an I/O error occurs
+ * @throws RepositoryException if a repository error occurs.
+ */
+ public VaultFileSystemImpl(Aggregate rootAggregate, String rootPath, boolean ownMgr)
+ throws IOException, RepositoryException {
+ if (!rootAggregate.allowsChildren()) {
+ throw new IOException("Root node must allow children.");
+ }
+ this.mgr = rootAggregate.getManager();
+ this.isOwnManager = ownMgr;
+
+ // create root files
+ VaultFileNode rootFileNode = new VaultFileNode(null, (AggregateImpl) rootAggregate);
+ this.rootPath = rootPath == null || rootPath.equals("/") ? "" : rootPath;
+ this.rootPattern = this.rootPath + "/";
+ this.root = new VaultFileImpl(this, this.rootPath, rootFileNode);
+ }
+
+ public VaultFile getRoot() {
+ return root;
+ }
+
+ public AggregateManager getAggregateManager() {
+ return mgr;
+ }
+
+ public VaultFile getFile(String path)
+ throws IOException, RepositoryException {
+ if (path.charAt(0) != '/') {
+ throw new IOException("Only absolute paths allowed");
+ }
+ if (rootPath.length() > 0) {
+ if (!path.equals(rootPath) && !path.startsWith(rootPattern)) {
+ throw new IOException("Path not under mountpoint.");
+ }
+ path = path.substring(rootPath.length());
+ }
+ return getFile(root, path);
+ }
+
+ public VaultFile getFile(VaultFile parent, String path)
+ throws IOException, RepositoryException {
+ if (path == null || path.equals("") || path.equals(".")) {
+ return parent;
+ } else if (path.equals("/")) {
+ return getRoot();
+ }
+ String[] pathElems = PathUtil.makePath((String[])null, path);
+ for (int i=0; i<pathElems.length && parent != null; i++) {
+ String elem = pathElems[i];
+ if (elem.equals("/")) {
+ parent = getRoot();
+ } else if (elem.equals("..")) {
+ parent = parent.getParent();
+ } else {
+ parent = parent.getChild(elem);
+ }
+ }
+ return parent;
+ }
+
+ public VaultFsTransaction startTransaction() {
+ return new TransactionImpl(this);
+ }
+
+ public void invalidate() throws RepositoryException {
+ // create root files
+ AggregateImpl rootAggregate = (AggregateImpl) root.getAggregate();
+ rootAggregate.invalidate();
+ VaultFileNode rootFileNode = new VaultFileNode(null, rootAggregate);
+ root = new VaultFileImpl(this, rootPath, rootFileNode);
+ log.info("Filesystem invalidated.");
+ }
+
+ public VaultFsConfig getConfig() {
+ return mgr.getConfig();
+ }
+
+ public WorkspaceFilter getWorkspaceFilter() {
+ return mgr.getWorkspaceFilter();
+ }
+}
\ No newline at end of file