org.apache.felix.webconsole.plugins.scriptconsole.internal.ScriptEngineManager.java Source code

Java tutorial

Introduction

Here is the source code for org.apache.felix.webconsole.plugins.scriptconsole.internal.ScriptEngineManager.java

Source

/*
 * Licensed to the Apache Software Foundation (ASF) under one
 * or more contributor license agreements.  See the NOTICE file
 * distributed with this work for additional information
 * regarding copyright ownership.  The ASF licenses this file
 * to you 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.webconsole.plugins.scriptconsole.internal;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;
import java.util.concurrent.ConcurrentHashMap;

import javax.script.ScriptEngine;
import javax.script.ScriptEngineFactory;

import org.apache.commons.io.IOUtils;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.ServiceReference;
import org.osgi.service.log.LogService;
import org.osgi.util.tracker.ServiceTracker;
import org.osgi.util.tracker.ServiceTrackerCustomizer;

/**
 * It is based on org.apache.sling.scripting.core.impl.ScriptEngineManagerFactory
 */
class ScriptEngineManager implements BundleListener, ServiceTrackerCustomizer {
    private static final String ENGINE_FACTORY_SERVICE = "META-INF/services/" + ScriptEngineFactory.class.getName();
    private final Set<Bundle> engineSpiBundles = new HashSet<Bundle>();
    private final Map<ServiceReference, ScriptEngineFactoryState> engineSpiServices = new ConcurrentHashMap<ServiceReference, ScriptEngineFactoryState>();

    private final Logger log;
    private EngineManagerState state = new EngineManagerState();

    /**
     * ServiceTracker for ScriptEngineFactory
     */
    private ServiceTracker scriptFactoryTracker;
    private final BundleContext context;

    public ScriptEngineManager(BundleContext context, Logger logger) {
        this.log = logger;
        this.context = context;

        this.context.addBundleListener(this);

        Bundle[] bundles = this.context.getBundles();
        synchronized (this.engineSpiBundles) {
            for (Bundle bundle : bundles) {
                if (bundle.getState() == Bundle.ACTIVE && bundle.getEntry(ENGINE_FACTORY_SERVICE) != null) {
                    this.engineSpiBundles.add(bundle);
                }
            }
        }

        // create a script engine manager
        this.refreshScriptEngineManager();

        this.scriptFactoryTracker = new ServiceTracker(context, ScriptEngineFactory.class.getName(), this);
        this.scriptFactoryTracker.open();
    }

    public List<ScriptEngineFactory> getEngineFactories() {
        return state.factories;
    }

    public ScriptEngine getEngineByExtension(String extension) {
        ScriptEngineFactory factory = state.extensionAssociations.get(extension);
        if (factory == null)
            return null;

        ScriptEngine engine = factory.getScriptEngine();

        //We do not support global scope for now
        //engine.setBindings(globalScope, ScriptContext.GLOBAL_SCOPE);

        return engine;
    }

    // ---------- BundleListener interface -------------------------------------

    public void bundleChanged(BundleEvent event) {
        if (event.getType() == BundleEvent.STARTED && event.getBundle().getEntry(ENGINE_FACTORY_SERVICE) != null) {
            synchronized (this.engineSpiBundles) {
                this.engineSpiBundles.add(event.getBundle());
            }
            this.refreshScriptEngineManager();
        } else if (event.getType() == BundleEvent.STOPPED) {
            boolean refresh;
            synchronized (this.engineSpiBundles) {
                refresh = this.engineSpiBundles.remove(event.getBundle());
            }
            if (refresh) {
                this.refreshScriptEngineManager();
            }
        }
    }

    // ---------- ServiceTrackerCustomizer interface -------------------------------------

    public Object addingService(ServiceReference reference) {
        ScriptEngineFactory service = (ScriptEngineFactory) context.getService(reference);
        engineSpiServices.put(reference, new ScriptEngineFactoryState(service, getServiceProperties(reference)));
        refreshScriptEngineManager();
        return service;
    }

    public void modifiedService(ServiceReference reference, Object service) {
        ScriptEngineFactoryState state = engineSpiServices.get(reference);
        state.properties = getServiceProperties(reference);
        refreshScriptEngineManager();
    }

