jackrabbit-oak-commits mailing list archives

Site index · List index
Message view « Date » · « Thread »
Top « Date » · « Thread »
From ju...@apache.org
Subject svn commit: r1478405 - in /jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core: ./ PersistenceCopier.java RepositoryUpgrade.java
Date Thu, 02 May 2013 15:14:46 GMT
Author: jukka
Date: Thu May  2 15:14:45 2013
New Revision: 1478405

URL: http://svn.apache.org/r1478405
Log:
OAK-806: Content migration from Jackrabbit to Oak

First draft of an automatic repository upgrade tool

Added:
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/PersistenceCopier.java
  (with props)
    jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/RepositoryUpgrade.java
  (with props)

Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/PersistenceCopier.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/PersistenceCopier.java?rev=1478405&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/PersistenceCopier.java
(added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/PersistenceCopier.java
Thu May  2 15:14:45 2013
@@ -0,0 +1,330 @@
+/*
+ * 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;
+
+import static com.google.common.collect.Lists.newArrayListWithCapacity;
+import static org.apache.jackrabbit.JcrConstants.JCR_MIXINTYPES;
+import static org.apache.jackrabbit.JcrConstants.JCR_PRIMARYTYPE;
+import static org.apache.jackrabbit.oak.api.Type.NAME;
+
+import java.io.IOException;
+import java.math.BigDecimal;
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.PropertyType;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.core.id.NodeId;
+import org.apache.jackrabbit.core.id.PropertyId;
+import org.apache.jackrabbit.core.persistence.PersistenceManager;
+import org.apache.jackrabbit.core.state.ChildNodeEntry;
+import org.apache.jackrabbit.core.state.ItemStateException;
+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.oak.api.Blob;
+import org.apache.jackrabbit.oak.api.Type;
+import org.apache.jackrabbit.oak.plugins.memory.PropertyStates;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.Path;
+
+/**
+ * Tool for copying item states from a Jackrabbit persistence manager to
+ * an Oak node builder. Used for migrating repository content from Jackrabbit
+ * to Oak.
+ */
+class PersistenceCopier {
+
+    /**
+     * Source persistence manager.
+     */
+    private final PersistenceManager source;
+
+    /**
+     * Source namespace registry.
+     */
+    private final NamespaceRegistry registry;
+
+    /**
+     * Target node store.
+     */
+    private final NodeStore 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>();
+
+    public PersistenceCopier(
+            PersistenceManager source, NamespaceRegistry registry,
+            NodeStore store) {
+        this.source = source;
+        this.registry = registry;
+        this.store = store;
+    }
+
+    private String getOakName(Name name) throws RepositoryException {
+        String uri = name.getNamespaceURI();
+        String local = name.getLocalName();
+        if (uri == null || uri.isEmpty()) {
+            return local;
+        } else {
+            return registry.getPrefix(uri) + ":" + local;
+        }
+    }
+
+    private String getOakPath(Path path) throws RepositoryException {
+        StringBuilder builder = new StringBuilder();
+        for (Path.Element element : path.getElements()) {
+            if (builder.length() > 1
+                    || (builder.length() == 1 && !"/".equals(builder.toString())))
{
+                builder.append('/');
+            }
+            if (element.denotesRoot()) {
+                builder.append('/');
+            } else if (element.denotesIdentifier()) {
+                builder.append('[').append(element.getIdentifier()).append(']');
+            } else if (element.denotesName()) {
+                builder.append(getOakName(element.getName()));
+                if (element.getIndex() >= Path.INDEX_DEFAULT) {
+                    builder.append('[').append(element.getIndex()).append(']');
+                }
+            } else if (element.denotesParent()) {
+                builder.append("..");
+            } else if (element.denotesCurrent()) {
+                builder.append('.');
+            } else {
+                throw new RepositoryException(
+                        "Unknown path element: " + element);
+            }
+        }
+        return builder.toString();
+    }
+
+    /**
+     * 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, NodeBuilder builder)
+            throws RepositoryException, IOException {
+        try {
+            NodeState node = source.load(id);
+            copy(node, builder);
+
+            for (ChildNodeEntry entry : node.getChildNodeEntries()) {
+                NodeId childId = entry.getId();
+                if (!exclude.contains(childId)) {
+                    exclude.add(childId);
+                    String name = getOakName(entry.getName());
+                    System.out.println(name);
+                    copy(childId, builder.child(name));
+                    exclude.remove(childId);
+                }
+            }
+        } catch (ItemStateException e) {
+            throw new RepositoryException("Unable to copy " + id, e);
+        }
+    }
+
+    /**
+     * Copies the given node state and all associated property states
+     * to the node builder.
+     *
+     * @param sourceNode source node state
+     * @throws RepositoryException if the copy operation fails
+     */
+    private void copy(NodeState sourceNode, NodeBuilder builder)
+            throws RepositoryException, IOException, ItemStateException {
+        // Copy the node state
+        String primary = getOakName(sourceNode.getNodeTypeName());
+        builder.setProperty(JCR_PRIMARYTYPE, primary, NAME);
+
+        Set<Name> mixinNames = sourceNode.getMixinTypeNames();
+        if (!mixinNames.isEmpty()) {
+            List<String> mixins = newArrayListWithCapacity(mixinNames.size());
+            for (Name name : mixinNames) {
+                mixins.add(getOakName(name));
+            }
+            builder.setProperty(JCR_MIXINTYPES, mixins, Type.NAMES);
+        }
+
+        // Copy all associated property states
+        for (Name name : sourceNode.getPropertyNames()) {
+            PropertyId id = new PropertyId(sourceNode.getNodeId(), name);
+            PropertyState sourceState = source.load(id);
+
+            InternalValue[] values = sourceState.getValues();
+            int type = sourceState.getType();
+            String oakName = getOakName(name);
+            System.out.println("- " + oakName);
+            if (sourceState.isMultiValued() || values.length != 1) {
+                builder.setProperty(getProperty(oakName, values, type));
+            } else {
+                builder.setProperty(getProperty(oakName, values[0], type));
+            }
+        }
+    }
+
+    private org.apache.jackrabbit.oak.api.PropertyState getProperty(
+            String name, InternalValue[] values, int type)
+            throws RepositoryException, IOException {
+        switch (type) {
+        case PropertyType.BINARY:
+            List<Blob> binaries = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                binaries.add(store.createBlob(value.getStream()));
+            }
+            return PropertyStates.createProperty(name, binaries, Type.BINARIES);
+        case PropertyType.BOOLEAN:
+            List<Boolean> booleans = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                booleans.add(value.getBoolean());
+            }
+            return PropertyStates.createProperty(name, booleans, Type.BOOLEANS);
+        case PropertyType.DATE:
+            List<Long> dates = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                dates.add(value.getCalendar().getTimeInMillis());
+            }
+            return PropertyStates.createProperty(name, dates, Type.DATES);
+        case PropertyType.DECIMAL:
+            List<BigDecimal> decimals = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                decimals.add(value.getDecimal());
+            }
+            return PropertyStates.createProperty(name, decimals, Type.DECIMALS);
+        case PropertyType.DOUBLE:
+            List<Double> doubles = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                doubles.add(value.getDouble());
+            }
+            return PropertyStates.createProperty(name, doubles, Type.DOUBLES);
+        case PropertyType.LONG:
+            List<Long> longs = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                longs.add(value.getLong());
+            }
+            return PropertyStates.createProperty(name, longs, Type.LONGS);
+        case PropertyType.NAME:
+            List<String> names = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                names.add(getOakName(value.getName()));
+            }
+            return PropertyStates.createProperty(name, names, Type.NAMES);
+        case PropertyType.PATH:
+            List<String> paths = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                paths.add(getOakPath(value.getPath()));
+            }
+            return PropertyStates.createProperty(name, paths, Type.PATHS);
+        case PropertyType.REFERENCE:
+            List<String> references = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                references.add(value.getNodeId().toString());
+            }
+            return PropertyStates.createProperty(name, references, Type.REFERENCES);
+        case PropertyType.STRING:
+            List<String> strings = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                strings.add(value.getString());
+            }
+            return PropertyStates.createProperty(name, strings, Type.STRINGS);
+        case PropertyType.URI:
+            List<String> uris = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                uris.add(value.getURI().toString());
+            }
+            return PropertyStates.createProperty(name, uris, Type.URIS);
+        case PropertyType.WEAKREFERENCE:
+            List<String> weakreferences = newArrayListWithCapacity(values.length);
+            for (InternalValue value : values) {
+                weakreferences.add(value.getNodeId().toString());
+            }
+            return PropertyStates.createProperty(name, weakreferences, Type.WEAKREFERENCES);
+        default:
+            throw new RepositoryException("Unknown value type: " + type);
+        }
+    }
+
+    private org.apache.jackrabbit.oak.api.PropertyState getProperty(
+            String name, InternalValue value, int type)
+            throws RepositoryException, IOException {
+        switch (type) {
+        case PropertyType.BINARY:
+            return PropertyStates.createProperty(
+                    name, store.createBlob(value.getStream()), Type.BINARY);
+        case PropertyType.BOOLEAN:
+            return PropertyStates.createProperty(
+                    name, value.getBoolean(), Type.BOOLEAN);
+        case PropertyType.DATE:
+            return PropertyStates.createProperty(
+                    name, value.getCalendar().getTimeInMillis(), Type.DATE);
+        case PropertyType.DECIMAL:
+            return PropertyStates.createProperty(
+                    name, value.getDecimal(), Type.DECIMAL);
+        case PropertyType.DOUBLE:
+            return PropertyStates.createProperty(
+                    name, value.getDouble(), Type.DOUBLE);
+        case PropertyType.LONG:
+            return PropertyStates.createProperty(
+                    name, value.getLong(), Type.LONG);
+        case PropertyType.NAME:
+            return PropertyStates.createProperty(
+                    name, getOakName(value.getName()), Type.NAME);
+        case PropertyType.PATH:
+            return PropertyStates.createProperty(
+                    name, getOakPath(value.getPath()), Type.PATH);
+        case PropertyType.REFERENCE:
+            return PropertyStates.createProperty(
+                    name, value.getNodeId().toString(), Type.REFERENCE);
+        case PropertyType.STRING:
+            return PropertyStates.createProperty(
+                    name, value.getString(), Type.STRING);
+        case PropertyType.URI:
+            return PropertyStates.createProperty(
+                    name, value.getURI().toString(), Type.URI);
+        case PropertyType.WEAKREFERENCE:
+            return PropertyStates.createProperty(
+                    name, value.getNodeId().toString(), Type.WEAKREFERENCE);
+        default:
+            throw new RepositoryException("Unknown value type: " + type);
+        }
+    }
+
+}

