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.node.protocol;
020
021import java.io.*;
022import java.util.*;
023
024import org.jppf.scripting.*;
025import org.jppf.utils.FileUtils;
026
027/**
028 * A task implementation which executes a script in a specified JSR 223 ({@code javax.script} APIs) script language.
029 * <p>In addition to the user-specified bindings, this task always provides to the script engine a reference to itself
030 * with the name 'jppfTask'.
031 * <p>A reusableId can be specified via the constructors, indicating that, if the script engine has that capability,
032 * compiled scripts will be stored and reused, to avoid compiling the same scripts repeatedly.
033 * @param <T> the type of result returned by this task.
034 * @author Laurent Cohen
035 */
036public class ScriptedTask<T> extends AbstractTask<T> {
037  /**
038   * Explicit serialVersionUID.
039   */
040  private static final long serialVersionUID = 1L;
041  /**
042   * Specfications for the script to execute.
043   */
044  protected ScriptDefinition scriptSpec;
045
046  /**
047   * Default constructor provided as a convenience for subclassing.
048   */
049  protected ScriptedTask() {
050  }
051
052  /**
053   * Initialize this task with the specified script language, script provided as a string,
054   * and a set of variable bindings to be used in the scripts.
055   * @param language the JSR 223 script language to use.
056   * @param script the script to execute from this task.
057   * @param reusableId a unique identifier for the script, which allows reusing a compiled version if one has already been produced.
058   * @param bindings a set of variables that will be available to the script.
059   * @throws IllegalArgumentException if {@code language} or {@code script} is {@code null}.
060   */
061  public ScriptedTask(final String language, final String script, final String reusableId, final Map<String, Object> bindings) throws IllegalArgumentException {
062    this.scriptSpec = new ScriptDefinition(language, script, reusableId, bindings);
063  }
064
065  /**
066   * Initialize this task with the specified script language, script provided from a {@link Reader},
067   * and a set of variable bindings to be used in the scripts.
068   * @param language the JSR 223 script language to use.
069   * @param scriptReader a reader form which to read the script source.
070   * @param reusableId a unique identifier for the script, which allows reusing a compiled version if one has already been produced.
071   * @param bindings a set of variables that will be available to the script.
072   * @throws IllegalArgumentException if {@code language} or {@code script} is {@code null}.
073   * @throws IOException if an I/O error occurs while reading the script source.
074   */
075  public ScriptedTask(final String language, final Reader scriptReader, final String reusableId, final Map<String, Object> bindings)
076      throws IllegalArgumentException, IOException {
077    this.scriptSpec = new ScriptDefinition(language, FileUtils.readTextFile(scriptReader), reusableId, bindings);
078  }
079
080  /**
081   * Initialize this task with the specified script language, script provided as a file,
082   * and a set of variable bindings to be used in the scripts.
083   * @param language the JSR 223 script language to use.
084   * @param scriptFile a file from which to read the script source.
085   * @param reusableId a unique identifier for the script, which allows reusing a compiled version if one has already been produced.
086   * @param bindings a set of variables that will be available to the script.
087   * @throws IllegalArgumentException if {@code language} or {@code script} is {@code null}.
088   * @throws IOException if an I/O error occurs while reading the script source.
089   */
090  public ScriptedTask(final String language, final File scriptFile, final String reusableId, final Map<String, Object> bindings)
091      throws IllegalArgumentException, IOException {
092    this.scriptSpec = new ScriptDefinition(language, FileUtils.readTextFile(scriptFile), reusableId, bindings);
093  }
094
095  @Override
096  public void run() {
097    try {
098      final Map<String, Object> bnd = new HashMap<>();
099      bnd.put("jppfTask", this);
100      @SuppressWarnings("unchecked")
101      final T result = (T) scriptSpec.evaluate(bnd);
102      // may have been set from the script
103      if ((getResult() == null) && (result != null)) setResult(result);
104    } catch(final JPPFScriptingException e) {
105      setThrowable(e);
106    }
107  }
108
109  /**
110   * Get the JSR 223 script language to use.
111   * @return the script language a s a script.
112   */
113  public String getLanguage() {
114    return scriptSpec.getLanguage();
115  }
116
117  /**
118   * Get the script to execute from this task.
119   * @return the script source as a string.
120   */
121  public String getScript() {
122    return scriptSpec.getScript();
123  }
124
125  /**
126   * Get the unique identifier for the script.
127   * @return the identifier as a string.
128   */
129  public String getReusableId() {
130    return scriptSpec.getId();
131  }
132
133  /**
134   * Get the user-defined variable bindings.
135   * @return a map of variable names to their value.
136   */
137  public Map<String, Object> getBindings() {
138    return scriptSpec.getBindings();
139  }
140
141  /**
142   * Add the specified variable to the user-defined bindings.
143   * @param name the name of the variable binding to add.
144   * @param value the value of the variable.
145   * @return this task, for method chaining.
146   */
147  public ScriptedTask<T> addBinding(final String name, final Object value) {
148    scriptSpec.addBinding(name, value);
149    return this;
150  }
151
152  /**
153   * Remove the specified variable from the user-defined bindings.
154   * @param name the name of the variable binding to remove.
155   * @return the value of the variable, or {@code null} if the variable was not bound.
156   */
157  public Object removeBinding(final String name) {
158    return scriptSpec.removeBinding(name);
159  }
160}