001/*
002 * JPPF.
003 * Copyright (C) 2005-2019 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.Map;
023
024import org.jppf.utils.*;
025import org.slf4j.*;
026
027/**
028 * Base class for implementing and embedding all sorts of scripted logic. Scripts are evaluated using the {@code javax.script.*} API (JSR-223).
029 * @author Laurent Cohen
030 * @since 5.2
031 */
032public class BaseScriptEvaluator implements Serializable {
033  /**
034   * Explicit serialVersionUID.
035   */
036  private static final long serialVersionUID = 1L;
037  /**
038   * Logger for this class.
039   */
040  private static Logger log = LoggerFactory.getLogger(BaseScriptEvaluator.class);
041  /**
042   * The language of the script to evaluate.
043   */
044  private String language;
045  /**
046   * The script to evaluate.
047   */
048  private String script;
049  /**
050   * Auto-generated uuid assigned to the script so its compiled version can be reused repeatedly.
051   */
052  private transient String scriptId;
053  /**
054   * Flag set if an error is raised while executing the script, to avoid evaluating the script again.
055   */
056  private transient boolean evaluationError = false;
057
058  /**
059   * Initialize this selector with the specfied language and script.
060   * @param language the language of the script to evaluate.
061   * @param script the script to evaluate.
062   */
063  public BaseScriptEvaluator(final String language, final String script) {
064    this.language = language;
065    this.script = script;
066  }
067
068  /**
069   * Initialize this selector with the specfied language and script reader.
070   * @param language the language of the script to evaluate.
071   * @param scriptReader a reader from which to read the script to evaluate.
072   * @throws IOException if the script could not be read.
073   */
074  public BaseScriptEvaluator(final String language, final Reader scriptReader) throws IOException {
075    this.language = language;
076    this.script = FileUtils.readTextFile(scriptReader);
077  }
078
079  /**
080   * Initialize this selector with the specfied language and script file.
081   * @param language the language of the script to evaluate.
082   * @param scriptFile a file from which to read the script to evaluate.
083   * @throws IOException if the script could not be read.
084   */
085  public BaseScriptEvaluator(final String language, final File scriptFile) throws IOException {
086    this.language = language;
087    this.script = FileUtils.readTextFile(scriptFile);
088  }
089
090  /**
091   * Get the language of the script to evaluate.
092   * @return the language as a string.
093   */
094  public String getLanguage() {
095    return language;
096  }
097
098  /**
099   * Get the script to evaluate.
100   * @return th script as a string.
101   */
102  public String getScript() {
103    return script;
104  }
105
106  /**
107   * Evaluate the script with the specified variable bindings.
108   * @param variables the variables to use i the script.
109   * @return whatever is returned by the script, or {@code null} if the script evaluation raised an exception.
110   */
111  protected Object evaluate(final Map<String, Object> variables) {
112    if ((script == null) || evaluationError) return false;
113    if (scriptId == null) scriptId = JPPFUuid.normalUUID();
114    ScriptRunner runner = null;
115    try {
116      runner = ScriptRunnerFactory.getScriptRunner(language);
117      if (runner != null) {
118        final Object result = runner.evaluate(scriptId, script, variables);
119        return result;
120      }
121    } catch (final Exception|NoClassDefFoundError e) {
122      if (!evaluationError) {
123        evaluationError = true;
124        log.error("exception occurred evaluting script: {}\nscript language: {}, script content:\n{}", ExceptionUtils.getStackTrace(e), language, script);
125      }
126    } finally {
127      if (runner != null) ScriptRunnerFactory.releaseScriptRunner(runner);
128    }
129    return null;
130  }
131
132  @Override
133  public String toString() {
134    final StringBuilder sb = new StringBuilder(getClass().getSimpleName())
135      .append('[')
136      .append("language=").append(language)
137      .append(", script=").append(script)
138      .append(']');
139    return sb.toString();
140  }
141}