/* * Copyright 2000-2004 The Apache Software Foundation. * * Licensed 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.jetspeed.services.webpage; // java.io import java.io.IOException; import java.io.FileOutputStream; import java.io.InputStream; import java.io.ByteArrayOutputStream; // java.util import java.util.HashMap; // java.net import java.net.URL; import java.net.URLConnection; import java.net.HttpURLConnection; import java.net.MalformedURLException; import org.apache.log4j.Logger; /* * Implements the WebPage service's static resource cache, for optimizing access to * static resources from proxied hosts. Resources currently cached are read-only, static * resources such as images, style sheets and scripts. * */ public class WebPageCache { // The Cache private static HashMap cache = new HashMap(); // the log file singleton instance static Logger log = Logger.getLogger(WebPageCache.class); /** * Given a cacheable web resource, writes the content of that resource to the servlet * output stream. The first time that the resource is requested, the content is * fetched from the proxied host. From then on, the resource content is served up from * the cache. * * @param resource the resource that is being cached. * @param neid the network element id. * @param base the proxied host's base URL. * @param host the Proxy Server base URL. * @param data the RunData for the request. * @return boolean true if the resource was read from the cache, * false if read from the server. */ public static boolean getResourceFromCache(String resource, long sid, String base, String host, ProxyRunData data) { try { CachedResource cr = (CachedResource)cache.get(resource); if (null != cr) // is it in the cache... { // yes, return cached item byte[] bytes = cr.getContent(); data.getResponse().getOutputStream().write(bytes, 0, bytes.length); return true; } // not found in cache, so get it from proxied host URL baseURL = new URL(base); URL u = new URL(baseURL, resource); HttpURLConnection con = (HttpURLConnection)u.openConnection(); con.setDoInput(true); con.setAllowUserInteraction(false); int contentType = WebPageHelper.getContentType(con.getHeaderField("content-type"), resource); byte[] content; // get the proxied content, if its script, rewrite it if (WebPageHelper.CT_JS == contentType) content = rewriteScript(con, sid, host, data, resource, base); else content = getContentAndWrite(con, data); // create a new cached resource and put it in the cache cr = new CachedResource(contentType, content); cache.put(resource, cr); } catch (MalformedURLException ex) { log.error("CACHE URL EX:" + ex); return false; } catch (IOException ex) { log.error("CACHE IO:" + ex); return false; } return true; } /** * Determines if a resource is cacheable, dependent on the extension: * defined in CACHEABLE_RESOURCES (gif, jpeg, jpg, png, js, css) * * @param resource the resource that is being proxied. * @return boolean true if the resource is a cacheable, otherwise false. * */ public static String[] CACHEABLE_RESOURCES = { ".gif", ".jpeg", ".jpg", ".png", ".js", ".css" }; public static boolean isCacheableResource(String resource) { int pos = resource.lastIndexOf('.'); if (pos == -1) return false; if (resource.endsWith(".html")) return false; int length = resource.length(); if (pos >= length) return false; String ext = resource.substring(pos); for (int ix=0; ix < CACHEABLE_RESOURCES.length; ix++) { if (ext.equalsIgnoreCase(CACHEABLE_RESOURCES[ix])) { return true; } } return false; } /** * Retrieves the content from the proxied host for the requested. * Per cacheable resource, this is only called once. All further requests will * return the cached content. The content is immediately written to the servlet's * response output stream. * * @param con the HTTP connection to the proxied host. * @param response the servlet response. * @return byte[] the resource content, which will be stored in the cache. */ public static byte[] getContentAndWrite(URLConnection con, ProxyRunData data) throws IOException { int CAPACITY = 4096; InputStream is = con.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] bytes = new byte[CAPACITY]; int readCount = 0; while( ( readCount = is.read( bytes )) > 0 ) { buffer.write( bytes, 0, readCount); data.getResponse().getOutputStream().write(bytes, 0, readCount); } is.close(); return buffer.toByteArray(); } /** * Retrieves the script content from the proxied host for the requested. * Per cacheable resource, this is only called once. All further requests will * return the cached content. The content is first rewritten, rewriting all links * found in the script back to the Proxy server. Then, the content is immediately * written to the servlet's response output stream. * * @param con the HTTP connection to the proxied host. * @param response the servlet response. * @return byte[] the resource content, which will be stored in the cache. */ public static byte[] rewriteScript(URLConnection con, long sid, String host, ProxyRunData data, String resource, String base) throws IOException { int CAPACITY = 4096; Configuration config = Configuration.getInstance(); InputStream is = con.getInputStream(); ByteArrayOutputStream buffer = new ByteArrayOutputStream(); byte[] bytes = new byte[CAPACITY]; FileOutputStream fos = null; boolean logging = config.getEnableContentLog(); // log content to a file if enabled if (logging) { String fileName = data.getServlet().getServletContext().getRealPath( config.getLogLocation() ); fos = new FileOutputStream(fileName, true); WebPageHelper.writeHeader(fos, resource); } int readCount = 0; // read in the script while( ( readCount = is.read( bytes )) > 0 ) { buffer.write( bytes, 0, readCount); if (logging) fos.write( bytes, 0, readCount); } if (logging) fos.close(); is.close(); String script = buffer.toString(); if (sid == -1) { // FIXME: I seem to have lost this code.... // return HTMLRewriter.rewriteScript(script, resource, host, base); } return script.getBytes(); // FIXME: not rewriting scripts... // return Rewriter.rewriteScript(script, sid, proxyHost, base); } }