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 [21/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/packaging/impl/JcrPackageManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageManagerImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageManagerImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrPackageManagerImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,677 @@
+/*
+ * 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.packaging.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.io.InputStream;
+import java.io.InputStreamReader;
+import java.io.OutputStream;
+import java.util.Calendar;
+import java.util.Collections;
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jcr.ItemExistsException;
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.spi.CNDReader;
+import org.apache.jackrabbit.vault.fs.spi.NodeTypeInstaller;
+import org.apache.jackrabbit.vault.fs.spi.ServiceProviderFactory;
+import org.apache.jackrabbit.vault.packaging.Dependency;
+import org.apache.jackrabbit.vault.packaging.ExportOptions;
+import org.apache.jackrabbit.vault.packaging.JcrPackage;
+import org.apache.jackrabbit.vault.packaging.JcrPackageDefinition;
+import org.apache.jackrabbit.vault.packaging.JcrPackageManager;
+import org.apache.jackrabbit.vault.packaging.PackageException;
+import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.apache.jackrabbit.vault.packaging.Version;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+import org.apache.jackrabbit.vault.util.Text;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Extends the <code>PackageManager</code> by JCR specific methods
+ */
+public class JcrPackageManagerImpl extends PackageManagerImpl implements JcrPackageManager {
+
+ /**
+ * default logger
+ */
+ private static final Logger log = LoggerFactory.getLogger(JcrPackageManagerImpl.class);
+
+ /**
+ * name of node types resource
+ */
+ private static final String DEFAULT_NODETYPES = "nodetypes.cnd";
+
+ /**
+ * suggested folder types
+ */
+ private final static String[] FOLDER_TYPES = {"sling:Folder", "nt:folder", "nt:unstructured", null};
+
+ /**
+ * internal session
+ */
+ private final Session session;
+
+ /**
+ * package root (/etc/packages)
+ */
+ private Node packRoot;
+
+ /**
+ * Creates a new package manager using the given session.
+ *
+ * @param session repository session
+ */
+ public JcrPackageManagerImpl(Session session) {
+ this.session = session;
+ initNodeTypes();
+ }
+
+ public JcrPackage open(PackageId id) throws RepositoryException {
+ String path = id.getInstallationPath();
+ String[] exts = new String[]{"", ".zip", ".jar"};
+ for (String ext: exts) {
+ if (session.nodeExists(path + ext)) {
+ return open(session.getNode(path + ext));
+ }
+ }
+ return null;
+ }
+
+ public JcrPackage open(Node node) throws RepositoryException {
+ return open(node, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage open(Node node, boolean allowInvalid) throws RepositoryException {
+ JcrPackage pack = new JcrPackageImpl(node);
+ if (pack.isValid()) {
+ return pack;
+ } else if (allowInvalid
+ && node.isNodeType(JcrConstants.NT_HIERARCHYNODE)
+ && node.hasProperty(JcrConstants.JCR_CONTENT + "/" + JcrConstants.JCR_DATA)) {
+ return pack;
+ } else {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageId resolve(Dependency dependency, boolean onlyInstalled) throws RepositoryException {
+ if (!getPackageRoot().hasNode(dependency.getGroup())) {
+ return null;
+ }
+ Node groupNode = getPackageRoot().getNode(dependency.getGroup());
+ NodeIterator iter = groupNode.getNodes();
+ PackageId bestId = null;
+ while (iter.hasNext()) {
+ Node child = iter.nextNode();
+ if (child.getName().equals(".snapshot")) {
+ continue;
+ }
+ JcrPackageImpl pack = new JcrPackageImpl(child);
+ if (pack.isValid()) {
+ if (onlyInstalled && !pack.isInstalled()) {
+ continue;
+ }
+ PackageId id = pack.getDefinition().getId();
+ if (dependency.matches(id)) {
+ if (bestId == null || id.getVersion().compareTo(bestId.getVersion()) > 0) {
+ bestId = id;
+ }
+ }
+ }
+
+ }
+ return bestId;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage upload(InputStream in, boolean replace) throws RepositoryException, IOException {
+ return upload(in, replace, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage upload(InputStream in, boolean replace, boolean strict)
+ throws RepositoryException, IOException {
+ File file = File.createTempFile("vault", ".tmp");
+ OutputStream out = FileUtils.openOutputStream(file);
+ try {
+ IOUtils.copy(in, out);
+ } finally {
+ IOUtils.closeQuietly(in);
+ IOUtils.closeQuietly(out);
+ }
+ return upload(file, true, replace, null, strict);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage upload(File file, boolean isTmpFile, boolean replace, String nameHint)
+ throws RepositoryException, IOException {
+ return upload(file, isTmpFile, replace, nameHint, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage upload(File file, boolean isTmpFile, boolean replace, String nameHint, boolean strict)
+ throws RepositoryException, IOException {
+
+ // open zip packages
+ ZipVaultPackage pack = new ZipVaultPackage(file, isTmpFile, strict);
+ if (pack.getArchive().getJcrRoot() == null) {
+ String msg = "Zip File is not a content package. Missing 'jcr_root'.";
+ log.error(msg);
+ pack.close();
+ throw new IOException(msg);
+ }
+
+ // invalidate pid if path is unknown
+ PackageId pid = pack.getId();
+ if (pid != null && pid.getInstallationPath().equals(ZipVaultPackage.UNKNOWN_PATH)) {
+ pid = null;
+ }
+ if (pid == null) {
+ if (nameHint == null || nameHint.length() == 0) {
+ throw new IOException("Package does not contain a path specification and not name hint is given.");
+ }
+ pid = new PackageId(nameHint);
+ }
+ // create parent node
+ String path = pid.getInstallationPath() + ".zip";
+ String parentPath = Text.getRelativeParent(path, 1);
+ String name = Text.getName(path);
+ Node parent = mkdir(parentPath, false);
+
+ // remember installation state properties (GRANITE-2018)
+ JcrPackageDefinitionImpl.State state = null;
+
+ if (parent.hasNode(name)) {
+ JcrPackage oldPackage = new JcrPackageImpl(parent.getNode(name));
+ JcrPackageDefinitionImpl oldDef = (JcrPackageDefinitionImpl) oldPackage.getDefinition();
+ if (oldDef != null) {
+ state = oldDef.getState();
+ }
+
+ if (replace) {
+ parent.getNode(name).remove();
+ } else {
+ throw new ItemExistsException("Package already exists: " + path);
+ }
+ }
+ JcrPackage jcrPack = null;
+ try {
+ jcrPack = JcrPackageImpl.createNew(parent, pid, pack, false);
+ if (jcrPack != null) {
+ JcrPackageDefinitionImpl def = (JcrPackageDefinitionImpl) jcrPack.getDefinition();
+ if (state != null) {
+ def.setState(state);
+ }
+ }
+ return jcrPack;
+ } finally {
+ if (jcrPack == null) {
+ session.refresh(false);
+ } else {
+ session.save();
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage create(Node folder, String name)
+ throws RepositoryException, IOException {
+ if (folder == null) {
+ folder = getPackageRoot();
+ }
+ return JcrPackageImpl.createNew(folder, new PackageId(name), null, true);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage create(String group, String name)
+ throws RepositoryException, IOException {
+ return create(group, name, null);
+ }
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage create(String group, String name, String version)
+ throws RepositoryException, IOException {
+ // sanitize name
+ String ext = Text.getName(name, '.');
+ if (ext.equals("zip") || ext.equals("jar")) {
+ name = name.substring(0, name.length() - 4);
+ }
+ PackageId pid = new PackageId(group, name, version);
+ Node folder = mkdir(Text.getRelativeParent(pid.getInstallationPath(), 1), false);
+ try {
+ return JcrPackageImpl.createNew(folder, pid, null, false);
+ } finally {
+ session.save();
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void remove(JcrPackage pack) throws RepositoryException {
+ JcrPackage snap = pack.getSnapshot();
+ if (snap != null) {
+ snap.getNode().remove();
+ }
+ pack.getNode().remove();
+ session.save();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage rename(JcrPackage pack, String group, String name)
+ throws PackageException, RepositoryException {
+ return rename(pack, group, name, null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage rename(JcrPackage pack, String group, String name, String version)
+ throws PackageException, RepositoryException {
+ if (!pack.isValid()) {
+ throw new PackageException("Package is not valid.");
+ }
+ if (pack.getSize() > 0 && !pack.getDefinition().isUnwrapped()) {
+ throw new PackageException("Package definition not unwrapped.");
+ }
+ JcrPackageDefinition def = pack.getDefinition();
+ PackageId id = def.getId();
+ PackageId newId = new PackageId(
+ group == null ? id.getGroup() : group,
+ name == null ? id.getName() : name,
+ version == null ? id.getVersion() : Version.create(version)
+ );
+ String dstPath = newId.getInstallationPath() + ".zip";
+ if (id.equals(newId) && pack.getNode().getPath().equals(dstPath)) {
+ log.info("Package id not changed. won't rename.");
+ return pack;
+ }
+ def.setId(newId, false);
+
+ // only move if not already at correct location
+ if (!pack.getNode().getPath().equals(dstPath)) {
+ if (session.nodeExists(dstPath)) {
+ throw new PackageException("Node at " + dstPath + " already exists.");
+ }
+ // ensure parent path exists
+ mkdir(Text.getRelativeParent(dstPath, 1), false);
+ session.move(pack.getNode().getPath(), dstPath);
+ }
+
+ session.save();
+ Node newNode = session.getRootNode().getNode(dstPath.substring(1));
+ return open(newNode);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void assemble(JcrPackage pack, ProgressTrackerListener listener)
+ throws PackageException, RepositoryException, IOException {
+ pack.verifyId(true, true);
+ assemble(pack.getNode(), pack.getDefinition(), listener);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void assemble(Node packNode, JcrPackageDefinition definition,
+ ProgressTrackerListener listener)
+ throws PackageException, RepositoryException, IOException {
+ Calendar now = Calendar.getInstance();
+ JcrPackageDefinitionImpl def = (JcrPackageDefinitionImpl) definition;
+ validateSubPackages(def);
+ def.sealForAssembly(now, true);
+
+ ExportOptions opts = new ExportOptions();
+ opts.setMetaInf(def.getMetaInf());
+ opts.setListener(listener);
+ opts.setPostProcessor(def.getInjectProcessor());
+
+ VaultPackage pack = assemble(packNode.getSession(), opts, (File) null);
+
+ // update this content
+ Node contentNode = packNode.getNode(JcrConstants.JCR_CONTENT);
+ InputStream in;
+ try {
+ in = FileUtils.openInputStream(pack.getFile());
+ } catch (IOException e) {
+ throw new PackageException(e);
+ }
+ // stay jcr 1.0 compatible
+ //noinspection deprecation
+ contentNode.setProperty(JcrConstants.JCR_DATA, in);
+ contentNode.setProperty(JcrConstants.JCR_LASTMODIFIED, now);
+ contentNode.setProperty(JcrConstants.JCR_MIMETYPE, JcrPackage.MIME_TYPE);
+ packNode.save();
+ pack.close();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ private void validateSubPackages(JcrPackageDefinitionImpl def)
+ throws RepositoryException, PackageException {
+ List<JcrPackage> subs = listPackages(def.getMetaInf().getFilter());
+ PackageId id = def.getId();
+ for (JcrPackage p: subs) {
+ // check if not include itself
+ if (p.getDefinition().getId().equals(id)) {
+ throw new PackageException("A package cannot include itself. Check filter definition.");
+ }
+ if (!p.isSealed()) {
+ throw new PackageException("Only sealed (built) sub packages allowed: " + p.getDefinition().getId());
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void assemble(JcrPackageDefinition definition,
+ ProgressTrackerListener listener, OutputStream out)
+ throws IOException, RepositoryException, PackageException {
+ JcrPackageDefinitionImpl def = (JcrPackageDefinitionImpl) definition;
+ validateSubPackages(def);
+ Calendar now = Calendar.getInstance();
+ def.sealForAssembly(now, true);
+
+ ExportOptions opts = new ExportOptions();
+ opts.setMetaInf(def.getMetaInf());
+ opts.setListener(listener);
+ opts.setPostProcessor(def.getInjectProcessor());
+
+ assemble(def.getNode().getSession(), opts, out);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void rewrap(JcrPackage pack, ProgressTrackerListener listener)
+ throws PackageException, RepositoryException, IOException {
+ VaultPackage src = pack.getPackage();
+
+ Calendar now = Calendar.getInstance();
+ pack.verifyId(true, false);
+ JcrPackageDefinitionImpl def = (JcrPackageDefinitionImpl) pack.getDefinition();
+ def.sealForRewrap(now, true);
+
+ ExportOptions opts = new ExportOptions();
+ opts.setMetaInf(def.getMetaInf());
+ opts.setListener(listener);
+ opts.setPostProcessor(def.getInjectProcessor());
+
+ VaultPackage dst = rewrap(opts, src, (File) null);
+
+ // update this content
+ Node packNode = pack.getNode();
+ Node contentNode = packNode.getNode(JcrConstants.JCR_CONTENT);
+ InputStream in;
+ try {
+ in = FileUtils.openInputStream(dst.getFile());
+ } catch (IOException e) {
+ throw new PackageException(e);
+ }
+ // stay jcr 1.0 compatible
+ //noinspection deprecation
+ contentNode.setProperty(JcrConstants.JCR_DATA, in);
+ contentNode.setProperty(JcrConstants.JCR_LASTMODIFIED, now);
+ contentNode.setProperty(JcrConstants.JCR_MIMETYPE, JcrPackage.MIME_TYPE);
+ packNode.save();
+ dst.close();
+ }
+
+
+ /**
+ * {@inheritDoc}
+ */
+ public Node getPackageRoot() throws RepositoryException {
+ return getPackageRoot(false);
+ }
+
+ /**
+ * yet another Convenience method to create intermediate nodes.
+ * @param path path to create
+ * @param autoSave if <code>true</code> all changes are automatically persisted
+ * @return the node
+ * @throws RepositoryException if an error occurrs
+ */
+ protected Node mkdir(String path, boolean autoSave) throws RepositoryException {
+ if (session.nodeExists(path)) {
+ return session.getNode(path);
+ }
+ String parentPath = Text.getRelativeParent(path, 1);
+ if (path == null || (path.equals("/") && parentPath.equals(path))) {
+ throw new RepositoryException("could not crete intermediate nodes");
+ }
+ Node parent = mkdir(parentPath, autoSave);
+ Node node = null;
+ RepositoryException lastError = null;
+ for (int i=0; node == null && i<FOLDER_TYPES.length; i++) {
+ try {
+ node = parent.addNode(Text.getName(path), FOLDER_TYPES[i]);
+ } catch (RepositoryException e) {
+ lastError = e;
+ }
+ }
+ if (node == null) {
+ throw lastError;
+ }
+ if (autoSave) {
+ parent.save();
+ }
+ return node;
+ }
+
+ /**
+ * Initializes vlt node types (might not be the correct location)
+ */
+ private void initNodeTypes() {
+ // check if node types are registered
+ try {
+ session.getWorkspace().getNodeTypeManager().getNodeType(JcrPackage.NT_VLT_PACKAGE);
+ // also check/register nodetypes needed for assembly
+ session.getWorkspace().getNodeTypeManager().getNodeType("vlt:HierarchyNode");
+ session.getWorkspace().getNodeTypeManager().getNodeType("vlt:FullCoverage");
+ return;
+ } catch (RepositoryException e) {
+ // ignore
+ }
+ InputStream in = null;
+ try {
+ in = getClass().getResourceAsStream(DEFAULT_NODETYPES);
+ if (in == null) {
+ throw new InternalError("Could not load " + DEFAULT_NODETYPES + " resource.");
+ }
+ NodeTypeInstaller installer = ServiceProviderFactory.getProvider().getDefaultNodeTypeInstaller(session);
+ CNDReader types = ServiceProviderFactory.getProvider().getCNDReader();
+ types.read(new InputStreamReader(in, "utf8"), DEFAULT_NODETYPES, null);
+ installer.install(null, types);
+ } catch (Throwable e) {
+ log.warn("Error while registering nodetypes. Package installation might not work correctly.", e);
+ } finally {
+ IOUtils.closeQuietly(in);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Node getPackageRoot(boolean noCreate) throws RepositoryException {
+ if (this.packRoot == null) {
+ Node packRoot = session.getRootNode();
+ if (packRoot.hasNode("etc")) {
+ packRoot = packRoot.getNode("etc");
+ } else {
+ if (noCreate) {
+ return null;
+ }
+ if (packRoot.isModified()) {
+ throw new RepositoryException("Unwilling to create package root folder while session has transient changes.");
+ }
+ packRoot = packRoot.addNode("etc", JcrConstants.NT_FOLDER);
+ }
+ if (packRoot.hasNode("packages")) {
+ packRoot = packRoot.getNode("packages");
+ } else {
+ if (noCreate) {
+ return null;
+ }
+ packRoot = packRoot.addNode("packages", JcrConstants.NT_FOLDER);
+ try {
+ session.save();
+ } catch (RepositoryException e) {
+ try {
+ session.refresh(false);
+ } catch (RepositoryException e1) {
+ // ignore and re-throw original exception
+ }
+ throw e;
+ }
+ }
+ this.packRoot = packRoot;
+ }
+ return this.packRoot;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<JcrPackage> listPackages() throws RepositoryException {
+ return listPackages(null);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<JcrPackage> listPackages(WorkspaceFilter filter) throws RepositoryException {
+ Node root = getPackageRoot(true);
+ if (root == null) {
+ return Collections.emptyList();
+ } else {
+ List<JcrPackage> packages = new LinkedList<JcrPackage>();
+ listPackages(root, packages, filter, false, false);
+ Collections.sort(packages);
+ return packages;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public List<JcrPackage> listPackages(String group, boolean built) throws RepositoryException {
+ Node pRoot = getPackageRoot(true);
+ if (pRoot == null) {
+ return Collections.emptyList();
+ }
+ List<JcrPackage> packages = new LinkedList<JcrPackage>();
+ if (group == null) {
+ listPackages(pRoot, packages, null, built, false);
+ } else {
+ Node root = pRoot;
+ if (group.length() > 0) {
+ if (group.equals(pRoot.getPath())) {
+ group = "";
+ } else if (group.startsWith(pRoot.getPath() + "/")) {
+ group = group.substring(pRoot.getPath().length() + 1);
+ }
+ if (group.startsWith("/")) {
+ // don't scan outside /etc/packages
+ return packages;
+ } else if (group.length() > 0) {
+ if (root.hasNode(group)) {
+ root = root.getNode(group);
+ } else {
+ return packages;
+ }
+ }
+ }
+ listPackages(root, packages, null, built, true);
+ }
+ Collections.sort(packages);
+ return packages;
+ }
+
+ /**
+ * internally adds the packages below <code>root</code> to the given list
+ * recursively.
+ *
+ * @param root root node
+ * @param packages list for the packages
+ * @param filter optional filter to filter out packages
+ * @param built if <code>true</code> only packages with size > 0 are returned
+ * @param shallow if <code>true</code> don't recurs
+ * @throws RepositoryException if an error occurs
+ */
+ private void listPackages(Node root, List<JcrPackage> packages,
+ WorkspaceFilter filter, boolean built, boolean shallow)
+ throws RepositoryException {
+ if (root != null) {
+ for (NodeIterator iter = root.getNodes(); iter.hasNext();) {
+ Node child = iter.nextNode();
+ if (child.getName().equals(".snapshot")) {
+ continue;
+ }
+ JcrPackageImpl pack = new JcrPackageImpl(child);
+ if (pack.isValid()) {
+ if (filter == null || filter.contains(child.getPath())) {
+ if (!built || pack.getSize() > 0) {
+ packages.add(pack);
+ }
+ }
+ } else if (child.hasNodes() && !shallow){
+ listPackages(child, packages, filter, built, shallow);
+ }
+ }
+ }
+ }
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrWorkspaceFilter.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrWorkspaceFilter.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrWorkspaceFilter.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/JcrWorkspaceFilter.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,155 @@
+/*
+ * 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.packaging.impl;
+
+import java.util.LinkedList;
+import java.util.List;
+
+import javax.jcr.Node;
+import javax.jcr.NodeIterator;
+import javax.jcr.Property;
+import javax.jcr.RepositoryException;
+import javax.jcr.Value;
+
+import org.apache.jackrabbit.vault.fs.api.ImportMode;
+import org.apache.jackrabbit.vault.fs.api.ItemFilterSet;
+import org.apache.jackrabbit.vault.fs.api.PathFilterSet;
+import org.apache.jackrabbit.vault.fs.api.WorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.config.DefaultWorkspaceFilter;
+import org.apache.jackrabbit.vault.fs.filter.DefaultPathFilter;
+
+/**
+ * <code>JcrWorkspaceFilter</code> implements serializations of a workspace
+ * filter that is stored in the repository
+ */
+public class JcrWorkspaceFilter {
+
+ public static DefaultWorkspaceFilter loadFilter(Node defNode) throws RepositoryException {
+ DefaultWorkspaceFilter wsp = new DefaultWorkspaceFilter();
+ if (defNode.hasNode(JcrPackageDefinitionImpl.NN_FILTER)) {
+ defNode = defNode.getNode(JcrPackageDefinitionImpl.NN_FILTER);
+ }
+ for (NodeIterator filters = defNode.getNodes(); filters.hasNext();) {
+ Node filter = filters.nextNode();
+ String root = filter.hasProperty(JcrPackageDefinitionImpl.PN_ROOT)
+ ? filter.getProperty(JcrPackageDefinitionImpl.PN_ROOT).getString()
+ : "";
+ if (root.length() == 0) {
+ continue;
+ }
+ String mode = filter.hasProperty(JcrPackageDefinitionImpl.PN_MODE)
+ ? filter.getProperty(JcrPackageDefinitionImpl.PN_MODE).getString()
+ : "";
+ PathFilterSet set = new PathFilterSet(root);
+ if (mode.length() > 0) {
+ set.setImportMode(ImportMode.valueOf(mode.toUpperCase()));
+ }
+ if (filter.hasProperty(JcrPackageDefinitionImpl.PN_RULES)) {
+ // new version with mv rules property
+ Property p = filter.getProperty(JcrPackageDefinitionImpl.PN_RULES);
+ Value[] values = p.getDefinition().isMultiple() ? p.getValues() : new Value[]{p.getValue()};
+ for (Value value: values) {
+ String rule = value.getString();
+ int idx = rule.indexOf(':');
+ String type = idx > 0 ? rule.substring(0, idx) : "include";
+ String patt = idx > 0 ? rule.substring(idx + 1) : "";
+ DefaultPathFilter pf = new DefaultPathFilter(patt);
+ if (type.equals("include")) {
+ set.addInclude(pf);
+ } else {
+ set.addExclude(pf);
+ }
+ }
+ } else {
+ for (NodeIterator rules = filter.getNodes(); rules.hasNext();) {
+ Node rule = rules.nextNode();
+ String type = rule.getProperty(JcrPackageDefinitionImpl.PN_TYPE).getString();
+ String pattern = rule.getProperty(JcrPackageDefinitionImpl.PN_PATTERN).getString();
+ DefaultPathFilter pf = new DefaultPathFilter(pattern);
+ if (type.equals("include")) {
+ set.addInclude(pf);
+ } else {
+ set.addExclude(pf);
+ }
+ }
+ }
+ wsp.add(set);
+ }
+ return wsp;
+ }
+
+ public static void saveLegacyFilter(WorkspaceFilter filter, Node defNode, boolean save)
+ throws RepositoryException {
+ // delete all nodes first
+ for (NodeIterator iter = defNode.getNodes(); iter.hasNext();) {
+ iter.nextNode().remove();
+ }
+ int nr = 0;
+ for (PathFilterSet set: filter.getFilterSets()) {
+ Node setNode = defNode.addNode("f" + nr);
+ setNode.setProperty(JcrPackageDefinitionImpl.PN_ROOT, set.getRoot());
+ int eNr = 0;
+ for (ItemFilterSet.Entry e: set.getEntries()) {
+ // expect path filter
+ if (!(e.getFilter() instanceof DefaultPathFilter)) {
+ throw new IllegalArgumentException("Can only handle default path filters.");
+ }
+ Node eNode = setNode.addNode("f" + eNr);
+ eNode.setProperty(JcrPackageDefinitionImpl.PN_TYPE, e.isInclude() ? "include" : "exclude");
+ eNode.setProperty(JcrPackageDefinitionImpl.PN_PATTERN, ((DefaultPathFilter) e.getFilter()).getPattern());
+ eNr++;
+ }
+ nr++;
+ }
+ if (save) {
+ defNode.save();
+ }
+ }
+
+
+ public static void saveFilter(WorkspaceFilter filter, Node defNode, boolean save)
+ throws RepositoryException {
+ if (defNode.hasNode(JcrPackageDefinitionImpl.NN_FILTER)) {
+ defNode.getNode(JcrPackageDefinitionImpl.NN_FILTER).remove();
+ }
+ Node filterNode = defNode.addNode(JcrPackageDefinitionImpl.NN_FILTER);
+ int nr = 0;
+ for (PathFilterSet set: filter.getFilterSets()) {
+ Node setNode = filterNode.addNode("f" + nr);
+ setNode.setProperty(JcrPackageDefinitionImpl.PN_ROOT, set.getRoot());
+ setNode.setProperty(JcrPackageDefinitionImpl.PN_MODE, set.getImportMode().name().toLowerCase());
+ List<String> rules = new LinkedList<String>();
+ for (ItemFilterSet.Entry e: set.getEntries()) {
+ // expect path filter
+ if (!(e.getFilter() instanceof DefaultPathFilter)) {
+ throw new IllegalArgumentException("Can only handle default path filters.");
+ }
+ String type = e.isInclude() ? "include" : "exclude";
+ String patt = ((DefaultPathFilter) e.getFilter()).getPattern();
+ rules.add(type + ":" + patt);
+ }
+ setNode.setProperty(JcrPackageDefinitionImpl.PN_RULES, rules.toArray(new String[rules.size()]));
+ nr++;
+ }
+ if (save) {
+ defNode.save();
+ }
+ }
+
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,217 @@
+/*
+ * 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.packaging.impl;
+
+import java.io.ByteArrayInputStream;
+import java.io.ByteArrayOutputStream;
+import java.io.File;
+import java.io.IOException;
+import java.io.OutputStream;
+import java.net.URISyntaxException;
+import java.util.Enumeration;
+import java.util.HashSet;
+import java.util.Set;
+import java.util.zip.ZipEntry;
+import java.util.zip.ZipFile;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.commons.io.IOUtils;
+import org.apache.jackrabbit.vault.fs.Mounter;
+import org.apache.jackrabbit.vault.fs.api.RepositoryAddress;
+import org.apache.jackrabbit.vault.fs.api.VaultFileSystem;
+import org.apache.jackrabbit.vault.fs.config.DefaultMetaInf;
+import org.apache.jackrabbit.vault.fs.config.MetaInf;
+import org.apache.jackrabbit.vault.fs.io.JarExporter;
+import org.apache.jackrabbit.vault.fs.spi.ProgressTracker;
+import org.apache.jackrabbit.vault.packaging.ExportOptions;
+import org.apache.jackrabbit.vault.packaging.PackageManager;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.apache.jackrabbit.vault.util.Constants;
+
+/**
+ * Implements the package manager
+ */
+public class PackageManagerImpl implements PackageManager {
+
+ /**
+ * {@inheritDoc}
+ */
+ public VaultPackage open(File file) throws IOException {
+ return open(file, false);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VaultPackage open(File file, boolean strict) throws IOException {
+ return new ZipVaultPackage(file, false, strict);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VaultPackage assemble(Session s, ExportOptions opts, File file)
+ throws IOException, RepositoryException {
+ OutputStream out = null;
+ boolean isTmp = false;
+ boolean success = false;
+ try {
+ if (file == null) {
+ file = File.createTempFile("filevault", ".zip");
+ isTmp = true;
+ }
+ out = FileUtils.openOutputStream(file);
+ assemble(s, opts, out);
+ IOUtils.closeQuietly(out);
+ success = true;
+ return new ZipVaultPackage(file, isTmp);
+ } finally {
+ IOUtils.closeQuietly(out);
+ if (isTmp && !success) {
+ FileUtils.deleteQuietly(file);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void assemble(Session s, ExportOptions opts, OutputStream out)
+ throws IOException, RepositoryException {
+ RepositoryAddress addr;
+ try {
+ String mountPath = opts.getMountPath();
+ if (mountPath == null || mountPath.length() == 0) {
+ mountPath = "/";
+ }
+ addr = new RepositoryAddress("/" + s.getWorkspace().getName() + mountPath);
+ } catch (URISyntaxException e) {
+ throw new IllegalArgumentException(e);
+ }
+ MetaInf metaInf = opts.getMetaInf();
+ if (metaInf == null) {
+ metaInf = new DefaultMetaInf();
+ }
+ VaultFileSystem jcrfs = Mounter.mount(metaInf.getConfig(), metaInf.getFilter(), addr, opts.getRootPath(), s);
+ JarExporter exporter = new JarExporter(out);
+ exporter.setProperties(metaInf.getProperties());
+ if (opts.getListener() != null) {
+ exporter.setVerbose(opts.getListener());
+ }
+ if (opts.getPostProcessor() != null) {
+ exporter.export(jcrfs.getRoot(), true);
+ opts.getPostProcessor().process(exporter);
+ exporter.close();
+ } else {
+ exporter.export(jcrfs.getRoot());
+ }
+ jcrfs.unmount();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public VaultPackage rewrap(ExportOptions opts, VaultPackage src, File file)
+ throws IOException, RepositoryException {
+ OutputStream out = null;
+ boolean isTmp = false;
+ boolean success = false;
+ try {
+ if (file == null) {
+ file = File.createTempFile("filevault", ".zip");
+ isTmp = true;
+ }
+ out = FileUtils.openOutputStream(file);
+ rewrap(opts, src, out);
+ IOUtils.closeQuietly(out);
+ success = true;
+ return new ZipVaultPackage(file, isTmp);
+ } finally {
+ IOUtils.closeQuietly(out);
+ if (isTmp && !success) {
+ FileUtils.deleteQuietly(file);
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void rewrap(ExportOptions opts, VaultPackage src, OutputStream out)
+ throws IOException {
+ MetaInf metaInf = opts.getMetaInf();
+ if (metaInf == null) {
+ metaInf = new DefaultMetaInf();
+ }
+ JarExporter exporter = new JarExporter(out);
+ exporter.open();
+ exporter.setProperties(metaInf.getProperties());
+ ProgressTracker tracker = null;
+ if (opts.getListener() != null) {
+ tracker = new ProgressTracker();
+ exporter.setVerbose(opts.getListener());
+ }
+
+ // merge
+ MetaInf inf = opts.getMetaInf();
+ ZipFile zip = new ZipFile(src.getFile(), ZipFile.OPEN_READ);
+ if (opts.getPostProcessor() == null) {
+ // no post processor, we keep all files except the properties
+ Enumeration e = zip.entries();
+ while (e.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) e.nextElement();
+ String path = entry.getName();
+ if (!path.equals(Constants.META_DIR + "/" + Constants.PROPERTIES_XML)) {
+ exporter.write(zip, entry);
+ }
+ }
+ } else {
+ Set<String> keep = new HashSet<String>();
+ keep.add(Constants.META_DIR + "/");
+ keep.add(Constants.META_DIR + "/" + Constants.NODETYPES_CND);
+ keep.add(Constants.META_DIR + "/" + Constants.CONFIG_XML);
+ keep.add(Constants.META_DIR + "/" + Constants.FILTER_XML);
+ Enumeration e = zip.entries();
+ while (e.hasMoreElements()) {
+ ZipEntry entry = (ZipEntry) e.nextElement();
+ String path = entry.getName();
+ if (!path.startsWith(Constants.META_DIR + "/") || keep.contains(path)) {
+ exporter.write(zip, entry);
+ }
+ }
+ }
+ zip.close();
+
+ // write updated properties
+ ByteArrayOutputStream tmpOut = new ByteArrayOutputStream();
+ inf.getProperties().storeToXML(tmpOut, "FileVault Package Properties", "utf-8");
+ exporter.writeFile(new ByteArrayInputStream(tmpOut.toByteArray()), Constants.META_DIR + "/" + Constants.PROPERTIES_XML);
+ if (tracker != null) {
+ tracker.track("A", Constants.META_DIR + "/" + Constants.PROPERTIES_XML);
+ }
+
+ if (opts.getPostProcessor() != null) {
+ opts.getPostProcessor().process(exporter);
+ }
+ exporter.close();
+ }
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerMBean.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerMBean.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerMBean.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerMBean.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,28 @@
+/*
+ * 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.packaging.impl;
+
+import javax.management.openmbean.TabularData;
+
+/**
+ * <code>PackageManagerMBean</code>...
+ */
+public interface PackageManagerMBean {
+
+ TabularData getPackages();
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerMBeanImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerMBeanImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerMBeanImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackageManagerMBeanImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,151 @@
+/*
+ * 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.packaging.impl;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+import javax.management.DynamicMBean;
+import javax.management.MBeanAttributeInfo;
+import javax.management.MBeanConstructorInfo;
+import javax.management.MBeanInfo;
+import javax.management.NotCompliantMBeanException;
+import javax.management.StandardMBean;
+import javax.management.openmbean.CompositeDataSupport;
+import javax.management.openmbean.CompositeType;
+import javax.management.openmbean.OpenDataException;
+import javax.management.openmbean.OpenType;
+import javax.management.openmbean.SimpleType;
+import javax.management.openmbean.TabularData;
+import javax.management.openmbean.TabularDataSupport;
+import javax.management.openmbean.TabularType;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Property;
+import org.apache.felix.scr.annotations.Reference;
+import org.apache.felix.scr.annotations.ReferenceCardinality;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.vault.packaging.JcrPackage;
+import org.apache.jackrabbit.vault.packaging.JcrPackageManager;
+import org.apache.sling.jcr.api.SlingRepository;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * <code>PackageManagerMBeanImpl</code> provide a MBean that lists all available packages as tabular data.
+ * it uses the "com.adobe.granite.packaging" to better fit into the granite hierarchy.
+ */
+@Component()
+@Property(name = "jmx.objectname", value="com.adobe.granite.packaging:type=manager")
+@Service(value = DynamicMBean.class)
+public class PackageManagerMBeanImpl extends StandardMBean implements PackageManagerMBean {
+
+ /**
+ * default logger
+ */
+ private static final Logger log = LoggerFactory.getLogger(PackageManagerMBeanImpl.class);
+
+ @Reference(cardinality = ReferenceCardinality.OPTIONAL_UNARY)
+ private SlingRepository repository;
+
+ private static final String[] packageItemNames = {
+ "id",
+ "size",
+ "installed",
+ "installedDate",
+ "installedBy"
+ };
+ private static final String[] packageItemDescriptions = {
+ "Package Id",
+ "Package Size",
+ "Is installed",
+ "Install Date",
+ "Install User"
+ };
+
+ private static final OpenType[] packageItemTypes = {
+ SimpleType.STRING,
+ SimpleType.LONG,
+ SimpleType.BOOLEAN,
+ SimpleType.DATE,
+ SimpleType.STRING
+ };
+
+ private static final String[] packageIndexNames = { "id" };
+
+ private final CompositeType packageType;
+
+ private final TabularType packageTabularType;
+
+ public PackageManagerMBeanImpl() throws NotCompliantMBeanException, OpenDataException {
+ super(PackageManagerMBean.class);
+ packageType = new CompositeType("package", "Package Info", packageItemNames, packageItemDescriptions, packageItemTypes);
+ packageTabularType = new TabularType("packages", "List of Packages", packageType, packageIndexNames);
+ }
+
+ @Override
+ protected String getDescription(MBeanInfo info) {
+ return "Package Manager Information";
+ }
+
+ @Override
+ protected String getDescription(MBeanAttributeInfo info) {
+ if (info.getName().equals("Packages")) {
+ return "Available Packages";
+ }
+ return super.getDescription(info);
+ }
+
+ @Override
+ protected MBeanConstructorInfo[] getConstructors(MBeanConstructorInfo[] ctors, Object impl) {
+ return null;
+ }
+
+ public TabularData getPackages() {
+ TabularDataSupport packageData = new TabularDataSupport(packageTabularType);
+ if (repository != null) {
+ Session session = null;
+ try {
+ session = repository.loginAdministrative(null);
+ JcrPackageManager pkgMgr = new JcrPackageManagerImpl(session);
+ for (JcrPackage pkg: pkgMgr.listPackages()) {
+ try {
+ Object[] values = {
+ pkg.getDefinition().getId().toString(),
+ pkg.getSize(),
+ pkg.isInstalled(),
+ pkg.getDefinition().getLastUnpacked() == null ? null : pkg.getDefinition().getLastUnpacked().getTime(),
+ pkg.getDefinition().getLastUnpackedBy()
+ };
+ packageData.put(new CompositeDataSupport(packageType, packageItemNames, values));
+ } catch (Exception e) {
+ log.warn("Can't add composite data", e);
+ }
+ pkg.close();
+ }
+ } catch (RepositoryException e) {
+ log.error("Repository error while retrieving package list", e);
+ } finally {
+ if (session != null) {
+ session.logout();
+ }
+ }
+ } else {
+ log.warn("Unable to provide package list. Repository not bound.");
+ }
+ return packageData;
+ }
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagingImpl.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagingImpl.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagingImpl.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/PackagingImpl.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,80 @@
+/*
+ * 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.packaging.impl;
+
+import javax.jcr.Node;
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.felix.scr.annotations.Component;
+import org.apache.felix.scr.annotations.Service;
+import org.apache.jackrabbit.vault.packaging.JcrPackage;
+import org.apache.jackrabbit.vault.packaging.JcrPackageDefinition;
+import org.apache.jackrabbit.vault.packaging.JcrPackageManager;
+import org.apache.jackrabbit.vault.packaging.PackageManager;
+import org.apache.jackrabbit.vault.packaging.Packaging;
+import org.apache.jackrabbit.vault.util.JcrConstants;
+
+/**
+ * <code>PackagingImpl</code>...
+ */
+@Component(metatype = false, immediate = true)
+@Service(value = Packaging.class)
+public class PackagingImpl implements Packaging {
+
+ /**
+ * package manager is a singleton
+ */
+ private final PackageManager pkgManager = new PackageManagerImpl();
+
+ /**
+ * {@inheritDoc}
+ */
+ public PackageManager getPackageManager() {
+ return pkgManager;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackageManager getPackageManager(Session session) {
+ return new JcrPackageManagerImpl(session);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackageDefinition createPackageDefinition(Node defNode) {
+ return new JcrPackageDefinitionImpl(defNode);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public JcrPackage open(Node node, boolean allowInvalid) throws RepositoryException {
+ JcrPackage pack = new JcrPackageImpl(node);
+ if (pack.isValid()) {
+ return pack;
+ } else if (allowInvalid
+ && node.isNodeType(JcrConstants.NT_HIERARCHYNODE)
+ && node.hasProperty(JcrConstants.JCR_CONTENT + "/" + JcrConstants.JCR_DATA)) {
+ return pack;
+ } else {
+ return null;
+ }
+ }
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/packaging/impl/ZipVaultPackage.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,400 @@
+/*
+ * 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.packaging.impl;
+
+import java.io.File;
+import java.io.IOException;
+import java.util.Calendar;
+import java.util.List;
+import java.util.Properties;
+import java.util.regex.PatternSyntaxException;
+
+import javax.jcr.RepositoryException;
+import javax.jcr.Session;
+
+import org.apache.commons.io.FileUtils;
+import org.apache.jackrabbit.util.ISO8601;
+import org.apache.jackrabbit.vault.fs.config.MetaInf;
+import org.apache.jackrabbit.vault.fs.io.AccessControlHandling;
+import org.apache.jackrabbit.vault.fs.io.Archive;
+import org.apache.jackrabbit.vault.fs.io.ImportOptions;
+import org.apache.jackrabbit.vault.fs.io.Importer;
+import org.apache.jackrabbit.vault.fs.io.ZipArchive;
+import org.apache.jackrabbit.vault.packaging.Dependency;
+import org.apache.jackrabbit.vault.packaging.InstallContext;
+import org.apache.jackrabbit.vault.packaging.PackageException;
+import org.apache.jackrabbit.vault.packaging.PackageId;
+import org.apache.jackrabbit.vault.packaging.VaultPackage;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+/**
+ * Implements a vault package that is a zipped representation of a file vault
+ * export.
+ */
+public class ZipVaultPackage implements VaultPackage {
+
+ private static final Logger log = LoggerFactory.getLogger(ZipVaultPackage.class);
+
+ public static final String UNKNOWN_PATH = "/etc/packages/unknown";
+
+ private File file;
+
+ private Archive archive;
+
+ private boolean isTmpFile;
+
+ private PackageId id;
+
+ protected ZipVaultPackage(File file, boolean isTmpFile) throws IOException {
+ this(file, isTmpFile, false);
+ }
+
+ protected ZipVaultPackage(File file, boolean isTmpFile, boolean strict)
+ throws IOException {
+ this.file = file;
+ this.isTmpFile = isTmpFile;
+ if (strict) {
+ try {
+ archive = new ZipArchive(file);
+ archive.open(strict);
+ } catch (IOException e) {
+ log.error("Error while loading package {}.", file.getPath());
+ throw e;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void close() {
+ if (archive != null) {
+ archive.close();
+ archive = null;
+ }
+ if (file != null && isTmpFile) {
+ FileUtils.deleteQuietly(file);
+ }
+ file = null;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Archive getArchive() {
+ if (archive == null) {
+ if (file == null) {
+ log.error("Package already closed: " + id);
+ throw new IllegalStateException("Package already closed: " + id);
+ }
+ archive = new ZipArchive(file);
+ try {
+ archive.open(false);
+ } catch (IOException e) {
+ log.error("Archive not valid.", e);
+ throw new IllegalStateException("Archive not valid for file " + file, e);
+ }
+ }
+ return archive;
+ }
+
+ public PackageId getId() {
+ if (id == null) {
+ String version = getProperty(NAME_VERSION);
+ if (version == null) {
+ log.warn("Package does not specify a version. setting to ''");
+ version = "";
+ }
+ String group = getProperty(NAME_GROUP);
+ String name = getProperty(NAME_NAME);
+ if (group != null && name != null) {
+ id = new PackageId(group, name, version);
+ } else {
+ // check for legacy packages that only contains a 'path' property
+ String path = getProperty("path");
+ if (path == null || path.length() == 0) {
+ log.warn("Package does not specify a path. setting to 'unknown'");
+ path = UNKNOWN_PATH;
+ }
+ id = new PackageId(path, version);
+ }
+ }
+ return id;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isValid() {
+ try {
+ return getMetaInf().getFilter() != null;
+ } catch (Exception e) {
+ return false;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean isClosed() {
+ return file == null;
+ }
+
+ /**
+ * Returns the file this package is based on.
+ * @return the file of this package or <code>null</code>.
+ */
+ public File getFile() {
+ return file;
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public MetaInf getMetaInf() {
+ try {
+ return getArchive().getMetaInf();
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public long getSize() {
+ return file == null
+ ? -1
+ : file.length();
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Calendar getLastModified() {
+ return getDateProperty(NAME_LAST_MODIFIED);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLastModifiedBy() {
+ return getProperty(NAME_LAST_MODIFIED_BY);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Calendar getCreated() {
+ return getDateProperty(NAME_CREATED);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getCreatedBy() {
+ return getProperty(NAME_CREATED_BY);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Calendar getLastWrapped() {
+ return getDateProperty(NAME_LAST_WRAPPED);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getLastWrappedBy() {
+ return getProperty(NAME_LAST_WRAPPED_BY);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public String getDescription() {
+ return getProperty(NAME_DESCRIPTION);
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public AccessControlHandling getACHandling() {
+ String ac = getProperty(NAME_AC_HANDLING);
+ if (ac == null) {
+ return AccessControlHandling.IGNORE;
+ } else {
+ try {
+ return AccessControlHandling.valueOf(ac.toUpperCase());
+ } catch (IllegalArgumentException e) {
+ log.warn("invalid access control handling configured: {}", ac);
+ return AccessControlHandling.IGNORE;
+ }
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public boolean requiresRoot() {
+ return "true".equals(getProperty(NAME_REQUIRES_ROOT));
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public Dependency[] getDependencies() {
+ String deps = getProperty(NAME_DEPENDENCIES);
+ if (deps == null) {
+ return Dependency.EMPTY;
+ } else {
+ return Dependency.parse(deps);
+ }
+ }
+
+ /**
+ * {@inheritDoc}
+ */
+ public void extract(Session session, ImportOptions opts) throws RepositoryException, PackageException {
+ extract(prepareExtract(session, opts), null);
+ }
+
+ /**
+ * Prepares extraction.
+ *
+ * @param session repository session
+ * @param opts import options
+ *
+ * @throws RepositoryException if a repository error during installation occurs.
+ * @throws PackageException if an error during packaging occurs
+ * @throws IllegalStateException if the package is not valid.
+ * @return installation context
+ */
+ protected InstallContextImpl prepareExtract(Session session, ImportOptions opts) throws RepositoryException, PackageException {
+ if (!isValid()) {
+ throw new IllegalStateException("Package not valid.");
+ }
+ // try to find any hooks
+ InstallHookProcessor hooks = new InstallHookProcessor();
+ if (!opts.isDryRun()) {
+ hooks.registerHooks(archive, opts.getHookClassLoader());
+ }
+
+ if (requiresRoot() || hooks.hasHooks()) {
+ if (!AdminPermissionChecker.hasAdministrativePermissions(session)) {
+ log.error("Package extraction requires admin session.");
+ throw new PackageException("Package extraction requires admin session (userid not allowed).");
+ }
+ }
+
+ Importer importer = new Importer(opts);
+ AccessControlHandling ac = getACHandling();
+ if (opts.getAccessControlHandling() == null) {
+ opts.setAccessControlHandling(ac);
+ }
+ String cndPattern = getProperty(NAME_CND_PATTERN);
+ if (cndPattern != null) {
+ try {
+ opts.setCndPattern(cndPattern);
+ } catch (PatternSyntaxException e) {
+ throw new PackageException("Specified CND pattern not valid.", e);
+ }
+ }
+
+ return new InstallContextImpl(session.getRootNode(), this, importer, hooks);
+ }
+
+ /**
+ * Same as above but the given subPackages argument receives a list of
+ * potential sub packages.
+ *
+ * @param ctx install context
+ * @param subPackages receives the list of potential sub packages
+ *
+ * @throws RepositoryException if a repository error during installation occurs.
+ * @throws PackageException if an error during packaging occurs
+ * @throws IllegalStateException if the package is not valid.
+ */
+ protected void extract(InstallContextImpl ctx,
+ List<String> subPackages)
+ throws RepositoryException, PackageException {
+ log.info("Extracting {}", getId());
+ InstallHookProcessor hooks = ctx.getHooks();
+ Importer importer = ctx.getImporter();
+ try {
+ if (!hooks.execute(ctx)) {
+ throw new PackageException("Import aborted during prepare phase.");
+ }
+ try {
+ importer.run(archive, ctx.getImportRoot());
+ } catch (Exception e) {
+ log.error("Error during install.", e);
+ ctx.setPhase(InstallContext.Phase.INSTALL_FAILED);
+ hooks.execute(ctx);
+ throw new PackageException(e);
+ }
+ ctx.setPhase(InstallContext.Phase.INSTALLED);
+ hooks.execute(ctx);
+ if (importer.hasErrors() && ctx.getOptions().isStrict()) {
+ ctx.setPhase(InstallContext.Phase.INSTALL_FAILED);
+ hooks.execute(ctx);
+ throw new PackageException("Errors during import.");
+ }
+ } finally {
+ ctx.setPhase(InstallContext.Phase.END);
+ hooks.execute(ctx);
+ }
+ if (subPackages != null) {
+ subPackages.addAll(importer.getSubPackages());
+ }
+ log.info("Extracting {} completed.", getId());
+ }
+
+ private Calendar getDateProperty(String name) {
+ try {
+ String p = getProperty(name);
+ return p == null
+ ? null
+ : ISO8601.parse(p);
+ } catch (Exception e) {
+ log.error("Error while converting date property", e);
+ return null;
+ }
+ }
+
+ private String getProperty(String name) {
+ try {
+ Properties props = getMetaInf().getProperties();
+ return props == null ? null : props.getProperty(name);
+ } catch (Exception e) {
+ return null;
+ }
+ }
+
+ @Override
+ protected void finalize() throws Throwable {
+ try {
+ close();
+ } catch (Throwable e) {
+ // ignore
+ }
+ super.finalize();
+ }
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/BinaryCheckOutputStream.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/BinaryCheckOutputStream.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/BinaryCheckOutputStream.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/BinaryCheckOutputStream.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,81 @@
+/*
+ * 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.util;
+
+import java.io.IOException;
+import java.io.OutputStream;
+
+/**
+ * <code>BinaryCheckOutputStream</code>...
+ */
+public class BinaryCheckOutputStream extends OutputStream {
+
+ private final OutputStream out;
+
+ private boolean binary;
+
+ private static final boolean[] binaries = new boolean[256];
+ static {
+ for (int i=0; i<32; i++) {
+ binaries[i] = true;
+ }
+ binaries['\r'] = false;
+ binaries['\n'] = false;
+ binaries['\t'] = false;
+ binaries['\b'] = false;
+ binaries['\f'] = false;
+ }
+
+ public BinaryCheckOutputStream(OutputStream out) {
+ this.out = out;
+ }
+
+ public boolean isBinary() {
+ return binary;
+ }
+
+ public void write(int b) throws IOException {
+ if (!binary) {
+ binary = binaries[b & 0xff];
+ }
+ out.write(b);
+ }
+
+ public void write(byte[] b) throws IOException {
+ for (int i=0; i < b.length && !binary; i++) {
+ binary = binaries[b[i] & 0xff];
+ }
+ out.write(b);
+ }
+
+ public void write(byte[] b, int off, int len) throws IOException {
+ for (int i=0; i < len && !binary; i++) {
+ binary = binaries[b[i+off] & 0xff];
+ }
+ out.write(b, off, len);
+ }
+
+ public void flush() throws IOException {
+ out.flush();
+ }
+
+ public void close() throws IOException {
+ out.close();
+ }
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/Constants.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/Constants.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/Constants.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/Constants.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,123 @@
+/*
+ * 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.util;
+
+/**
+ * Defines some (file) name constants.
+ *
+ */
+public interface Constants {
+
+ /**
+ * the namespace uri of the 'vlt' namespace
+ */
+ String VAULT_NS_URI = "http://www.day.com/jcr/vault/1.0";
+
+ /**
+ * name of the {@value} directory
+ */
+ String META_INF = "META-INF";
+
+ /**
+ * name of the {@value} directory inside the {@value #META_INF}.
+ */
+ String VAULT_DIR = "vault";
+
+ /**
+ * default encoding for strings
+ */
+ String ENCODING = "utf-8";
+
+ /**
+ * path of the meta directory
+ */
+ String META_DIR = META_INF + "/" + VAULT_DIR;
+
+ /**
+ * name of the "hooks" directory
+ */
+ String HOOKS_DIR = "hooks";
+
+ /**
+ * name of the root directory under which all content goes.
+ */
+ String ROOT_DIR = "jcr_root";
+
+ /**
+ * name of the vault fs config file.
+ */
+ String CONFIG_XML = "config.xml";
+
+ /**
+ * name of the filter file.
+ */
+ String FILTER_XML = "filter.xml";
+
+ /**
+ * name of the filter file.
+ */
+ String FILTER_VLT_XML = "filter-vlt.xml";
+
+ /**
+ * name of the global settings file.
+ */
+ String SETTINGS_XML = "settings.xml";
+
+ /**
+ * name of the package definition directory
+ */
+ String PACKAGE_DEFINITION_XML = "definition/.content.xml";
+
+ /**
+ * name of the auth config
+ */
+ String AUTH_XML = "auth.xml";
+
+ /**
+ * name of the node types file.
+ */
+ String NODETYPES_CND = "nodetypes.cnd";
+
+ /**
+ * name of the custom export properties file
+ */
+ String PROPERTIES_XML = "properties.xml";
+
+ /**
+ * name of the custom privileges file
+ * @since 3.0
+ */
+ String PRIVILEGES_XML = "privileges.xml";
+
+ /**
+ * name of the file for generic serializations
+ */
+ String DOT_CONTENT_XML = ".content.xml";
+
+ /**
+ * empty string array
+ */
+ String[] EMPTY_STRING_ARRAY = new String[0];
+
+ /**
+ * the native file separator char
+ */
+ String FS_NATIVE = System.getProperty("file.separator");
+
+
+}
\ No newline at end of file
Added: jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DefaultProgressListener.java
URL: http://svn.apache.org/viewvc/jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DefaultProgressListener.java?rev=1512568&view=auto
==============================================================================
--- jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DefaultProgressListener.java (added)
+++ jackrabbit/commons/filevault/trunk/vault-core/src/main/java/org/apache/jackrabbit/vault/util/DefaultProgressListener.java Sat Aug 10 05:53:42 2013
@@ -0,0 +1,57 @@
+/*
+ * 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.util;
+
+import java.io.PrintWriter;
+
+import org.apache.jackrabbit.vault.fs.api.ProgressTrackerListener;
+
+/**
+ * <code>DefaultProgressListener</code>...
+*/
+public class DefaultProgressListener implements ProgressTrackerListener {
+
+ private final PrintWriter out;
+
+ public DefaultProgressListener() {
+ this(new PrintWriter(System.out));
+ }
+
+ public DefaultProgressListener(PrintWriter out) {
+ this.out = out;
+ }
+
+ public void onMessage(Mode mode, String action, String path) {
+ String name = path;
+ if (mode == Mode.PATHS) {
+ name = path.substring(path.lastIndexOf('/') + 1);
+ }
+ out.printf("%s %s%n", action, name);
+ out.flush();
+ }
+
+ public void onError(Mode mode, String path, Exception e) {
+ String name = path;
+ if (mode == Mode.PATHS) {
+ name = path.substring(path.lastIndexOf('/') + 1);
+ }
+ out.printf("E %s (%s)%n", name, e.toString());
+ out.flush();
+ }
+
+}
\ No newline at end of file