View Javadoc

1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one
3    * or more contributor license agreements.  See the NOTICE file
4    * distributed with this work for additional information
5    * regarding copyright ownership.  The ASF licenses this file
6    * to you under the Apache License, Version 2.0 (the
7    * "License"); you may not use this file except in compliance
8    * with the License.  You may obtain a copy of the License at
9    *
10   *   http://www.apache.org/licenses/LICENSE-2.0
11   *
12   * Unless required by applicable law or agreed to in writing,
13   * software distributed under the License is distributed on an
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
15   * KIND, either express or implied.  See the License for the
16   * specific language governing permissions and limitations
17   * under the License.
18   */
19  
20  package org.apache.myfaces.component.html.util;
21  
22  import java.io.IOException;
23  import java.io.PrintWriter;
24  
25  import javax.servlet.ServletContext;
26  import javax.servlet.http.HttpServletRequest;
27  import javax.servlet.http.HttpServletResponse;
28  
29  import org.apache.commons.logging.Log;
30  import org.apache.commons.logging.LogFactory;
31  import org.apache.myfaces.renderkit.html.util.ResourceLoader;
32  
33  /**
34   * Serve component-specific resources that MUST be embedded in the HEAD
35   * of an html page.
36   * <p>
37   * Currently, there is only one case where resources <i>must</i> be in the
38   * document head: inline CSS or links to CSS stylesheets.
39   * <p>
40   * When using the StreamingAddResource class, a single link is output in the
41   * document HEAD for each page which embeds the name of this class in the url.
42   * This causes the browser to make a GET request to that link url when rendering
43   * the page; the tomahawk extensions filter sees the embedded ResourceLoader
44   * class name and creates an instance of this class to handle the request.
45   * <p>
46   * Note that for other resources the StreamingAddResources class generates urls
47   * that embed the standard MyFacesResourceLoader url, ie this class does not
48   * handle serving of resources other than the ones that MUST be in the head 
49   * section.
50   * <p>
51   * The url also embeds a "request id" which is unique for each page served. This
52   * id is then used as a key into a global-scoped cache. The data there was inserted
53   * during the previous request, and is deleted as soon as it is served up by this 
54   * class.
55   */
56  public class StreamingResourceLoader implements ResourceLoader
57  {
58      private final static Log log = LogFactory.getLog(StreamingResourceLoader.class);
59      
60      public StreamingResourceLoader()
61      {
62      }
63  
64      public void serveResource(ServletContext context, HttpServletRequest request, HttpServletResponse response, String resourceUri) throws IOException
65      {
66          // Technically here we should check for "/header.css" on the end of the url. But right now,
67          // this ResourceLoader only ever serves that one "virtual" css resource, so this request
68          // cannot be for anything else...
69  
70          int pos = resourceUri.indexOf("/");
71          Long requestId = new Long(Long.parseLong(resourceUri.substring(0, pos), 10));
72          
73          StreamingThreadManager manager = (StreamingThreadManager) context.getAttribute(StreamingThreadManager.KEY);
74          
75          StreamingThreadManager.HeaderInfoEntry headerInfoEntry = manager.getHeaderInfo(requestId);
76          if (headerInfoEntry == null)
77          {
78              log.warn("No streamable resources found for request: " + requestId + " resourceUri: " + resourceUri);
79              return;
80          }
81  
82          /*
83           * Ensure the browser doesn't cache this response. We never generate the same url twice
84           * (the requestId value embedded in the url changes for each request) so storing the
85           * response in a browser cache is just a waste; the cached data would never be used again.
86           */
87          response.setHeader("pragma", "no-cache");
88          response.setHeader("Cache-control", "no-cache, must-revalidate");
89          
90          try
91          {
92              PrintWriter pw = response.getWriter();
93              
94              StreamingAddResource.StreamablePositionedInfo positionedInfo;
95              try
96              {
97                  while ((positionedInfo = headerInfoEntry.fetchInfo()) != null)
98                  {
99                      positionedInfo.writePositionedInfo(response, pw);
100                     pw.flush();
101                 }
102                 pw.close();
103             }
104             catch (InterruptedException e)
105             {
106                 throw (IOException) new IOException().initCause(e);
107             }
108         }
109         finally
110         {
111             manager.removeHeaderInfo(requestId);
112         }
113     }
114 
115 }