Package org.apache.jackrabbit.oak.plugins.index

Source Code of org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate$TypedEditorProvider

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

import static com.google.common.base.Preconditions.checkNotNull;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.ASYNC_PROPERTY_NAME;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.INDEX_DEFINITIONS_NAME;
import static org.apache.jackrabbit.oak.plugins.index.IndexConstants.TYPE_PROPERTY_NAME;
import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.getBoolean;
import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.getString;
import static org.apache.jackrabbit.oak.plugins.index.IndexUtils.isIndexNodeType;

import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javax.annotation.Nonnull;

import org.apache.jackrabbit.oak.api.CommitFailedException;
import org.apache.jackrabbit.oak.plugins.memory.EmptyNodeState;
import org.apache.jackrabbit.oak.spi.commit.DefaultEditor;
import org.apache.jackrabbit.oak.spi.commit.Editor;
import org.apache.jackrabbit.oak.spi.commit.EditorHook;
import org.apache.jackrabbit.oak.spi.commit.EditorProvider;
import org.apache.jackrabbit.oak.spi.commit.EmptyHook;
import org.apache.jackrabbit.oak.spi.commit.VisibleEditor;
import org.apache.jackrabbit.oak.spi.state.NodeBuilder;
import org.apache.jackrabbit.oak.spi.state.NodeState;
import org.apache.jackrabbit.oak.spi.state.NodeStore;
import org.apache.jackrabbit.oak.spi.state.NodeStoreBranch;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

public class AsyncIndexUpdate implements Runnable {

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

    private final static int CONFIG_WATCH_DELAY_MS = 30000;

    // TODO index impl run frequency could be picked up from the index config
    // directly
    private final static int INDEX_TASK_DELAY_MS = 5000;

    private final NodeStore store;

    private final ScheduledExecutorService executor;

    private final IndexEditorProvider provider;

    private NodeState current = EmptyNodeState.EMPTY_NODE;

    final Map<String, IndexTask> active = new ConcurrentHashMap<String, IndexTask>();

    private boolean started;

    public AsyncIndexUpdate(@Nonnull NodeStore store,
            @Nonnull ScheduledExecutorService executor,
            @Nonnull IndexEditorProvider provider) {
        this.store = checkNotNull(store);
        this.executor = checkNotNull(executor);
        this.provider = checkNotNull(provider);
    }

    public synchronized void start() {
        if (started) {
            log.error("Background index config watcher task already started");
            return;
        }
        started = true;
        executor.scheduleWithFixedDelay(this, 100, CONFIG_WATCH_DELAY_MS,
                TimeUnit.MILLISECONDS);
    }

    @Override
    public void run() {
        log.debug("Running background index config watcher task");
        NodeState after = store.getRoot();
        try {
            EditorHook hook = new EditorHook(new EditorProvider() {
                @Override
                public Editor getRootEditor(NodeState before, NodeState after,
                        NodeBuilder builder) {
                    return VisibleEditor.wrap(new IndexConfigWatcher());
                }
            });
            hook.processCommit(current, after);
            current = after;
        } catch (CommitFailedException e) {
            log.warn("IndexTask update failed", e);
        }
    }

    public synchronized void replace(Map<String, Set<String>> async) {
        Set<String> in = new HashSet<String>(async.keySet());
        Set<String> existing = active.keySet();
        for (String type : existing) {
            if (in.remove(type)) {
                Set<String> defs = async.get(type);
                if (defs.isEmpty()) {
                    remove(type);
                } else {
                    addOrUpdate(type, defs);
                }
            } else {
                remove(type);
            }
        }
        for (String type : in) {
            addOrUpdate(type, async.get(type));
        }
    }

    void addOrUpdate(String type, Set<String> defs) {
        IndexTask task = active.get(type);
        if (task == null) {
            task = new IndexTask(store, provider, type, defs);
            active.put(type, task);
            task.start(executor);
        } else {
            task.update(defs);
        }
    }

    void remove(String type) {
        IndexTask task = active.remove(type);
        if (task != null) {
            task.stop();
        }
    }

