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}