org.pascani.dsl.lib.sca.PascaniUtils.java Source code

Java tutorial

Introduction

Here is the source code for org.pascani.dsl.lib.sca.PascaniUtils.java

Source

/*
 * Copyright  2015 Universidad Icesi
 * 
 * This file is part of the Pascani project.
 * 
 * The Pascani project is free software: you can redistribute it and/or modify
 * it under the terms of the GNU Lesser General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or (at your
 * option) any later version.
 * 
 * The Pascani project 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 Lesser General Public License
 * for more details.
 * 
 * You should have received a copy of the GNU Lesser General Public License
 * along with The Pascani project. If not, see <http://www.gnu.org/licenses/>.
 */
package org.pascani.dsl.lib.sca;

import static org.pascani.dsl.lib.sca.FrascatiUtils.eval;

import java.io.IOException;
import java.net.URI;
import java.net.URL;
import java.nio.charset.Charset;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.script.ScriptException;

import org.pascani.dsl.lib.Event;
import org.pascani.dsl.lib.PascaniRuntime;
import org.pascani.dsl.lib.Probe;
import org.pascani.dsl.lib.events.ExceptionEvent;
import org.pascani.dsl.lib.events.InvokeEvent;
import org.pascani.dsl.lib.events.ReturnEvent;
import org.pascani.dsl.lib.events.TimeLapseEvent;
import org.pascani.dsl.lib.infrastructure.ProbeProxy;
import org.pascani.dsl.lib.util.Exceptions;

import com.google.common.base.MoreObjects;
import com.google.common.io.Resources;

/**
 * @author Miguel Jimnez - Initial contribution and API
 */
public class PascaniUtils {

    private static Map<URI, Boolean> registeredScripts = new HashMap<URI, Boolean>();

    /**
     * Introduces a new SCA intent in the specified FraSCAti runtime, bound to
     * the specified target. It also adds a shutdown hook for removing the probe
     * once execution terminates.
     * 
     * @param target
     *            A FPath selector
     * @param routingKey
     *            A unique name (within the
     *            {@code org.pascani.dsl.lib.PascaniRuntime.Context#PROBE}
     *            context) for the new probe
     * @param intentName
     *            The name of the Pascani intent
     * @param bindingUri
     *            The URI where the FraSCAti runtime is running
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     * @see FrascatiUtils#DEFAULT_BINDING_URI
     */
    public static void newIntent(final String target, final String routingKey, final String intentName,
            final URI bindingUri) throws IOException, ScriptException {
        registerPascaniScripts(bindingUri);
        String[] data = target.split("/");
        String parent = data[0] + "/" + data[1];
        StringBuilder params = new StringBuilder();
        params.append(parent + ", ");
        params.append(target + ", ");
        params.append("\"" + intentName + "\", ");
        params.append("\"" + routingKey + "\"");
        eval("pascani-add-intent(" + params.toString() + ");", bindingUri);
        Runtime.getRuntime().addShutdownHook(new Thread() {
            public void run() {
                try {
                    PascaniUtils.removeProbe(target, routingKey, bindingUri);
                } catch (Exception e) {
                    Exceptions.sneakyThrow(e);
                }
            }
        });
    }

    /**
     * Updates the given probe property, in the specified FraSCAti runtime.
     * 
     * @param target
     *            A FPath selector
     * @param routingKey
     *            The probe's routing key
     * @param propertyName
     *            The name of the property to update
     * @param propertyValue
     *            The new property value
     * @param bindingUri
     *            The URI where the FraSCAti runtime is running
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     * @see FrascatiUtils#DEFAULT_BINDING_URI
     */
    public static void setProbeProperty(String target, String routingKey, String propertyName, String propertyValue,
            URI bindingUri) throws IOException, ScriptException {
        registerPascaniScripts(bindingUri);
        String[] data = target.split("/");
        String parent = data[0] + "/" + data[1];
        String keyValue = propertyName + "=" + propertyValue;
        eval("pascani-probe-set(" + parent + ", \"" + routingKey + "\", \"" + keyValue + "\")", bindingUri);
    }

    /**
     * Introduces a new SCA intent in the specified FraSCAti runtime, bound to
     * the specified target and returns a proxy pointing to the corresponding
     * {@link Probe}.
     * 
     * @param target
     *            A FPath selector
     * @param routingKey
     *            A unique name (within the
     *            {@code org.pascani.dsl.lib.PascaniRuntime.Context#PROBE}
     *            context) for the new probe
     * @param intentName
     *            The name of the Pascani intent
     * @param activateProducer
     *            Whether or not the event producer must be activated
     * @param bindingUri
     *            The URI where the FraSCAti runtime is running
     * @return a {@link ProbeProxy} instance pointing to the introduced
     *         {@link Probe} or {@code null} if something bad happens while
     *         evaluating the FScript commands.
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     */
    public static ProbeProxy newProbe(String target, String routingKey, String intentName, boolean activateProducer,
            URI bindingUri) throws IOException, ScriptException {
        newIntent(target, routingKey, intentName, bindingUri);
        for (String key : PascaniRuntime.getEnvironment().keySet()) {
            setProbeProperty(target, routingKey, "pascani." + key, PascaniRuntime.getEnvironment().get(key),
                    bindingUri);
        }
        setProbeProperty(target, routingKey, "routingkey", routingKey, bindingUri);
        setProbeProperty(target, routingKey, "probe", Boolean.TRUE.toString(), bindingUri);
        if (activateProducer)
            setProbeProperty(target, routingKey, "producer", Boolean.TRUE.toString(), bindingUri);
        return Exceptions.sneakyInitializer(ProbeProxy.class, routingKey);
    }

