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 default factory permissions. 052 */ 053 private static JexlPermissions defaultPermissions; 054 055 /** 056 * The engine permissions. 057 */ 058 private final JexlPermissions permissions; 059 060 /** 061 * Default constructor. 062 */ 063 public JexlScriptEngineFactory() { this(null); } // Keep Javadoc happy 064 065 /** 066 * Constructor with permissions. 067 * <p>Meant to reduce dependency to JEXL for extraordinary use case of JSR233.</p> 068 * @param jexlPermissions the permissions instance to use or null to use the {@link JexlScriptEngineFactory} default 069 */ 070 public JexlScriptEngineFactory(final JexlPermissions jexlPermissions) { 071 permissions = jexlPermissions != null ? jexlPermissions : defaultPermissions; 072 } 073 074 @Override 075 public ScriptEngine getScriptEngine() { 076 return new JexlScriptEngine(this); 077 } 078 079 /** 080 * Creates an engine. 081 * @return the JexlEngine instance, create it if necessary 082 */ 083 protected JexlEngine getEngine() { 084 return createJexlEngine(); 085 } 086 087 /** 088 * Creates a new JexlEngine instance. 089 * @return a new JexlEngine instance 090 */ 091 protected JexlEngine createJexlEngine() { 092 final JexlBuilder builder = new JexlBuilder() 093 .strict(true) 094 .safe(false) 095 .logger(JexlScriptEngine.LOG) 096 .cache(JexlScriptEngine.CACHE_SIZE); 097 JexlPermissions p = permissions; 098 if (p == null) { 099 p = defaultPermissions; 100 if (p == null) { 101 p = builder.permissions(); 102 if (p == null) { 103 p = JexlPermissions.RESTRICTED; 104 } 105 } 106 } 107 final JexlPermissions required = new JexlPermissions.ClassPermissions(p, JexlScriptEngine.JexlScriptObject.class); 108 builder.permissions(required); 109 return builder.create(); 110 } 111 112 /** 113 * Sets the permissions instance used to create the script engine. 114 * <p>To restore 3.2 <em>unsafe</em> script behavior:</p> 115 * {@code 116 * JexlScriptEngineFactory.setDefaultPermissions(JexlPermissions.UNRESTRICTED); 117 * } 118 * 119 * @param permissions the permissions instance to use or null to use the {@link JexlBuilder} default 120 * @since 3.6.3 121 */ 122 public static void setDefaultPermissions(final JexlPermissions permissions) { 123 defaultPermissions = permissions; 124 } 125 126 @Override 127 public String getEngineName() { 128 return "JEXL Engine"; 129 } 130 131 @Override 132 public String getEngineVersion() { 133 return "3.6"; // ensure this is updated if function changes are made to this class 134 } 135 136 @Override 137 public List<String> getExtensions() { 138 return Collections.unmodifiableList(Arrays.asList("jexl", "jexl2", "jexl3")); 139 } 140 141 @Override 142 public String getLanguageName() { 143 return "JEXL"; 144 } 145 146 @Override 147 public String getLanguageVersion() { 148 return "3.6"; // this should be derived from the actual version 149 } 150 151 @Override 152 public String getMethodCallSyntax(final String obj, final String m, final String... args) { 153 final StringBuilder sb = new StringBuilder(); 154 sb.append(obj); 155 sb.append('.'); 156 sb.append(m); 157 sb.append('('); 158 boolean needComma = false; 159 for(final String arg : args){ 160 if (needComma) { 161 sb.append(','); 162 } 163 sb.append(arg); 164 needComma = true; 165 } 166 sb.append(')'); 167 return sb.toString(); 168 } 169 170 @Override 171 public List<String> getMimeTypes() { 172 return Collections.unmodifiableList(Arrays.asList("application/x-jexl", 173 "application/x-jexl2", 174 "application/x-jexl3")); 175 } 176 177 @Override 178 public List<String> getNames() { 179 return Collections.unmodifiableList(Arrays.asList("JEXL", "Jexl", "jexl", 180 "JEXL2", "Jexl2", "jexl2", 181 "JEXL3", "Jexl3", "jexl3")); 182 } 183 184 @Override 185 public String getOutputStatement(final String toDisplay) { 186 if (toDisplay == null) { 187 return "JEXL.out.print(null)"; 188 } 189 return "JEXL.out.print("+StringParser.escapeString(toDisplay, '\'')+")"; 190 } 191 192 @Override 193 public Object getParameter(final String key) { 194 switch (key) { 195 case ScriptEngine.ENGINE: 196 return getEngineName(); 197 case ScriptEngine.ENGINE_VERSION: 198 return getEngineVersion(); 199 case ScriptEngine.NAME: 200 return getNames(); 201 case ScriptEngine.LANGUAGE: 202 return getLanguageName(); 203 case ScriptEngine.LANGUAGE_VERSION: 204 return getLanguageVersion(); 205 case "THREADING": 206 /* 207 * To implement multithreading, the scripting engine context (inherited from AbstractScriptEngine) 208 * would need to be made thread-safe; so would the setContext/getContext methods. 209 * It is easier to share the underlying Uberspect and JEXL engine instance, especially 210 * with an expression cache. 211 */ 212 default: 213 return null; 214 } 215 } 216 217 @Override 218 public String getProgram(final String... statements) { 219 final StringBuilder sb = new StringBuilder(); 220 for(final String statement : statements){ 221 sb.append(statement.trim()); 222 if (!statement.endsWith(";")){ 223 sb.append(';'); 224 } 225 } 226 return sb.toString(); 227 } 228 229}