added script context for executing bundled scripts
diff --git a/main/java/org/apache/sling/scripting/resolver/BundledScriptFinder.java b/main/java/org/apache/sling/scripting/resolver/BundledScriptFinder.java
index 2de45bc..0b7d647 100644
--- a/main/java/org/apache/sling/scripting/resolver/BundledScriptFinder.java
+++ b/main/java/org/apache/sling/scripting/resolver/BundledScriptFinder.java
@@ -27,6 +27,16 @@
 @ProviderType
 public interface BundledScriptFinder {
 
+    /**
+     * Given a {@code request}, this method finds the most appropriate bundled script to execute, taking into account the {@code
+     * scriptExtensions} priority.
+     *
+     * @param request          the request for which the script has to be found
+     * @param bundle           the bundle in which the scripts for the current resource type are packed (see
+     *                         {@link SlingHttpServletRequest#getResource()})
+     * @return the script to execute
+     * @throws IOException if the script cannot be located
+     */
     Script getScript(SlingHttpServletRequest request, Bundle bundle) throws IOException;
 
 }
diff --git a/main/java/org/apache/sling/scripting/resolver/Script.java b/main/java/org/apache/sling/scripting/resolver/Script.java
index ab0c893..34ff3a3 100644
--- a/main/java/org/apache/sling/scripting/resolver/Script.java
+++ b/main/java/org/apache/sling/scripting/resolver/Script.java
@@ -18,21 +18,22 @@
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 package org.apache.sling.scripting.resolver;
 
-import java.io.Reader;
+import java.io.IOException;
+import java.net.URL;
 
-import javax.annotation.Nonnull;
-import javax.script.Bindings;
-import javax.script.ScriptException;
+import javax.script.ScriptEngine;
 
 import org.osgi.annotation.versioning.ProviderType;
 
 @ProviderType
 public interface Script {
 
-    Reader getSourceCodeReader();
+    String getSourceCode() throws IOException;
 
-    String getSourceCode();
+    ScriptEngine getScriptEngine();
 
-    Object eval(@Nonnull Bindings props) throws ScriptException;
+    URL getURL();
+
+    String getName();
 
 }
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptContext.java b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptContext.java
new file mode 100644
index 0000000..e6af123
--- /dev/null
+++ b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptContext.java
@@ -0,0 +1,156 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.resolver.internal;
+
+import java.io.Reader;
+import java.io.Writer;
+import java.util.Arrays;
+import java.util.List;
+
+import javax.script.Bindings;
+import javax.script.SimpleBindings;
+import javax.script.SimpleScriptContext;
+
+import org.apache.sling.api.scripting.SlingScriptConstants;
+
+class BundledScriptContext extends SimpleScriptContext {
+
+    private static final Integer[] SCOPES = {SlingScriptConstants.SLING_SCOPE, GLOBAL_SCOPE, ENGINE_SCOPE};
+
+    private Bindings globalScope;
+    private Bindings engineScope;
+
+    private Bindings slingScope = new SimpleBindings();
+
+    @Override
+    public void setBindings(final Bindings bindings, final int scope) {
+        switch (scope) {
+            case SlingScriptConstants.SLING_SCOPE : this.slingScope = bindings;
+                break;
+            case 100: if (bindings == null) throw new NullPointerException("Bindings for ENGINE scope is null");
+                this.engineScope = bindings;
+                break;
+            case 200: this.globalScope = bindings;
+                break;
+            default: throw new IllegalArgumentException("Invalid scope");
+        }
+    }
+
+    @Override
+    public Bindings getBindings(final int scope) {
+        switch (scope) {
+            case SlingScriptConstants.SLING_SCOPE : return slingScope;
+            case 100: return this.engineScope;
+            case 200: return this.globalScope;
+        }
+        throw new IllegalArgumentException("Invalid scope");
+    }
+
+    @Override
+    public void setAttribute(final String name, final Object value, final int scope) {
+        if (name == null) throw new IllegalArgumentException("Name is null");
+        final Bindings bindings = getBindings(scope);
+        if (bindings != null) {
+            bindings.put(name, value);
+        }
+    }
+
+    @Override
+    public Object getAttribute(final String name, final int scope) {
+        if (name == null) throw new IllegalArgumentException("Name is null");
+        final Bindings bindings = getBindings(scope);
+        if (bindings != null) {
+            return bindings.get(name);
+        }
+        return null;
+    }
+
+    @Override
+    public Object removeAttribute(final String name, final int scope) {
+        if (name == null) throw new IllegalArgumentException("Name is null");
+        final Bindings bindings = getBindings(scope);
+        if (bindings != null) {
+            return bindings.remove(name);
+        }
+        return null;
+    }
+
+    @Override
+    public Object getAttribute(String name) {
+        if (name == null) throw new IllegalArgumentException("Name is null");
+        for (final int scope : SCOPES) {
+            final Bindings bindings = getBindings(scope);
+            if ( bindings != null ) {
+                final Object o = bindings.get(name);
+                if ( o != null ) {
+                    return o;
+                }
+            }
+        }
+        return null;
+    }
+
+    @Override
+    public int getAttributesScope(String name) {
+        if (name == null) throw new IllegalArgumentException("Name is null");
+        for (final int scope : SCOPES) {
+            if ((getBindings(scope) != null) && (getBindings(scope).containsKey(name))) {
+                return scope;
+            }
+        }
+        return -1;
+    }
+
+    @Override
+    public List<Integer> getScopes() {
+        return Arrays.asList(SCOPES);
+    }
+
+    @Override
+    public Writer getWriter() {
+        return writer;
+    }
+
+    @Override
+    public Writer getErrorWriter() {
+        return errorWriter;
+    }
+
+    @Override
+    public void setWriter(Writer writer) {
+        this.writer = writer;
+    }
+
+    @Override
+    public void setErrorWriter(Writer writer) {
+        this.errorWriter = writer;
+    }
+
+    @Override
+    public Reader getReader() {
+        return reader;
+    }
+
+    @Override
+    public void setReader(Reader reader) {
+        this.reader = reader;
+    }
+
+
+}
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptFinderImpl.java b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptFinderImpl.java
index a1c1aad..b8ccb01 100644
--- a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptFinderImpl.java
+++ b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptFinderImpl.java
@@ -20,7 +20,6 @@
 
 import java.io.IOException;
 import java.net.URL;
