<commit_before>/*
 * 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.sling.scripting.core.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

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

import org.apache.sling.api.scripting.SlingScriptConstants;
import org.apache.sling.scripting.core.impl.helper.SlingScriptEngineManager;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Component which exposes a ScriptEngineManager service.
 *
 * @scr.component metatype="no" immediate="true"
 * @scr.reference name="ScriptEngineFactory"
 *                interface="javax.script.ScriptEngineFactory"
 *                cardinality="0..n" policy="dynamic"
 */
public class ScriptEngineManagerFactory implements BundleListener {

    private final Logger log = LoggerFactory.getLogger(ScriptEngineManagerFactory.class);

    private static final String ENGINE_FACTORY_SERVICE = "META-INF/services/" + ScriptEngineFactory.class.getName();

    private BundleContext bundleContext;

    /**
     * The service tracker for the event admin
     */
    private ServiceTracker eventAdminTracker;

    private volatile ScriptEngineManager scriptEngineManager;

    private final Set<Bundle> engineSpiBundles = new HashSet<Bundle>();

    private final Set<ScriptEngineFactory> engineSpiServices = new HashSet<ScriptEngineFactory>();

    private ServiceRegistration scriptEngineManagerRegistration;

    /**
     * Refresh the script engine manager.
     * This method is called from within a synchronized block,
     * no other sync is required!
     */
    private ScriptEngineManager refreshScriptEngineManager() {
        if ( this.scriptEngineManager != null ) {
            return this.scriptEngineManager;
        }

        if (scriptEngineManagerRegistration != null) {
            scriptEngineManagerRegistration.unregister();
            scriptEngineManagerRegistration = null;
        }

        // create (empty) script engine manager
        final ClassLoader loader = getClass().getClassLoader();
        final SlingScriptEngineManager tmp = new SlingScriptEngineManager(loader);

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

        // register script engines from registered services
        for (final ScriptEngineFactory factory : this.engineSpiServices) {
            extensions.addAll(registerFactory(tmp, factory));
        }

        scriptEngineManager = tmp;

        if (bundleContext != null) {
            scriptEngineManagerRegistration = bundleContext.registerService(
                ScriptEngineManager.class.getName(), scriptEngineManager,
                new Hashtable<String, Object>());
        }

        // Log messages to verify which ScriptEngine is actually used
        // for our registered extensions
        if (log.isInfoEnabled()) {
            for (Object o : extensions) {
                final String ext = o.toString();
                final ScriptEngine e = scriptEngineManager.getEngineByExtension(ext);
                if (e == null) {
                    log.warn("No ScriptEngine found for extension '{}' that was just registered", ext);
                } else {
                    log.info("Script extension '{}' is now handled by ScriptEngine '{}', version='{}', class='{}'", new Object[] { ext,
                            e.getFactory().getEngineName(), e.getFactory().getEngineVersion(), e.getClass().getName() });
                }
            }
        }
        return tmp;
    }

