Package org.apache.felix.mishell

Source Code of org.apache.felix.mishell.OSGiScriptEngineManager

/*
*   Copyright 2005 The Apache Software Foundation
*
*   Licensed under the Apache License, Version 2.0 (the "License");
*   you may not use this file except in compliance with the License.
*   You may obtain a copy of the License at
*
*       http://www.apache.org/licenses/LICENSE-2.0
*
*   Unless required by applicable law or agreed to in writing, software
*   distributed under the License is distributed on an "AS IS" BASIS,
*   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
*   See the License for the specific language governing permissions and
*   limitations under the License.
*
*/
package org.apache.felix.mishell;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

import javax.script.Bindings;
import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;
import javax.script.ScriptEngineManager;
import javax.script.SimpleBindings;

import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;

/**
* This class acts as a delegate for all the available ScriptEngineManagers. Unluckily, the standard did not
* define it as an interface, so we need to extend it to allow polymorphism. However, no calls to super are used.
* It wraps all available ScriptEngineManagers in the OSGi ServicePlatform into a merged ScriptEngineManager.
*
* Internally, what this class does is creating ScriptEngineManagers for each bundle
* that contains a ScriptEngineFactory and includes a META-INF/services/javax.script.ScriptEngineFactory file.
* It assumes that the file contains a list of @link ScriptEngineFactory classes. For each bundle, it creates a
* ScriptEngineManager, then merges them. @link ScriptEngineFactory objects are wrapped
* into @link OSGiScriptEngineFactory objects to deal with problems of context class loader:
* Those scripting engines that rely on the ContextClassloader for finding resources need to use this wrapper
* and the @link OSGiScriptFactory. Mainly, jruby does.
*
* Note that even if no context classloader issues arose, it would still be needed to search manually for the
* factories and either use them directly (losing the mimeType/extension/shortName mechanisms for finding engines
* or manually registering them) or still use this class, which would be smarter. In the latter case,
* it would only be needed to remove the hack that temporarily sets the context classloader to the appropriate,
* bundle-related, class loader.
*
* Caveats:
* <ul><li>
* All factories are wrapped with an {@link OSGiScriptEngineFactory}. As Engines are not wrapped,
* calls like
* <code>
* ScriptEngineManager osgiManager=new OSGiScriptEngineManager(context);<br>
* ScriptEngine engine=osgiManager.getEngineByName("ruby");
* ScriptEngineFactory factory=engine.getFactory() //this does not return the OSGiFactory wrapper
* factory.getScriptEngine(); //this might fail, as it does not use OSGiScriptEngineFactory wrapper
* </code>
* might result in unexpected errors. Future versions may wrap the ScriptEngine with a OSGiScriptEngine to solve this
* issue, but for the moment it is not needed.
* </li>
*
*/
public class OSGiScriptEngineManager extends ScriptEngineManager{
  private Bindings bindings;
  private Map <ScriptEngineManager, ClassLoader> classLoaders;
  private BundleContext context;
 
  public OSGiScriptEngineManager(BundleContext context){
    this.context=context;
    bindings=new SimpleBindings();
    this.classLoaders=findManagers(context);
  }
  /**
   * This method is the only one that is visible and not part of the ScriptEngineManager class.
   * Its purpose is to find new managers that weren't available before, but keeping the globalScope bindings
   * set.
   * If you want to clean the bindings you can either get a fresh instance of OSGiScriptManager or
   * setting up a new bindings object.
   * This can be done with:
   * <code>
   * ScriptEngineManager manager=new OSGiScriptEngineManager(context);
   * (...)//do stuff
   * osgiManager=(OSGiScriptEngineManager)manager;//cast to ease reading
   * osgiManager.reloadManagers();
   *
   * manager.setBindings(new OSGiBindings());//or you can use your own bindings implementation
   *
   * </code>  
   *
   */
  public void reloadManagers(){
    this.classLoaders=findManagers(context);
  }
 
  public Object get(String key) {
    return bindings.get(key);
  }

  public Bindings getBindings() {
    return bindings;
  }

  public ScriptEngine getEngineByExtension(String extension) {
    //TODO this is a hack to deal with context class loader issues
    ScriptEngine engine=null;
    for(ScriptEngineManager manager: classLoaders.keySet()){
      ClassLoader old=Thread.currentThread().getContextClassLoader();
      Thread.currentThread().setContextClassLoader(classLoaders.get(manager));
      engine=manager.getEngineByExtension(extension);
      Thread.currentThread().setContextClassLoader(old);
      if (engine!=null) break;
    }
     return engine;
  }

