dpfmanager.shell.modules.threading.core.ThreadingService.java Source code

Java tutorial

Introduction

Here is the source code for dpfmanager.shell.modules.threading.core.ThreadingService.java

Source

/**
 * <h1>ThreadingService.java</h1> <p> This program 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; or,
 * at your choice, under the terms of the Mozilla Public License, v. 2.0. SPDX GPL-3.0+ or MPL-2.0+.
 * </p> <p> 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 and the Mozilla Public License for more details. </p>
 * <p> You should have received a copy of the GNU General Public License and the Mozilla Public
 * License along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>
 * and at <a href="http://mozilla.org/MPL/2.0">http://mozilla.org/MPL/2.0</a> . </p> <p> NB: for the
 *  statement, include Easy Innova SL or other company/Person contributing the code. </p> <p> 
 * 2015 Easy Innova, SL </p>
 *
 * @author Adri Llorens
 * @version 1.0
 * @since 23/7/2015
 */

package dpfmanager.shell.modules.threading.core;

import dpfmanager.conformancechecker.configuration.Configuration;
import dpfmanager.shell.core.DPFManagerProperties;
import dpfmanager.shell.core.DpFManagerConstants;
import dpfmanager.shell.core.adapter.DpfService;
import dpfmanager.shell.core.config.BasicConfig;
import dpfmanager.shell.core.config.GuiConfig;
import dpfmanager.shell.core.context.DpfContext;
import dpfmanager.shell.core.messages.ReportsMessage;
import dpfmanager.shell.interfaces.gui.workbench.GuiWorkbench;
import dpfmanager.shell.modules.database.messages.CheckTaskMessage;
import dpfmanager.shell.modules.database.messages.JobsMessage;
import dpfmanager.shell.modules.messages.messages.CloseMessage;
import dpfmanager.shell.modules.messages.messages.ExceptionMessage;
import dpfmanager.shell.modules.messages.messages.LogMessage;
import dpfmanager.shell.modules.report.core.IndividualReport;
import dpfmanager.shell.modules.report.messages.GlobalReportMessage;
import dpfmanager.shell.modules.threading.messages.GlobalStatusMessage;
import dpfmanager.shell.modules.threading.messages.RunnableMessage;
import dpfmanager.shell.modules.threading.messages.ThreadsMessage;
import dpfmanager.shell.modules.threading.runnable.DpfRunnable;
import dpfmanager.shell.modules.timer.messages.TimerMessage;
import dpfmanager.shell.modules.timer.tasks.JobsStatusTask;

import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.Level;
import org.springframework.context.annotation.Scope;
import org.springframework.stereotype.Service;

import java.awt.*;
import java.io.File;
import java.net.URI;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.Map;
import java.util.Queue;
import java.util.ResourceBundle;

import javax.annotation.PostConstruct;
import javax.annotation.PreDestroy;

/**
 * Created by Adri Llorens on 07/04/2016.
 */
@Service(BasicConfig.SERVICE_THREADING)
@Scope("singleton")
public class ThreadingService extends DpfService {

    /**
     * The main executor service
     */
    private DpfExecutor myExecutor;

    /**
     * The number of threads
     */
    private int cores;

    /**
     * The resource bundle
     */
    private ResourceBundle bundle;

    private Map<Long, FileCheck> checks;
    private Queue<FileCheck> pendingChecks;

    private boolean needReload;
    private int totalChecks;

    @PostConstruct
    public void init() {
        // No context yet
        bundle = DPFManagerProperties.getBundle();
        checks = new HashMap<>();
        pendingChecks = new LinkedList<>();
        needReload = true;
        totalChecks = 0;
    }

    @PreDestroy
    public void finish() {
        // Finish executor
        myExecutor.shutdownNow();
    }

    @Override
    protected void handleContext(DpfContext context) {
        cores = Runtime.getRuntime().availableProcessors() - 1;
        if (cores < 1) {
            cores = 1;
        }

        // Check for the -t option for tests
        if (GuiWorkbench.getAppParams() != null) {
            for (String param : GuiWorkbench.getAppParams().getRaw()) {
                if (param.startsWith("-t") && param.length() == 3) {
                    String number = param.substring(2);
                    int threads = Integer.valueOf(number);
                    if (threads < cores && threads > 1) {
                        cores = threads;
                    }
                    break;
                }
            }
        }

        myExecutor = new DpfExecutor(cores);
        myExecutor.handleContext(context);
    }

    public void run(DpfRunnable runnable, Long uuid) {
        runnable.setContext(getContext());
        runnable.setUuid(uuid);
        myExecutor.myExecute(runnable);
    }

