View Javadoc

1   /*
2    * Copyright 2000-2004 The Apache Software Foundation.
3    * 
4    * Licensed under the Apache License, Version 2.0 (the "License");
5    * you may not use this file except in compliance with the License.
6    * You may obtain a copy of the License at
7    * 
8    *      http://www.apache.org/licenses/LICENSE-2.0
9    * 
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS,
12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13   * See the License for the specific language governing permissions and
14   * limitations under the License.
15   */
16  
17  package org.apache.jetspeed.services.webpage;
18  
19  // java.io
20  import java.io.IOException;
21  import java.io.FileOutputStream;
22  import java.io.InputStream;
23  import java.io.ByteArrayOutputStream;
24  
25  // java.util
26  import java.util.HashMap;
27  
28  // java.net
29  import java.net.URL;
30  import java.net.URLConnection;
31  import java.net.HttpURLConnection;
32  import java.net.MalformedURLException;
33  
34  import org.apache.log4j.Logger;
35  
36  
37  /*
38   * Implements the WebPage service's static resource cache, for optimizing access to 
39   * static resources from proxied hosts. Resources currently cached are read-only, static
40   * resources such as images, style sheets and scripts.
41   *
42   */
43  
44  public class WebPageCache
45  {
46  
47      // The Cache
48      private static HashMap cache = new HashMap();
49  
50      // the log file singleton instance
51      static Logger log = Logger.getLogger(WebPageCache.class);
52  
53      /***
54        * Given a cacheable web resource, writes the content of that resource to the servlet
55        * output stream. The first time that the resource is requested, the content is
56        * fetched from the proxied host. From then on, the resource content is served up from
57        * the cache.
58        *
59        * @param resource the resource that is being cached.
60        * @param neid the network element id.
61        * @param base the proxied host's base URL.      
62        * @param host the Proxy Server base URL.
63        * @param data the RunData for the request.
64        * @return boolean true if the resource was read from the cache, 
65        *         false if read from the server.
66        */
67      public static boolean getResourceFromCache(String resource, 
68                                                 long sid, 
69                                                 String base,
70                                                 String host,
71                                                 ProxyRunData data)
72      {
73          try
74          {
75              CachedResource cr = (CachedResource)cache.get(resource);
76              if (null != cr) // is it in the cache...
77              {      
78                  // yes, return cached item
79                  byte[] bytes = cr.getContent();                
80                  data.getResponse().getOutputStream().write(bytes, 0, bytes.length);
81                  return true;
82              }
83          
84              // not found in cache, so get it from proxied host
85              URL baseURL = new URL(base);
86              URL u = new URL(baseURL, resource);
87              HttpURLConnection con = (HttpURLConnection)u.openConnection();  
88          
89              con.setDoInput(true);
90              con.setAllowUserInteraction(false);        
91          
92              int contentType = WebPageHelper.getContentType(con.getHeaderField("content-type"), resource);        
93          
94              byte[] content;
95  
96              // get the proxied content, if its script, rewrite it
97              if (WebPageHelper.CT_JS == contentType)
98                  content = rewriteScript(con, sid, host, data, resource, base);
99              else
100                 content = getContentAndWrite(con, data);
101         
102             // create a new cached resource and put it in the cache
103             cr = new CachedResource(contentType, content);
104             cache.put(resource, cr);
105 
106         }
107         catch (MalformedURLException ex)
108         {
109             log.error("CACHE URL EX:" +  ex);
110             return false;
111 
112         }
113         catch (IOException ex)
114         {
115             log.error("CACHE IO:" +  ex);
116             return false;
117         }
118         return true;
119     }
120 
121 
122    /***
123      * Determines if a resource is cacheable, dependent on the extension:
124      *   defined in CACHEABLE_RESOURCES (gif, jpeg, jpg, png, js, css)
125      *
126      * @param resource the resource that is being proxied.
127      * @return boolean true if the resource is a cacheable, otherwise false.
128      *
129      */
130     public static String[] CACHEABLE_RESOURCES = {
131         ".gif", ".jpeg", ".jpg", ".png", ".js", ".css" };
132 
133     public static boolean isCacheableResource(String resource)
134     {
135         int pos = resource.lastIndexOf('.');
136         if (pos == -1) 
137             return false;
138 
139         if (resource.endsWith(".html")) 
140             return false;
141 
142         int length = resource.length();
143         if (pos >= length) 
144             return false;
145 
146         String ext = resource.substring(pos);
147         for (int ix=0; ix < CACHEABLE_RESOURCES.length; ix++) {
148             if (ext.equalsIgnoreCase(CACHEABLE_RESOURCES[ix])) {
149                 return true;
150             }
151         }
152         return false;        
153     }
154 
155     
156     /***
157       * Retrieves the content from the proxied host for the requested.
158       * Per cacheable resource, this is only called once. All further requests will 
159       * return the cached content. The content is immediately written to the servlet's 
160       * response output stream.
161       *
162       * @param con the HTTP connection to the proxied host.
163       * @param response the servlet response.
164       * @return byte[] the resource content, which will be stored in the cache.
165       */
166     public static byte[] getContentAndWrite(URLConnection con,
167                                             ProxyRunData data) throws IOException 
168     {
169         int CAPACITY = 4096;
170     
171         InputStream is = con.getInputStream();
172         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
173         byte[] bytes = new byte[CAPACITY];
174     
175         int readCount = 0;
176         while( ( readCount = is.read( bytes )) > 0 ) {
177     
178             buffer.write( bytes, 0, readCount);
179             data.getResponse().getOutputStream().write(bytes, 0, readCount);
180         }        
181     
182         is.close();
183         return buffer.toByteArray();
184     }
185 
186     /***
187       * Retrieves the script content from the proxied host for the requested.
188       * Per cacheable resource, this is only called once. All further requests will 
189       * return the cached content. The content is first rewritten, rewriting all links
190       * found in the script back to the Proxy server. Then, the content is immediately 
191       * written to the servlet's response output stream.
192       *
193       * @param con the HTTP connection to the proxied host.
194       * @param response the servlet response.
195       * @return byte[] the resource content, which will be stored in the cache.
196       */    
197     public static byte[] rewriteScript(URLConnection con,
198                                        long sid,
199                                        String host,
200                                        ProxyRunData data,
201                                        String resource,
202                                        String base)
203                             throws IOException 
204     {
205         int CAPACITY = 4096;  
206 
207         Configuration config = Configuration.getInstance();
208         InputStream is = con.getInputStream();
209         ByteArrayOutputStream buffer = new ByteArrayOutputStream();
210         byte[] bytes = new byte[CAPACITY];
211     
212         FileOutputStream fos = null;
213         boolean logging = config.getEnableContentLog();
214 
215         // log content to a file if enabled
216         if (logging) 
217         {
218             String fileName = data.getServlet().getServletContext().getRealPath(
219                                     config.getLogLocation() );
220             fos = new FileOutputStream(fileName, true);
221             WebPageHelper.writeHeader(fos, resource);
222         }
223 
224         int readCount = 0;
225 
226         // read in the script 
227         while( ( readCount = is.read( bytes )) > 0 ) {  
228             
229             buffer.write( bytes, 0, readCount);
230             if (logging)
231                 fos.write( bytes, 0, readCount);
232         }        
233     
234         if (logging) 
235             fos.close();
236 
237         is.close();
238 
239 
240         String script = buffer.toString();
241 
242 
243         if (sid == -1)
244         {   // FIXME: I seem to have lost this code....
245             // return HTMLRewriter.rewriteScript(script, resource, host, base);
246         }
247         return script.getBytes();
248 
249         // FIXME:  not rewriting scripts...
250         // return Rewriter.rewriteScript(script, sid, proxyHost, base);
251 
252     }
253 
254 
255 }