Package org.apache.jackrabbit.oak.plugins.identifier

Source Code of org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager

/*
* 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.oak.plugins.identifier;

import java.io.UnsupportedEncodingException;
import java.text.ParseException;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.UUID;

import javax.annotation.CheckForNull;
import javax.annotation.Nonnull;
import javax.jcr.PropertyType;
import javax.jcr.query.Query;

import com.google.common.base.Function;
import com.google.common.base.Predicate;
import com.google.common.collect.Iterables;
import com.google.common.collect.Sets;
import org.apache.jackrabbit.JcrConstants;
import org.apache.jackrabbit.oak.api.CoreValue;
import org.apache.jackrabbit.oak.api.PropertyState;
import org.apache.jackrabbit.oak.api.Result;
import org.apache.jackrabbit.oak.api.ResultRow;
import org.apache.jackrabbit.oak.api.Root;
import org.apache.jackrabbit.oak.api.SessionQueryEngine;
import org.apache.jackrabbit.oak.api.Tree;
import org.apache.jackrabbit.oak.commons.PathUtils;
import org.apache.jackrabbit.oak.namepath.NamePathMapper;
import org.apache.jackrabbit.oak.plugins.memory.StringValue;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
* IdentifierManager...
*/
public class IdentifierManager {

    private static final Logger log = LoggerFactory.getLogger(IdentifierManager.class);

    private final Root root;
    private final SessionQueryEngine queryEngine;

    public IdentifierManager(SessionQueryEngine queryEngine, Root root) {
        this.root = root;
        this.queryEngine = queryEngine;
    }

    @Nonnull
    public static String generateUUID() {
        return UUID.randomUUID().toString();
    }

    @Nonnull
    public static String generateUUID(String hint) {
        try {
            UUID uuid = UUID.nameUUIDFromBytes(hint.getBytes("UTF-8"));
            return uuid.toString();
        } catch (UnsupportedEncodingException e) {
            throw new RuntimeException("Unexpected error while creating uuid", e);
        }
    }

    public static boolean isValidUUID(String uuid) {
        try {
            UUID.fromString(uuid);
            return true;
        } catch (IllegalArgumentException e) {
            return false;
        }
    }

    /**
     *
     * @param uuid
     * @throws IllegalArgumentException If the specified uuid has an invalid format.
     */
    public static void checkUUIDFormat(String uuid) throws IllegalArgumentException {
        UUID.fromString(uuid);
    }

    @Nonnull
    public String getIdentifier(Tree tree) {
        PropertyState property = tree.getProperty(JcrConstants.JCR_UUID);
        if (property == null) {
            // TODO calculate the identifier from closest referenceable parent
            // TODO and a relative path irrespective of the accessibility of the parent node(s)
            return tree.getPath();
        } else {
            return property.getValue().getString();
        }
    }

    /**
     * The tree identified by the specified {@code identifier} or {@code null}.
     *
     * @param identifier The identifier of the Node such as exposed by {@link javax.jcr.Node#getIdentifier()}
     * @return The tree with the given {@code identifier} or {@code null} if no
     * such tree exists or isn't accessible to the content session.
     */
    @CheckForNull
    public Tree getTree(String identifier) {
        if (isValidUUID(identifier)) {
            String path = resolveUUID(identifier);
            return (path == null) ? null : root.getTree(path);
        } else {
            // TODO as stated in NodeDelegate#getIdentifier() a non-uuid ID should
            // TODO consisting of closest referenceable parent and a relative path
            // TODO irrespective of the accessibility of the parent node(s)
            return root.getTree(identifier);
        }
    }

    /**
     * The path of the tree identified by the specified {@code identifier} or {@code null}.
     *
     * @param identifier The identifier of the Tree such as exposed by {@link javax.jcr.Node#getIdentifier()}
     * @return The tree with the given {@code identifier} or {@code null} if no
     * such tree exists or isn't accessible to the content session.
     */
    @CheckForNull
    public String getPath(String identifier) {
        if (isValidUUID(identifier)) {
            return resolveUUID(identifier);
        } else {
            // TODO as stated in NodeDelegate#getIdentifier() a non-uuid ID should
            // TODO consisting of closest referenceable parent and a relative path
            // TODO irrespective of the accessibility of the parent node(s)
            Tree tree = root.getTree(identifier);
            return tree == null ? null : tree.getPath();
        }
    }

    /**
     * Returns the path of the tree references by the specified (weak)
     * reference {@code CoreValue value}.
     *
     * @param referenceValue A (weak) reference value.
     * @return The tree with the given {@code identifier} or {@code null} if no
     * such tree exists or isn't accessible to the content session.
     */
    @CheckForNull
    public String getPath(CoreValue referenceValue) {
        int type = referenceValue.getType();
        if (type == PropertyType.REFERENCE || type == PropertyType.WEAKREFERENCE) {
            return resolveUUID(referenceValue);
        } else {
            throw new IllegalArgumentException("Invalid value type");
        }
    }