    /**
     * Introduces a new SCA intent in the default FraSCAti runtime, bound to the
     * specified target and returns a proxy pointing to the corresponding
     * {@link Probe}. The probe created manages events of type
     * {@link TimeLapseEvent}, {@link InvokeEvent}, {@link ExceptionEvent},
     * {@link ReturnEvent}.
     * 
     * @param target
     *            A FPath selector
     * @param routingKey
     *            A unique name (within the
     *            {@code org.pascani.dsl.lib.PascaniRuntime.Context#PROBE}
     *            context) for the new probe
     * @param activateProducer
     *            Whether or not the event producer must be activated
     * @return a {@link ProbeProxy} instance pointing to the introduced
     *         {@link Probe} or {@code null} if something bad happens while
     *         evaluating the FScript commands.
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     */
    public static ProbeProxy newProbe(String target, String routingKey, boolean activateProducer)
            throws IOException, ScriptException {
        return newProbe(target, routingKey, "pascani-all-events-intent", activateProducer,
                FrascatiUtils.DEFAULT_BINDING_URI);
    }

    /**
     * Introduces a new SCA intent in the specified FraSCAti runtime, bound to
     * the specified target and returns a proxy pointing to the corresponding
     * {@link Probe}. The probe created manages events of the specified type.
     * 
     * @param target
     *            A FPath selector
     * @param routingKey
     *            A unique name (within the
     *            {@code org.pascani.dsl.lib.PascaniRuntime.Context#PROBE}
     *            context) for the new probe
     * @param eventType
     *            The type of events managed by the {@link Probe}
     * @param activateProducer
     *            Whether or not the event producer must be activated
     * @param bindingUri
     *            The URI where the FraSCAti runtime is running
     * @return a {@link ProbeProxy} instance pointing to the introduced
     *         {@link Probe} or {@code null} if something bad happens while
     *         evaluating the FScript commands.
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     */
    public static ProbeProxy newProbe(String target, String routingKey, Class<? extends Event<?>> eventType,
            boolean activateProducer, URI bindingUri) throws IOException, ScriptException {
        String intentName = intentName(eventType);
        return newProbe(target, routingKey, intentName, activateProducer, bindingUri);
    }

    /**
     * Introduces a new SCA intent in the default FraSCAti runtime, bound to the
     * specified target and returns a proxy pointing to the corresponding
     * {@link Probe}. The probe created manages events of the specified type.
     * 
     * @param target
     *            A FPath selector
     * @param routingKey
     *            A unique name (within the
     *            {@code org.pascani.dsl.lib.PascaniRuntime.Context#PROBE}
     *            context) for the new probe
     * @param eventType
     *            The type of events managed by the {@link Probe}
     * @param activateProducer
     *            Whether or not the event producer must be activated
     * @return a {@link ProbeProxy} instance pointing to the introduced
     *         {@link Probe} or {@code null} if something bad happens while
     *         evaluating the FScript commands.
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     */
    public static ProbeProxy newProbe(String target, String routingKey, Class<? extends Event<?>> eventType,
            boolean activateProducer) throws IOException, ScriptException {
        return newProbe(target, routingKey, eventType, activateProducer, FrascatiUtils.DEFAULT_BINDING_URI);
    }

    /**
     * Removes the SCA intent containing the specified probe, in the specified
     * FraSCAti runtime.
     * 
     * @param target
     *            A FPath selector
     * @param routingKey
     *            The routing key of the probe to remove
     * @param bindingUri
     *            The URI where the FraSCAti runtime is running
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     */
    public static void removeProbe(String target, String routingKey, URI bindingUri)
            throws IOException, ScriptException {
        registerPascaniScripts(bindingUri);
        String[] data = target.split("/");
        String parent = data[0] + "/" + data[1];
        // A random name is necessary because FraSCAti 1.4 cannot stop a top
        // level component. This is necessary to remove child components. See
        // comments in pascani.fscript.
        String randomName = "\"removed-intent-" + System.nanoTime() + "\"";
        eval("pascani-remove-intent(" + parent + ", " + target + ", \"" + routingKey + "\", " + randomName + ");",
                bindingUri);
    }

