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

com.graphaware.runtime.BaseGraphAwareRuntime Maven / Gradle / Ivy

There is a newer version: 4.2.0.58
Show newest version
/*
 * Copyright (c) 2013-2016 GraphAware
 *
 * This file is part of the GraphAware Framework.
 *
 * GraphAware Framework is free software: you can redistribute it and/or modify it under the terms of
 * the GNU General Public License as published by the Free Software Foundation, either
 * version 3 of the License, or (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY;
 * without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details. You should have received a copy of
 * the GNU General Public License along with this program.  If not, see
 * .
 */

package com.graphaware.runtime;

import com.graphaware.common.ping.GoogleAnalyticsStatsCollector;
import com.graphaware.runtime.config.RuntimeConfiguration;
import com.graphaware.runtime.module.RuntimeModule;
import com.graphaware.tx.event.improved.api.ImprovedTransactionData;
import org.neo4j.graphdb.event.ErrorState;
import org.neo4j.graphdb.event.KernelEventHandler;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.util.Set;
import java.util.concurrent.TimeUnit;

/**
 * Abstract base-class for {@link GraphAwareRuntime} implementations. Handles lifecycle of the runtime and basic
 * module-related sanity checks.
 */
public abstract class BaseGraphAwareRuntime implements GraphAwareRuntime, KernelEventHandler {

    private static final Logger LOG = LoggerFactory.getLogger(BaseGraphAwareRuntime.class);

    private static final ThreadLocal startingThread = new ThreadLocal() {
        @Override
        protected Boolean initialValue() {
            return false;
        }
    };

    private final RuntimeConfiguration configuration;

    private volatile State state = State.NONE;

    private enum State {
        NONE,
        REGISTERED,
        STARTING,
        STARTED,
        SHUTDOWN
    }

    /**
     * Create a new instance.
     *
     * @param configuration config.
     */
    protected BaseGraphAwareRuntime(RuntimeConfiguration configuration) {
        this.configuration = configuration;

        if (!State.NONE.equals(state)) {
            throw new IllegalStateException("Only one instance of the GraphAware Runtime should ever be instantiated and started.");
        }

        state = State.REGISTERED;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public RuntimeConfiguration getConfiguration() {
        return configuration;
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final synchronized void registerModule(RuntimeModule module) {
        if (!State.REGISTERED.equals(state)) {
            LOG.error("Modules must be registered before GraphAware Runtime is started!");
            throw new IllegalStateException("Modules must be registered before GraphAware Runtime is started!");
        }

        LOG.info("Registering module " + module.getId() + " with GraphAware Runtime.");

        checkNotAlreadyRegistered(module);
        doRegisterModule(module);

        GoogleAnalyticsStatsCollector.getInstance().moduleStart(module.getClass().getCanonicalName());
    }

    /**
     * Check that the given module isn't already registered with the runtime.
     *
     * @param module to check.
     * @throws IllegalStateException in case the module is already registered.
     */
    protected abstract void checkNotAlreadyRegistered(RuntimeModule module);

    /**
     * Perform the actual module registration after sanity checks have passed.
     *
     * @param module to register.
     */
    protected abstract void doRegisterModule(RuntimeModule module);

    /**
     * {@inheritDoc}
     */
    @Override
    public final synchronized void start() {
        if (State.STARTED.equals(state)) {
            LOG.debug("GraphAware already started");
            return;
        }

        if (State.STARTING.equals(state)) {
            throw new IllegalStateException("Attempt to start GraphAware from multiple different threads. This is a bug");
        }

        if (!State.REGISTERED.equals(state)) {
            throw new IllegalStateException("Illegal Runtime state " + state + "! This is a bug");
        }

        startingThread.set(true);
        LOG.info("Starting GraphAware...");
        state = State.STARTING;

        startStatsCollector();
        startModules();
        startWriter();

        state = State.STARTED;
        LOG.info("GraphAware started.");
        startingThread.set(false);
    }

    /**
     * Start stats collector.
     */
    private void startStatsCollector() {
        GoogleAnalyticsStatsCollector.getInstance().runtimeStart();
    }

    /**
     * Perform the actual start of the runtime, being certain that it is the right time to do so.
     */
    protected void startModules() {
        LOG.info("Loading module metadata...");
        Set moduleIds = loadMetadata();
        cleanupMetadata(moduleIds);
        LOG.info("Module metadata loaded.");
    }

    /**
     * Start the database writer.
     */
    private void startWriter() {
        getDatabaseWriter().start();
    }

    /**
     * Load module metadata.
     *
     * @return IDs of all modules registered with the runtime, no matter whether they previously had some metadata in
     * the graph or not.
     */
    protected abstract Set loadMetadata();

    /**
     * Perform cleanup of metadata potentially written to the graph by modules that aren't used any more.
     *
     * @param usedModules IDs of all the used modules (should be the same as returned by {@link #loadMetadata()}.
     */
    protected abstract void cleanupMetadata(Set usedModules);

    /**
     * {@inheritDoc}
     */
    @Override
    public final void waitUntilStarted() {
        if (!isStarted(null)) {
            throw new IllegalStateException("It appears that the thread starting the runtime called waitUntilStarted() before it's finished its job. This is a bug");
        }
    }

    /**
     * Checks to see if this {@link GraphAwareRuntime} is started. Blocks until it is started, unless one of the following
     * conditions is met:
     * 
    *
  • it's already started, in which case the method returns true
  • *
  • hasn't even started starting for more than 1s, in which case an exception is thrown
  • *
  • hasn't been started yet, but the transaction triggering the call of this method isn't mutating, in which case it returns false
  • *
  • it's starting but the caller is the thread that starts the runtime itself, in which case it returns false
  • *
* * @return true iff the runtime is started. * false iff the runtime isn't started but it is safe to proceed. * @throws IllegalStateException in case the runtime hasn't been started at all. */ protected final boolean isStarted(ImprovedTransactionData transactionData) { if (State.NONE.equals(state)) { throw new IllegalStateException("Runtime has not been registered! This is a bug."); } if (State.SHUTDOWN.equals(state)) { throw new IllegalStateException("Runtime is being / has been shut down."); } int attempts = 0; while (!State.STARTED.equals(state)) { //workaround for https://github.com/neo4j/neo4j/issues/2804 if (transactionData != null && !transactionData.mutationsOccurred()) { return false; } if (State.STARTING.equals(state) && startingThread.get()) { return false; } try { attempts++; if (attempts > 100 && State.REGISTERED.equals(state)) { throw new IllegalStateException("Runtime has not been started!"); } TimeUnit.MILLISECONDS.sleep(10); } catch (InterruptedException e) { //just continue } } return true; } /** * {@inheritDoc} */ @Override public final void beforeShutdown() { LOG.info("Shutting down GraphAware Runtime... "); state = State.SHUTDOWN; shutdownModules(); stopWriter(); afterShutdown(); LOG.info("GraphAware Runtime shut down."); } /** * React to shutdown. */ protected void afterShutdown() { //for subclasses } /** * Shutdown all modules. */ protected abstract void shutdownModules(); /** * Stop database writer. */ private void stopWriter() { getDatabaseWriter().stop(); } /** * {@inheritDoc} */ @Override public final void kernelPanic(ErrorState error) { //do nothing } /** * {@inheritDoc} */ @Override public final Object getResource() { return null; } /** * {@inheritDoc} */ @Override public final ExecutionOrder orderComparedTo(KernelEventHandler other) { return ExecutionOrder.DOESNT_MATTER; } }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy