103928aee4356845252ac6b662d5c72c29903813eJake Slack//
203928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
303928aee4356845252ac6b662d5c72c29903813eJake Slack//  Copyright (c) 1995-2014 Mort Bay Consulting Pty. Ltd.
403928aee4356845252ac6b662d5c72c29903813eJake Slack//  ------------------------------------------------------------------------
503928aee4356845252ac6b662d5c72c29903813eJake Slack//  All rights reserved. This program and the accompanying materials
603928aee4356845252ac6b662d5c72c29903813eJake Slack//  are made available under the terms of the Eclipse Public License v1.0
703928aee4356845252ac6b662d5c72c29903813eJake Slack//  and Apache License v2.0 which accompanies this distribution.
803928aee4356845252ac6b662d5c72c29903813eJake Slack//
903928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Eclipse Public License is available at
1003928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.eclipse.org/legal/epl-v10.html
1103928aee4356845252ac6b662d5c72c29903813eJake Slack//
1203928aee4356845252ac6b662d5c72c29903813eJake Slack//      The Apache License v2.0 is available at
1303928aee4356845252ac6b662d5c72c29903813eJake Slack//      http://www.opensource.org/licenses/apache2.0.php
1403928aee4356845252ac6b662d5c72c29903813eJake Slack//
1503928aee4356845252ac6b662d5c72c29903813eJake Slack//  You may elect to redistribute this code under either of these licenses.
1603928aee4356845252ac6b662d5c72c29903813eJake Slack//  ========================================================================
1703928aee4356845252ac6b662d5c72c29903813eJake Slack//
1803928aee4356845252ac6b662d5c72c29903813eJake Slack
1903928aee4356845252ac6b662d5c72c29903813eJake Slackpackage org.eclipse.jetty.servlets;
2003928aee4356845252ac6b662d5c72c29903813eJake Slack
2103928aee4356845252ac6b662d5c72c29903813eJake Slackimport java.io.IOException;
2203928aee4356845252ac6b662d5c72c29903813eJake Slack
2303928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.RequestDispatcher;
2403928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.ServletContext;
2503928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.ServletException;
2603928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.http.HttpServlet;
2703928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.http.HttpServletRequest;
2803928aee4356845252ac6b662d5c72c29903813eJake Slackimport javax.servlet.http.HttpServletResponse;
2903928aee4356845252ac6b662d5c72c29903813eJake Slack
3003928aee4356845252ac6b662d5c72c29903813eJake Slack/* ------------------------------------------------------------ */
3103928aee4356845252ac6b662d5c72c29903813eJake Slack/** Concatenation Servlet
3203928aee4356845252ac6b662d5c72c29903813eJake Slack * This servlet may be used to concatenate multiple resources into
3303928aee4356845252ac6b662d5c72c29903813eJake Slack * a single response.  It is intended to be used to load multiple
3403928aee4356845252ac6b662d5c72c29903813eJake Slack * javascript or css files, but may be used for any content of the
3503928aee4356845252ac6b662d5c72c29903813eJake Slack * same mime type that can be meaningfully concatenated.
3603928aee4356845252ac6b662d5c72c29903813eJake Slack * <p>
3703928aee4356845252ac6b662d5c72c29903813eJake Slack * The servlet uses {@link RequestDispatcher#include(javax.servlet.ServletRequest, javax.servlet.ServletResponse)}
3803928aee4356845252ac6b662d5c72c29903813eJake Slack * to combine the requested content, so dynamically generated content
3903928aee4356845252ac6b662d5c72c29903813eJake Slack * may be combined (Eg engine.js for DWR).
4003928aee4356845252ac6b662d5c72c29903813eJake Slack * <p>
4103928aee4356845252ac6b662d5c72c29903813eJake Slack * The servlet uses parameter names of the query string as resource names
4203928aee4356845252ac6b662d5c72c29903813eJake Slack * relative to the context root.  So these script tags:
4303928aee4356845252ac6b662d5c72c29903813eJake Slack * <pre>
4403928aee4356845252ac6b662d5c72c29903813eJake Slack *  &lt;script type="text/javascript" src="../js/behaviour.js"&gt;&lt;/script&gt;
4503928aee4356845252ac6b662d5c72c29903813eJake Slack *  &lt;script type="text/javascript" src="../js/ajax.js&/chat/chat.js"&gt;&lt;/script&gt;
4603928aee4356845252ac6b662d5c72c29903813eJake Slack *  &lt;script type="text/javascript" src="../chat/chat.js"&gt;&lt;/script&gt;
4703928aee4356845252ac6b662d5c72c29903813eJake Slack * </pre> can be replaced with the single tag (with the ConcatServlet mapped to /concat):
4803928aee4356845252ac6b662d5c72c29903813eJake Slack * <pre>
4903928aee4356845252ac6b662d5c72c29903813eJake Slack *  &lt;script type="text/javascript" src="../concat?/js/behaviour.js&/js/ajax.js&/chat/chat.js"&gt;&lt;/script&gt;
5003928aee4356845252ac6b662d5c72c29903813eJake Slack * </pre>
5103928aee4356845252ac6b662d5c72c29903813eJake Slack * The {@link ServletContext#getMimeType(String)} method is used to determine the
5203928aee4356845252ac6b662d5c72c29903813eJake Slack * mime type of each resource.  If the types of all resources do not match, then a 415
5303928aee4356845252ac6b662d5c72c29903813eJake Slack * UNSUPPORTED_MEDIA_TYPE error is returned.
5403928aee4356845252ac6b662d5c72c29903813eJake Slack * <p>
5503928aee4356845252ac6b662d5c72c29903813eJake Slack * If the init parameter "development" is set to "true" then the servlet will run in
5603928aee4356845252ac6b662d5c72c29903813eJake Slack * development mode and the content will be concatenated on every request. Otherwise
5703928aee4356845252ac6b662d5c72c29903813eJake Slack * the init time of the servlet is used as the lastModifiedTime of the combined content
5803928aee4356845252ac6b662d5c72c29903813eJake Slack * and If-Modified-Since requests are handled with 206 NOT Modified responses if
5903928aee4356845252ac6b662d5c72c29903813eJake Slack * appropriate. This means that when not in development mode, the servlet must be
6003928aee4356845252ac6b662d5c72c29903813eJake Slack * restarted before changed content will be served.
6103928aee4356845252ac6b662d5c72c29903813eJake Slack *
6203928aee4356845252ac6b662d5c72c29903813eJake Slack *
6303928aee4356845252ac6b662d5c72c29903813eJake Slack *
6403928aee4356845252ac6b662d5c72c29903813eJake Slack */
6503928aee4356845252ac6b662d5c72c29903813eJake Slackpublic class ConcatServlet extends HttpServlet
6603928aee4356845252ac6b662d5c72c29903813eJake Slack{
6703928aee4356845252ac6b662d5c72c29903813eJake Slack    boolean _development;
6803928aee4356845252ac6b662d5c72c29903813eJake Slack    long _lastModified;
6903928aee4356845252ac6b662d5c72c29903813eJake Slack    ServletContext _context;
7003928aee4356845252ac6b662d5c72c29903813eJake Slack
7103928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
7203928aee4356845252ac6b662d5c72c29903813eJake Slack    public void init() throws ServletException
7303928aee4356845252ac6b662d5c72c29903813eJake Slack    {
7403928aee4356845252ac6b662d5c72c29903813eJake Slack        _lastModified=System.currentTimeMillis();
7503928aee4356845252ac6b662d5c72c29903813eJake Slack        _context=getServletContext();
7603928aee4356845252ac6b662d5c72c29903813eJake Slack        _development="true".equals(getInitParameter("development"));
7703928aee4356845252ac6b662d5c72c29903813eJake Slack    }
7803928aee4356845252ac6b662d5c72c29903813eJake Slack
7903928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
8003928aee4356845252ac6b662d5c72c29903813eJake Slack    /*
8103928aee4356845252ac6b662d5c72c29903813eJake Slack     * @return The start time of the servlet unless in development mode, in which case -1 is returned.
8203928aee4356845252ac6b662d5c72c29903813eJake Slack     */
8303928aee4356845252ac6b662d5c72c29903813eJake Slack    protected long getLastModified(HttpServletRequest req)
8403928aee4356845252ac6b662d5c72c29903813eJake Slack    {
8503928aee4356845252ac6b662d5c72c29903813eJake Slack        return _development?-1:_lastModified;
8603928aee4356845252ac6b662d5c72c29903813eJake Slack    }
8703928aee4356845252ac6b662d5c72c29903813eJake Slack
8803928aee4356845252ac6b662d5c72c29903813eJake Slack    /* ------------------------------------------------------------ */
8903928aee4356845252ac6b662d5c72c29903813eJake Slack    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException
9003928aee4356845252ac6b662d5c72c29903813eJake Slack    {
9103928aee4356845252ac6b662d5c72c29903813eJake Slack        String q=req.getQueryString();
9203928aee4356845252ac6b662d5c72c29903813eJake Slack        if (q==null)
9303928aee4356845252ac6b662d5c72c29903813eJake Slack        {
9403928aee4356845252ac6b662d5c72c29903813eJake Slack            resp.sendError(HttpServletResponse.SC_NO_CONTENT);
9503928aee4356845252ac6b662d5c72c29903813eJake Slack            return;
9603928aee4356845252ac6b662d5c72c29903813eJake Slack        }
9703928aee4356845252ac6b662d5c72c29903813eJake Slack
9803928aee4356845252ac6b662d5c72c29903813eJake Slack        String[] parts = q.split("\\&");
9903928aee4356845252ac6b662d5c72c29903813eJake Slack        String type=null;
10003928aee4356845252ac6b662d5c72c29903813eJake Slack        for (int i=0;i<parts.length;i++)
10103928aee4356845252ac6b662d5c72c29903813eJake Slack        {
10203928aee4356845252ac6b662d5c72c29903813eJake Slack            String t = _context.getMimeType(parts[i]);
10303928aee4356845252ac6b662d5c72c29903813eJake Slack            if (t!=null)
10403928aee4356845252ac6b662d5c72c29903813eJake Slack            {
10503928aee4356845252ac6b662d5c72c29903813eJake Slack                if (type==null)
10603928aee4356845252ac6b662d5c72c29903813eJake Slack                    type=t;
10703928aee4356845252ac6b662d5c72c29903813eJake Slack                else if (!type.equals(t))
10803928aee4356845252ac6b662d5c72c29903813eJake Slack                {
10903928aee4356845252ac6b662d5c72c29903813eJake Slack                    resp.sendError(HttpServletResponse.SC_UNSUPPORTED_MEDIA_TYPE);
11003928aee4356845252ac6b662d5c72c29903813eJake Slack                    return;
11103928aee4356845252ac6b662d5c72c29903813eJake Slack                }
11203928aee4356845252ac6b662d5c72c29903813eJake Slack            }
11303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
11403928aee4356845252ac6b662d5c72c29903813eJake Slack
11503928aee4356845252ac6b662d5c72c29903813eJake Slack        if (type!=null)
11603928aee4356845252ac6b662d5c72c29903813eJake Slack            resp.setContentType(type);
11703928aee4356845252ac6b662d5c72c29903813eJake Slack
11803928aee4356845252ac6b662d5c72c29903813eJake Slack        for (int i=0;i<parts.length;i++)
11903928aee4356845252ac6b662d5c72c29903813eJake Slack        {
12003928aee4356845252ac6b662d5c72c29903813eJake Slack            RequestDispatcher dispatcher=_context.getRequestDispatcher(parts[i]);
12103928aee4356845252ac6b662d5c72c29903813eJake Slack            if (dispatcher!=null)
12203928aee4356845252ac6b662d5c72c29903813eJake Slack                dispatcher.include(req,resp);
12303928aee4356845252ac6b662d5c72c29903813eJake Slack        }
12403928aee4356845252ac6b662d5c72c29903813eJake Slack    }
12503928aee4356845252ac6b662d5c72c29903813eJake Slack}
126