    /**
     * Removes the SCA intent containing the specified probe, in the default
     * FraSCAti runtime.
     * 
     * @param target
     *            A FPath selector
     * @param routingKey
     *            The routing key of the probe to remove
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     */
    public static void removeProbe(String target, String routingKey) throws IOException, ScriptException {
        removeProbe(target, routingKey, FrascatiUtils.DEFAULT_BINDING_URI);
    }

    /**
     * Selects the SCA intent corresponding to the given {@link Event} type
     * 
     * @param eventType
     *            The type of Event
     * @return the name of the corresponding Pascani intent
     */
    public static String intentName(Class<? extends Event<?>> eventType) {
        String intentName = null;
        if (eventType.equals(TimeLapseEvent.class))
            intentName = "pascani-performance-intent";
        else if (eventType.equals(ExceptionEvent.class))
            intentName = "pascani-exception-intent";
        else if (eventType.equals(InvokeEvent.class))
            intentName = "pascani-invoke-intent";
        else if (eventType.equals(ReturnEvent.class))
            intentName = "pascani-return-intent";
        return intentName;
    }

    /**
     * Registers the Pascani FScript procedures in the specified FraSCAti
     * runtime
     * 
     * @param bindingUri
     *            The URI where the FraSCAti runtime is running
     * @throws IOException
     *             If there is a problem loading the Pascani FScript procedures
     *             from the resources
     * @throws ScriptException
     *             If there is a problem executing any of the scripts
     */
    private static void registerPascaniScripts(URI bindingUri) throws IOException, ScriptException {
        Boolean registered = registeredScripts.get(bindingUri);
        if (registered == null || !registered) {
            ClassLoader loader = MoreObjects.firstNonNull(Thread.currentThread().getContextClassLoader(),
                    PascaniUtils.class.getClassLoader());
            URL url = loader.getResource("pascani.fscript");
            if (url == null)
                url = ClassLoader.getSystemResource("pascani.fscript");
            String fscript = "";
            if (url != null) {
                fscript = Resources.toString(url, Charset.defaultCharset());
                System.out.println("------ pascani.fscript LOADED FROM RESOURCES ------");
            } else {
                System.out.println("------ pascani.fscript NOT FOUND. USING BACKUP ------");
                fscript = "" + "function pascani-element-exists(selector) {\n" + "   return size($selector) > 0;\n"
                        + "}\n" + "action pascani-add-intent(parent, target, intentName, routingKey) {\n"
                        + "   -- 1. Create a new instance of the intent composite\n"
                        + "   clone = sca-new($intentName);\n"
                        + "   -- 2. Add the intent instance as a child of the target's parent\n"
                        + "   add-scachild($parent, $clone);\n" + "   intent = $parent/scachild::$intentName;\n"
                        + "   -- 3. Change the name of the intent component to be the routing key\n"
                        + "   set-name($intent, $routingKey);\n" + "   intent = $parent/scachild::$routingKey;\n"
                        + "   -- 4. Sets the routing key\n"
                        + "   property = $intent/scachild::probe/scaproperty::property;\n"
                        + "   set-value($property, concat(\"routingkey=\", $routingKey));\n"
                        + "   -- 5. Add the REST binding to the Resumable interface, and then add the SCA intent\n"
                        + "   add-scaintent($target, $intent);\n" + "   -- 6. Wire the event handler service\n"
                        + "   service = $intent/scachild::probe/scaservice::handler;\n"
                        + "   reference = $intent/scachild::primitiveIntentHandler/scareference::handler;\n"
                        + "   add-scawire($reference, $service);\n" + "   -- 7. Clean things up\n"
                        + "   sca-remove($intentName); \n" + "   set-state($intent, \"STARTED\");\n"
                        + "   set-state($parent, \"STARTED\");\n" + "}\n"
                        + "action pascani-remove-intent(parent, target, routingKey, randomName) {\n"
                        + "   -- 1. Remove the SCA intent\n" + "   intent = $parent/scachild::$routingKey;\n"
                        + "   remove-scaintent($target, $intent);\n"
                        + "   -- 2. Remove the intent component from the target's parent\n"
                        + "   set-state($intent, \"STOPPED\");\n"
                        + "   -- FraSCAti freezes when stopping a top level component.\n"
                        + "   -- This is a requirement for removing child components though.\n"
                        + "   -- set-state($parent, \"STOPPED\");\n" + "   -- remove-scachild($parent, $intent);\n"
                        + "   -- set-state($parent, \"STARTED\");\n"
                        + "   -- Instead of that, rename the component to a random name and shutdown probe & producer\n"
                        + "   pascani-probe-set($parent, $routingKey, \"shutdown=both\");"
                        + "   set-name($intent, $randomName);\n" + "}\n"
                        + "action pascani-probe-set(parent, routingKey, key_value) {\n"
                        + "   intent = $parent/scachild::$routingKey;\n"
                        + "   property = $intent/scachild::probe/scaproperty::property;\n"
                        + "   set-value($property, $key_value);\n" + "}";
            }
            List<String> scripts = FrascatiUtils.registerScript(fscript, bindingUri);
            registeredScripts.put(bindingUri, scripts.size() > 0);
        }
    }

}