Source code
Description: source: package: org.apache.commons.jexl3.scripting, class: JexlScriptEngineFactory

    001/*
    002 * Licensed to the Apache Software Foundation (ASF) under one or more
    003 * contributor license agreements.  See the NOTICE file distributed with
    004 * this work for additional information regarding copyright ownership.
    005 * The ASF licenses this file to You under the Apache License, Version 2.0
    006 * (the "License"); you may not use this file except in compliance with
    007 * the License.  You may obtain a copy of the License at
    008 *
    009 *      https://www.apache.org/licenses/LICENSE-2.0
    010 *
    011 * Unless required by applicable law or agreed to in writing, software
    012 * distributed under the License is distributed on an "AS IS" BASIS,
    013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    014 * See the License for the specific language governing permissions and
    015 * limitations under the License.
    016 */
    017
    018package org.apache.commons.jexl3.scripting;
    019
    020import java.util.Arrays;
    021import java.util.Collections;
    022import java.util.List;
    023
    024import javax.script.ScriptEngine;
    025import javax.script.ScriptEngineFactory;
    026
    027import org.apache.commons.jexl3.JexlBuilder;
    028import org.apache.commons.jexl3.JexlEngine;
    029import org.apache.commons.jexl3.introspection.JexlPermissions;
    030import org.apache.commons.jexl3.parser.StringParser;
    031
    032/**
    033 * Implements the JEXL ScriptEngineFactory for JSF-223.
    034 * <p>
    035 * Supports the following:<br>
    036 * </p>
    037 * <ul>
    038 * <li>Language short names: "JEXL", "Jexl", "jexl", "JEXL2", "Jexl2", "jexl2", "JEXL3", "Jexl3", "jexl3"</li>
    039 * <li>File Extensions: ".jexl", ".jexl2", ".jexl3"</li>
    040 * <li>"jexl3" etc. were added for engineVersion="3.0"</li>
    041 * </ul>
    042 * <p>
    043 * See
    044 * <a href="https://java.sun.com/javase/6/docs/api/javax/script/package-summary.html">Java Scripting API</a>
    045 * Javadoc.
    046 *
    047 * @since 2.0
    048 */
    049public class JexlScriptEngineFactory implements ScriptEngineFactory {
    050    /**
    051     * The engine and language version.
    052     */
    053    private static final String JEXL_VERSION = "3.7";
    054    /**
    055     * The default factory permissions.
    056     */
    057    private static JexlPermissions defaultPermissions;
    058
    059    /**
    060     * The engine permissions.
    061     */
    062    private final JexlPermissions permissions;
    063
    064    /**
    065     * Default constructor.
    066     */
    067    public JexlScriptEngineFactory() { this(null); } // Keep Javadoc happy
    068
    069    /**
    070     * Constructor with permissions.
    071     * <p>Meant to reduce dependency to JEXL for extraordinary use case of JSR233.</p>
    072     * @param jexlPermissions the permissions instance to use or null to use the {@link JexlScriptEngineFactory} default
    073     */
    074    public JexlScriptEngineFactory(final JexlPermissions jexlPermissions) {
    075        permissions = jexlPermissions != null ? jexlPermissions : defaultPermissions;
    076    }
    077
    078    @Override
    079    public ScriptEngine getScriptEngine() {
    080        return new JexlScriptEngine(this);
    081    }
    082
    083    /**
    084     * Creates an engine.
    085     * @return the JexlEngine instance, create it if necessary
    086     */
    087    protected JexlEngine getEngine() {
    088        return createJexlEngine();
    089    }
    090
    091    /**
    092     * Creates a new JexlEngine instance.
    093     * @return a new JexlEngine instance
    094     */
    095    protected JexlEngine createJexlEngine() {
    096        final JexlBuilder builder = new JexlBuilder()
    097          .strict(true)
    098          .safe(false)
    099          .logger(JexlScriptEngine.LOG)
    100          .cache(JexlScriptEngine.CACHE_SIZE);
    101        JexlPermissions p = permissions;
    102        if (p == null) {
    103            p = defaultPermissions;
    104            if (p == null) {
    105                p = builder.permissions();
    106                if (p == null) {
    107                    p = JexlPermissions.RESTRICTED;
    108                }
    109            }
    110        }
    111        final JexlPermissions required = new JexlPermissions.ClassPermissions(p, JexlScriptEngine.JexlScriptObject.class);
    112        builder.permissions(required);
    113        return builder.create();
    114    }
    115
    116    /**
    117     * Sets the permissions instance used to create the script engine.
    118     * <p>To restore 3.2 <em>unsafe</em> script behavior:</p>
    119     * {@code
    120     *         JexlScriptEngineFactory.setDefaultPermissions(JexlPermissions.UNRESTRICTED);
    121     * }
    122     *
    123     * @param permissions the permissions instance to use or null to use the {@link JexlBuilder} default
    124     * @since 3.6.3
    125     */
    126    public static void setDefaultPermissions(final JexlPermissions permissions) {
    127        defaultPermissions = permissions;
    128    }
    129
    130    @Override
    131    public String getEngineName() {
    132        return "JEXL Engine";
    133    }
    134
    135    @Override
    136    public String getEngineVersion() {
    137        return JEXL_VERSION;
    138    }
    139
    140    @Override
    141    public List<String> getExtensions() {
    142        return Collections.unmodifiableList(Arrays.asList("jexl", "jexl2", "jexl3"));
    143    }
    144
    145    @Override
    146    public String getLanguageName() {
    147        return "JEXL";
    148    }
    149
    150    @Override
    151    public String getLanguageVersion() {
    152        return JEXL_VERSION;
    153    }
    154
    155    @Override
    156    public String getMethodCallSyntax(final String obj, final String m, final String... args) {
    157        final StringBuilder sb = new StringBuilder();
    158        sb.append(obj);
    159        sb.append('.');
    160        sb.append(m);
    161        sb.append('(');
    162        boolean needComma = false;
    163        for(final String arg : args){
    164            if (needComma) {
    165                sb.append(',');
    166            }
    167            sb.append(arg);
    168            needComma = true;
    169        }
    170        sb.append(')');
    171        return sb.toString();
    172    }
    173
    174    @Override
    175    public List<String> getMimeTypes() {
    176        return Collections.unmodifiableList(Arrays.asList("application/x-jexl",
    177                                                          "application/x-jexl2",
    178                                                          "application/x-jexl3"));
    179    }
    180
    181    @Override
    182    public List<String> getNames() {
    183        return Collections.unmodifiableList(Arrays.asList("JEXL", "Jexl", "jexl",
    184                                                          "JEXL2", "Jexl2", "jexl2",
    185                                                          "JEXL3", "Jexl3", "jexl3"));
    186    }
    187
    188    @Override
    189    public String getOutputStatement(final String toDisplay) {
    190        if (toDisplay == null) {
    191            return "JEXL.out.print(null)";
    192        }
    193        return "JEXL.out.print("+StringParser.escapeString(toDisplay, '\'')+")";
    194    }
    195
    196    @Override
    197    public Object getParameter(final String key) {
    198        switch (key) {
    199            case ScriptEngine.ENGINE:
    200                return getEngineName();
    201            case ScriptEngine.ENGINE_VERSION:
    202                return getEngineVersion();
    203            case ScriptEngine.NAME:
    204                return getNames();
    205            case ScriptEngine.LANGUAGE:
    206                return getLanguageName();
    207            case ScriptEngine.LANGUAGE_VERSION:
    208                return getLanguageVersion();
    209            case "THREADING":
    210                /*
    211                 * To implement multithreading, the scripting engine context (inherited from AbstractScriptEngine)
    212                 * would need to be made thread-safe; so would the setContext/getContext methods.
    213                 * It is easier to share the underlying Uberspect and JEXL engine instance, especially
    214                 * with an expression cache.
    215                 */
    216            default:
    217                return null;
    218        }
    219    }
    220
    221    @Override
    222    public String getProgram(final String... statements) {
    223        final StringBuilder sb = new StringBuilder();
    224        for(final String statement : statements){
    225            sb.append(statement.trim());
    226            if (!statement.endsWith(";")){
    227                sb.append(';');
    228            }
    229        }
    230        return sb.toString();
    231    }
    232
    233}