1 /* 2 * Copyright (c) 2005, 2012, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. Oracle designates this 8 * particular file as subject to the "Classpath" exception as provided 9 * by Oracle in the LICENSE file that accompanied this code. 10 * 11 * This code is distributed in the hope that it will be useful, but WITHOUT 12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 14 * version 2 for more details (a copy is included in the LICENSE file that 15 * accompanied this code). 16 * 17 * You should have received a copy of the GNU General Public License version 18 * 2 along with this work; if not, write to the Free Software Foundation, 19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 20 * 21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 22 * or visit www.oracle.com if you need additional information or have any 23 * questions. 24 */ 25 26 package javax.script; 27 import java.util.*; 28 import java.security.*; 29 import java.util.ServiceLoader; 30 import java.util.ServiceConfigurationError; 31 32 /** 33 * The <code>ScriptEngineManager</code> implements a discovery and instantiation 34 * mechanism for <code>ScriptEngine</code> classes and also maintains a 35 * collection of key/value pairs storing state shared by all engines created 36 * by the Manager. This class uses the <a href="../../../technotes/guides/jar/jar.html#Service%20Provider">service provider</a> mechanism to enumerate all the 37 * implementations of <code>ScriptEngineFactory</code>. <br><br> 38 * The <code>ScriptEngineManager</code> provides a method to return a list of all these factories 39 * as well as utility methods which look up factories on the basis of language name, file extension 40 * and mime type. 41 * <p> 42 * The <code>Bindings</code> of key/value pairs, referred to as the "Global Scope" maintained 43 * by the manager is available to all instances of <code>ScriptEngine</code> created 44 * by the <code>ScriptEngineManager</code>. The values in the <code>Bindings</code> are 45 * generally exposed in all scripts. 46 * 47 * @author Mike Grogan 48 * @author A. Sundararajan 49 * @since 1.6 50 */ 51 public class ScriptEngineManager { 52 private static final boolean DEBUG = false; 53 /** 54 * The effect of calling this constructor is the same as calling 55 * <code>ScriptEngineManager(Thread.currentThread().getContextClassLoader())</code>. 56 * 57 * @see java.lang.Thread#getContextClassLoader 58 */ 59 public ScriptEngineManager() { 60 ClassLoader ctxtLoader = Thread.currentThread().getContextClassLoader(); 61 init(ctxtLoader); 62 } 63 64 /** 65 * This constructor loads the implementations of 66 * <code>ScriptEngineFactory</code> visible to the given 67 * <code>ClassLoader</code> using the <a href="../../../technotes/guides/jar/jar.html#Service%20Provider">service provider</a> mechanism.<br><br> 68 * If loader is <code>null</code>, the script engine factories that are 69 * bundled with the platform and that are in the usual extension 70 * directories (installed extensions) are loaded. <br><br> 71 * 72 * @param loader ClassLoader used to discover script engine factories. 73 */ 74 public ScriptEngineManager(ClassLoader loader) { 75 init(loader); 76 } 77 78 private void init(final ClassLoader loader) { 79 globalScope = new SimpleBindings(); 80 engineSpis = new HashSet<ScriptEngineFactory>(); 81 nameAssociations = new HashMap<String, ScriptEngineFactory>(); 82 extensionAssociations = new HashMap<String, ScriptEngineFactory>(); 83 mimeTypeAssociations = new HashMap<String, ScriptEngineFactory>(); 84 AccessController.doPrivileged(new PrivilegedAction<Object>() { 85 public Object run() { 86 initEngines(loader); 87 return null; 88 } 89 }); 90 } 91 92 private void initEngines(final ClassLoader loader) { 93 Iterator<ScriptEngineFactory> itr = null; 94 try { 95 ServiceLoader<ScriptEngineFactory> sl; 96 if (loader != null) { 97 sl = ServiceLoader.load(ScriptEngineFactory.class, loader); 98 } else { 99 sl = ServiceLoader.loadInstalled(ScriptEngineFactory.class); 100 } 101 itr = sl.iterator(); 102 } catch (ServiceConfigurationError err) { 103 System.err.println("Can't find ScriptEngineFactory providers: " + 104 err.getMessage()); 105 if (DEBUG) { 106 err.printStackTrace(); 107 } 108 // do not throw any exception here. user may want to 109 // manage his/her own factories using this manager 110 // by explicit registratation (by registerXXX) methods. 111 return; 112 } 113 114 try { 115 while (itr.hasNext()) { 116 try { 117 ScriptEngineFactory fact = itr.next(); 118 engineSpis.add(fact); 119 } catch (ServiceConfigurationError err) { 120 System.err.println("ScriptEngineManager providers.next(): " 121 + err.getMessage()); 122 if (DEBUG) { 123 err.printStackTrace(); 124 } 125 // one factory failed, but check other factories... 126 continue; 127 } 128 } 129 } catch (ServiceConfigurationError err) { 130 System.err.println("ScriptEngineManager providers.hasNext(): " 131 + err.getMessage()); 132 if (DEBUG) { 133 err.printStackTrace(); 134 } 135 // do not throw any exception here. user may want to 136 // manage his/her own factories using this manager 137 // by explicit registratation (by registerXXX) methods. 138 return; 139 } 140 } 141 142 /** 143 * <code>setBindings</code> stores the specified <code>Bindings</code> 144 * in the <code>globalScope</code> field. ScriptEngineManager sets this 145 * <code>Bindings</code> as global bindings for <code>ScriptEngine</code> 146 * objects created by it. 147 * 148 * @param bindings The specified <code>Bindings</code> 149 * @throws IllegalArgumentException if bindings is null. 150 */ 151 public void setBindings(Bindings bindings) { 152 if (bindings == null) { 153 throw new IllegalArgumentException("Global scope cannot be null."); 154 } 155 156 globalScope = bindings; 157 } 158 159 /** 160 * <code>getBindings</code> returns the value of the <code>globalScope</code> field. 161 * ScriptEngineManager sets this <code>Bindings</code> as global bindings for 162 * <code>ScriptEngine</code> objects created by it. 163 * 164 * @return The globalScope field. 165 */ 166 public Bindings getBindings() { 167 return globalScope; 168 } 169 170 /** 171 * Sets the specified key/value pair in the Global Scope. 172 * @param key Key to set 173 * @param value Value to set. 174 * @throws NullPointerException if key is null. 175 * @throws IllegalArgumentException if key is empty string. 176 */ 177 public void put(String key, Object value) { 178 globalScope.put(key, value); 179 } 180 181 /** 182 * Gets the value for the specified key in the Global Scope 183 * @param key The key whose value is to be returned. 184 * @return The value for the specified key. 185 */ 186 public Object get(String key) { 187 return globalScope.get(key); 188 } 189 190 /** 191 * Looks up and creates a <code>ScriptEngine</code> for a given name. 192 * The algorithm first searches for a <code>ScriptEngineFactory</code> that has been 193 * registered as a handler for the specified name using the <code>registerEngineName</code> 194 * method. 195 * <br><br> If one is not found, it searches the set of <code>ScriptEngineFactory</code> instances 196 * stored by the constructor for one with the specified name. If a <code>ScriptEngineFactory</code> 197 * is found by either method, it is used to create instance of <code>ScriptEngine</code>. 198 * @param shortName The short name of the <code>ScriptEngine</code> implementation. 199 * returned by the <code>getNames</code> method of its <code>ScriptEngineFactory</code>. 200 * @return A <code>ScriptEngine</code> created by the factory located in the search. Returns null 201 * if no such factory was found. The <code>ScriptEngineManager</code> sets its own <code>globalScope</code> 202 * <code>Bindings</code> as the <code>GLOBAL_SCOPE</code> <code>Bindings</code> of the newly 203 * created <code>ScriptEngine</code>. 204 * @throws NullPointerException if shortName is null. 205 */ 206 public ScriptEngine getEngineByName(String shortName) { 207 if (shortName == null) throw new NullPointerException(); 208 //look for registered name first 209 Object obj; 210 if (null != (obj = nameAssociations.get(shortName))) { 211 ScriptEngineFactory spi = (ScriptEngineFactory)obj; 212 try { 213 ScriptEngine engine = spi.getScriptEngine(); 214 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 215 return engine; 216 } catch (Exception exp) { 217 if (DEBUG) exp.printStackTrace(); 218 } 219 } 220 221 for (ScriptEngineFactory spi : engineSpis) { 222 List<String> names = null; 223 try { 224 names = spi.getNames(); 225 } catch (Exception exp) { 226 if (DEBUG) exp.printStackTrace(); 227 } 228 229 if (names != null) { 230 for (String name : names) { 231 if (shortName.equals(name)) { 232 try { 233 ScriptEngine engine = spi.getScriptEngine(); 234 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 235 return engine; 236 } catch (Exception exp) { 237 if (DEBUG) exp.printStackTrace(); 238 } 239 } 240 } 241 } 242 } 243 244 return null; 245 } 246 247 /** 248 * Look up and create a <code>ScriptEngine</code> for a given extension. The algorithm 249 * used by <code>getEngineByName</code> is used except that the search starts 250 * by looking for a <code>ScriptEngineFactory</code> registered to handle the 251 * given extension using <code>registerEngineExtension</code>. 252 * @param extension The given extension 253 * @return The engine to handle scripts with this extension. Returns <code>null</code> 254 * if not found. 255 * @throws NullPointerException if extension is null. 256 */ 257 public ScriptEngine getEngineByExtension(String extension) { 258 if (extension == null) throw new NullPointerException(); 259 //look for registered extension first 260 Object obj; 261 if (null != (obj = extensionAssociations.get(extension))) { 262 ScriptEngineFactory spi = (ScriptEngineFactory)obj; 263 try { 264 ScriptEngine engine = spi.getScriptEngine(); 265 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 266 return engine; 267 } catch (Exception exp) { 268 if (DEBUG) exp.printStackTrace(); 269 } 270 } 271 272 for (ScriptEngineFactory spi : engineSpis) { 273 List<String> exts = null; 274 try { 275 exts = spi.getExtensions(); 276 } catch (Exception exp) { 277 if (DEBUG) exp.printStackTrace(); 278 } 279 if (exts == null) continue; 280 for (String ext : exts) { 281 if (extension.equals(ext)) { 282 try { 283 ScriptEngine engine = spi.getScriptEngine(); 284 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 285 return engine; 286 } catch (Exception exp) { 287 if (DEBUG) exp.printStackTrace(); 288 } 289 } 290 } 291 } 292 return null; 293 } 294 295 /** 296 * Look up and create a <code>ScriptEngine</code> for a given mime type. The algorithm 297 * used by <code>getEngineByName</code> is used except that the search starts 298 * by looking for a <code>ScriptEngineFactory</code> registered to handle the 299 * given mime type using <code>registerEngineMimeType</code>. 300 * @param mimeType The given mime type 301 * @return The engine to handle scripts with this mime type. Returns <code>null</code> 302 * if not found. 303 * @throws NullPointerException if mimeType is null. 304 */ 305 public ScriptEngine getEngineByMimeType(String mimeType) { 306 if (mimeType == null) throw new NullPointerException(); 307 //look for registered types first 308 Object obj; 309 if (null != (obj = mimeTypeAssociations.get(mimeType))) { 310 ScriptEngineFactory spi = (ScriptEngineFactory)obj; 311 try { 312 ScriptEngine engine = spi.getScriptEngine(); 313 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 314 return engine; 315 } catch (Exception exp) { 316 if (DEBUG) exp.printStackTrace(); 317 } 318 } 319 320 for (ScriptEngineFactory spi : engineSpis) { 321 List<String> types = null; 322 try { 323 types = spi.getMimeTypes(); 324 } catch (Exception exp) { 325 if (DEBUG) exp.printStackTrace(); 326 } 327 if (types == null) continue; 328 for (String type : types) { 329 if (mimeType.equals(type)) { 330 try { 331 ScriptEngine engine = spi.getScriptEngine(); 332 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 333 return engine; 334 } catch (Exception exp) { 335 if (DEBUG) exp.printStackTrace(); 336 } 337 } 338 } 339 } 340 return null; 341 } 342 343 /** 344 * Returns a list whose elements are instances of all the <code>ScriptEngineFactory</code> classes 345 * found by the discovery mechanism. 346 * @return List of all discovered <code>ScriptEngineFactory</code>s. 347 */ 348 public List<ScriptEngineFactory> getEngineFactories() { 349 List<ScriptEngineFactory> res = new ArrayList<ScriptEngineFactory>(engineSpis.size()); 350 for (ScriptEngineFactory spi : engineSpis) { 351 res.add(spi); 352 } 353 return Collections.unmodifiableList(res); 354 } 355 356 /** 357 * Registers a <code>ScriptEngineFactory</code> to handle a language 358 * name. Overrides any such association found using the Discovery mechanism. 359 * @param name The name to be associated with the <code>ScriptEngineFactory</code>. 360 * @param factory The class to associate with the given name. 361 * @throws NullPointerException if any of the parameters is null. 362 */ 363 public void registerEngineName(String name, ScriptEngineFactory factory) { 364 if (name == null || factory == null) throw new NullPointerException(); 365 nameAssociations.put(name, factory); 366 } 367 368 /** 369 * Registers a <code>ScriptEngineFactory</code> to handle a mime type. 370 * Overrides any such association found using the Discovery mechanism. 371 * 372 * @param type The mime type to be associated with the 373 * <code>ScriptEngineFactory</code>. 374 * 375 * @param factory The class to associate with the given mime type. 376 * @throws NullPointerException if any of the parameters is null. 377 */ 378 public void registerEngineMimeType(String type, ScriptEngineFactory factory) { 379 if (type == null || factory == null) throw new NullPointerException(); 380 mimeTypeAssociations.put(type, factory); 381 } 382 383 /** 384 * Registers a <code>ScriptEngineFactory</code> to handle an extension. 385 * Overrides any such association found using the Discovery mechanism. 386 * 387 * @param extension The extension type to be associated with the 388 * <code>ScriptEngineFactory</code>. 389 * @param factory The class to associate with the given extension. 390 * @throws NullPointerException if any of the parameters is null. 391 */ 392 public void registerEngineExtension(String extension, ScriptEngineFactory factory) { 393 if (extension == null || factory == null) throw new NullPointerException(); 394 extensionAssociations.put(extension, factory); 395 } 396 397 /** Set of script engine factories discovered. */ 398 private HashSet<ScriptEngineFactory> engineSpis; 399 400 /** Map of engine name to script engine factory. */ 401 private HashMap<String, ScriptEngineFactory> nameAssociations; 402 403 /** Map of script file extension to script engine factory. */ 404 private HashMap<String, ScriptEngineFactory> extensionAssociations; 405 406 /** Map of script script MIME type to script engine factory. */ 407 private HashMap<String, ScriptEngineFactory> mimeTypeAssociations; 408 409 /** Global bindings associated with script engines created by this manager. */ 410 private Bindings globalScope; 411 }