    public void processThreadMessage(ThreadsMessage tm) {
        if (tm.isPause() && tm.isRequest()) {
            myExecutor.pause(tm.getUuid());
        } else if (tm.isResume()) {
            context.send(BasicConfig.MODULE_DATABASE, new JobsMessage(JobsMessage.Type.RESUME, tm.getUuid()));
            myExecutor.resume(tm.getUuid());
        } else if (tm.isCancel() && tm.isRequest()) {
            cancelRequest(tm.getUuid());
        } else if (tm.isCancel() && !tm.isRequest()) {
            cancelFinish(tm.getUuid());
        } else if (tm.isPause() && !tm.isRequest()) {
            pauseFinish(tm.getUuid());
        }
    }

    public void closeRequested() {
        context.send(BasicConfig.MODULE_MESSAGE, new CloseMessage(CloseMessage.Type.THREADING, !checks.isEmpty()));
    }

    private void cancelRequest(Long uuid) {
        // Check if is pending
        FileCheck pending = null;
        for (FileCheck check : pendingChecks) {
            if (uuid.equals(check.getUuid())) {
                pending = check;
                break;
            }
        }
        if (pending != null) {
            // Cancel pending
            pendingChecks.remove(pending);
            context.send(GuiConfig.COMPONENT_PANE, new CheckTaskMessage(CheckTaskMessage.Target.CANCEL, uuid));
        } else {
            // Cancel threads
            myExecutor.cancel(uuid);
        }
    }

    public void cancelFinish(Long uuid) {
        // Update db
        getContext().send(BasicConfig.MODULE_DATABASE, new JobsMessage(JobsMessage.Type.CANCEL, uuid));

        if (checks.get(uuid) != null) {
            // Remove folder
            String internal = checks.get(uuid).getInternal();
            if (internal != null) {
                removeInternalFolder(internal);
            }

            // Remove from checks pool
            checks.remove(uuid);
        }
    }

    public void pauseFinish(Long uuid) {
        // Update db
        getContext().send(BasicConfig.MODULE_DATABASE, new JobsMessage(JobsMessage.Type.PAUSE, uuid));
        // Refresh tasks
        context.send(BasicConfig.MODULE_TIMER, new TimerMessage(TimerMessage.Type.RUN, JobsStatusTask.class));
    }

    public void handleGlobalStatus(GlobalStatusMessage gm, boolean silence) {
        if (gm.isNew()) {
            // New file check
            Long uuid = gm.getUuid();
            FileCheck fc = new FileCheck(uuid);
            boolean pending = false;
            if (runningChecks() >= DpFManagerConstants.MAX_CHECKS) {
                // Add pending check
                fc.setInitialTask(gm.getRunnable());
                pendingChecks.add(fc);
                pending = true;
            } else {
                //Start now
                checks.put(uuid, fc);
                context.send(BasicConfig.MODULE_THREADING, new RunnableMessage(uuid, gm.getRunnable()));
            }
            context.send(BasicConfig.MODULE_DATABASE,
                    new JobsMessage(JobsMessage.Type.NEW, uuid, gm.getInput(), pending));
        } else if (gm.isInit()) {
            // Init file check
            FileCheck fc = checks.get(gm.getUuid());
            fc.init(gm.getSize(), gm.getConfig(), gm.getInternal(), gm.getInput());
            context.send(BasicConfig.MODULE_MESSAGE, new LogMessage(getClass(), Level.DEBUG,
                    bundle.getString("startingCheck").replace("%1", gm.getInput())));
            context.send(BasicConfig.MODULE_DATABASE,
                    new JobsMessage(JobsMessage.Type.INIT, fc.getUuid(), fc.getTotal(), fc.getInternal()));
        } else if (gm.isFinish() || gm.isCancel()) {
            // Finish file check
            FileCheck fc = checks.get(gm.getUuid());
            removeZipFolder(fc.getInternal());
            removeDownloadFolder(fc.getInternal());
            moveServerFolder(fc.getUuid(), fc.getInternal());
            if (context.isGui()) {
                // Notify task manager
                needReload = true;
            } else if (!silence) {
                // No ui, show to user
                showToUser(fc.getInternal(), fc.getConfig());
            }
            if (!gm.isCancel()) {
                context.send(BasicConfig.MODULE_DATABASE, new JobsMessage(JobsMessage.Type.FINISH, gm.getUuid()));
            } else {
                removeReportFolderIfEmpty(gm.getInternal());
            }
            checks.remove(gm.getUuid());
            totalChecks++;
            if (totalChecks >= 10) {
                System.gc();
                context.send(BasicConfig.MODULE_MESSAGE,
                        new LogMessage(getClass(), Level.DEBUG, bundle.getString("runGC")));
                totalChecks = 0;
            }
            // Start pending checks
            startPendingChecks();
        } else if (context.isGui() && gm.isReload()) {
            // Ask for reload
            if (needReload) {
                needReload = false;
                context.send(GuiConfig.PERSPECTIVE_REPORTS + "." + GuiConfig.COMPONENT_REPORTS,
                        new ReportsMessage(ReportsMessage.Type.RELOAD));
            }
        }
    }

