private GremlinScriptEngine createGremlinScriptEngine(final GremlinScriptEngineFactory spi) {
        final GremlinScriptEngine engine = spi.getScriptEngine();

        // merge in bindings that are marked with global scope. these get applied to all GremlinScriptEngine instances
        getCustomizers(spi.getEngineName()).stream()
                .filter(p -> p instanceof BindingsCustomizer)
                .map(p -> ((BindingsCustomizer) p))
                .filter(bc -> bc.getScope() == ScriptContext.GLOBAL_SCOPE)
                .flatMap(bc -> bc.getBindings().entrySet().stream())
                .forEach(kv -> {
                    if (globalScope.containsKey(kv.getKey())) {
                        logger.warn("Overriding the global binding [{}] - was [{}] and is now [{}]",
                                kv.getKey(), globalScope.get(kv.getKey()), kv.getValue());
                    }

                    globalScope.put(kv.getKey(), kv.getValue());
                });
        engine.setBindings(getBindings(), ScriptContext.GLOBAL_SCOPE);

        // merge in bindings that are marked with engine scope. there typically won't be any of these but it's just
        // here for completeness. bindings will typically apply with global scope only as engine scope will generally
        // be overridden at the time of eval() with the bindings that are supplied to it
        getCustomizers(spi.getEngineName()).stream()
                .filter(p -> p instanceof BindingsCustomizer)
                .map(p -> ((BindingsCustomizer) p))
                .filter(bc -> bc.getScope() == ScriptContext.ENGINE_SCOPE)
                .forEach(bc -> engine.getBindings(ScriptContext.ENGINE_SCOPE).putAll(bc.getBindings()));

        final List<ScriptCustomizer> scriptCustomizers = getCustomizers(spi.getEngineName()).stream()
                .filter(p -> p instanceof ScriptCustomizer)
                .map(p -> ((ScriptCustomizer) p))
                .collect(Collectors.toList());

        // since the bindings aren't added until after the ScriptEngine is constructed, running init scripts that
        // require bindings creates a problem. as a result, init scripts are applied here
        scriptCustomizers.stream().flatMap(sc -> sc.getScripts().stream()).
                map(l -> String.join(System.lineSeparator(), l)).forEach(initScript -> {
            try {
                // need to apply global bindings here as part of the engine or else they don't get their binding types
                // registered by certain GremlinScriptEngine instances (pretty much talking about gremlin-groovy here)
                // where type checking is made important. this may not be a good generic way to handled this in the
                // long run, but for now we only have two GremlinScriptEngines to be concerned about so thus far it
                // presents no real pains. global bindings are applied automatically to the context via
                // AbstractScriptEngine.getScriptContext() - passing them again here to eval() will just make the
                // global bindings behave as engine bindings and then you get weird things happening (like local vars
                // becoming global).
                final Object initializedBindings = engine.eval(initScript);
                if (initializedBindings != null && initializedBindings instanceof Map)
                    ((Map<String,Object>) initializedBindings).forEach((k,v) -> put(k,v));
            } catch (Exception ex) {
                throw new IllegalStateException(ex);
            }
        });

        return engine;
    }
Registers a {@link GremlinScriptEngineFactory} to handle an extension. Overrides any such association found
using the discovery mechanism.

@param extension The extension type to be associated with the {@link GremlinScriptEngineFactory}
@param factory The class to associate with the given extension.
@throws NullPointerException if any of the parameters is null.
gremlin-core/src/main/java/org/apache/tinkerpop/gremlin/jsr223/DefaultGremlinScriptEngineManager.java