    /**
     * This Editor is responsible for watching over changes on async index defs:
     * added index defs and deleted index defs are pushed forward to the
     * #replace method
     *
     */
    class IndexConfigWatcher extends DefaultEditor {

        private final Map<String, Set<String>> async = new HashMap<String, Set<String>>();

        @Override
        public void enter(NodeState before, NodeState after)
                throws CommitFailedException {
            if (!after.hasChildNode(INDEX_DEFINITIONS_NAME)) {
                return;
            }
            NodeState index = after.getChildNode(INDEX_DEFINITIONS_NAME);
            for (String indexName : index.getChildNodeNames()) {
                NodeState indexChild = index.getChildNode(indexName);
                if (isIndexNodeType(indexChild)) {
                    boolean isasync = getBoolean(indexChild,
                            ASYNC_PROPERTY_NAME);
                    String type = getString(indexChild, TYPE_PROPERTY_NAME);
                    if (type == null || !isasync) {
                        // skip null and non-async types
                        continue;
                    }
                    Set<String> defs = async.get(type);
                    if (defs == null) {
                        defs = new HashSet<String>();
                        async.put(type, defs);
                    }
                    defs.add(type);
                }
            }
        }

        @Override
        public void leave(NodeState before, NodeState after)
                throws CommitFailedException {
            replace(async);
            async.clear();
        }

    }

    static class IndexTask implements Runnable {

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

        private final NodeStore store;

        private final String type;
        private Set<String> defs;

        private final IndexEditorProvider provider;

        private ScheduledFuture<?> future;

        private NodeState before;

        public IndexTask(NodeStore store, IndexEditorProvider provider,
                String type, Set<String> defs) {
            this.store = store;
            this.provider = provider;
            this.type = type;
            this.defs = defs;
            this.before = EmptyNodeState.EMPTY_NODE;
        }

        public void update(Set<String> defs) {
            // check of there are any changes
            // TODO what happens when I move a def? (rm + add appears as a no-op
            // in the set)
            if (this.defs.equals(defs)) {
                // no-op
                return;
            }

            log.debug("Updated index def for type {}, reindexing", type);
            this.defs = defs;
            this.before = EmptyNodeState.EMPTY_NODE;
        }

        public synchronized void start(ScheduledExecutorService executor) {
            if (future != null) {
                throw new IllegalStateException("IndexTask has already started");
            }
            future = executor.scheduleWithFixedDelay(this, 100,
                    INDEX_TASK_DELAY_MS, TimeUnit.MILLISECONDS);
        }

        public synchronized void stop() {
            if (future == null) {
                log.warn("IndexTask has already stopped.");
                return;
            }
            future.cancel(true);
        }

        @Override
        public void run() {
            log.debug("Running background index task for type {}.", type);
            NodeStoreBranch branch = store.branch();
            NodeState after = branch.getHead();
            try {
                EditorHook hook = new EditorHook(new TypedEditorProvider(
                        provider, type));
                NodeState processed = hook.processCommit(before, after);
                branch.setRoot(processed);
                branch.merge(EmptyHook.INSTANCE);
                before = after;
            } catch (CommitFailedException e) {
                log.warn("IndexTask update failed", e);
            }
        }
    }

    /**
     * This creates a composite editor from a type-filtered index provider.
     *
     */
    private static class TypedEditorProvider implements EditorProvider {

        private final IndexEditorProvider provider;

        private final String type;

        public TypedEditorProvider(IndexEditorProvider provider, String type) {
            this.type = type;
            this.provider = provider;
        }

        /**
         * This does not make any effort to filter async definitions. The
         * assumption is that given an index type, all of the returned index
         * hooks inherit the same async assumption.
         *
         */
        @Override
        public Editor getRootEditor(NodeState before, NodeState after,
                NodeBuilder builder) {
            return VisibleEditor.wrap(provider.getIndexEditor(type, builder));
        }
    }

}
TOP

Related Classes of org.apache.jackrabbit.oak.plugins.index.AsyncIndexUpdate$TypedEditorProvider

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.