All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.cometd.oort.OortContainer Maven / Gradle / Ivy

There is a newer version: 8.0.6
Show newest version
/*
 * Copyright (c) 2008-2019 the original author or authors.
 *
 * Licensed 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.cometd.oort;

import java.util.Comparator;
import java.util.HashMap;
import java.util.Map;
import java.util.PriorityQueue;
import java.util.Queue;
import java.util.concurrent.ConcurrentHashMap;

public abstract class OortContainer extends OortObject {
    private static final Map STALE_UPDATE = new HashMap<>();

    private final Map updaters = new ConcurrentHashMap<>();

    public OortContainer(Oort oort, String name, Factory factory) {
        super(oort, name, factory);
    }

    @Override
    protected void doStop() {
        super.doStop();
        updaters.clear();
    }

    @Override
    public void cometLeft(Event event) {
        super.cometLeft(event);
        updaters.remove(event.getCometURL());
    }

    @Override
    protected void onObject(Map data) {
        String oortURL = (String)data.get(Info.OORT_URL_FIELD);
        Updater updater = updater(oortURL);
        if (isItemUpdate(data)) {
            Info info = getInfo(oortURL);
            if (info == null) {
                updater.enqueue(data);
                pullInfo(oortURL);
            } else {
                if (info.isLocal()) {
                    onItem(info, data);
                } else {
                    updater.enqueue(data);
                    process(info, updater);
                }
            }
        } else {
            super.onObject(data);
            Info info = getInfo(oortURL);
            // Info may be null if the OortObject is stopped concurrently.
            if (info != null) {
                updater.pulling = false;
                updater.version = info.getVersion();
                process(info, updater);
            }
        }
    }

    private Updater updater(String oortURL) {
        Updater updater = updaters.get(oortURL);
        if (updater == null) {
            updater = new Updater();
            updaters.put(oortURL, updater);
        }
        return updater;
    }

    private void process(Info info, Updater updater) {
        while (true) {
            Map data = updater.dequeue();
            if (data == null) {
                return;
            }
            if (data == STALE_UPDATE) {
                if (!updater.pulling) {
                    updater.pulling = true;
                    pullInfo(info.getOortURL());
                }
                return;
            }
            onItem(info, data);
        }
    }

    protected abstract boolean isItemUpdate(Map data);

    protected abstract void onItem(Info info, Map data);

    /**
     * Item updates from other nodes may arrive out-of-order.
     * This class queues the updates, so that they can
     * be applied in the order they were generated, not
     * in the order they arrived.
     */
    private class Updater {
        private final Queue> updates = new PriorityQueue<>(2, new VersionComparator());
        private boolean pulling;
        private long version;

        private void enqueue(Map data) {
            updates.offer(data);
        }

        private Map dequeue() {
            while (true) {
                Map result = updates.peek();
                if (logger.isDebugEnabled()) {
                    logger.debug("Dequeued update version={}, data={}", version, result);
                }
                if (result == null) {
                    return null;
                }
                long actual = ((Number)result.get(Info.VERSION_FIELD)).longValue();
                if (actual <= version) {
                    updates.poll();
                } else {
                    long expected = version + 1;
                    if (actual > expected) {
                        return STALE_UPDATE;
                    }
                    version = expected;
                    return updates.poll();
                }
            }
        }
    }

    private static class VersionComparator implements Comparator> {
        @Override
        public int compare(Map o1, Map o2) {
            long v1 = ((Number)o1.get(Info.VERSION_FIELD)).longValue();
            long v2 = ((Number)o2.get(Info.VERSION_FIELD)).longValue();
            return Long.compare(v1, v2);
        }
    }
}




© 2015 - 2025 Weber Informatics LLC | Privacy Policy