Propchange: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/PersistenceCopier.java
------------------------------------------------------------------------------
    svn:eol-style = native

Added: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/RepositoryUpgrade.java
URL: http://svn.apache.org/viewvc/jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/RepositoryUpgrade.java?rev=1478405&view=auto
==============================================================================
--- jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/RepositoryUpgrade.java
(added)
+++ jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/RepositoryUpgrade.java
Thu May  2 15:14:45 2013
@@ -0,0 +1,228 @@
+/*
+ * 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;
+
+import static org.apache.jackrabbit.JcrConstants.JCR_NODETYPENAME;
+import static org.apache.jackrabbit.JcrConstants.JCR_SYSTEM;
+import static org.apache.jackrabbit.JcrConstants.JCR_VERSIONSTORAGE;
+import static org.apache.jackrabbit.oak.plugins.nodetype.NodeTypeConstants.JCR_NODE_TYPES;
+
+import java.io.File;
+import java.io.IOException;
+
+import javax.jcr.NamespaceException;
+import javax.jcr.NamespaceRegistry;
+import javax.jcr.RepositoryException;
+
+import org.apache.jackrabbit.core.RepositoryContext;
+import org.apache.jackrabbit.core.RepositoryImpl;
+import org.apache.jackrabbit.core.RepositoryImpl.WorkspaceInfo;
+import org.apache.jackrabbit.core.config.RepositoryConfig;
+import org.apache.jackrabbit.core.nodetype.NodeTypeRegistry;
+import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
+import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
+import org.apache.jackrabbit.oak.spi.state.NodeStore;
+import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
+import org.apache.jackrabbit.spi.Name;
+import org.apache.jackrabbit.spi.QNodeTypeDefinition;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+public class RepositoryUpgrade {
+
+    /**
+     * Logger instance
+     */
+    private static final Logger logger =
+        LoggerFactory.getLogger(RepositoryUpgrade.class);
+
+    /**
+     * Source repository context.
+     */
+    private final RepositoryContext source;
+
+    /**
+     * Target node store.
+     */
+    private final NodeStore target;
+
+    /**
+     * Copies the contents of the repository in the given source directory
+     * to the given target node store.
+     *
+     * @param source source repository directory
+     * @param target target node store
+     * @throws RepositoryException if the copy operation fails
+     */
+    public static void copy(File source, NodeStore target)
+            throws RepositoryException {
+        copy(RepositoryConfig.create(source), target);
+    }
+
+    /**
+     * Copies the contents of the repository with the given configuration
+     * to the given target node builder.
+     *
+     * @param source source repository configuration
+     * @param target target node store
+     * @throws RepositoryException if the copy operation fails
+     */
+    public static void copy(RepositoryConfig source, NodeStore target)
+            throws RepositoryException {
+        RepositoryImpl repository = RepositoryImpl.create(source);
+        try {
+            copy(repository, target);
+        } finally {
+            repository.shutdown();
+        }
+    }
+
+    /**
+     * Copies the contents of the given source repository to the given
+     * target node store.
+     * <p>
+     * The source repository <strong>must not be modified</strong> while
+     * the copy operation is running to avoid an inconsistent copy.
+     *
+     * @param source source repository directory
+     * @param target target node store
+     * @throws RepositoryException if the copy operation fails
+     * @throws IOException if the target repository can not be initialized
+     */
+    public static void copy(RepositoryImpl source, NodeStore target)
+            throws RepositoryException {
+        new RepositoryUpgrade(source, target).copy();
+    }
+
+    /**
+     * Creates a tool for copying the full contents of the source repository
+     * to the given target repository. Any existing content in the target
+     * repository will be overwritten.
+     *
+     * @param source source repository
+     * @param target target node store
+     */
+    public RepositoryUpgrade(RepositoryImpl source, NodeStore target) {
+        this.source = source.getRepositoryContext();
+        this.target = target;
+    }
+
+    /**
+     * Copies the full content from the source to the target repository.
+     * <p>
+     * The source repository <strong>must not be modified</strong> while
+     * the copy operation is running to avoid an inconsistent copy.
+     * <p>
+     * This method leaves the search indexes of the target repository in
+     * an 
+     * Note that both the source and the target repository must be closed
+     * during the copy operation as this method requires exclusive access
+     * to the repositories.
+     *
+     * @throws RepositoryException if the copy operation fails
+     */
+    public void copy() throws RepositoryException {
+        logger.info(
+                "Copying repository content from {} to Oak",
+                source.getRepository().repConfig.getHomeDir());
+        try {
+            NodeStoreBranch branch = target.branch();
+            NodeBuilder builder = branch.getHead().builder();
+
+            copyNamespaces(builder);
+            copyNodeTypes(builder);
+            copyVersionStore(builder);
+            copyWorkspaces(builder);
+
+            branch.setRoot(builder.getNodeState());
+            branch.merge(EmptyHook.INSTANCE); // TODO: default hooks?
+        } catch (Exception e) {
+            throw new RepositoryException("Failed to copy content", e);
+        }
+    }
+
+    private String getOakName(Name name) throws NamespaceException {
+        String uri = name.getNamespaceURI();
+        String local = name.getLocalName();
+        if (uri == null || uri.isEmpty()) {
+            return local;
+        } else {
+            return source.getNamespaceRegistry().getPrefix(uri) + ":" + local;
+        }
+    }
+
+    private void copyNamespaces(NodeBuilder root) throws RepositoryException {
+        NamespaceRegistry sourceRegistry = source.getNamespaceRegistry();
+        NodeBuilder system = root.child(JCR_SYSTEM);
+        NodeBuilder namespaces = system.child("rep:namespaces");
+
+        logger.info("Copying registered namespaces");
+        for (String uri : sourceRegistry.getURIs()) {
+            namespaces.setProperty(sourceRegistry.getPrefix(uri), uri);
+        }
+    }
+
+    private void copyNodeTypes(NodeBuilder root) throws RepositoryException {
+        NodeTypeRegistry sourceRegistry = source.getNodeTypeRegistry();
+        NodeBuilder system = root.child(JCR_SYSTEM);
+        NodeBuilder types = system.child(JCR_NODE_TYPES);
+
+        logger.info("Copying registered node types");
+        for (Name name : sourceRegistry.getRegisteredNodeTypes()) {
+            QNodeTypeDefinition def = sourceRegistry.getNodeTypeDef(name);
+            NodeBuilder type = types.child(getOakName(name));
+            type.setProperty(JCR_NODETYPENAME, getOakName(name));
+            // TODO ...
+        }
+    }
+
+    private void copyVersionStore(NodeBuilder root)
+            throws RepositoryException, IOException {
+        logger.info("Copying version histories");
+        NodeBuilder system = root.child(JCR_SYSTEM);
+        NodeBuilder versionStorage = system.child(JCR_VERSIONSTORAGE);
+        NodeBuilder activities = system.child("rep:activities");
+
+        PersistenceCopier copier = new PersistenceCopier(
+                source.getInternalVersionManager().getPersistenceManager(),
+                source.getNamespaceRegistry(), target);
+        copier.copy(RepositoryImpl.VERSION_STORAGE_NODE_ID, versionStorage);
+        copier.copy(RepositoryImpl.ACTIVITIES_NODE_ID, activities);
+    }   
+
+    private void copyWorkspaces(NodeBuilder root)
+            throws RepositoryException, IOException {
+        logger.info("Copying default workspace");
+
+        // Copy all the default workspace content
+
+        RepositoryImpl repository = source.getRepository();
+        RepositoryConfig config = repository.getConfig();
+        String name = config.getDefaultWorkspaceName();
+        WorkspaceInfo workspace = repository.getWorkspaceInfo(name);
+
+        PersistenceCopier copier = new PersistenceCopier(
+                workspace.getPersistenceManager(),
+                source.getNamespaceRegistry(), target);
+        copier.excludeNode(RepositoryImpl.SYSTEM_ROOT_NODE_ID);
+        copier.copy(RepositoryImpl.ROOT_NODE_ID, root);
+
+        // TODO: Copy all the active open-scoped locks
+    }
+
+
+}

Propchange: jackrabbit/oak/trunk/oak-run/src/main/java/org/apache/jackrabbit/core/RepositoryUpgrade.java
------------------------------------------------------------------------------
    svn:eol-style = native



Mime
View raw message