    public void finishIndividual(IndividualReport ir, Long uuid, Configuration config) {
        FileCheck fc = checks.get(uuid);
        if (fc != null) {
            if (fc.getConfig() == null) {
                fc.setConfig(config);
            }
            if (ir != null) {
                // Individual report finished
                fc.addIndividual(ir);
            } else {
                // Individual with errors
                fc.addError();
            }
            // Check if all finished
            if (fc.allFinished()) {
                // Tell reports module
                context.send(BasicConfig.MODULE_REPORT,
                        new GlobalReportMessage(uuid, fc.getIndividuals(), fc.getConfig()));
            }
            context.send(BasicConfig.MODULE_DATABASE, new JobsMessage(JobsMessage.Type.UPDATE, uuid));
        }
    }

    private void startPendingChecks() {
        if (!pendingChecks.isEmpty()) {
            FileCheck fc = pendingChecks.poll();
            checks.put(fc.getUuid(), fc);
            context.send(BasicConfig.MODULE_DATABASE, new JobsMessage(JobsMessage.Type.START, fc.getUuid()));
            context.send(BasicConfig.MODULE_THREADING, new RunnableMessage(fc.getUuid(), fc.getInitialTask()));
        }
    }

    private int runningChecks() {
        return checks.size();
    }

    /**
     * Remove functions
     */
    public void removeZipFolder(String internal) {
        try {
            File zipFolder = new File(internal + "zip");
            if (zipFolder.exists() && zipFolder.isDirectory()) {
                FileUtils.deleteDirectory(zipFolder);
            }
        } catch (Exception e) {
            context.send(BasicConfig.MODULE_MESSAGE, new ExceptionMessage(bundle.getString("excZip"), e));
        }
    }

    private void removeDownloadFolder(String internal) {
        try {
            File zipFolder = new File(internal + "download");
            if (zipFolder.exists() && zipFolder.isDirectory()) {
                FileUtils.deleteDirectory(zipFolder);
            }
        } catch (Exception e) {
            context.send(BasicConfig.MODULE_MESSAGE, new ExceptionMessage(bundle.getString("excDownload"), e));
        }
    }

    private void removeInternalFolder(String internal) {
        try {
            File folder = new File(internal);
            if (folder.exists() && folder.isDirectory()) {
                FileUtils.deleteDirectory(folder);
            }
        } catch (Exception e) {
            context.send(BasicConfig.MODULE_MESSAGE, new ExceptionMessage(bundle.getString("excInternal"), e));
        }
    }

    private void removeServerFolder(Long uuid) {
        try {
            File folder = new File(DPFManagerProperties.getServerDir() + "/" + uuid);
            if (folder.exists() && folder.isDirectory()) {
                FileUtils.deleteDirectory(folder);
            }
        } catch (Exception e) {
            context.send(BasicConfig.MODULE_MESSAGE, new ExceptionMessage(bundle.getString("excServer"), e));
        }
    }

    private void removeReportFolderIfEmpty(String internal) {
        try {
            File folder = new File(internal);
            if (folder.exists() && folder.isDirectory() && folder.list().length == 0) {
                FileUtils.deleteDirectory(folder);
            }
        } catch (Exception e) {
            context.send(BasicConfig.MODULE_MESSAGE, new ExceptionMessage(bundle.getString("excInternal"), e));
        }
    }

    private void moveServerFolder(Long uuid, String internal) {
        try {
            File dest = new File(internal + "input");
            File src = new File(DPFManagerProperties.getServerDir() + "/" + uuid);
            if (src.exists() && src.isDirectory()) {
                FileUtils.moveDirectory(src, dest);
            }
        } catch (Exception e) {
            context.send(BasicConfig.MODULE_MESSAGE, new ExceptionMessage("Exception in remove server folder", e));
        }
    }

    /**
     * Show report
     */
    private void showToUser(String internal, Configuration config) {
        String name = "";
        String path;
        if (config.getFormats().contains("HTML")) {
            name = "report.html";
        } else if (config.getFormats().contains("PDF")) {
            name = "report.pdf";
        }

        path = internal + name;
        if (config.getOutput() != null) {
            path = config.getOutput() + "/" + name;
        }
        File file = new File(path);
        if (file.exists() && Desktop.isDesktopSupported()) {
            try {
                String fullPath = file.getAbsolutePath();
                fullPath = fullPath.replaceAll("\\\\", "/");
                Desktop.getDesktop().browse(new URI("file:///" + fullPath.replaceAll(" ", "%20")));
            } catch (Exception e) {
                context.send(BasicConfig.MODULE_MESSAGE, new ExceptionMessage(bundle.getString("browserError"), e));
            }
        } else {
            context.send(BasicConfig.MODULE_MESSAGE,
                    new LogMessage(getClass(), Level.DEBUG, bundle.getString("deskServError")));
        }
    }

}