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

groovy.servlet.ServletBinding Maven / Gradle / Ivy

There is a newer version: 5.0.0-alpha-11
Show newest version
/*
 *  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 groovy.servlet;

import groovy.lang.Binding;
import groovy.xml.MarkupBuilder;
import org.codehaus.groovy.GroovyBugError;
import org.codehaus.groovy.runtime.MethodClosure;

import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.WriteListener;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.io.Writer;
import java.lang.reflect.Constructor;
import java.util.Enumeration;
import java.util.LinkedHashMap;
import java.util.Locale;
import java.util.Map;

/**
 * Servlet-specific binding extension to lazy load the writer or the output
 * stream from the response.
 * 

*

Eager variables

*
    *
  • "request" : the HttpServletRequest object
  • *
  • "response" : the HttpServletRequest object
  • *
  • "context" : the ServletContext object
  • *
  • "application" : same as context
  • *
  • "session" : shorthand for request.getSession(false) - can be null!
  • *
  • "params" : map of all form parameters - can be empty
  • *
  • "headers" : map of all request header fields
  • *
*

*

Lazy variables

*
    *
  • "out" : response.getWriter()
  • *
  • "sout" : response.getOutputStream()
  • *
  • "html" : new MarkupBuilder(response.getWriter()) - expandEmptyElements flag is set to true
  • *
  • "json" : new JsonBuilder()
  • *
* As per the Servlet specification, a call to response.getWriter() should not be * done if a call to response.getOutputStream() has already occurred or the other way * around. You may wonder then how the above lazy variables can possibly be provided - since * setting them up would involve calling both of the above methods. The trick is catered for * behind the scenes using lazy variables. Lazy bound variables can be requested without side * effects; under the covers the writer and stream are wrapped. That means * response.getWriter() is never directly called until some output is done using * 'out' or 'html'. Once a write method call is done using either of these variable, then an attempt * to write using 'sout' will cause an IllegalStateException. Similarly, if a write method * call on 'sout' has been done already, then any further write method call on 'out' or 'html' will cause an * IllegalStateException. *

*

Reserved internal variable names (see "Methods" below)

*
    *
  • "forward"
  • *
  • "include"
  • *
  • "redirect"
  • *
* * If response.getWriter() is called directly (without using out), then a write method * call on 'sout' will not cause the IllegalStateException, but it will still be invalid. * It is the responsibility of the user of this class to not mix these different usage * styles. The same applies to calling response.getOutputStream() and using 'out' or 'html'. * *

Methods

*
    *
  • "forward(String path)" : request.getRequestDispatcher(path).forward(request, response)
  • *
  • "include(String path)" : request.getRequestDispatcher(path).include(request, response)
  • *
  • "redirect(String location)" : response.sendRedirect(location)
  • *
*/ public class ServletBinding extends Binding { /** * A OutputStream dummy that will throw a GroovyBugError for any * write method call to it. */ private static class InvalidOutputStream extends OutputStream { /** * Will always throw a GroovyBugError * @see java.io.OutputStream#write(int) */ @Override public void write(int b) { throw new GroovyBugError("Any write calls to this stream are invalid!"); } } /** * A class to manage the response output stream and writer. * If the stream have been 'used', then using the writer will cause * a IllegalStateException. If the writer have been 'used', then * using the stream will cause a IllegalStateException. 'used' means * any write method has been called. Simply requesting the objects will * not cause an exception. */ private static class ServletOutput { private final HttpServletResponse response; private ServletOutputStream outputStream; private PrintWriter writer; public ServletOutput(HttpServletResponse response) { this.response = response; } private ServletOutputStream getResponseStream() throws IOException { if (writer != null) throw new IllegalStateException("The variable 'out' or 'html' have been used already. Use either out/html or sout, not both."); if (outputStream == null) outputStream = response.getOutputStream(); return outputStream; } public ServletOutputStream getOutputStream() { return new ServletOutputStream() { @Override public boolean isReady() { try { return getResponseStream().isReady(); } catch (IOException e) { throw new RuntimeException(e); } } @Override public void setWriteListener(WriteListener writeListener) { try { getResponseStream().setWriteListener(writeListener); } catch (IOException e) { throw new RuntimeException(e); } } @Override public void write(int b) throws IOException { getResponseStream().write(b); } @Override public void close() throws IOException { getResponseStream().close(); } @Override public void flush() throws IOException { getResponseStream().flush(); } @Override public void write(byte[] b) throws IOException { getResponseStream().write(b); } @Override public void write(byte[] b, int off, int len) throws IOException { getResponseStream().write(b, off, len); } }; } private PrintWriter getResponseWriter() { if (outputStream != null) throw new IllegalStateException("The variable 'sout' have been used already. Use either out/html or sout, not both."); if (writer == null) { try { writer = response.getWriter(); } catch (IOException ioe) { writer = new PrintWriter(new ByteArrayOutputStream()); throw new IllegalStateException("unable to get response writer",ioe); } } return writer; } public PrintWriter getWriter() { return new PrintWriter(new InvalidOutputStream()) { @Override public boolean checkError() { return getResponseWriter().checkError(); } @Override public void close() { getResponseWriter().close(); } @Override public void flush() { getResponseWriter().flush(); } @Override public void write(char[] buf) { getResponseWriter().write(buf); } @Override public void write(char[] buf, int off, int len) { getResponseWriter().write(buf, off, len); } @Override public void write(int c) { getResponseWriter().write(c); } @Override public void write(String s, int off, int len) { getResponseWriter().write(s, off, len); } @Override public void println() { getResponseWriter().println(); } @Override public PrintWriter format(String format, Object... args) { getResponseWriter().format(format, args); return this; } @Override public PrintWriter format(Locale l, String format, Object... args) { getResponseWriter().format(l, format, args); return this; } }; } } private boolean initialized; /** * Initializes a servlet binding. * * @param request the HttpServletRequest object * @param response the HttpServletRequest object * @param context the ServletContext object */ public ServletBinding(HttpServletRequest request, HttpServletResponse response, ServletContext context) { /* * Bind the default variables. */ super.setVariable("request", request); super.setVariable("response", response); super.setVariable("context", context); super.setVariable("application", context); /* * Bind the HTTP session object - if there is one. * Note: we don't create one here! */ super.setVariable("session", request.getSession(false)); /* * Bind form parameter key-value hash map. * * If there are multiple, they are passed as an array. */ Map params = collectParams(request); super.setVariable("params", params); /* * Bind request header key-value hash map. */ Map headers = new LinkedHashMap(); for (Enumeration names = request.getHeaderNames(); names.hasMoreElements();) { String headerName = (String) names.nextElement(); String headerValue = request.getHeader(headerName); headers.put(headerName, headerValue); } super.setVariable("headers", headers); } @SuppressWarnings("unchecked") private Map collectParams(HttpServletRequest request) { Map params = new LinkedHashMap(); for (Enumeration names = request.getParameterNames(); names.hasMoreElements();) { String name = (String) names.nextElement(); if (!super.getVariables().containsKey(name)) { String[] values = request.getParameterValues(name); if (values.length == 1) { params.put(name, values[0]); } else { params.put(name, values); } } } return params; } @Override public void setVariable(String name, Object value) { lazyInit(); validateArgs(name, "Can't bind variable to"); excludeReservedName(name, "out"); excludeReservedName(name, "sout"); excludeReservedName(name, "html"); excludeReservedName(name, "json"); excludeReservedName(name, "forward"); excludeReservedName(name, "include"); excludeReservedName(name, "redirect"); super.setVariable(name, value); } @Override public Map getVariables() { lazyInit(); return super.getVariables(); } /** * @return a writer, an output stream, a markup builder or another requested object */ @Override public Object getVariable(String name) { lazyInit(); validateArgs(name, "No variable with"); return super.getVariable(name); } private void lazyInit() { if (initialized) return; initialized = true; HttpServletResponse response = (HttpServletResponse) super.getVariable("response"); ServletOutput output = new ServletOutput(response); super.setVariable("out", output.getWriter()); super.setVariable("sout", output.getOutputStream()); MarkupBuilder builder = new MarkupBuilder(output.getWriter()); builder.setExpandEmptyElements(true); super.setVariable("html", builder); try { // load using reflection to avoid needing a hard requirement on groovy-json for those not needing JSON support Class jsonBuilderClass = this.getClass().getClassLoader().loadClass("groovy.json.StreamingJsonBuilder"); Constructor writerConstructor = getWriterConstructor(jsonBuilderClass); super.setVariable("json", writerConstructor.newInstance(output.getWriter())); } catch (Throwable t) { t.printStackTrace(); } // bind forward method MethodClosure c = new MethodClosure(this, "forward"); super.setVariable("forward", c); // bind include method c = new MethodClosure(this, "include"); super.setVariable("include", c); // bind redirect method c = new MethodClosure(this, "redirect"); super.setVariable("redirect", c); } @SuppressWarnings("unchecked") private Constructor getWriterConstructor(Class jsonBuilderClass) throws NoSuchMethodException { return jsonBuilderClass.getConstructor(Writer.class); } private static void validateArgs(String name, String message) { if (name == null) { throw new IllegalArgumentException(message + " null key."); } if (name.length() == 0) { throw new IllegalArgumentException(message + " blank key name. [length=0]"); } } private static void excludeReservedName(String name, String reservedName) { if (reservedName.equals(name)) { throw new IllegalArgumentException("Can't bind variable to key named '" + name + "'."); } } public void forward(String path) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) super.getVariable("request"); HttpServletResponse response = (HttpServletResponse) super.getVariable("response"); RequestDispatcher dispatcher = request.getRequestDispatcher(path); dispatcher.forward(request, response); } public void include(String path) throws ServletException, IOException { HttpServletRequest request = (HttpServletRequest) super.getVariable("request"); HttpServletResponse response = (HttpServletResponse) super.getVariable("response"); RequestDispatcher dispatcher = request.getRequestDispatcher(path); dispatcher.include(request, response); } public void redirect(String location) throws IOException { HttpServletResponse response = (HttpServletResponse) super.getVariable("response"); response.sendRedirect(location); } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy