/* * ProActive Parallel Suite(TM): * The Open Source library for parallel and distributed * Workflows & Scheduling, Orchestration, Cloud Automation * and Big Data Analysis on Enterprise Grids & Clouds. * * Copyright (c) 2007 - 2017 ActiveEon * Contact: contact@activeeon.com * * This library is free software: you can redistribute it and/or * modify it under the terms of the GNU Affero General Public License * as published by the Free Software Foundation: version 3 of * the License. * * 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 Affero General Public License for more details. * * You should have received a copy of the GNU Affero General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. * * If needed, contact us to obtain a release under GPL Version 2 or 3 * or a different license than the AGPL. */ package org.ow2.proactive.scripting; import java.io.File; import java.io.Reader; import java.io.Serializable; import java.io.StringReader; import java.net.URL; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; import java.util.List; import javax.script.Bindings; import org.apache.log4j.Logger; import org.objectweb.proactive.annotation.PublicAPI; /** * A selection Script : return true if the resource tested is correct. * * There are 2 type of selection scripts :<br> * -static scripts, aimed to test test static property of a resource (node), OS type * RAM total space, dynamic libraries present.... * -dynamic script, aimed to test dynamic properties if a resource, free disk space... * * A static script is executed once on a node and result of script's execution is memorized * for a next script execution request, so that we avoid a second execution of a static script. * A dynamic script is always executed, because we suppose that script tests dynamic properties * able to change. By default a script is dynamic. * * * @author The ProActive Team * @since ProActive Scheduling 0.9 */ @PublicAPI public class SelectionScript extends Script<Boolean> { /** Loggers */ public static final Logger logger = Logger.getLogger(SelectionScript.class); /** * The variable name which must be set after the evaluation * of a verifying script. */ public static final String RESULT_VARIABLE = "selected"; /** If true, script result is not cached */ private boolean dynamic = true; /** * Hash digest of the script */ protected byte[] id_; /** ProActive needed constructor */ public SelectionScript() { } @Override protected String getDefaultScriptName() { return "SelectionScript"; } /** Directly create a script with a String. * @param script String representing a script code * @param engineName String a script execution engine. * @throws InvalidScriptException if the creation fails. */ public SelectionScript(String script, String engineName) throws InvalidScriptException { super(script, engineName, "SelectionScript"); buildSelectionScriptId(); } /** Directly create a script with a string. * @param script String representing a script code * @param engineName String a script execution engine. * @param dynamic tell if the script is dynamic or static * @throws InvalidScriptException if the creation fails. */ public SelectionScript(String script, String engineName, boolean dynamic) throws InvalidScriptException { super(script, engineName, "SelectionScript"); this.dynamic = dynamic; buildSelectionScriptId(); } /** Directly create a script with a string. * @param script String representing a script code * @param parameters script execution arguments. * @param engineName String a script execution engine. * @param dynamic tell if the script is dynamic or static * @throws InvalidScriptException if the creation fails. */ public SelectionScript(String script, String engineName, Serializable[] parameters, boolean dynamic) throws InvalidScriptException { super(script, engineName, parameters, "SelectionScript"); this.dynamic = dynamic; buildSelectionScriptId(); } /** Create a selection script from a file. * @param file a file containing the script * @param parameters script execution arguments. * @throws InvalidScriptException if the creation fails. */ public SelectionScript(File file, Serializable[] parameters) throws InvalidScriptException { super(file, parameters); buildSelectionScriptId(); } /** Create a selection script from a file. * @param file a file containing script code * @param parameters script execution arguments. * @param dynamic tell if script is dynamic or static * @throws InvalidScriptException if the creation fails. */ public SelectionScript(File file, Serializable[] parameters, boolean dynamic) throws InvalidScriptException { super(file, parameters); this.dynamic = dynamic; buildSelectionScriptId(); } /** Create a selection script from an URL. * @param url an URL representing a script code * @param parameters script execution argument. * @throws InvalidScriptException if the creation fails */ public SelectionScript(URL url, String[] parameters) throws InvalidScriptException { super(url, parameters); buildSelectionScriptId(); } /** Create a selection script from an URL. * @param url an URL representing a script code * @param parameters execution arguments * @param dynamic true if the script is dynamic * @throws InvalidScriptException if the creation fails. */ public SelectionScript(URL url, String[] parameters, boolean dynamic) throws InvalidScriptException { super(url, parameters); this.dynamic = dynamic; buildSelectionScriptId(); } /** Create a selection script from another selection script * @param script selection script source * @param dynamic true if the script is dynamic * @throws InvalidScriptException if the creation fails. */ public SelectionScript(Script<?> script, boolean dynamic) throws InvalidScriptException { super(script); this.dynamic = dynamic; buildSelectionScriptId(); } /** * Build script ID. ID is a way to compare a script with another. * ID is a String made of script_code+script_parameters+type * */ public void buildSelectionScriptId() { //get code of script String stringId = this.script; //concatenate the script type (dynamic or static) stringId += this.dynamic; //concatenate parameters if any if (this.parameters != null) { for (Serializable param : this.parameters) { stringId += param; } } try { this.id_ = MessageDigest.getInstance("SHA-1").digest(stringId.getBytes()); } catch (NoSuchAlgorithmException e) { logger.error("", e); this.id_ = stringId.getBytes(); } } /** * @see org.ow2.proactive.scripting.Script#getId() */ @Override public String getId() { return this.id; } @Override protected Reader getReader() { return new StringReader(script); } /** * SelectionScript must give its result in the 'result_script' variable. * * @see org.ow2.proactive.scripting.Script#getResult(Object, Bindings) */ @Override protected ScriptResult<Boolean> getResult(Object evalResult, Bindings bindings) { if (bindings.containsKey(RESULT_VARIABLE)) { Object result = bindings.get(RESULT_VARIABLE); if (result instanceof Boolean) { return new ScriptResult<>((Boolean) result); } else if (result instanceof Integer) { return new ScriptResult<>((Integer) result != 0); } else if (result instanceof CharSequence) { return new ScriptResult<>(!(result.equals("false") || result.equals("False"))); } else { return new ScriptResult<>(new Exception("Bad result format : awaited Boolean (or Integer when not existing), found " + result.getClass().getName())); } } else { String msg = "No binding for key : " + RESULT_VARIABLE + "\na Selection script must define a variable named '" + RESULT_VARIABLE + "' set to true or false"; logger.error(msg); return new ScriptResult<>(new Exception(msg)); } } /** Say if the script is static or dynamic * @return true if the script is dynamic, false otherwise */ public boolean isDynamic() { return dynamic; } /** * There is no parameter to give to the selection script. */ @Override protected void prepareSpecialBindings(Bindings bindings) { } /** * @see org.ow2.proactive.scripting.Script#equals(java.lang.Object) */ @Override public boolean equals(Object o) { if (o == this) { return true; } if (o instanceof SelectionScript) { return compareByteArray(this.id_, ((SelectionScript) o).id_); } return false; } /** * @see java.lang.Object#hashCode() */ @Override public int hashCode() { return new String(id_).hashCode(); } /** * Get MD5 hash value * * @return MD5 hash value * @throws NoSuchAlgorithmException */ public byte[] digest() throws NoSuchAlgorithmException { return MessageDigest.getInstance("MD5").digest(id_); } /** Compare two arrays of bytes * @param array1 first array to compare * @param array2 second array to compare * @return true is the two contains the same bytes values, false otherwise */ public boolean compareByteArray(byte[] array1, byte[] array2) { if (array1.length != array2.length) return false; else { for (int i = 0; i < array1.length; i++) { if (array1[i] != array2[i]) return false; } return true; } } /** * Util method to get an hashCode of a list of selection script. Can be used to compare two lists of selectionScript * and see if it is the same. * If the list is empty or null, then the return value will be 0, meaning no selection scripts are provided. * * @param selScriptsList a list of selection scripts * @return 0 if the list is null or empty, otherwise, an hashCode of the list content */ public static int hashCodeFromList(List<SelectionScript> selScriptsList) { if (selScriptsList == null || selScriptsList.size() == 0) { return 0; } int toReturn = 0; for (SelectionScript ss : selScriptsList) { toReturn += ss.hashCode(); } return toReturn; } /** * String representation */ public String toString() { return hashCode() + "\n" + script; } }