-import java.nio.charset.StandardCharsets;
 import java.util.ArrayList;
 import java.util.Collections;
 import java.util.List;
@@ -28,7 +27,6 @@
 import javax.script.ScriptEngineFactory;
 import javax.script.ScriptEngineManager;
 
-import org.apache.commons.io.IOUtils;
 import org.apache.commons.lang3.StringUtils;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.scripting.resolver.BundledScriptFinder;
@@ -75,8 +73,7 @@
         for (String extension : scriptEngineExtensions) {
             URL bundledScriptURL = bundle.getEntry(BundledScriptTracker.NS_JAVAX_SCRIPT_CAPABILITY + "/" + scriptPath + "." + extension);
             if (bundledScriptURL != null) {
-                return new ScriptImpl(scriptEngineManager.getEngineByExtension(extension),
-                        IOUtils.toString(bundledScriptURL.openStream(), StandardCharsets.UTF_8));
+                return new ScriptImpl(bundledScriptURL, scriptEngineManager.getEngineByExtension(extension));
             }
         }
         return null;
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptServlet.java b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptServlet.java
index fd7a972..de727d6 100644
--- a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptServlet.java
+++ b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptServlet.java
@@ -19,42 +19,61 @@
 package org.apache.sling.scripting.resolver.internal;
 
 import java.io.IOException;
-import java.net.URL;
-import java.nio.charset.StandardCharsets;
-import java.util.ArrayList;
-import java.util.Collections;
-import java.util.List;
+import java.net.URI;
+import java.util.Map;
+import java.util.concurrent.ConcurrentHashMap;
 
-import javax.script.ScriptEngineFactory;
-import javax.script.ScriptEngineManager;
-import javax.servlet.ServletException;
+import javax.script.CompiledScript;
+import javax.script.ScriptContext;
+import javax.script.ScriptException;
 import javax.servlet.http.HttpServletResponse;
 
-import org.apache.commons.io.IOUtils;
-import org.apache.commons.lang3.StringUtils;
+import org.apache.sling.api.SlingConstants;
 import org.apache.sling.api.SlingHttpServletRequest;
 import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.scripting.ScriptEvaluationException;
 import org.apache.sling.api.servlets.SlingAllMethodsServlet;
 import org.apache.sling.scripting.resolver.BundledScriptFinder;
