001/*
002 * JPPF.
003 * Copyright (C) 2005-2018 JPPF Team.
004 * http://www.jppf.org
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License");
007 * you may not use this file except in compliance with the License.
008 * You may obtain a copy of the License at
009 *
010 *   http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software
013 * distributed under the License is distributed on an "AS IS" BASIS,
014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
015 * See the License for the specific language governing permissions and
016 * limitations under the License.
017 */
018
019package org.jppf.scripting;
020
021import java.io.*;
022import java.util.*;
023
024import org.jppf.utils.*;
025
026/**
027 * An easy to use wrapper around the JPPF scripting APIs.
028 * @author Laurent Cohen
029 */
030public class ScriptDefinition implements Serializable {
031  /**
032   * The script language.
033   */
034  private final String language;
035  /**
036   * The script content as text.
037   */
038  private final String script;
039  /**
040   * Identifier which allows reusing the compiled script.
041   */
042  private final String id;
043  /**
044   * Optional variable bindings made available to the script at execution time.
045   */
046  private final Map<String, Object> bindings;
047
048  /**
049   * Initialize this object with the specified language and a script given as text.
050   * @param language the script language.
051   * @param script the script content as text.
052   */
053  public ScriptDefinition(final String language, final String script) {
054    this(language, script, null, null);
055  }
056
057  /**
058   * Initialize this object with the specified language, variable bindings and a script given as text.
059   * @param language the script language.
060   * @param script the script content as text.
061   * @param bindings optional variable bindings made available to the script at execution time.
062   */
063  public ScriptDefinition(final String language, final String script, final Map<String, Object> bindings) {
064    this(language, script, null, bindings);
065  }
066
067  /**
068   * Initialize this object with the specified language and a script to read from a {@link Reader}.
069   * @param language the script language.
070   * @param reader a {@link Reader} from which to read the script content.
071   * @throws IOException if the script content could not be read from the reader.
072   */
073  public ScriptDefinition(final String language, final Reader reader) throws IOException {
074    this(language, FileUtils.readTextFile(reader), null, null);
075  }
076
077  /**
078   * Initialize this object with the specified language, variable bindings and a script to read from a {@link Reader}.
079   * @param language the script language.
080   * @param reader a {@link Reader} from which to read the script content.
081   * @param bindings optional variable bindings made available to the script at execution time.
082   * @throws IOException if the script content could not be read from the reader.
083   */
084  public ScriptDefinition(final String language, final Reader reader, final Map<String, Object> bindings) throws IOException {
085    this(language, FileUtils.readTextFile(reader), null, bindings);
086  }
087
088  /**
089   * Initialize this object with the specified language and a script to read from a file.
090   * @param language the script language.
091   * @param file a file from which to read the script content.
092   * @throws IOException if the script content could not be read from the reader.
093   */
094  public ScriptDefinition(final String language, final File file) throws IOException {
095    this(language, FileUtils.readTextFile(file), null, null);
096  }
097
098  /**
099   * Initialize this object with the specified language, variable bindings and a script to read from a file.
100   * @param language the script language.
101   * @param file a file from which to read the script content.
102   * @param bindings optional variable bindings made available to the script at execution time.
103   * @throws IOException if the script content could not be read from the file.
104   */
105  public ScriptDefinition(final String language, final File file, final Map<String, Object> bindings) throws IOException {
106    this(language, FileUtils.readTextFile(file), null, bindings);
107  }
108
109  /**
110   *
111   * @param language the script language.
112   * @param script the script content as text.
113   * @param id an identifier which allows reusing the compiled script.
114   * @param bindings optional variable bindings made available to the script at execution time.
115   * @exclude
116   */
117  public ScriptDefinition(final String language, final String script, final String id, final Map<String, Object> bindings) {
118    if (language == null) throw new IllegalArgumentException("the script language cannot be null");
119    if (script == null) throw new IllegalArgumentException("the script content cannot be null");
120    this.language = language;
121    this.script = script;
122    this.id = (id == null) ? JPPFUuid.normalUUID() : id;
123    this.bindings = (bindings == null) ? new HashMap<String, Object>() : new HashMap<>(bindings);
124  }
125
126  /**
127   * @return the script language.
128   */
129  public String getLanguage() {
130    return language;
131  }
132
133  /**
134   * @return the script content as text.
135   */
136  public String getScript() {
137    return script;
138  }
139
140  /**
141   * @return the identifier which allows reusing the compiled script.
142   * @exclude
143   */
144  public String getId() {
145    return id;
146  }
147
148  /**
149   * Get the vairable bindings.
150   * @return the optional variable bindings made available to the script at execution time.
151   */
152  public Map<String, Object> getBindings() {
153    return new HashMap<>(bindings);
154  }
155
156  /**
157   * Execute the script and return its execution result, using the initial variable bindings.
158   * @return the script execution result, or {@code null} if there is no result.
159   * @throws JPPFScriptingException if any error occurs while executing the script.
160   */
161  public Object evaluate() throws JPPFScriptingException {
162    return evaluate(null);
163  }
164
165  /**
166   * Execute the script and return its execution result, using additional bindings.
167   * @param additionalBindings bindings to use in addition to the initial bindings specified in the constructor.
168   * These optional bindings are only used for a single script execution and are not kept in this {@code ScriptDefintion}.
169   * @return the script execution result, or {@code null} if there is no result.
170   * @throws JPPFScriptingException if any error occurs while executing the script.
171   */
172  public Object evaluate(final Map<String, Object> additionalBindings) throws JPPFScriptingException {
173    final Map<String, Object> bnd;
174    if (additionalBindings == null) bnd = this.bindings;
175    else {
176      bnd = new HashMap<>(this.bindings);
177      bnd.putAll(additionalBindings);
178    }
179    Object result = null;
180    ScriptRunner runner = null;
181    try {
182      runner = ScriptRunnerFactory.getScriptRunner(language);
183      if (runner == null) throw new JPPFScriptingException("Could not obtain a script runner for '" + language + "' language");
184      result = runner.evaluate(id, script, bnd);
185    } finally {
186      if (runner != null) ScriptRunnerFactory.releaseScriptRunner(runner);
187    }
188    return result;
189  }
190
191
192  /**
193   * Add the specified variable to the user-defined bindings.
194   * @param name the name of the variable binding to add.
195   * @param value the value of the variable.
196   * @return this {@code ScriptDefintion}, for method call chaining.
197   */
198  public ScriptDefinition addBinding(final String name, final Object value) {
199    bindings.put(name, value);
200    return this;
201  }
202
203  /**
204   * Remove the specified variable from the user-defined bindings.
205   * @param name the name of the variable binding to remove.
206   * @return the value of the variable, or {@code null} if the variable was not bound.
207   */
208  public Object removeBinding(final String name) {
209    return bindings.remove(name);
210  }
211}