    @SuppressWarnings("unchecked")
    private Collection<?> registerFactories(final SlingScriptEngineManager 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));
            String line;
            while ((line = reader.readLine()) != null) {
                try {
                    Class<ScriptEngineFactory> clazz = bundle.loadClass(line);
                    ScriptEngineFactory spi = clazz.newInstance();
                    registerFactory(mgr, spi);
                    extensions.addAll(spi.getExtensions());
                } catch (Throwable t) {
                    log.error("Cannot register ScriptEngineFactory " + line, t);
                }
            }
        } catch (IOException ioe) {
            // ignore
        } finally {
            if (ins != null) {
                try {
                    ins.close();
                } catch (IOException ioe) {
                }
            }
        }

        return extensions;
    }

    private Collection<?> registerFactory(final SlingScriptEngineManager mgr, final ScriptEngineFactory factory) {
        log.info("Adding ScriptEngine {}, {} for language {}, {}", new Object[] { factory.getEngineName(), factory.getEngineVersion(),
                factory.getLanguageName(), factory.getLanguageVersion() });

        mgr.registerScriptEngineFactory(factory);

        return factory.getExtensions();
    }

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

    public void bundleChanged(BundleEvent event) {
        if (event.getType() == BundleEvent.STARTED && event.getBundle().getEntry(ENGINE_FACTORY_SERVICE) != null) {

            synchronized ( this ) {
                this.engineSpiBundles.add(event.getBundle());
                this.scriptEngineManager = null;
            }

        } else if (event.getType() == BundleEvent.STOPPED  ) {
            synchronized ( this ) {
                if ( this.engineSpiBundles.remove(event.getBundle()) ) {
                    this.scriptEngineManager = null;
                }
            }
        }
    }

    // ---------- SCR integration ----------------------------------------------

    protected void activate(ComponentContext context) {
        this.bundleContext = context.getBundleContext();

        // setup tracker first as this is used in the bind/unbind methods
        this.eventAdminTracker = new ServiceTracker(this.bundleContext, EventAdmin.class.getName(), null);<commit_msg>SLING-1848 - also need to do a refresh inside bundleChanged()<commit_after>/*
 * 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.sling.scripting.core.impl;

import java.io.BufferedReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.util.Collection;
import java.util.Dictionary;
import java.util.HashSet;
import java.util.Hashtable;
import java.util.List;
import java.util.Set;
import java.util.SortedSet;
import java.util.TreeSet;

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

import org.apache.sling.api.scripting.SlingScriptConstants;
import org.apache.sling.scripting.core.impl.helper.SlingScriptEngineManager;
import org.osgi.framework.Bundle;
import org.osgi.framework.BundleContext;
import org.osgi.framework.BundleEvent;
import org.osgi.framework.BundleListener;
import org.osgi.framework.ServiceRegistration;
import org.osgi.service.component.ComponentContext;
import org.osgi.service.event.Event;
import org.osgi.service.event.EventAdmin;
import org.osgi.util.tracker.ServiceTracker;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * Component which exposes a ScriptEngineManager service.
 *
 * @scr.component metatype="no" immediate="true"
 * @scr.reference name="ScriptEngineFactory"
 *                interface="javax.script.ScriptEngineFactory"
 *                cardinality="0..n" policy="dynamic"
 */
public class ScriptEngineManagerFactory implements BundleListener {

    private final Logger log = LoggerFactory.getLogger(ScriptEngineManagerFactory.class);

    private static final String ENGINE_FACTORY_SERVICE = "META-INF/services/" + ScriptEngineFactory.class.getName();

    private BundleContext bundleContext;

    /**
     * The service tracker for the event admin
     */
    private ServiceTracker eventAdminTracker;

    private volatile ScriptEngineManager scriptEngineManager;

    private final Set<Bundle> engineSpiBundles = new HashSet<Bundle>();

    private final Set<ScriptEngineFactory> engineSpiServices = new HashSet<ScriptEngineFactory>();

    private ServiceRegistration scriptEngineManagerRegistration;

    /**
     * Refresh the script engine manager.
     * This method is called from within a synchronized block,
     * no other sync is required!
     */
    private ScriptEngineManager refreshScriptEngineManager() {
        if ( this.scriptEngineManager != null ) {
            return this.scriptEngineManager;
        }

        if (scriptEngineManagerRegistration != null) {
            scriptEngineManagerRegistration.unregister();
            scriptEngineManagerRegistration = null;
        }

        // create (empty) script engine manager
        final ClassLoader loader = getClass().getClassLoader();
        final SlingScriptEngineManager tmp = new SlingScriptEngineManager(loader);

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

        // register script engines from registered services
        for (final ScriptEngineFactory factory : this.engineSpiServices) {
            extensions.addAll(registerFactory(tmp, factory));
        }

        scriptEngineManager = tmp;

        if (bundleContext != null) {
            scriptEngineManagerRegistration = bundleContext.registerService(
                ScriptEngineManager.class.getName(), scriptEngineManager,
                new Hashtable<String, Object>());
        }

        // Log messages to verify which ScriptEngine is actually used
        // for our registered extensions
        if (log.isInfoEnabled()) {
            for (Object o : extensions) {
                final String ext = o.toString();
                final ScriptEngine e = scriptEngineManager.getEngineByExtension(ext);
                if (e == null) {
                    log.warn("No ScriptEngine found for extension '{}' that was just registered", ext);
                } else {
                    log.info("Script extension '{}' is now handled by ScriptEngine '{}', version='{}', class='{}'", new Object[] { ext,
                            e.getFactory().getEngineName(), e.getFactory().getEngineVersion(), e.getClass().getName() });
                }
            }
        }
        return tmp;
    }

    @SuppressWarnings("unchecked")
    private Collection<?> registerFactories(final SlingScriptEngineManager 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));
            String line;
            while ((line = reader.readLine()) != null) {
                try {
                    Class<ScriptEngineFactory> clazz = bundle.loadClass(line);
                    ScriptEngineFactory spi = clazz.newInstance();
                    registerFactory(mgr, spi);
                    extensions.addAll(spi.getExtensions());
                } catch (Throwable t) {
                    log.error("Cannot register ScriptEngineFactory " + line, t);
                }
            }
        } catch (IOException ioe) {
            // ignore
        } finally {
            if (ins != null) {
                try {
                    ins.close();
                } catch (IOException ioe) {
                }
            }
        }

        return extensions;
    }

    private Collection<?> registerFactory(final SlingScriptEngineManager mgr, final ScriptEngineFactory factory) {
        log.info("Adding ScriptEngine {}, {} for language {}, {}", new Object[] { factory.getEngineName(), factory.getEngineVersion(),
                factory.getLanguageName(), factory.getLanguageVersion() });

        mgr.registerScriptEngineFactory(factory);

        return factory.getExtensions();
    }

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

    public void bundleChanged(BundleEvent event) {
        if (event.getType() == BundleEvent.STARTED && event.getBundle().getEntry(ENGINE_FACTORY_SERVICE) != null) {

            synchronized ( this ) {
                this.engineSpiBundles.add(event.getBundle());
                this.scriptEngineManager = null;
                this.refreshScriptEngineManager();
            }

        } else if (event.getType() == BundleEvent.STOPPED  ) {
            synchronized ( this ) {
                if ( this.engineSpiBundles.remove(event.getBundle()) ) {
                    this.scriptEngineManager = null;
                    this.refreshScriptEngineManager();
                }
            }
        }
    }

    // ---------- SCR integration ----------------------------------------------

    protected void activate(ComponentContext context) {
        this.bundleContext = context.getBundleContext();

        // setup tracker first as this is used in the bind/unbind methods
        this.eventAdminTracker = new ServiceTracker(this.bundleContext, EventAdmin.class.getName(), null);
{'fraction_non_alphanumeric': 0.10404116465863454, 'fraction_numerical': 0.0011295180722891566, 'mean_word_length': 2.529007971656333, 'pattern_counts': {'":': 0, '<': 27, '<?xml version=': 0, '>': 27, 'https://': 0, 'lorem ipsum': 0, 'www.': 2, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '4236002', 'n_tokens_mistral': 3940, 'n_tokens_neox': 3823, 'n_words': 1051}