All Downloads are FREE. Search and download functionalities are using the official Maven repository.

org.kohsuke.stapler.jelly.DefaultScriptInvoker Maven / Gradle / Ivy

There is a newer version: 1.263
Show newest version
/*
 * Copyright (c) 2004-2010, Kohsuke Kawaguchi
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification, are permitted provided
 * that the following conditions are met:
 *
 *     * Redistributions of source code must retain the above copyright notice, this list of
 *       conditions and the following disclaimer.
 *     * Redistributions in binary form must reproduce the above copyright notice, this list of
 *       conditions and the following disclaimer in the documentation and/or other materials
 *       provided with the distribution.
 *
 * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS
 * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 * (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA,
 * OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER
 * IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF
 * THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.kohsuke.stapler.jelly;

import edu.umd.cs.findbugs.annotations.SuppressFBWarnings;
import org.apache.commons.jelly.JellyContext;
import org.kohsuke.stapler.Stapler;
import org.kohsuke.stapler.StaplerRequest;
import org.kohsuke.stapler.StaplerResponse;
import org.apache.commons.jelly.Script;
import org.apache.commons.jelly.JellyTagException;
import org.apache.commons.jelly.XMLOutput;
import org.apache.commons.jelly.XMLOutputFactory;
import org.apache.commons.jelly.impl.TagScript;

import javax.annotation.Nonnull;
import javax.servlet.ServletContext;
import java.io.BufferedOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.Writer;
import java.util.Enumeration;

/**
 * Standard implementation of {@link ScriptInvoker}.
 * 
 * @author Kohsuke Kawaguchi
 */
public class DefaultScriptInvoker implements ScriptInvoker, XMLOutputFactory {
    public void invokeScript(StaplerRequest req, StaplerResponse rsp, Script script, Object it) throws IOException, JellyTagException {
        XMLOutput xmlOutput = createXMLOutput(req, rsp, script, it);

        invokeScript(req,rsp,script,it,xmlOutput);
        
        xmlOutput.flush();
        xmlOutput.close();
    }

    public void invokeScript(StaplerRequest req, StaplerResponse rsp, Script script, Object it, XMLOutput out) throws IOException, JellyTagException {
        JellyContext context = createContext(req,rsp,script,it);
        exportVariables(req, rsp, script, it, context);

        script.run(context,out);
    }

    protected XMLOutput createXMLOutput(StaplerRequest req, StaplerResponse rsp, Script script, Object it) throws IOException {
        // TODO: make XMLOutput auto-close OutputStream to avoid leak
        String ct = rsp.getContentType();
        XMLOutput output;
        if (ct != null && !ct.startsWith("text/html")) {
            output = XMLOutput.createXMLOutput(createOutputStream(req, rsp, script, it));
        } else {
            output = HTMLWriterOutput.create(createOutputStream(req, rsp, script, it));

        }
        return output;
    }

    private boolean doCompression(Script script) {
        if (COMPRESS_BY_DEFAULT)    return true;
        if (script instanceof TagScript) {
            TagScript ts = (TagScript) script;
            if(ts.getLocalName().equals("compress"))
                return true;
        }
        return false;
    }

    private interface OutputStreamSupplier {
        @Nonnull OutputStream get() throws IOException;
    }

    private static class LazyOutputStreamSupplier implements OutputStreamSupplier {
        private final OutputStreamSupplier supplier;
        private volatile OutputStream out;

        private LazyOutputStreamSupplier(OutputStreamSupplier supplier) {
            this.supplier = supplier;
        }

        @Override
        @Nonnull
        public OutputStream get() throws IOException {
            if (out == null) {
                synchronized (this) {
                    if (out == null) {
                        out = supplier.get();
                    }
                }
            }
            return out;
        }
    }

    protected OutputStream createOutputStream(StaplerRequest req, StaplerResponse rsp, Script script, Object it) throws IOException {
        OutputStreamSupplier out = new LazyOutputStreamSupplier(() -> {
            req.getWebApp().getDispatchValidator().requireDispatchAllowed(req, rsp);
            return doCompression(script) ? rsp.getCompressedOutputStream(req) : new BufferedOutputStream(rsp.getOutputStream());
        });
        return new OutputStream() {
            @Override
            public void write(int b) throws IOException {
                out.get().write(b);
            }

            @Override
            public void write(byte[] b) throws IOException {
                out.get().write(b);
            }

            @Override
            public void write(byte[] b, int off, int len) throws IOException {
                out.get().write(b, off, len);
            }

            @Override
            public void flush() throws IOException {
                // flushing ServletOutputStream causes Tomcat to
                // send out headers, making it impossible to set contentType from the script.
                // so don't let Jelly flush.
            }

            @Override
            public void close() throws IOException {
                out.get().close();
            }
        };
    }

    protected void exportVariables(StaplerRequest req, StaplerResponse rsp, Script script, Object it, JellyContext context) {
        Enumeration en = req.getAttributeNames();
        // expose request attributes, just like JSP
        while (en.hasMoreElements()) {
            String name = (String) en.nextElement();
            context.setVariable(name,req.getAttribute(name));
        }

        context.setVariable("request",req);
        context.setVariable("response",rsp);
        context.setVariable("it",it);
        ServletContext servletContext = req.getServletContext();
        context.setVariable("servletContext",servletContext);
        context.setVariable("app",servletContext.getAttribute("app"));
        // property bag to store request scope variables
        context.setVariable("requestScope",context.getVariables());
        // this variable is needed to make "jelly:fmt" taglib work correctly
        context.setVariable("org.apache.commons.jelly.tags.fmt.locale",req.getLocale());
    }

    protected JellyContext createContext(final StaplerRequest req, StaplerResponse rsp, Script script, Object it) {
        CustomJellyContext context = new CustomJellyContext();
        // let Jelly see the whole classes
        context.setClassLoader(req.getWebApp().getClassLoader());
        // so TagScript.getBodyText() will use HTMLWriterOutput
        context.setVariable(XMLOutputFactory.class.getName(), this);
        return context;
    }

    public XMLOutput createXMLOutput(Writer writer, boolean escapeText) {
        StaplerResponse rsp = Stapler.getCurrentResponse();
        String ct = rsp!=null ? rsp.getContentType() : "?";
        if (ct != null && !ct.startsWith("text/html"))
            return XMLOutput.createXMLOutput(writer, escapeText);
        return HTMLWriterOutput.create(writer, escapeText);
    }

    /**
     * Whether gzip compression of the dynamic content is enabled by default or not.
     *
     * 

* For non-trivial web applications, where the performance matters, it is normally a good trade-off to spend * a bit of CPU cycles to compress data. This is because: * *

    *
  • CPU is already 1 or 2 order of magnitude faster than RAM and network. *
  • CPU is getting faster than any other components, such as RAM and network. *
  • Because of the TCP window slow start, on a large latency network, compression makes difference in * the order of 100ms to 1sec to the completion of a request by saving multiple roundtrips. *
* * Stuff rendered by Jelly is predominantly text, so the compression would work well. * * @see Latency Trumps All */ @SuppressFBWarnings(value = "MS_SHOULD_BE_FINAL", justification = "Legacy switch.") public static boolean COMPRESS_BY_DEFAULT = Boolean.parseBoolean(System.getProperty(DefaultScriptInvoker.class.getName()+".compress","true")); }




© 2015 - 2025 Weber Informatics LLC | Privacy Policy