| /* |
| * 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.core.persistence; |
| |
| import java.io.IOException; |
| import java.io.InputStream; |
| import java.util.HashSet; |
| import java.util.Set; |
| |
| import javax.jcr.PropertyType; |
| import javax.jcr.RepositoryException; |
| |
| import org.apache.jackrabbit.core.data.DataStore; |
| import org.apache.jackrabbit.core.id.NodeId; |
| import org.apache.jackrabbit.core.id.PropertyId; |
| import org.apache.jackrabbit.core.state.ChangeLog; |
| import org.apache.jackrabbit.core.state.ChildNodeEntry; |
| import org.apache.jackrabbit.core.state.ItemStateException; |
| import org.apache.jackrabbit.core.state.NodeReferences; |
| import org.apache.jackrabbit.core.state.NodeState; |
| import org.apache.jackrabbit.core.state.PropertyState; |
| import org.apache.jackrabbit.core.value.InternalValue; |
| import org.apache.jackrabbit.spi.Name; |
| |
| /** |
| * Tool for copying item states from one persistence manager to another. |
| * Used for backing up or migrating repository content. |
| * |
| * @since Apache Jackrabbit 1.6 |
| */ |
| public class PersistenceCopier { |
| |
| /** |
| * Source persistence manager. |
| */ |
| private final PersistenceManager source; |
| |
| /** |
| * Target persistence manager. |
| */ |
| private final PersistenceManager target; |
| |
| /** |
| * Target data store, possibly <code>null</code>. |
| */ |
| private final DataStore store; |
| |
| /** |
| * Identifiers of the nodes that have already been copied or that |
| * should explicitly not be copied. Used to avoid duplicate copies |
| * of shareable nodes and to avoid trying to copy "missing" nodes |
| * like the virtual "/jcr:system" node. |
| */ |
| private final Set<NodeId> exclude = new HashSet<NodeId>(); |
| |
| /** |
| * Creates a tool for copying content from one persistence manager |
| * to another. |
| * |
| * @param source source persistence manager |
| * @param target target persistence manager |
| * @param store target data store |
| */ |
| public PersistenceCopier( |
| PersistenceManager source, PersistenceManager target, |
| DataStore store) { |
| this.source = source; |
| this.target = target; |
| this.store = store; |
| } |
| |
| /** |
| * Explicitly exclude the identified node from being copied. Used for |
| * excluding virtual nodes like "/jcr:system" from the copy process. |
| * |
| * @param id identifier of the node to be excluded |
| */ |
| public void excludeNode(NodeId id) { |
| exclude.add(id); |
| } |
| |
| /** |
| * Recursively copies the identified node and all its descendants. |
| * Explicitly excluded nodes and nodes that have already been copied |
| * are automatically skipped. |
| * |
| * @param id identifier of the node to be copied |
| * @throws RepositoryException if the copy operation fails |
| */ |
| public void copy(NodeId id) throws RepositoryException { |
| if (!exclude.contains(id)) { |
| try { |
| NodeState node = source.load(id); |
| |
| for (ChildNodeEntry entry : node.getChildNodeEntries()) { |
| copy(entry.getId()); |
| } |
| |
| copy(node); |
| exclude.add(id); |
| } catch (ItemStateException e) { |
| throw new RepositoryException("Unable to copy " + id, e); |
| } |
| } |
| } |
| |
| /** |
| * Copies the given node state and all associated property states |
| * to the target persistence manager. |
| * |
| * @param sourceNode source node state |
| * @throws RepositoryException if the copy operation fails |
| */ |
| private void copy(NodeState sourceNode) throws RepositoryException { |
| try { |
| ChangeLog changes = new ChangeLog(); |
| |
| // Copy the node state |
| NodeState targetNode = target.createNew(sourceNode.getNodeId()); |
| targetNode.setParentId(sourceNode.getParentId()); |
| targetNode.setNodeTypeName(sourceNode.getNodeTypeName()); |
| targetNode.setMixinTypeNames(sourceNode.getMixinTypeNames()); |
| targetNode.setPropertyNames(sourceNode.getPropertyNames()); |
| targetNode.setChildNodeEntries(sourceNode.getChildNodeEntries()); |
| if (target.exists(targetNode.getNodeId())) { |
| changes.modified(targetNode); |
| } else { |
| changes.added(targetNode); |
| } |
| |
| // Copy all associated property states |
| for (Name name : sourceNode.getPropertyNames()) { |
| PropertyId id = new PropertyId(sourceNode.getNodeId(), name); |
| PropertyState sourceState = source.load(id); |
| PropertyState targetState = target.createNew(id); |
| targetState.setType(sourceState.getType()); |
| targetState.setMultiValued(sourceState.isMultiValued()); |
| InternalValue[] values = sourceState.getValues(); |
| if (sourceState.getType() == PropertyType.BINARY) { |
| for (int i = 0; i < values.length; i++) { |
| InputStream stream = values[i].getStream(); |
| try { |
| values[i] = InternalValue.create(stream, store); |
| } finally { |
| stream.close(); |
| } |
| } |
| } |
| targetState.setValues(values); |
| if (target.exists(targetState.getPropertyId())) { |
| changes.modified(targetState); |
| } else { |
| changes.added(targetState); |
| } |
| } |
| |
| // Copy all node references |
| if (source.existsReferencesTo(sourceNode.getNodeId())) { |
| changes.modified(source.loadReferencesTo(sourceNode.getNodeId())); |
| } else if (target.existsReferencesTo(sourceNode.getNodeId())) { |
| NodeReferences references = |
| target.loadReferencesTo(sourceNode.getNodeId()); |
| references.clearAllReferences(); |
| changes.modified(references); |
| } |
| |
| // Persist the copied states |
| target.store(changes); |
| } catch (IOException e) { |
| throw new RepositoryException( |
| "Unable to copy binary values of " + sourceNode, e); |
| } catch (ItemStateException e) { |
| throw new RepositoryException("Unable to copy " + sourceNode, e); |
| } |
| } |
| |
| } |