ru.iris.events.EventsService.java Source code

Java tutorial

Introduction

Here is the source code for ru.iris.events.EventsService.java

Source

/*
 * Copyright 2012-2014 Nikolay A. Viguro
 * <p/>
 * 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
 * <p/>
 * http://www.apache.org/licenses/LICENSE-2.0
 * <p/>
 * 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 ru.iris.events;

import com.avaje.ebean.Ebean;
import org.apache.commons.io.FileUtils;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import ru.iris.common.database.model.Event;
import ru.iris.common.messaging.JsonEnvelope;
import ru.iris.common.messaging.JsonMessaging;
import ru.iris.common.messaging.JsonNotification;
import ru.iris.common.messaging.model.command.CommandAdvertisement;
import ru.iris.common.messaging.model.events.*;
import ru.iris.common.modulestatus.Status;

import javax.script.*;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

/**
 * Author: Nikolay A. Viguro
 * Date: 19.11.13
 * Time: 11:25
 */

public class EventsService {
    private final Logger LOGGER = LogManager.getLogger(EventsService.class.getName());
    private final Compilable engine = (Compilable) new ScriptEngineManager().getEngineByName("nashorn");
    private Map<String, CompiledScript> compiledScriptMap;
    private Map<String, CompiledScript> compiledCommandScriptMap = new HashMap<>();
    private final JsonMessaging jsonMessaging = new JsonMessaging(UUID.randomUUID(), "events");
    private final Logger scriptLogger = LogManager.getLogger(EventsService.class.getName());
    private List<Event> events;

    public EventsService() {
        Status status = new Status("Events");

        if (status.checkExist()) {
            status.running();
        } else {
            status.addIntoDB("Events", "Service that listen for events and exec scripts as needed");
        }

        try {

            events = Ebean.find(Event.class).findList();

            // take pause to save/remove new entity
            try {
                Thread.sleep(500);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }

            // load all scripts, compile and put into map
            compiledScriptMap = loadAndCompile(events);

            // Pass jsonmessaging instance to js engine
            Bindings bindings = new SimpleBindings();
            bindings.put("jsonMessaging", jsonMessaging);
            bindings.put("LOGGER", scriptLogger);

            // subscribe to events from db
            for (Event event : events) {
                jsonMessaging.subscribe(event.getSubject());
                LOGGER.debug("Subscribe to subject: " + event.getSubject());
            }

            // command launch
            jsonMessaging.subscribe("event.command");

            // scripts
            jsonMessaging.subscribe("event.script.get");
            jsonMessaging.subscribe("event.script.save");
            jsonMessaging.subscribe("event.script.delete");
            jsonMessaging.subscribe("event.script.list");
            jsonMessaging.subscribe("event.reload");

            jsonMessaging.setNotification(new JsonNotification() {

                @Override
                public void onNotification(JsonEnvelope envelope) {

                    LOGGER.debug("Got envelope with subject: " + envelope.getSubject());

                    try {

                        // Get script content
                        if (envelope.getObject() instanceof EventGetScriptAdvertisement) {
                            LOGGER.debug("Return JS script to: " + envelope.getReceiverInstance());
                            EventGetScriptAdvertisement advertisement = envelope.getObject();
                            File jsFile;

                            if (advertisement.isCommand())
                                jsFile = new File("./scripts/command/" + advertisement.getName());
                            else
                                jsFile = new File("./scripts/" + advertisement.getName());

                            jsonMessaging.response(envelope,
                                    new EventResponseGetScriptAdvertisement(FileUtils.readFileToString(jsFile)));

                        }
                        // Save new/existing script
                        else if (envelope.getObject() instanceof EventResponseSaveScriptAdvertisement) {

                            EventResponseSaveScriptAdvertisement advertisement = envelope.getObject();
                            LOGGER.debug("Request to save changes: " + advertisement.getName());
                            File jsFile;

                            if (advertisement.isCommand())
                                jsFile = new File("./scripts/command/" + advertisement.getName());
                            else
                                jsFile = new File("./scripts/" + advertisement.getName());

                            FileUtils.writeStringToFile(jsFile, advertisement.getBody());
                            LOGGER.info("Restart event service (reason: script change)");
                            reloadService();
                        }
                        // Remove script
                        else if (envelope.getObject() instanceof EventRemoveScriptAdvertisement) {

                            EventRemoveScriptAdvertisement advertisement = envelope.getObject();
                            LOGGER.debug("Request to remove script: " + advertisement.getName());
                            File jsFile;

                            if (advertisement.isCommand())
                                jsFile = new File("./scripts/command/" + advertisement.getName());
                            else
                                jsFile = new File("./scripts/" + advertisement.getName());

                            FileUtils.forceDelete(jsFile);
                            LOGGER.info("Restart event service (reason: script removed)");
                            reloadService();
                        }
                        // List available scripts
                        else if (envelope.getObject() instanceof EventListScriptsAdvertisement) {

                            EventListScriptsAdvertisement advertisement = envelope.getObject();
                            File jsFile;

                            if (advertisement.isCommand())
                                jsFile = new File("./scripts/command/");
                            else
                                jsFile = new File("./scripts/");

                            EventResponseListScriptsAdvertisement response = new EventResponseListScriptsAdvertisement();
                            response.setScripts(
                                    (List<File>) FileUtils.listFiles(jsFile, new String[] { "js" }, false));

                            jsonMessaging.response(envelope, response);
                        }
                        // Check command and launch script
                        else if (envelope.getObject() instanceof CommandAdvertisement) {

                            CommandAdvertisement advertisement = envelope.getObject();
                            bindings.put("advertisement", envelope.getObject());

                            if (compiledCommandScriptMap.get(advertisement.getScript()) == null) {

                                LOGGER.debug("Compile command script: " + advertisement.getScript());

                                File jsFile = new File("./scripts/command/" + advertisement.getScript());
                                CompiledScript compile = engine.compile(FileUtils.readFileToString(jsFile));
                                compiledCommandScriptMap.put(advertisement.getScript(), compile);

                                LOGGER.debug("Launch compiled command script: " + advertisement.getScript());
                                compile.eval(bindings);
                            } else {
                                LOGGER.info("Launch compiled command script: " + advertisement.getScript());
                                compiledCommandScriptMap.get(advertisement.getScript()).eval(bindings);
                            }
                        } else if (envelope.getObject() instanceof EventChangesAdvertisement) {
                            reloadService();
                        } else {
                            for (Event event : events) {
                                if (envelope.getSubject().equals(event.getSubject())
                                        || wildCardMatch(event.getSubject(), envelope.getSubject())) {

                                    LOGGER.debug("Run compiled script: " + event.getScript());

                                    try {
                                        bindings.put("advertisement", envelope.getObject());
                                        CompiledScript script = compiledScriptMap.get(event.getScript());

                                        if (script != null)
                                            script.eval(bindings);
                                        else
                                            LOGGER.error("Error! Script " + event.getScript() + " is NULL!");

                                    } catch (ScriptException e) {
                                        LOGGER.error("Error in script scripts/command/" + event.getScript() + ": "
                                                + e.toString());
                                        e.printStackTrace();
                                    }
                                }
                            }
                        }
                    } catch (ScriptException | IOException e) {
                        LOGGER.error("Error in script: " + e.toString());
                        e.printStackTrace();
                    }
                }
            });

            jsonMessaging.start();
        } catch (final RuntimeException t) {
            LOGGER.error("Error in Events!");
            status.crashed();
            t.printStackTrace();
        }
    }

