1 /* 2 * Copyright (c) 2005, 2013, 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 initEngines(loader); 85 } 86 87 private ServiceLoader<ScriptEngineFactory> getServiceLoader(final ClassLoader loader) { 88 if (loader != null) { 89 return ServiceLoader.load(ScriptEngineFactory.class, loader); 90 } else { 91 return ServiceLoader.loadInstalled(ScriptEngineFactory.class); 92 } 93 } 94 95 private void initEngines(final ClassLoader loader) { 96 Iterator<ScriptEngineFactory> itr = null; 97 try { 98 ServiceLoader<ScriptEngineFactory> sl = AccessController.doPrivileged( 99 new PrivilegedAction<ServiceLoader<ScriptEngineFactory>>() { 100 @Override 101 public ServiceLoader<ScriptEngineFactory> run() { 102 return getServiceLoader(loader); 103 } 104 }); 105 106 itr = sl.iterator(); 107 } catch (ServiceConfigurationError err) { 108 System.err.println("Can't find ScriptEngineFactory providers: " + 109 err.getMessage()); 110 if (DEBUG) { 111 err.printStackTrace(); 112 } 113 // do not throw any exception here. user may want to 114 // manage his/her own factories using this manager 115 // by explicit registratation (by registerXXX) methods. 116 return; 117 } 118 119 try { 120 while (itr.hasNext()) { 121 try { 122 ScriptEngineFactory fact = itr.next(); 123 engineSpis.add(fact); 124 } catch (ServiceConfigurationError err) { 125 System.err.println("ScriptEngineManager providers.next(): " 126 + err.getMessage()); 127 if (DEBUG) { 128 err.printStackTrace(); 129 } 130 // one factory failed, but check other factories... 131 continue; 132 } 133 } 134 } catch (ServiceConfigurationError err) { 135 System.err.println("ScriptEngineManager providers.hasNext(): " 136 + err.getMessage()); 137 if (DEBUG) { 138 err.printStackTrace(); 139 } 140 // do not throw any exception here. user may want to 141 // manage his/her own factories using this manager 142 // by explicit registratation (by registerXXX) methods. 143 return; 144 } 145 } 146 147 /** 148 * <code>setBindings</code> stores the specified <code>Bindings</code> 149 * in the <code>globalScope</code> field. ScriptEngineManager sets this 150 * <code>Bindings</code> as global bindings for <code>ScriptEngine</code> 151 * objects created by it. 152 * 153 * @param bindings The specified <code>Bindings</code> 154 * @throws IllegalArgumentException if bindings is null. 155 */ 156 public void setBindings(Bindings bindings) { 157 if (bindings == null) { 158 throw new IllegalArgumentException("Global scope cannot be null."); 159 } 160 161 globalScope = bindings; 162 } 163 164 /** 165 * <code>getBindings</code> returns the value of the <code>globalScope</code> field. 166 * ScriptEngineManager sets this <code>Bindings</code> as global bindings for 167 * <code>ScriptEngine</code> objects created by it. 168 * 169 * @return The globalScope field. 170 */ 171 public Bindings getBindings() { 172 return globalScope; 173 } 174 175 /** 176 * Sets the specified key/value pair in the Global Scope. 177 * @param key Key to set 178 * @param value Value to set. 179 * @throws NullPointerException if key is null. 180 * @throws IllegalArgumentException if key is empty string. 181 */ 182 public void put(String key, Object value) { 183 globalScope.put(key, value); 184 } 185 186 /** 187 * Gets the value for the specified key in the Global Scope 188 * @param key The key whose value is to be returned. 189 * @return The value for the specified key. 190 */ 191 public Object get(String key) { 192 return globalScope.get(key); 193 } 194 195 /** 196 * Looks up and creates a <code>ScriptEngine</code> for a given name. 197 * The algorithm first searches for a <code>ScriptEngineFactory</code> that has been 198 * registered as a handler for the specified name using the <code>registerEngineName</code> 199 * method. 200 * <br><br> If one is not found, it searches the set of <code>ScriptEngineFactory</code> instances 201 * stored by the constructor for one with the specified name. If a <code>ScriptEngineFactory</code> 202 * is found by either method, it is used to create instance of <code>ScriptEngine</code>. 203 * @param shortName The short name of the <code>ScriptEngine</code> implementation. 204 * returned by the <code>getNames</code> method of its <code>ScriptEngineFactory</code>. 205 * @return A <code>ScriptEngine</code> created by the factory located in the search. Returns null 206 * if no such factory was found. The <code>ScriptEngineManager</code> sets its own <code>globalScope</code> 207 * <code>Bindings</code> as the <code>GLOBAL_SCOPE</code> <code>Bindings</code> of the newly 208 * created <code>ScriptEngine</code>. 209 * @throws NullPointerException if shortName is null. 210 */ 211 public ScriptEngine getEngineByName(String shortName) { 212 if (shortName == null) throw new NullPointerException(); 213 //look for registered name first 214 Object obj; 215 if (null != (obj = nameAssociations.get(shortName))) { 216 ScriptEngineFactory spi = (ScriptEngineFactory)obj; 217 try { 218 ScriptEngine engine = spi.getScriptEngine(); 219 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 220 return engine; 221 } catch (Exception exp) { 222 if (DEBUG) exp.printStackTrace(); 223 } 224 } 225 226 for (ScriptEngineFactory spi : engineSpis) { 227 List<String> names = null; 228 try { 229 names = spi.getNames(); 230 } catch (Exception exp) { 231 if (DEBUG) exp.printStackTrace(); 232 } 233 234 if (names != null) { 235 for (String name : names) { 236 if (shortName.equals(name)) { 237 try { 238 ScriptEngine engine = spi.getScriptEngine(); 239 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 240 return engine; 241 } catch (Exception exp) { 242 if (DEBUG) exp.printStackTrace(); 243 } 244 } 245 } 246 } 247 } 248 249 return null; 250 } 251 252 /** 253 * Look up and create a <code>ScriptEngine</code> for a given extension. The algorithm 254 * used by <code>getEngineByName</code> is used except that the search starts 255 * by looking for a <code>ScriptEngineFactory</code> registered to handle the 256 * given extension using <code>registerEngineExtension</code>. 257 * @param extension The given extension 258 * @return The engine to handle scripts with this extension. Returns <code>null</code> 259 * if not found. 260 * @throws NullPointerException if extension is null. 261 */ 262 public ScriptEngine getEngineByExtension(String extension) { 263 if (extension == null) throw new NullPointerException(); 264 //look for registered extension first 265 Object obj; 266 if (null != (obj = extensionAssociations.get(extension))) { 267 ScriptEngineFactory spi = (ScriptEngineFactory)obj; 268 try { 269 ScriptEngine engine = spi.getScriptEngine(); 270 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 271 return engine; 272 } catch (Exception exp) { 273 if (DEBUG) exp.printStackTrace(); 274 } 275 } 276 277 for (ScriptEngineFactory spi : engineSpis) { 278 List<String> exts = null; 279 try { 280 exts = spi.getExtensions(); 281 } catch (Exception exp) { 282 if (DEBUG) exp.printStackTrace(); 283 } 284 if (exts == null) continue; 285 for (String ext : exts) { 286 if (extension.equals(ext)) { 287 try { 288 ScriptEngine engine = spi.getScriptEngine(); 289 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 290 return engine; 291 } catch (Exception exp) { 292 if (DEBUG) exp.printStackTrace(); 293 } 294 } 295 } 296 } 297 return null; 298 } 299 300 /** 301 * Look up and create a <code>ScriptEngine</code> for a given mime type. The algorithm 302 * used by <code>getEngineByName</code> is used except that the search starts 303 * by looking for a <code>ScriptEngineFactory</code> registered to handle the 304 * given mime type using <code>registerEngineMimeType</code>. 305 * @param mimeType The given mime type 306 * @return The engine to handle scripts with this mime type. Returns <code>null</code> 307 * if not found. 308 * @throws NullPointerException if mimeType is null. 309 */ 310 public ScriptEngine getEngineByMimeType(String mimeType) { 311 if (mimeType == null) throw new NullPointerException(); 312 //look for registered types first 313 Object obj; 314 if (null != (obj = mimeTypeAssociations.get(mimeType))) { 315 ScriptEngineFactory spi = (ScriptEngineFactory)obj; 316 try { 317 ScriptEngine engine = spi.getScriptEngine(); 318 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 319 return engine; 320 } catch (Exception exp) { 321 if (DEBUG) exp.printStackTrace(); 322 } 323 } 324 325 for (ScriptEngineFactory spi : engineSpis) { 326 List<String> types = null; 327 try { 328 types = spi.getMimeTypes(); 329 } catch (Exception exp) { 330 if (DEBUG) exp.printStackTrace(); 331 } 332 if (types == null) continue; 333 for (String type : types) { 334 if (mimeType.equals(type)) { 335 try { 336 ScriptEngine engine = spi.getScriptEngine(); 337 engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE); 338 return engine; 339 } catch (Exception exp) { 340 if (DEBUG) exp.printStackTrace(); 341 } 342 } 343 } 344 } 345 return null; 346 } 347 348 /** 349 * Returns a list whose elements are instances of all the <code>ScriptEngineFactory</code> classes 350 * found by the discovery mechanism. 351 * @return List of all discovered <code>ScriptEngineFactory</code>s. 352 */ 353 public List<ScriptEngineFactory> getEngineFactories() { 354 List<ScriptEngineFactory> res = new ArrayList<ScriptEngineFactory>(engineSpis.size()); 355 for (ScriptEngineFactory spi : engineSpis) { 356 res.add(spi); 357 } 358 return Collections.unmodifiableList(res); 359 } 360 361 /** 362 * Registers a <code>ScriptEngineFactory</code> to handle a language 363 * name. Overrides any such association found using the Discovery mechanism. 364 * @param name The name to be associated with the <code>ScriptEngineFactory</code>. 365 * @param factory The class to associate with the given name. 366 * @throws NullPointerException if any of the parameters is null. 367 */ 368 public void registerEngineName(String name, ScriptEngineFactory factory) { 369 if (name == null || factory == null) throw new NullPointerException(); 370 nameAssociations.put(name, factory); 371 } 372 373 /** 374 * Registers a <code>ScriptEngineFactory</code> to handle a mime type. 375 * Overrides any such association found using the Discovery mechanism. 376 * 377 * @param type The mime type to be associated with the 378 * <code>ScriptEngineFactory</code>. 379 * 380 * @param factory The class to associate with the given mime type. 381 * @throws NullPointerException if any of the parameters is null. 382 */ 383 public void registerEngineMimeType(String type, ScriptEngineFactory factory) { 384 if (type == null || factory == null) throw new NullPointerException(); 385 mimeTypeAssociations.put(type, factory); 386 } 387 388 /** 389 * Registers a <code>ScriptEngineFactory</code> to handle an extension. 390 * Overrides any such association found using the Discovery mechanism. 391 * 392 * @param extension The extension type to be associated with the 393 * <code>ScriptEngineFactory</code>. 394 * @param factory The class to associate with the given extension. 395 * @throws NullPointerException if any of the parameters is null. 396 */ 397 public void registerEngineExtension(String extension, ScriptEngineFactory factory) { 398 if (extension == null || factory == null) throw new NullPointerException(); 399 extensionAssociations.put(extension, factory); 400 } 401 402 /** Set of script engine factories discovered. */ 403 private HashSet<ScriptEngineFactory> engineSpis; 404 405 /** Map of engine name to script engine factory. */ 406 private HashMap<String, ScriptEngineFactory> nameAssociations; 407 408 /** Map of script file extension to script engine factory. */ 409 private HashMap<String, ScriptEngineFactory> extensionAssociations; 410 411 /** Map of script script MIME type to script engine factory. */ 412 private HashMap<String, ScriptEngineFactory> mimeTypeAssociations; 413 414 /** Global bindings associated with script engines created by this manager. */ 415 private Bindings globalScope; 416 }