1 13 package info.magnolia.cms.core; 14 15 import info.magnolia.cms.Aggregator; 16 import info.magnolia.cms.beans.runtime.Cache; 17 import info.magnolia.cms.security.SecureURI; 18 19 import java.io.File ; 20 import java.io.FileInputStream ; 21 import java.io.FileOutputStream ; 22 import java.io.IOException ; 23 import java.io.InputStream ; 24 import java.io.OutputStream ; 25 import java.net.URL ; 26 import java.net.URLConnection ; 27 import java.text.MessageFormat ; 28 import java.util.Date ; 29 import java.util.zip.GZIPOutputStream ; 30 31 import javax.servlet.ServletOutputStream ; 32 import javax.servlet.http.HttpServletRequest ; 33 import javax.servlet.http.HttpServletResponse ; 34 35 import org.apache.commons.lang.StringUtils; 36 import org.apache.log4j.Logger; 37 38 39 45 public class CacheHandler extends Thread { 46 47 50 public static final String CACHE_DIRECTORY = Path.getCacheDirectoryPath(); 51 52 55 private static final String DEFAULT_STORE = "/default"; 57 60 private static final String COMPRESSED_STORE = "/optimized"; 62 65 private static Logger log = Logger.getLogger(CacheHandler.class); 66 67 71 public static synchronized void cacheURI(HttpServletRequest request) { 72 73 if (Cache.isCached(request) || CacheHandler.hasRedirect(request)) { 74 return; 75 } 76 77 String uri = request.getRequestURI(); 78 79 if (uri.startsWith(request.getContextPath())) { 81 uri = StringUtils.substringAfter(uri, request.getContextPath()); 82 } 83 84 String repositoryURI = Path.getURI(request); 85 86 FileOutputStream out = null; 87 int size = 0; 88 int compressedSize = 0; 89 try { 90 if (!info.magnolia.cms.beans.config.Cache.isCacheable(request)) { 91 if (log.isDebugEnabled()) 92 log.debug("Request:" + request.getServletPath() + " not cacheable"); 93 return; 94 } 95 File file = getDestinationFile(repositoryURI, DEFAULT_STORE); 96 97 if (!file.exists()) { 98 file.createNewFile(); 99 out = new FileOutputStream (file); 100 boolean success = streamURI(uri, out, request); 101 out.flush(); 102 out.close(); 103 if (!success) { 104 file.delete(); 106 log.error(MessageFormat.format("NOT Caching uri [{0}] due to a previous error", new Object []{uri})); 108 } 109 else { 110 if (log.isInfoEnabled()) { 111 log.info(MessageFormat.format("Successfully cached URI [{0}]", new Object []{uri})); 113 } 114 } 115 } 116 117 size = (int) file.length(); 118 119 if (info.magnolia.cms.beans.config.Cache.applyCompression(Path.getExtension(request))) { 120 File gzipFile = getDestinationFile(repositoryURI, COMPRESSED_STORE); 121 if (!gzipFile.exists()) { 122 gzipFile.createNewFile(); 123 out = new FileOutputStream (gzipFile); 124 GZIPOutputStream gzipOut = new GZIPOutputStream (out); 125 streamURI(uri, gzipOut, request); 126 gzipOut.close(); 127 } 128 compressedSize = (new Long (gzipFile.length())).intValue(); 129 } 130 Cache.addToCachedURIList(repositoryURI, new Date ().getTime(), size, compressedSize); 131 } 132 catch (IOException e) { 133 log.error(e.getMessage(), e); 134 } 135 finally { 136 if (out != null) { 137 try { 138 out.close(); 139 } 140 catch (IOException e) { 141 } 143 } 144 Cache.removeFromInProcessURIList(repositoryURI); 145 } 146 } 147 148 153 private static boolean hasRedirect(HttpServletRequest request) { 154 Object obj = request.getAttribute(Aggregator.ACTPAGE); 155 if (obj == null) { 156 return false; } 158 Content page = (Content) obj; 159 160 if (StringUtils.isEmpty(page.getNodeData("redirectURL").getString())) { return false; 162 } 163 return true; 164 165 } 166 167 174 private static boolean streamURI(String uri, OutputStream out, HttpServletRequest request) { 175 176 String domain = info.magnolia.cms.beans.config.Cache.getDomain(); 177 178 if (StringUtils.isEmpty(domain)) { 179 domain = getAppURL(request); 180 } 181 182 try { 183 URL url = new URL (domain + uri); 184 if (log.isDebugEnabled()) { 185 log.debug("Streaming uri:" + url.toExternalForm()); } 187 URLConnection urlConnection = url.openConnection(); 188 if (SecureURI.isProtected(uri)) { 189 urlConnection.setRequestProperty("Authorization", request.getHeader("Authorization")); } 191 byte[] buffer = new byte[8192]; 192 int read = 0; 193 InputStream in = urlConnection.getInputStream(); 194 while ((read = in.read(buffer)) > 0) { 195 out.write(buffer, 0, read); 196 } 197 return true; 198 } 199 catch (IOException e) { 200 log.error(MessageFormat.format("Failed to stream [{0}] due to a {1}: {2}", new Object []{uri, e.getClass().getName(), e.getMessage()}), e); 202 } 203 204 return false; 205 } 206 207 212 private static String getAppURL(HttpServletRequest request) { 213 StringBuffer url = new StringBuffer (); 214 int port = request.getServerPort(); 215 if (port < 0) { 216 port = 80; } 218 String scheme = request.getScheme(); 219 url.append(scheme); 220 url.append("://"); url.append(request.getServerName()); 222 if ((scheme.equals("http") && (port != 80)) || (scheme.equals("https") && (port != 443))) { url.append(':'); 224 url.append(port); 225 } 226 String contextPath = request.getContextPath(); 227 if (!(contextPath.length() > 0 && contextPath.charAt(0) == '/')) { 228 url.append("/"); } 230 url.append(contextPath); 231 232 return url.toString(); 233 } 234 235 241 private static File getDestinationFile(String uri, String type) { 242 validatePath(CACHE_DIRECTORY); 243 validatePath(CACHE_DIRECTORY + type); 244 String [] items = uri.split("/"); StringBuffer buffer = new StringBuffer (); 246 int i = 0; 247 for (; i < (items.length - 1); i++) { 248 if (StringUtils.isEmpty(items[i])) { 249 continue; 250 } 251 buffer.append("/" + items[i]); validatePath(CACHE_DIRECTORY + type + buffer.toString()); 253 } 254 buffer.append("/" + items[i]); return (new File (CACHE_DIRECTORY + type + buffer.toString())); 256 } 257 258 262 public static void validatePath(String path) { 263 264 File file = new File (path); 265 if (!file.isDirectory()) { 266 if (!file.mkdir()) { 267 log.error("Can not create directory - " + path); } 269 } 270 } 271 272 280 public static boolean streamFromCache(HttpServletRequest request, HttpServletResponse response) { 281 282 287 if (request.getMethod().toLowerCase().equals("post") || !request.getParameterMap().isEmpty()) { return false; 290 } 291 292 boolean compress = canCompress(request); 293 FileInputStream fin = null; 294 try { 295 File file; 296 if (compress) { 297 file = new File (CACHE_DIRECTORY + COMPRESSED_STORE + Path.getURI(request)); 298 } 299 else { 300 file = new File (CACHE_DIRECTORY + DEFAULT_STORE + Path.getURI(request)); 301 } 302 if (!file.exists()) { 303 return false; 304 } 305 if (file.length() < 4) { 306 return false; 307 } 308 309 if (log.isDebugEnabled()) { 310 log.debug("Streaming from cache the file:" + file.getAbsolutePath()); } 312 313 fin = new FileInputStream (file); 314 if (compress) { 315 response.setContentLength(Cache.getCompressedSize(request)); 316 sendCompressed(fin, response); 317 } 318 else { 319 response.setContentLength(Cache.getSize(request)); 320 send(fin, response); 321 } 322 } 323 catch (IOException e) { 324 if (log.isDebugEnabled()) { 325 log.debug("Error while reading cache for " + e.getMessage(), e); } 327 } 328 finally { 329 if (fin != null) { 330 try { 331 fin.close(); 332 } 333 catch (IOException e) { 334 } 336 } 337 } 338 return true; 339 } 340 341 347 private static void sendCompressed(InputStream is, HttpServletResponse res) throws IOException { 348 res.setHeader("Content-Encoding", "gzip"); send(is, res); 350 } 351 352 358 private static void send(InputStream is, HttpServletResponse res) throws IOException { 359 ServletOutputStream os = res.getOutputStream(); 360 byte[] buffer = new byte[8192]; 361 int read = 0; 362 while ((read = is.read(buffer)) > 0) { 363 os.write(buffer, 0, read); 364 } 365 os.flush(); 366 os.close(); 367 } 368 369 374 private static boolean canCompress(HttpServletRequest request) { 375 if (!info.magnolia.cms.beans.config.Cache.applyCompression(Path.getExtension(request))) { 376 return false; 377 } 378 String encoding = request.getHeader("Accept-Encoding"); if (encoding != null) { 380 return StringUtils.contains(encoding.toLowerCase(), "gzip"); } 382 return false; 383 } 384 385 389 public static void flushResource(String uri) { 390 File file = new File (uri); 391 try { 392 if (file.isDirectory()) { 393 emptyDirectory(file); 394 file.delete(); 395 } 396 else { 397 if (log.isDebugEnabled()) { 398 log.debug("Flushing - " + uri); } 400 file.delete(); 401 Cache.removeFromCachedURIList(uri); 402 Cache.removeFromInProcessURIList(uri); 403 } 404 } 405 catch (Exception e) { 406 log.error("Failed to flush [" + uri + "]: " + e.getMessage(), e); } 408 } 409 410 414 private static void emptyDirectory(File directory) { 415 File [] children = directory.listFiles(); 416 if (children != null) { 418 for (int i = 0; i < children.length; i++) { 419 if (children[i] != null && children[i].isDirectory()) { 420 emptyDirectory(children[i]); 421 children[i].delete(); 422 } 423 else { 424 if (log.isDebugEnabled()) { 425 log.debug("Flushing - " + children[i].getPath()); } 427 String path = StringUtils.substringAfter(children[i].getPath(), Path.getCacheDirectoryPath()); 428 Cache.removeFromCachedURIList(path); 429 Cache.removeFromInProcessURIList(path); 430 children[i].delete(); 431 } 432 } 433 } 434 } 435 436 439 public static void flushCache() { 440 log.debug("Flushing entire cache"); try { 442 CacheHandler.flushResource(CACHE_DIRECTORY); 443 CacheHandler.validatePath(CACHE_DIRECTORY); 445 CacheHandler.validatePath(CACHE_DIRECTORY + DEFAULT_STORE); 446 CacheHandler.validatePath(CACHE_DIRECTORY + COMPRESSED_STORE); 447 448 Cache.clearCachedURIList(); 450 } 451 catch (Exception e) { 452 log.error(e.getMessage(), e); 453 } 454 } 455 } 456 | Popular Tags |