+import org.apache.sling.scripting.resolver.Script;
 import org.osgi.framework.Bundle;
 
 class BundledScriptServlet extends SlingAllMethodsServlet {
 
     private final Bundle m_bundle;
     private final BundledScriptFinder m_bundledScriptFinder;
+    private final ScriptContextProvider m_scriptContextProvider;
 
-    BundledScriptServlet(BundledScriptFinder bundledScriptFinder, Bundle bundle) {
+    private Map<URI, CompiledScript> compiledScriptsMap = new ConcurrentHashMap<>();
+
+    BundledScriptServlet(BundledScriptFinder bundledScriptFinder, Bundle bundle, ScriptContextProvider scriptContextProvider) {
         m_bundle = bundle;
         m_bundledScriptFinder = bundledScriptFinder;
+        m_scriptContextProvider = scriptContextProvider;
     }
 
     @Override
     protected void doGet(SlingHttpServletRequest request, SlingHttpServletResponse response) throws IOException {
-        String scriptSource = m_bundledScriptFinder.getScript(request, m_bundle).getSourceCode();
-        if (scriptSource != null) {
-            response.getWriter().append(scriptSource);
-            response.setContentType("text/plain");
-            response.flushBuffer();
+        Script script = m_bundledScriptFinder.getScript(request, m_bundle);
+        if (script != null) {
+            if (request.getAttribute(SlingConstants.ATTR_INCLUDE_SERVLET_PATH) == null) {
+                final String contentType = request.getResponseContentType();
+                if (contentType != null) {
+                    response.setContentType(contentType);
+
+                    // only set the character encoding for text/ content types
+                    // see SLING-679
+                    if (contentType.startsWith("text/")) {
+                        response.setCharacterEncoding("UTF-8");
+                    }
+                }
+            }
+            ScriptContext scriptContext = m_scriptContextProvider.prepareScriptContext(request, response, script);
+            try {
+                script.getScriptEngine().eval(script.getSourceCode(), scriptContext);
+            } catch (ScriptException e) {
+                Throwable cause = (e.getCause() == null) ? e : e.getCause();
+                throw new ScriptEvaluationException(script.getName(), e.getMessage(), cause);
+            }
         } else {
             response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
         }
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptTracker.java b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptTracker.java
index b7b2a37..ff86864 100644
--- a/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptTracker.java
+++ b/main/java/org/apache/sling/scripting/resolver/internal/BundledScriptTracker.java
@@ -56,6 +56,9 @@
     @Reference
     private BundledScriptFinder bundledScriptFinder;
 
+    @Reference
+    private ScriptContextProvider scriptContextProvider;
+
     public static final String NS_JAVAX_SCRIPT_CAPABILITY = "javax.script";
     public static final String AT_SLING_RESOURCE_TYPE = "sling.resourceType";
     public static final String AT_SLING_RESOURCE_TYPE_VERSION = "sling.resourceType.version";
@@ -96,7 +99,7 @@
             properties.put("sling.servlet.resourceTypes", resourceTypes);
             properties.put("sling.servlet.methods", new String[]{"TRACE", "OPTIONS", "GET", "HEAD", "POST", "PUT", "DELETE"});
             LOGGER.debug("Registering bundle {} for {} resourceTypes.", bundle.getSymbolicName(), Arrays.asList(resourceTypes));
-            return m_context.registerService(Servlet.class, new BundledScriptServlet(bundledScriptFinder, bundle), properties);
+            return m_context.registerService(Servlet.class, new BundledScriptServlet(bundledScriptFinder, bundle, scriptContextProvider), properties);
         } else {
             return null;
         }
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/LogWriter.java b/main/java/org/apache/sling/scripting/resolver/internal/LogWriter.java
new file mode 100644
index 0000000..aaf6925
--- /dev/null
+++ b/main/java/org/apache/sling/scripting/resolver/internal/LogWriter.java
@@ -0,0 +1,124 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.resolver.internal;
+
+import java.io.Writer;
+
+import org.slf4j.Logger;
+
+class LogWriter extends Writer {
+
+    /** The logger to which the error messages are written */
+    private Logger logger;
+
+    /**
+     * The internal buffer to gather message data until being flushed or a CR or
+     * LF is encountered in the message data.
+     */
+    private StringBuilder lineBuffer;
+
+    /**
+     * Creates a writer based on the given logger.
+     *
+     * @param logger the logger
+     */
+    public LogWriter(Logger logger) {
+        this.logger = logger;
+        this.lineBuffer = new StringBuilder();
+    }
+
+    /**
+     * Writes the character to the internal buffer unless the character is a CR
+     * or LF in which case the buffer is written to the logger as an error
+     * message.
+     */
+    @Override
+    public void write(int c) {
+        if (c == '\n' || c == '\r') {
+            flush();
+        } else {
+            synchronized (lineBuffer) {
+                lineBuffer.append((char) c);
+            }
+        }
+    }
+
+    /**
+     * Writes the indicated characters to the internal buffer, flushing the
+     * buffer on any occurrence of a CR of LF.
+     */
+    @Override
+    public void write(char[] cbuf, int off, int len) {
+        int i = off;
+        for (int n = 0; n < len; n++, i++) {
+            char c = cbuf[i];
+
+            // if CR/LF flush the line
+            if (c == '\n' || c == '\r') {
+
+                // append upto the CR/LF
+                int subLen = i - off;
+                if (subLen > 0) {
+                    synchronized (lineBuffer) {
+                        lineBuffer.append(cbuf, off, subLen);
+                    }
+                }
+
+                // and flush
+                flush();
+
+                // new offset is after the CR/LF
+                off = i + 1;
+            }
+        }
+
+        // remaining data in the buffer is just appended
+        if (off < i) {
+            synchronized (lineBuffer) {
+                lineBuffer.append(cbuf, off, i - off);
+            }
+        }
+    }
+
+    /**
+     * Writes any data conained in the buffer to the logger as an error message.
+     */
+    @Override
+    public void flush() {
+
+        String message;
+        synchronized (lineBuffer) {
+            if (lineBuffer.length() == 0) {
+                return;
+            }
+            message = lineBuffer.toString();
+            lineBuffer.setLength(0);
+        }
+
+        logger.error(message);
+    }
+
+    /**
+     * Just calls {@link #flush()}
+     */
+    @Override
+    public void close() {
+        flush();
+    }
+}
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/ProtectedBindings.java b/main/java/org/apache/sling/scripting/resolver/internal/ProtectedBindings.java
new file mode 100644
index 0000000..1f0ac33
--- /dev/null
+++ b/main/java/org/apache/sling/scripting/resolver/internal/ProtectedBindings.java
@@ -0,0 +1,145 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.resolver.internal;
+
+import java.util.Collection;
+import java.util.Collections;
+import java.util.Map;
+import java.util.Set;
+
+import javax.script.Bindings;
+
+class ProtectedBindings implements Bindings {
+
+    private final Bindings wrapped;
+    private final Set<String> protectedKeys;
+
+    public ProtectedBindings(Bindings wrapped, Set<String> protectedKeys) {
+        this.wrapped = wrapped;
+        this.protectedKeys = protectedKeys;
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if the key is protected
+     */
+    public Object put(String key, Object value) {
+        if (protectedKeys.contains(key)) {
+            throw new IllegalArgumentException(String.format("Key %s is protected.", key));
+        }
+        return wrapped.put(key, value);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public void putAll(Map<? extends String, ? extends Object> toMerge) {
+        for (Map.Entry<? extends String, ? extends Object> entry : toMerge.entrySet()) {
+            if (!protectedKeys.contains(entry.getKey())) {
+                wrapped.put(entry.getKey(), entry.getValue());
+            }
+        }
+    }
+
+    /**
+     * {@inheritDoc}
+     *
+     * @throws IllegalArgumentException if the key is protected
+     */
+    public Object remove(Object key) {
+        if (protectedKeys.contains(key)) {
+            throw new IllegalArgumentException(String.format("Key %s is protected.", key));
+        }
+        return wrapped.remove(key);
+    }
+
+    /**
+     * The clear operation is not supported.
+     */
+    public void clear() {
+        throw new UnsupportedOperationException("ProtectedBindings does not support clear()");
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean containsValue(Object value) {
+        return wrapped.containsValue(value);
+    }
+
+    /**
+     * Returns a Set view of the mappings contains in this map. The Set is
+     * unmodifiable.
+     *
+     * @return an unmodifiable Set view of the map
+     */
+    public Set<Entry<String, Object>> entrySet() {
+        return Collections.unmodifiableSet(wrapped.entrySet());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean isEmpty() {
+        return wrapped.isEmpty();
+    }
+
+    /**
+     * Returns a Set view of the keys contained in this map. The Set is
+     * unmodifiable.
+     *
+     * @return an unmodifiable Set view of the map's keys
+     */
+    public Set<String> keySet() {
+        return Collections.unmodifiableSet(wrapped.keySet());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public int size() {
+        return wrapped.size();
+    }
+
+    /**
+     * Returns a Collection view of the values contained in this map. The
+     * Collection is unmodifiable.
+     *
+     * @return an unmodifiable Collection view of the map's values
+     */
+    public Collection<Object> values() {
+        return Collections.unmodifiableCollection(wrapped.values());
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public boolean containsKey(Object key) {
+        return wrapped.containsKey(key);
+    }
+
+    /**
+     * {@inheritDoc}
+     */
+    public Object get(Object key) {
+        return wrapped.get(key);
+    }
+
+}
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/ScriptContextProvider.java b/main/java/org/apache/sling/scripting/resolver/internal/ScriptContextProvider.java
new file mode 100644
index 0000000..cd80fa3
--- /dev/null
+++ b/main/java/org/apache/sling/scripting/resolver/internal/ScriptContextProvider.java
@@ -0,0 +1,111 @@
+/*~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ ~ 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.resolver.internal;
+
+import java.io.IOException;
+import java.util.Arrays;
+import java.util.Collections;
+import java.util.HashSet;
+import java.util.Set;
+
+import javax.script.Bindings;
+import javax.script.ScriptContext;
+import javax.script.ScriptEngine;
+import javax.script.SimpleBindings;
+
+import org.apache.sling.api.SlingHttpServletRequest;
+import org.apache.sling.api.SlingHttpServletResponse;
+import org.apache.sling.api.scripting.SlingBindings;
+import org.apache.sling.api.scripting.SlingScriptConstants;
+import org.apache.sling.scripting.api.BindingsValuesProvider;
+import org.apache.sling.scripting.api.BindingsValuesProvidersByContext;
+import org.apache.sling.scripting.api.resource.ScriptingResourceResolverProvider;
+import org.apache.sling.scripting.core.ScriptHelper;
+import org.apache.sling.scripting.resolver.Script;
+import org.osgi.framework.BundleContext;
+import org.osgi.service.component.ComponentContext;
+import org.osgi.service.component.annotations.Activate;
+import org.osgi.service.component.annotations.Component;
+import org.osgi.service.component.annotations.Reference;
+import org.slf4j.Logger;
+import org.slf4j.LoggerFactory;
+
+@Component(
+        service = ScriptContextProvider.class
+)
+public class ScriptContextProvider {
+
+    private BundleContext m_bundleContext;
+
+    private static final Set<String> PROTECTED_BINDINGS = Collections.unmodifiableSet(new HashSet<>(Arrays.asList(
+            SlingBindings.REQUEST,
+            SlingBindings.RESPONSE,
+            SlingBindings.READER,
+            SlingBindings.RESOURCE,
+            SlingBindings.RESOLVER,
+            SlingBindings.OUT,
+            SlingBindings.LOG,
+            SlingBindings.SLING,
+            ScriptEngine.FILENAME
+    )));
+
+    @Reference
+    private BindingsValuesProvidersByContext bvpTracker;
+
+    @Reference
+    private ScriptingResourceResolverProvider scriptingResourceResolverProvider;
+
+    @Activate
+    private void activate(ComponentContext componentContext) {
+        m_bundleContext = componentContext.getBundleContext();
+    }
+
+    ScriptContext prepareScriptContext(SlingHttpServletRequest request, SlingHttpServletResponse response, Script script) throws IOException {
+        // prepare the SlingBindings
+        Bindings bindings = new SimpleBindings();
+        bindings.put(SlingBindings.REQUEST, request);
+        bindings.put(SlingBindings.RESPONSE, response);
+        bindings.put(SlingBindings.READER, request.getReader());
+        bindings.put(SlingBindings.RESOURCE, request.getResource());
+        bindings.put(SlingBindings.RESOLVER, request.getResource().getResourceResolver());
+        bindings.put(SlingBindings.OUT, response.getWriter());
+        Logger scriptLogger = LoggerFactory.getLogger(script.getName());
+        bindings.put(SlingBindings.LOG, scriptLogger);
+        bindings.put(SlingBindings.SLING, new ScriptHelper(m_bundleContext, null, request, response));
+        bindings.put(ScriptEngine.FILENAME, script.getName());
+
+        ProtectedBindings protectedBindings = new ProtectedBindings(bindings, PROTECTED_BINDINGS);
+        for (BindingsValuesProvider bindingsValuesProvider : bvpTracker.getBindingsValuesProviders(script.getScriptEngine().getFactory(),
+                BindingsValuesProvider.DEFAULT_CONTEXT)) {
+            bindingsValuesProvider.addBindings(protectedBindings);
+        }
+        ScriptContext scriptContext = new BundledScriptContext();
+        scriptContext.setBindings(new SimpleBindings(), SlingScriptConstants.SLING_SCOPE);
+        scriptContext.setAttribute(SlingScriptConstants.ATTR_SCRIPT_RESOURCE_RESOLVER, scriptingResourceResolverProvider
+                .getRequestScopedResourceResolver(), SlingScriptConstants.SLING_SCOPE);
+        scriptContext.setBindings(bindings, ScriptContext.ENGINE_SCOPE);
+        scriptContext.setWriter(response.getWriter());
+        scriptContext.setErrorWriter(new LogWriter(scriptLogger));
+        scriptContext.setReader(request.getReader());
+
+        return scriptContext;
+    }
+
+
+}
diff --git a/main/java/org/apache/sling/scripting/resolver/internal/ScriptImpl.java b/main/java/org/apache/sling/scripting/resolver/internal/ScriptImpl.java
index e7b3aac..52937f0 100644
--- a/main/java/org/apache/sling/scripting/resolver/internal/ScriptImpl.java
+++ b/main/java/org/apache/sling/scripting/resolver/internal/ScriptImpl.java
@@ -18,39 +18,46 @@
  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~*/
 package org.apache.sling.scripting.resolver.internal;
 
-import java.io.Reader;
-import java.io.StringReader;
+import java.io.IOException;
+import java.net.URL;
+import java.nio.charset.StandardCharsets;
 
-import javax.annotation.Nonnull;
-import javax.script.Bindings;
 import javax.script.ScriptEngine;
-import javax.script.ScriptException;
 
+import org.apache.commons.io.IOUtils;
 import org.apache.sling.scripting.resolver.Script;
 
-
 public class ScriptImpl implements Script {
 
+    private URL url;
     private ScriptEngine scriptEngine;
     private String sourceCode;
 
-    public ScriptImpl(ScriptEngine scriptEngine, String sourceCode) {
+    public ScriptImpl(URL url, ScriptEngine scriptEngine) {
+        this.url = url;
         this.scriptEngine = scriptEngine;
-        this.sourceCode = sourceCode;
     }
 
     @Override
-    public Reader getSourceCodeReader() {
-        return new StringReader(sourceCode);
-    }
-
-    @Override
-    public String getSourceCode() {
+    public String getSourceCode() throws IOException {
+        if (sourceCode == null) {
+            sourceCode = IOUtils.toString(url.openStream(), StandardCharsets.UTF_8);
+        }
         return sourceCode;
     }
 
     @Override
-    public Object eval(@Nonnull Bindings props) throws ScriptException {
-        return scriptEngine.eval(sourceCode, props);
+    public ScriptEngine getScriptEngine() {
+        return scriptEngine;
+    }
+
+    @Override
+    public URL getURL() {
+        return url;
+    }
+
+    @Override
+    public String getName() {
+        return url.getPath();
     }
 }
diff --git a/test/resources/findbugs-exclude.xml b/test/resources/findbugs-exclude.xml
new file mode 100644
index 0000000..24ab733
--- /dev/null
+++ b/test/resources/findbugs-exclude.xml
@@ -0,0 +1,23 @@
+<!--~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+  ~ 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.
+  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~-->
+<FindBugsFilter>
+    <Match>
+        <Bug pattern="SE_BAD_FIELD" />
+    </Match>
+</FindBugsFilter>