    public void removedService(ServiceReference reference, Object service) {
        context.ungetService(reference);
        engineSpiServices.remove(reference);
        refreshScriptEngineManager();

    }

    private void refreshScriptEngineManager() {
        EngineManagerState tmp = new EngineManagerState();
        // register script engines from bundles
        final SortedSet<Object> extensions = new TreeSet<Object>();
        synchronized (this.engineSpiBundles) {
            for (final Bundle bundle : this.engineSpiBundles) {
                extensions.addAll(registerFactories(tmp, bundle));
            }
        }

        // register script engines from registered services
        synchronized (this.engineSpiServices) {
            for (final ScriptEngineFactoryState state : this.engineSpiServices.values()) {
                extensions.addAll(registerFactory(tmp, state.scriptEngineFactory, state.properties));
            }
        }

        synchronized (this) {
            this.state = tmp;
        }
    }

    @SuppressWarnings("unchecked")
    private Collection<?> registerFactories(final EngineManagerState mgr, final Bundle bundle) {
        URL url = bundle.getEntry(ENGINE_FACTORY_SERVICE);
        InputStream ins = null;
        final SortedSet<String> extensions = new TreeSet<String>();
        try {
            ins = url.openStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(ins));
            for (String className : getClassNames(reader)) {
                try {
                    Class<ScriptEngineFactory> clazz = bundle.loadClass(className);
                    ScriptEngineFactory spi = clazz.newInstance();
                    registerFactory(mgr, spi, null);
                    extensions.addAll(spi.getExtensions());
                } catch (Throwable t) {
                    log.log(LogService.LOG_ERROR, "Cannot register ScriptEngineFactory " + className, t);
                }
            }

        } catch (IOException ioe) {
            // ignore
        } finally {
            IOUtils.closeQuietly(ins);
        }

        return extensions;
    }

    private Collection<?> registerFactory(final EngineManagerState mgr, final ScriptEngineFactory factory,
            final Map<Object, Object> props) {
        log.log(LogService.LOG_INFO,
                String.format("Adding ScriptEngine %s, %s for language %s, %s", factory.getEngineName(),
                        factory.getEngineVersion(), factory.getLanguageName(), factory.getLanguageVersion()));

        mgr.factories.add(factory);
        mgr.factoryProperties.put(factory, props);
        for (Object ext : factory.getExtensions()) {
            mgr.extensionAssociations.put((String) ext, factory);
        }
        return factory.getExtensions();
    }

    public void dispose() {
        if (scriptFactoryTracker != null) {
            scriptFactoryTracker.close();
        }
    }

    static List<String> getClassNames(BufferedReader reader) throws IOException {
        List<String> classNames = new ArrayList<String>();
        String line;
        while ((line = reader.readLine()) != null) {
            if (!line.startsWith("#") && line.trim().length() > 0) {
                int indexOfHash = line.indexOf('#');
                if (indexOfHash >= 0) {
                    line = line.substring(0, indexOfHash);
                }
                line = line.trim();
                classNames.add(line);
            }
        }
        return classNames;
    }

    private static Map<Object, Object> getServiceProperties(ServiceReference reference) {
        Map<Object, Object> props = new HashMap<Object, Object>();
        for (String key : reference.getPropertyKeys()) {
            props.put(key, reference.getProperty(key));
        }
        return props;
    }

    private static class ScriptEngineFactoryState {
        final ScriptEngineFactory scriptEngineFactory;
        Map<Object, Object> properties;

        private ScriptEngineFactoryState(ScriptEngineFactory scriptEngineFactory, Map<Object, Object> properties) {
            this.scriptEngineFactory = scriptEngineFactory;
            this.properties = properties;
        }
    }

    private static class EngineManagerState {
        private final List<ScriptEngineFactory> factories = new ArrayList<ScriptEngineFactory>();
        private final Map<ScriptEngineFactory, Map<Object, Object>> factoryProperties = new HashMap<ScriptEngineFactory, Map<Object, Object>>();
        private final Map<String, ScriptEngineFactory> extensionAssociations = new HashMap<String, ScriptEngineFactory>();

    }
}