  public ScriptEngine getEngineByMimeType(String mimeType) {
    //TODO this is a hack to deal with context class loader issues
    ScriptEngine engine=null;
    for(ScriptEngineManager manager: classLoaders.keySet()){
      ClassLoader old=Thread.currentThread().getContextClassLoader();
      Thread.currentThread().setContextClassLoader(classLoaders.get(manager));
      engine=manager.getEngineByMimeType(mimeType);
      Thread.currentThread().setContextClassLoader(old);
      if (engine!=null) break;
    }
     return engine;
  }

  public ScriptEngine getEngineByName(String shortName) {
    //TODO this is a hack to deal with context class loader issues
    for(ScriptEngineManager manager: classLoaders.keySet()){
      ClassLoader old=Thread.currentThread().getContextClassLoader();
      Thread.currentThread().setContextClassLoader(classLoaders.get(manager));
      ScriptEngine engine=manager.getEngineByName(shortName);
      Thread.currentThread().setContextClassLoader(old);
      if (engine!=null){
        return new OSGiScriptEngine(engine, new OSGiScriptEngineFactory(engine.getFactory(), classLoaders.get(manager)));
      }
    }
    return null;
  }
  public List<ScriptEngineFactory> getEngineFactories() {
    List<ScriptEngineFactory> osgiFactories=new ArrayList<ScriptEngineFactory>();
    for(ScriptEngineManager engineManager: classLoaders.keySet()){
      for (ScriptEngineFactory factory : engineManager.getEngineFactories()){
        osgiFactories.add(new OSGiScriptEngineFactory(factory, classLoaders.get(engineManager)));
    }
    }
    return osgiFactories;
  }

  public void put(String key, Object value) {
    bindings.put(key, value);
  }

  public void registerEngineExtension(String extension, ScriptEngineFactory factory) {
    for(ScriptEngineManager engineManager: classLoaders.keySet())
      engineManager.registerEngineExtension(extension, factory);
  }

  public void registerEngineMimeType(String type, ScriptEngineFactory factory) {
    for(ScriptEngineManager engineManager: classLoaders.keySet())
    engineManager.registerEngineMimeType(type, factory);
  }

  public void registerEngineName(String name, ScriptEngineFactory factory) {
    for(ScriptEngineManager engineManager: classLoaders.keySet())
      engineManager.registerEngineName(name, factory);
  }
  /**
   * Follows the same behavior of @link javax.script.ScriptEngineManager#setBindings(Bindings)
   * This means that the same bindings are applied to all the underlying managers.
   * @param bindings
   */
  public void setBindings(Bindings bindings) {
    this.bindings=bindings;
    for(ScriptEngineManager manager: classLoaders.keySet()){
      manager.setBindings(bindings);
    }

  }


  private Map<ScriptEngineManager, ClassLoader> findManagers(BundleContext context) {
    Map<ScriptEngineManager, ClassLoader> managers=new HashMap<ScriptEngineManager, ClassLoader>();
    try {
      for(String factoryName: findFactoryCandidates(context)){
        //We do not really need the class, but we need the classloader
        ClassLoader factoryLoader=Class.forName(factoryName).getClassLoader();
        ScriptEngineManager manager=new ScriptEngineManager(factoryLoader);
        manager.setBindings(bindings);
        managers.put(manager, factoryLoader);
      }
      return managers;
    } catch (IOException ioe) {
      throw new RuntimeException(ioe);
    } catch (ClassNotFoundException cnfe) {
      throw new RuntimeException(cnfe);
    }
  }
  /**
   * Iterates through all bundles to get the available @link ScriptEngineFactory classes
   * @return the names of the available ScriptEngineFactory classes
   * @throws IOException
   */
  private List<String> findFactoryCandidates(BundleContext context) throws IOException{
    Bundle[] bundles = context.getBundles();
    List<String> factoryCandidates = new ArrayList<String>();
    for (Bundle bundle : bundles) {
      System.out.println(bundle.getSymbolicName());
      if(bundle.getSymbolicName().equals("system.bundle")) continue;
      Enumeration urls = bundle.findEntries("META-INF/services",
          "javax.script.ScriptEngineFactory", false);
      if (urls == null)
        continue;
      while (urls.hasMoreElements()) {
        URL u = (URL) urls.nextElement();
        BufferedReader reader = new BufferedReader(
            new InputStreamReader(u.openStream()));
        String line;
        while ((line = reader.readLine()) != null) {
          factoryCandidates.add(line.trim());
    }
      }
    }
    return factoryCandidates;
  }
}
TOP

Related Classes of org.apache.felix.mishell.OSGiScriptEngineManager

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.