| /* |
| * 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.sample.filter; |
| |
| import static org.apache.sling.api.SlingConstants.ATTR_REQUEST_CONTENT; |
| import static org.apache.sling.api.wrappers.SlingRequestPaths.INCLUDE_REQUEST_URI; |
| |
| import java.io.IOException; |
| import java.io.OutputStream; |
| import java.io.OutputStreamWriter; |
| import java.io.PrintWriter; |
| import java.util.Iterator; |
| import java.util.Map; |
| import java.util.zip.DeflaterOutputStream; |
| import java.util.zip.GZIPOutputStream; |
| |
| import javax.servlet.Filter; |
| import javax.servlet.FilterChain; |
| import javax.servlet.FilterConfig; |
| import javax.servlet.ServletException; |
| import javax.servlet.ServletOutputStream; |
| import javax.servlet.ServletRequest; |
| import javax.servlet.ServletResponse; |
| |
| import org.apache.felix.scr.annotations.Component; |
| import org.apache.felix.scr.annotations.Properties; |
| import org.apache.felix.scr.annotations.Property; |
| import org.apache.felix.scr.annotations.Service; |
| import org.apache.sling.api.SlingHttpServletRequest; |
| import org.apache.sling.api.SlingHttpServletResponse; |
| import org.apache.sling.api.wrappers.SlingHttpServletResponseWrapper; |
| import org.apache.sling.engine.RequestUtil; |
| |
| /** |
| * The <code>ZipFilter</code> is an optionally installable global filter, |
| * which must be first in the filter chain for the compression functionality to |
| * be effective. |
| * <p> |
| * <b>NOTES</b> |
| * <p> |
| * When considering this filter, please keep the following notes in mind: |
| * <ul> |
| * <li>This filter serves as a model for implementing filters. |
| * <li>If using this filter it must be registered with a |
| * <code>filter.scope</code> parameter of <i><code>request</code></i> and |
| * a <code>filter.order</code> value which is smaller than any other |
| * <code>request</code> scope filter. |
| * <li>With small response data and fast networks this filter is not effective |
| * and in fact even slows response time down !! |
| * </ul> |
| * |
| */ |
| @Component(enabled=false) |
| @Service |
| @Properties({ |
| @Property(name="service.description", value="Sample Request Filter"), |
| @Property(name="filter.scope", value="request", propertyPrivate=true), |
| @Property(name="filter.order", intValue=-2147483648, propertyPrivate=true) |
| }) |
| public class ZipFilter implements Filter { |
| |
| /** |
| * Does nothing. This filter has not initialization requirement. |
| */ |
| public void init(FilterConfig filterConfig) { |
| } |
| |
| /** |
| * Checks the <code>Accept-Encoding</code> request header for its values. |
| * If <i>gzip</i> or <i>x-gzip</i> is desired, the response stream is |
| * compressed using the GZip algorithm. If <i>deflate</i> is desired, the |
| * response stream is compressed using the standard Zip Algorithm. Otherwise |
| * the response stream is transferred unmodified (<i>identity</i> |
| * encoding). |
| */ |
| public void doFilter(ServletRequest req, ServletResponse res, |
| FilterChain chain) throws IOException, ServletException { |
| |
| // request is Servlet API or Sling API included, do not filter |
| if (req.getAttribute(INCLUDE_REQUEST_URI) != null |
| || req.getAttribute(ATTR_REQUEST_CONTENT) != null) { |
| chain.doFilter(req, res); |
| return; |
| } |
| |
| SlingHttpServletRequest request = (SlingHttpServletRequest) req; |
| SlingHttpServletResponse response = (SlingHttpServletResponse) res; |
| |
| // check for compress header |
| String enc = request.getHeader("Accept-Encoding"); |
| if (enc == null || enc.length() == 0) { |
| // assume identity, no wrapping |
| chain.doFilter(request, response); |
| return; |
| } |
| |
| // get settings |
| enc = this.getEncoding(enc); |
| if ("gzip".equals(enc) || "x-gzip".equals(enc)) { |
| // mark the response encoded |
| response.setHeader("Content-Encoding", enc); |
| response = new DeflaterComponentResponse(response, false); |
| } else if ("deflate".equals(enc)) { |
| // mark the response encoded |
| response.setHeader("Content-Encoding", enc); |
| response = new DeflaterComponentResponse(response, true); |
| } |
| |
| // continue filtering |
| chain.doFilter(request, response); |
| |
| // finish the output |
| if (response instanceof DeflaterComponentResponse) { |
| ((DeflaterComponentResponse) response).finish(); |
| } |
| } |
| |
| /** |
| * Does nothing. This filter has not destroyal requirement. |
| */ |
| public void destroy() { |
| } |
| |
| // ---------- internal ----------------------------------------------------- |
| |
| /** |
| * Parses the <code>Accept-Encoding</code> header and returns the token |
| * with the highest <i>q</i> value or <code>null</code> if the header is |
| * empty. |
| */ |
| String getEncoding(String encodingHeader) { |
| String select = null; |
| double q = Double.MIN_VALUE; |
| Map<String, Double> settings = RequestUtil.parserAcceptHeader(encodingHeader); |
| for (Iterator<Map.Entry<String, Double>> ti = settings.entrySet().iterator(); ti.hasNext();) { |
| Map.Entry<String, Double> token = ti.next(); |
| Double qv = token.getValue(); |
| if (qv.doubleValue() > q) { |
| q = qv.doubleValue(); |
| select = token.getKey(); |
| } |
| } |
| return select; |
| } |
| |
| // ---------- internal classes --------------------------------------------- |
| |
| /** |
| * Compressing <code>ComponentResponseWrapper</code> which compresses the |
| * output stream using GZip or Zip algorithm depending on the constructor |
| * flag <code>deflate</code>. |
| * <p> |
| * This wrapper is only |
| */ |
| private static class DeflaterComponentResponse extends |
| SlingHttpServletResponseWrapper { |
| |
| private boolean deflate; |
| |
| private DeflaterServletOutputStream stream; |
| |
| private PrintWriter writer; |
| |
| public DeflaterComponentResponse(SlingHttpServletResponse delegatee, |
| boolean deflate) { |
| super(delegatee); |
| this.deflate = deflate; |
| } |
| |
| public ServletOutputStream getOutputStream() throws IOException { |
| if (this.stream != null) { |
| return this.stream; |
| } else if (this.writer != null) { |
| throw new IllegalStateException( |
| "Writer has already been obtained"); |
| } |
| |
| this.stream = new DeflaterServletOutputStream( |
| this.getDeflaterOutputStream()); |
| return this.stream; |
| } |
| |
| public PrintWriter getWriter() throws IOException { |
| if (this.writer != null) { |
| return this.writer; |
| } else if (this.stream != null) { |
| throw new IllegalStateException( |
| "OutputStream has already been obtained"); |
| } |
| |
| DeflaterOutputStream base = this.getDeflaterOutputStream(); |
| String enc = this.getCharacterEncoding(); |
| this.writer = new PrintWriter(new OutputStreamWriter(base, enc)); |
| return this.writer; |
| } |
| |
| public void flushBuffer() throws IOException { |
| if (this.writer != null) { |
| this.writer.flush(); |
| } else if (this.stream != null) { |
| this.stream.flush(); |
| } |
| |
| super.flushBuffer(); |
| } |
| |
| void finish() throws IOException { |
| if (this.writer != null) { |
| this.writer.flush(); |
| } else if (this.stream != null) { |
| this.stream.getDeflaterStream().finish(); |
| } |
| } |
| |
| protected DeflaterOutputStream getDeflaterOutputStream() |
| throws IOException { |
| OutputStream base = super.getOutputStream(); |
| return this.deflate |
| ? new DeflaterOutputStream(base) |
| : new GZIPOutputStream(base); |
| } |
| |
| private static class DeflaterServletOutputStream extends |
| ServletOutputStream { |
| |
| private DeflaterOutputStream deflaterStream; |
| |
| DeflaterServletOutputStream(DeflaterOutputStream deflaterStream) { |
| this.deflaterStream = deflaterStream; |
| } |
| |
| DeflaterOutputStream getDeflaterStream() { |
| return this.deflaterStream; |
| } |
| |
| public void write(int b) throws IOException { |
| this.deflaterStream.write(b); |
| } |
| |
| public void write(byte[] b) throws IOException { |
| this.deflaterStream.write(b); |
| } |
| |
| public void write(byte[] b, int off, int len) throws IOException { |
| this.deflaterStream.write(b, off, len); |
| } |
| |
| public void flush() throws IOException { |
| this.deflaterStream.flush(); |
| } |
| |
| public void close() throws IOException { |
| this.deflaterStream.close(); |
| } |
| } |
| } |
| } |