Package org.apache.jackrabbit.mongomk.command

Source Code of org.apache.jackrabbit.mongomk.command.CommitCommandMongo

/*
* 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.mongomk.command;

import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.apache.jackrabbit.mongomk.MongoConnection;
import org.apache.jackrabbit.mongomk.api.command.AbstractCommand;
import org.apache.jackrabbit.mongomk.api.model.Commit;
import org.apache.jackrabbit.mongomk.api.model.Instruction;
import org.apache.jackrabbit.mongomk.model.CommitCommandInstructionVisitor;
import org.apache.jackrabbit.mongomk.model.CommitMongo;
import org.apache.jackrabbit.mongomk.model.HeadMongo;
import org.apache.jackrabbit.mongomk.model.NodeMongo;
import org.apache.jackrabbit.mongomk.query.FetchNodesForRevisionQuery;
import org.apache.jackrabbit.mongomk.query.ReadAndIncHeadRevisionQuery;
import org.apache.jackrabbit.mongomk.query.SaveAndSetHeadRevisionQuery;
import org.apache.jackrabbit.mongomk.query.SaveCommitQuery;
import org.apache.jackrabbit.mongomk.query.SaveNodesQuery;
import org.apache.log4j.Logger;

import com.mongodb.BasicDBObject;
import com.mongodb.DBCollection;
import com.mongodb.DBObject;
import com.mongodb.QueryBuilder;
import com.mongodb.WriteResult;

/**
* A {@code Command} for committing into {@code MongoDB}.
*
* @author <a href="mailto:pmarx@adobe.com>Philipp Marx</a>
*/
public class CommitCommandMongo extends AbstractCommand<String> {

    private static final Logger logger = Logger.getLogger(CommitCommandMongo.class);

    private final Commit commit;
    private final MongoConnection mongoConnection;

    private Set<String> affectedPaths;
    private CommitMongo commitMongo;
    private List<NodeMongo> existingNodes;
    private HeadMongo headMongo;
    private Set<NodeMongo> nodeMongos;
    private String revisionId;

    /**
     * Constructs a new {@code CommitCommandMongo}.
     *
     * @param mongoConnection {@link MongoConnection}
     * @param commit {@link Commit}
     */
    public CommitCommandMongo(MongoConnection mongoConnection, Commit commit) {
        this.mongoConnection = mongoConnection;
        this.commit = commit;
    }

    @Override
    public String execute() throws Exception {
        logger.debug(String.format("Trying to commit: %s", commit.getDiff()));

        readAndIncHeadRevision();
        createRevision();
        createMongoNodes();
        createMongoCommit();
        readExistingNodes();
        mergeNodes();
        prepareMongoNodes();
        saveNodes();
        saveCommit();
        boolean success = saveAndSetHeadRevision();

        logger.debug(String.format("Success was: %b", success));

        if (!success) {
            markAsFailed();

            throw new ConflictingCommitException();
        }

        addRevisionId();

        return revisionId;
    }

    @Override
    public int getNumOfRetries() {
        return 10;
    }

    @Override
    public boolean needsRetry(Exception e) {
        return e instanceof ConflictingCommitException;
    }

    /**
     * This is protected for testing purposed only.
     *
     * @return N/A
     * @throws Exception
     */
    protected boolean saveAndSetHeadRevision() throws Exception {
        boolean success = true;

        HeadMongo headMongo = new SaveAndSetHeadRevisionQuery(mongoConnection, this.headMongo.getHeadRevisionId(),
                HeadMongo.toDBRepresentation(revisionId)).execute();
        if (headMongo == null) {
            // TODO: Check for conflicts!
            logger.warn(String
                    .format("Encounterd a conflicting update, thus can't commit revision %s and will be retried with new revision",
                            revisionId));

            success = false;
        }

        return success;
    }

    private void addRevisionId() {
        commit.setRevisionId(revisionId);
    }

    private void createMongoCommit() throws Exception {
        commitMongo = CommitMongo.fromCommit(commit);
        commitMongo.setRevisionId(revisionId);
        commitMongo.setAffectedPaths(new LinkedList<String>(affectedPaths));
        commitMongo.setBaseRevId(headMongo.getHeadRevisionId());
    }

    private void createMongoNodes() throws Exception {
        CommitCommandInstructionVisitor visitor = new CommitCommandInstructionVisitor(mongoConnection,
                headMongo.getHeadRevisionId());
        for (Instruction instruction : commit.getInstructions()) {
            instruction.accept(visitor);
        }

        Map<String, NodeMongo> pathNodeMap = visitor.getPathNodeMap();

        affectedPaths = pathNodeMap.keySet(); // TODO Original copies and moved nodes must be included!
        nodeMongos = new HashSet<NodeMongo>(pathNodeMap.values());
        for (NodeMongo nodeMongo : nodeMongos) {
            nodeMongo.setRevisionId(revisionId);
        }
    }

    private void createRevision() {
        revisionId = String.valueOf(headMongo.getNextRevisionId() - 1);
    }

    private void markAsFailed() throws Exception {
        DBCollection commitCollection = mongoConnection.getCommitCollection();
        DBObject query = QueryBuilder.start("_id").is(commitMongo.getObjectId("_id")).get();
        DBObject update = new BasicDBObject("$set", new BasicDBObject(CommitMongo.KEY_FAILED, Boolean.TRUE));
        WriteResult writeResult = commitCollection.update(query, update);
        if (writeResult.getError() != null) {
            throw new Exception(String.format("Update wasn't successful: %s", writeResult)); // TODO now what?
        }
    }

    private void mergeNodes() {
        for (NodeMongo existingNode : existingNodes) {
            for (NodeMongo committingNode : nodeMongos) {
                if (existingNode.getPath().equals(committingNode.getPath())) {
                    logger.debug(String.format("Found existing node to merge: %s", existingNode.getPath()));
                    logger.debug(String.format("Existing node: %s", existingNode));
                    logger.debug(String.format("Committing node: %s", committingNode));

                    Map<String, Object> existingProperties = existingNode.getProperties();

                    if (existingProperties != null) {
                        committingNode.setProperties(existingProperties);

                        logger.debug(String.format("Merged properties for %s: %s", existingNode.getPath(),
                                existingProperties));
                    }

                    List<String> existingChildren = existingNode.getChildren();

                    if (existingChildren != null) {
                        committingNode.setChildren(existingChildren);

                        logger.debug(String.format("Merged children for %s: %s", existingNode.getPath(), existingChildren));
                    }

                    committingNode.setBaseRevisionId(existingNode.getRevisionId());

                    logger.debug(String.format("Merged node for %s: %s", existingNode.getPath(), committingNode));

                    break;
                }
            }
        }
    }

    private void prepareMongoNodes() {
        for (NodeMongo committingNode : nodeMongos) {
            logger.debug(String.format("Preparing children (added and removed) of %s", committingNode.getPath()));
            logger.debug(String.format("Committing node: %s", committingNode));

            List<String> children = committingNode.getChildren();
            if (children == null) {
                children = new LinkedList<String>();
            }

            List<String> addedChildren = committingNode.getAddedChildren();
            if (addedChildren != null) {
                children.addAll(addedChildren);
            }

            List<String> removedChildren = committingNode.getRemovedChildren();
            if (removedChildren != null) {
                children.removeAll(removedChildren);
            }

            if (!children.isEmpty()) {
                Set<String> temp = new HashSet<String>(children); // remove all duplicates
                committingNode.setChildren(new LinkedList<String>(temp));
            } else {
                committingNode.setChildren(null);
            }

            Map<String, Object> properties = committingNode.getProperties();
            if (properties == null) {
                properties = new HashMap<String, Object>();
            }

            Map<String, Object> addedProperties = committingNode.getAddedProps();
            if (addedProperties != null) {
                properties.putAll(addedProperties);
            }

            Map<String, Object> removedProperties = committingNode.getRemovedProps();
            if (removedProperties != null) {
                for (Map.Entry<String, Object> entry : removedProperties.entrySet()) {
                    properties.remove(entry.getKey());
                }
            }

            if (!properties.isEmpty()) {
                committingNode.setProperties(properties);
            } else {
                committingNode.setProperties(null);
            }

            logger.debug(String.format("Prepared committing node: %s", committingNode));
        }
    }

    private void readAndIncHeadRevision() throws Exception {
        headMongo = new ReadAndIncHeadRevisionQuery(mongoConnection).execute();
    }

    private void readExistingNodes() {
        Set<String> paths = new HashSet<String>();
        for (NodeMongo nodeMongo : nodeMongos) {
            paths.add(nodeMongo.getPath());
        }

        existingNodes = new FetchNodesForRevisionQuery(mongoConnection, paths,
                String.valueOf(headMongo.getHeadRevisionId())).execute();
    }

    private void saveCommit() throws Exception {
        new SaveCommitQuery(mongoConnection, commitMongo).execute();
    }

    private void saveNodes() throws Exception {
        new SaveNodesQuery(mongoConnection, nodeMongos).execute();
    }
}
TOP

Related Classes of org.apache.jackrabbit.mongomk.command.CommitCommandMongo

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.