    private void reloadService() throws RuntimeException {
        LOGGER.info("Reload event service");

        // unsubscribe current events
        for (Event event : events) {
            jsonMessaging.unsubscribe(event.getSubject());
            LOGGER.debug("Unsubscribe from subject: " + event.getSubject());
        }

        events = Ebean.find(Event.class).findList();

        // subscribe to events from db
        for (Event event : events) {
            jsonMessaging.subscribe(event.getSubject());
            LOGGER.debug("Subscribe from subject: " + event.getSubject());
        }

        // take pause to save/remove new entity
        try {
            Thread.sleep(500);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }

        // load all scripts, compile and put into map
        compiledScriptMap = loadAndCompile(events);

        LOGGER.info("Reload event service done");
    }

    private Map<String, CompiledScript> loadAndCompile(List<Event> events) throws RuntimeException {
        Map<String, CompiledScript> compiledScriptMap = new HashMap<>();

        for (Event event : events) {
            File jsFile = new File("./scripts/" + event.getScript());
            CompiledScript compile = null;

            try {
                compile = engine.compile(FileUtils.readFileToString(jsFile));
            } catch (ScriptException | IOException e) {
                LOGGER.error("Compile error: " + e.getMessage());
            }

            compiledScriptMap.put(event.getScript(), compile);
        }

        return compiledScriptMap;
    }

    private boolean wildCardMatch(String pattern, String text) throws RuntimeException {

        // add sentinel so don't need to worry about *'s at end of pattern
        text += '\0';
        pattern += '\0';

        int N = pattern.length();

        boolean[] states = new boolean[N + 1];
        boolean[] old = new boolean[N + 1];
        old[0] = true;

        for (int i = 0; i < text.length(); i++) {
            char c = text.charAt(i);
            states = new boolean[N + 1]; // initialized to false
            for (int j = 0; j < N; j++) {
                char p = pattern.charAt(j);

                // hack to handle *'s that match 0 characters
                if (old[j] && (p == '*')) {
                    old[j + 1] = true;
                }

                if (old[j] && (p == c)) {
                    states[j + 1] = true;
                }
                if (old[j] && (p == '*')) {
                    states[j] = true;
                }
                if (old[j] && (p == '*')) {
                    states[j + 1] = true;
                }
            }
            old = states;
        }
        return states[N];
    }

    public void stop() {
        jsonMessaging.close();
    }
}