    /**
     * Searches all reference properties to the specified {@code tree} that match
     * the given name and node type constraints.
     *
     * @param weak  if {@code true} only weak references are returned. Otherwise only
     *              hard references are returned.
     * @param tree The tree for which references should be searched.
     * @param propertyName A name constraint for the reference properties;
     * {@code null} if no constraint should be enforced.
     * @param nodeTypeNames Node type constraints to be enforced when using
     * for reference properties.
     * @return A set of oak paths of those reference properties referring to the
     * specified {@code tree} and matching the constraints.
     */
    @Nonnull
    public Set<String> getReferences(boolean weak, Tree tree, final String propertyName, final String... nodeTypeNames) {
        if (!isReferenceable(tree)) {
            return Collections.emptySet();
        } else {
            try {
                final String uuid = getIdentifier(tree);
                String reference = weak ? PropertyType.TYPENAME_WEAKREFERENCE : PropertyType.TYPENAME_REFERENCE;
                String pName = propertyName == null ? "*" : propertyName;   // TODO: sanitize against injection attacks!?
                Map<String, ? extends CoreValue> bindings = Collections.singletonMap("uuid", new StringValue(uuid));

                Result result = queryEngine.executeQuery(
                        "SELECT * FROM [nt:base] WHERE PROPERTY([" + pName + "], '" + reference + "') = $uuid",
                        Query.JCR_SQL2, Long.MAX_VALUE, 0, bindings, root, new NamePathMapper.Default());

                Iterable<String> paths = Iterables.transform(result.getRows(),
                        new Function<ResultRow, String>() {
                            @Override
                            public String apply(ResultRow row) {
                                String pName = propertyName == null
                                    ? findProperty(row.getPath(), uuid)
                                    : propertyName;
                                return PathUtils.concat(row.getPath(), pName);
                            }
                });

                if (nodeTypeNames.length > 0) {
                    paths = Iterables.filter(paths, new Predicate<String>() {
                        @Override
                        public boolean apply(String path) {
                            Tree tree = root.getTree(PathUtils.getParentPath(path));
                            if (tree != null) {
                                for (String ntName : nodeTypeNames) {
                                    if (hasType(tree, ntName)) {
                                        return true;
                                    }
                                }
                            }
                            return false;
                        }
                    });
                }

                return Sets.newHashSet(paths);
            }
            catch (ParseException e) {
                log.error("query failed", e);
                return Collections.emptySet();
            }
        }
    }

    private String findProperty(String path, final String uuid) {
        // TODO (OAK-220) PropertyState can only be accessed from parent tree
        Tree tree = root.getTree(path);
        assert tree != null;
        final PropertyState refProp = Iterables.find(tree.getProperties(), new Predicate<PropertyState>() {
            @Override
            public boolean apply(PropertyState pState) {
                if (pState.isArray()) {
                    for (CoreValue value : pState.getValues()) {
                        if (uuid.equals(value.getString())) {
                            return true;
                        }
                    }
                    return false;
                }
                else {
                    return uuid.equals(pState.getValue().getString());
                }
            }
        });

        return refProp.getName();
    }

    private static boolean hasType(Tree tree, String ntName) {
        // TODO use NodeType.isNodeType to determine type membership instead of equality on type names
        PropertyState pType = tree.getProperty(JcrConstants.JCR_PRIMARYTYPE);
        if (pType != null) {
            String primaryType = pType.getValue().getString();
            if (ntName.equals(primaryType)) {
                return true;
            }
        }

        PropertyState pMixin = tree.getProperty(JcrConstants.JCR_MIXINTYPES);
        if (pMixin != null) {
            List<CoreValue> mixinTypes = pMixin.getValues();
            for (CoreValue mixinType : mixinTypes) {
                if (ntName.equals(mixinType.getString())) {
                    return true;
                }
            }
        }

        return false;
    }

    public boolean isReferenceable(Tree tree) {
        // TODO add proper implementation include node type eval
        return tree.hasProperty(JcrConstants.JCR_UUID);
    }

    @CheckForNull
    private String resolveUUID(String uuid) {
        return resolveUUID(new StringValue(uuid));
    }

    private String resolveUUID(CoreValue uuid) {
        try {
            Map<String, CoreValue> bindings = Collections.singletonMap("id", uuid);
            Result result = queryEngine.executeQuery(
                    "SELECT * FROM [nt:base] WHERE [jcr:uuid] = $id", Query.JCR_SQL2,
                    Long.MAX_VALUE, 0, bindings, root, new NamePathMapper.Default());

            String path = null;
            for (ResultRow rr : result.getRows()) {
                if (path != null) {
                    log.error("multiple results for identifier lookup: " + path + " vs. " + rr.getPath());
                    return null;
                } else {
                    path = rr.getPath();
                }
            }
            return path;
        } catch (ParseException ex) {
            log.error("query failed", ex);
            return null;
        }
    }

}
TOP

Related Classes of org.apache.jackrabbit.oak.plugins.identifier.IdentifierManager

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.