Package org.jrebirth.af.core.concurrent

Source Code of org.jrebirth.af.core.concurrent.JRebirthThread

/**
* Get more info at : www.jrebirth.org .
* Copyright JRebirth.org © 2011-2013
* Contact : sebastien.bordes@jrebirth.org
*
* 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.jrebirth.af.core.concurrent;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.PriorityBlockingQueue;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

import javafx.scene.Scene;

import org.jrebirth.af.core.application.JRebirthApplication;
import org.jrebirth.af.core.command.basic.ChainWaveCommand;
import org.jrebirth.af.core.command.basic.showmodel.ShowModelWaveBuilder;
import org.jrebirth.af.core.exception.CoreException;
import org.jrebirth.af.core.exception.JRebirthThreadException;
import org.jrebirth.af.core.facade.GlobalFacade;
import org.jrebirth.af.core.facade.GlobalFacadeBase;
import org.jrebirth.af.core.log.JRLogger;
import org.jrebirth.af.core.log.JRLoggerFactory;
import org.jrebirth.af.core.resource.provided.JRebirthParameters;
import org.jrebirth.af.core.service.basic.StyleSheetTrackerService;
import org.jrebirth.af.core.ui.Model;
import org.jrebirth.af.core.wave.JRebirthWaves;
import org.jrebirth.af.core.wave.Wave;
import org.jrebirth.af.core.wave.WaveBuilder;
import org.jrebirth.af.core.wave.WaveData;
import org.jrebirth.af.core.wave.WaveGroup;

/**
* The class <strong>JRebirthThread</strong>.
*
* @author Sébastien Bordes
*/
public final class JRebirthThread extends Thread implements ConcurrentMessages {

    /** The JRebirth Internal Thread name [JIT]. */
    public static final String JIT_NAME = "JIT";

    /** The class logger. */
    private static final JRLogger LOGGER = JRLoggerFactory.getLogger(JRebirthThread.class);

    /** The unique instance of the current class. */
    private static JRebirthThread internalThread;

    /** The Global Facade object that handle other sub facade. */
    private transient GlobalFacade facade;

    /** The javaFX application that launch this thread. */
    private transient JRebirthApplication<?> application;

    /** The list of tasks being processed, all access MUST BE synchronized. */
    private final PriorityBlockingQueue<JRebirthRunnable> processingTasks;

    /** Flag indicating that current thread has started and is ready to process events. */
    private final AtomicBoolean hasStarted = new AtomicBoolean(false);

    /** Flag to stop the infinite loop that process JRebirth Events. */
    private final AtomicBoolean infiniteLoop = new AtomicBoolean(true);

    /** Flag that indicate that the closure must be forced. */
    private final AtomicBoolean forceClose = new AtomicBoolean(false);

    /**
     * private final boolean readyToShutdown = false;
     *
     * /** Build the JRebirth Thread.
     */
    private JRebirthThread() {
        super(JIT_NAME);

        // Daemon-ize this thread, thus it will be killed with the main JavaFX Thread
        setDaemon(true);

        // Initialize the queue
        this.processingTasks = new PriorityBlockingQueue<JRebirthRunnable>(10, new JRebirthRunnableComparator());
    }

    /**
     * Run into thread pool.
     *
     * If a slot is available the task will be run immediately.<br />
     * Otherwise it will run as soon as a slot will be available according to the existing task queue
     *
     * @param runnable the task to run
     */
    public void runIntoJTP(final JRebirthRunnable runnable) {

        if (getFacade().getExecutorService().checkAvailability(runnable.getPriority())) {
            getFacade().getExecutorService().execute(runnable);
            LOGGER.log(JTP_QUEUED, runnable.toString());
        } else {
            getFacade().getHighPriorityExecutorService().execute(runnable);
            LOGGER.log(HPTP_QUEUED, runnable.toString());
        }

    }

    /**
     * Run this task as soon as possible. Enqueue the task to be run at the next event pulse. Run into the JRebirth Thread
     *
     * @param runnable the task to run
     */
    public void runLater(final JRebirthRunnable runnable) {
        this.processingTasks.add(runnable);
    }

    /**
     * Launch the JRebirth thread.
     *
     * @param application the javaFX application instance
     */
    public void prepare(final JRebirthApplication<?> application) {

        // Link the current application
        this.application = application;
        // Build the global facade at startup
        this.facade = new GlobalFacadeBase(application);

        // Start the thread (infinite loop)
        // start();
    }

    /**
     * Return true if JRebirth has been correctly started (boot action is done).
     *
     * @return true if JRebirth has been correctly started
     */
    public boolean hasStarted() {
        return this.hasStarted.get();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void run() {

        manageStyleSheetReloading(this.application.getScene());

        // Attach the first view and run pre and post command
        try {
            bootUp();
        } catch (final JRebirthThreadException e) {
            LOGGER.error(BOOT_UP_ERROR, e);
        }

        // JRebirth thread has boot up and is ready to process events
        this.hasStarted.set(true);

        while (this.infiniteLoop.get()) {
            try {
                if (!this.forceClose.get()) {
                    final JRebirthRunnable jrr = this.processingTasks.poll(100, TimeUnit.MILLISECONDS);
                    if (jrr != null) {
                        jrr.run();
                    }
                }
            } catch (final InterruptedException e) {
                LOGGER.error(JIT_ERROR, e);
            }
        }
        // Shutdown the application more properly
        shutdown();
    }

    /**
     * Manage style sheet reloading by using a custom service provide by JRebirth Core.
     *
     * @param scene the scene to reload in case of Style Sheet update
     */
    private void manageStyleSheetReloading(final Scene scene) {
        if (JRebirthParameters.DEVELOPER_MODE.get() && scene != null) {

            for (final String styleSheet : scene.getStylesheets()) {

                getFacade().getServiceFacade().retrieve(StyleSheetTrackerService.class).listen(styleSheet, this.application.getScene());
            }
            getFacade().getServiceFacade().retrieve(StyleSheetTrackerService.class).start();
        }
    }

    /**
     * Attach the first view and run pre and post command.
     *
     * @throws JRebirthThreadException if a problem occurred while calling the command
     */
    public void bootUp() throws JRebirthThreadException {

        final List<Wave> chainedWaveList = new ArrayList<>();

        // Manage waves to run before the First node creation
        final List<Wave> preBootList = getApplication().getPreBootWaveList();
        if (preBootList != null && !preBootList.isEmpty()) {
            chainedWaveList.addAll(preBootList);
        }

        // Manage the creation of the first node and show it !

        final Wave firstViewWave = getLaunchFirstViewWave();
        if (firstViewWave != null) {
            chainedWaveList.add(firstViewWave);
        }

        // Manage waves to run after the First node creation
        final List<Wave> postBootList = getApplication().getPostBootWaveList();
        if (postBootList != null && !postBootList.isEmpty()) {
            chainedWaveList.addAll(postBootList);
        }

        if (!chainedWaveList.isEmpty()) {
            getFacade().getNotifier().sendWave(
                    WaveBuilder.create()
                            .waveGroup(WaveGroup.CALL_COMMAND)
                            .relatedClass(ChainWaveCommand.class)
                            .data(WaveData.build(JRebirthWaves.CHAINED_WAVES, chainedWaveList))
                            .build());
        }
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public void interrupt() {
        super.interrupt();

        // Release all resources
        shutdown();
    }

    /**
     * This method can be called a lot of time while application is running.
     *
     * The first time to stop the infinite loop, then to purge all queues and let the thread terminate itself.
     */
    public void close() {

        // Infinite loop is still active
        if (this.infiniteLoop.get()) {
            // First attempt to close the application
            this.infiniteLoop.set(false);
        } else {
            // N-th attempt to close the application
            this.forceClose.set(true);

            // All Task Queues are cleared
            // this.queuedTasks.clear();
            this.processingTasks.clear();
        }

    }

    /**
     * Release all resources.
     */
    private void shutdown() {
        try {
            this.facade.stop();
            this.facade = null;
            // Destroy the static reference
            destroyInstance();
        } catch (final CoreException e) {
            LOGGER.log(SHUTDOWN_ERROR, e);
        }
    }

    /**
     * Destroy the singleton that hold the thread.
     */
    private static void destroyInstance() {
        internalThread = null;
    }

    /**
     * Launch the first view by adding it into the root node.
     *
     * @return the wave responsible of the creation of the first view
     */
    @SuppressWarnings("unchecked")
    protected Wave getLaunchFirstViewWave() {
        Wave firstWave = null;
        // Generates the command wave directly to win a Wave cycle
        if (this.application != null && this.application.getRootNode() != null && this.application.getFirstModelClass() != null) {
            firstWave = ShowModelWaveBuilder.create()
                    .childrenPlaceHolder(this.application.getRootNode().getChildren())
                    .showModelKey(getFacade().getUiFacade().buildKey((Class<Model>) this.application.getFirstModelClass()))
                    .build();
        }
        return firstWave;

    }

    /**
     * @return Returns the application.
     */
    public JRebirthApplication<?> getApplication() {
        return this.application;
    }

    /**
     * @return Returns the facade.
     */
    public GlobalFacade getFacade() {
        return this.facade;
    }

    /**
     * Return the JRebirthThread used to manage facades and waves.
     *
     * @return the JRebirthThread
     */
    public static JRebirthThread getThread() {
        synchronized (JRebirthThread.class) {
            if (internalThread == null) {
                internalThread = new JRebirthThread();
            }
        }
        return internalThread;
    }

}
TOP

Related Classes of org.jrebirth.af.core.concurrent.JRebirthThread

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.