001 package org.apache.tapestry.contrib.services.impl; 002 003 import org.apache.commons.logging.Log; 004 import org.apache.hivemind.util.Defense; 005 import org.apache.tapestry.IRequestCycle; 006 import org.apache.tapestry.engine.IEngineService; 007 import org.apache.tapestry.engine.ILink; 008 import org.apache.tapestry.error.RequestExceptionReporter; 009 import org.apache.tapestry.services.LinkFactory; 010 import org.apache.tapestry.services.ServiceConstants; 011 import org.apache.tapestry.util.ContentType; 012 import org.apache.tapestry.web.WebRequest; 013 import org.apache.tapestry.web.WebResponse; 014 015 import javax.imageio.ImageIO; 016 import javax.servlet.http.HttpServletResponse; 017 import java.awt.image.BufferedImage; 018 import java.io.ByteArrayOutputStream; 019 import java.io.IOException; 020 import java.io.OutputStream; 021 import java.util.HashMap; 022 import java.util.Map; 023 024 /** 025 * Provides generated rounded corner images in a similar use / fashion as 026 * outlined here: <a href="http://xach.livejournal.com/95656.html">google's own cornershop</a>. 027 */ 028 public class RoundedCornerService implements IEngineService { 029 030 public static final String SERVICE_NAME = "rounded"; 031 032 public static final String PARM_COLOR = "c"; 033 public static final String PARM_BACKGROUND_COLOR = "bc"; 034 public static final String PARM_WIDTH = "w"; 035 public static final String PARM_HEIGHT = "h"; 036 public static final String PARM_ANGLE = "a"; 037 038 public static final String PARM_SHADOW_WIDTH ="sw"; 039 public static final String PARM_SHADOW_OPACITY ="o"; 040 public static final String PARM_SHADOW_SIDE = "s"; 041 042 public static final String PARM_WHOLE_SHADOW = "shadow"; 043 public static final String PARM_ARC_HEIGHT = "ah"; 044 public static final String PARM_ARC_WIDTH = "aw"; 045 046 private static final long MONTH_SECONDS = 60 * 60 * 24 * 30; 047 048 private static final long EXPIRES = System.currentTimeMillis() + 365 * 24 * 60 * 60 * 1000L; 049 050 private RequestExceptionReporter _exceptionReporter; 051 052 private LinkFactory _linkFactory; 053 054 private WebRequest _request; 055 056 private WebResponse _response; 057 058 private RoundedCornerGenerator _generator = new RoundedCornerGenerator(); 059 060 // holds pre-built binaries for previously generated colors 061 private Map _imageCache = new HashMap(); 062 063 private Log _log; 064 065 public ILink getLink(boolean post, Object parameter) 066 { 067 Defense.notNull(parameter, "parameter"); 068 Defense.isAssignable(parameter, Object[].class, "parameter"); 069 070 Object[] parms = (Object[]) parameter; 071 072 Map parameters = new HashMap(); 073 parameters.put(ServiceConstants.SERVICE, getName()); 074 parameters.put(ServiceConstants.PARAMETER, parms); 075 076 return _linkFactory.constructLink(this, post, parameters, false); 077 } 078 079 public void service(IRequestCycle cycle) 080 throws IOException 081 { 082 if (_request.getHeader("If-Modified-Since") != null) 083 { 084 _response.setStatus(HttpServletResponse.SC_NOT_MODIFIED); 085 return; 086 } 087 088 String color = cycle.getParameter(PARM_COLOR); 089 String bgColor = cycle.getParameter(PARM_BACKGROUND_COLOR); 090 int width = getIntParam(cycle.getParameter(PARM_WIDTH)); 091 int height = getIntParam(cycle.getParameter(PARM_HEIGHT)); 092 String angle = cycle.getParameter(PARM_ANGLE); 093 094 int shadowWidth = getIntParam(cycle.getParameter(PARM_SHADOW_WIDTH)); 095 float shadowOpacity = getFloatParam(cycle.getParameter(PARM_SHADOW_OPACITY)); 096 String side = cycle.getParameter(PARM_SHADOW_SIDE); 097 098 boolean wholeShadow = Boolean.valueOf(cycle.getParameter(PARM_WHOLE_SHADOW)).booleanValue(); 099 float arcWidth = getFloatParam(cycle.getParameter(PARM_ARC_WIDTH)); 100 float arcHeight = getFloatParam(cycle.getParameter(PARM_ARC_HEIGHT)); 101 102 String hashKey = color + bgColor + width + height + angle + shadowWidth + shadowOpacity + side + wholeShadow; 103 104 ByteArrayOutputStream bo = null; 105 106 try { 107 108 String type = (bgColor != null) ? "gif" : "png"; 109 110 byte[] data = (byte[])_imageCache.get(hashKey); 111 if (data != null) 112 { 113 writeImageResponse(data, type); 114 return; 115 } 116 117 BufferedImage image = null; 118 119 if (wholeShadow) 120 { 121 image = _generator.buildShadow(color, bgColor, width, height, arcWidth, arcHeight, shadowWidth, shadowOpacity); 122 } else if (side != null) 123 { 124 image = _generator.buildSideShadow(side, shadowWidth, shadowOpacity); 125 } else 126 { 127 image = _generator.buildCorner(color, bgColor, width, height, angle, shadowWidth, shadowOpacity); 128 } 129 130 bo = new ByteArrayOutputStream(); 131 132 ImageIO.write(image, type, bo); 133 134 data = bo.toByteArray(); 135 136 if (data == null || data.length < 1) 137 { 138 _log.error("Image generated had zero length byte array from parameters of:\n" 139 + "[color:" + color + ", bgColor:" + bgColor 140 + ", width:" + width + ", height:" + height 141 + ", angle:" + angle + ", shadowWidth:" + shadowWidth 142 + ", shadowOpacity:" + shadowOpacity + ", side:" + side 143 + ", wholeShadow: " + wholeShadow + ", arcWidth: " + arcWidth 144 + ", arcHeight:" + arcHeight + "\n image: " + image); 145 146 _response.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR); 147 return; 148 } 149 150 _imageCache.put(hashKey, data); 151 152 writeImageResponse(data, type); 153 154 } catch (IOException eof) 155 { 156 // ignored / expected exceptions happen when browser prematurely abandons connections - IE does this a lot 157 } catch (Throwable ex) { 158 159 ex.printStackTrace(); 160 _exceptionReporter.reportRequestException("Error creating image.", ex); 161 } finally { 162 try { 163 if (bo != null) { 164 bo.close(); 165 } 166 } catch (Throwable t) { 167 // ignore 168 } 169 170 } 171 } 172 173 void writeImageResponse(byte[] data, String type) 174 throws Exception 175 { 176 OutputStream os = null; 177 178 try { 179 _response.setDateHeader("Expires", EXPIRES); 180 _response.setHeader("Cache-Control", "public, max-age=" + (MONTH_SECONDS * 3)); 181 _response.setContentLength(data.length); 182 183 os = _response.getOutputStream(new ContentType("image/" + type)); 184 185 os.write(data); 186 187 } finally { 188 try { 189 if (os != null) { 190 os.flush(); 191 os.close(); 192 } 193 } catch (Throwable t) { 194 // ignore 195 } 196 } 197 } 198 199 private int getIntParam(String value) 200 { 201 if (value == null) 202 return -1; 203 204 return Integer.valueOf(value).intValue(); 205 } 206 207 private float getFloatParam(String value) 208 { 209 if (value == null) 210 return -1f; 211 212 return Float.valueOf(value).floatValue(); 213 } 214 215 public String getName() 216 { 217 return SERVICE_NAME; 218 } 219 220 /* Injected */ 221 public void setExceptionReporter(RequestExceptionReporter exceptionReporter) 222 { 223 _exceptionReporter = exceptionReporter; 224 } 225 226 /* Injected */ 227 public void setLinkFactory(LinkFactory linkFactory) 228 { 229 _linkFactory = linkFactory; 230 } 231 232 /* Injected */ 233 public void setRequest(WebRequest request) 234 { 235 _request = request; 236 } 237 238 /* Injected */ 239 public void setResponse(WebResponse response) 240 { 241 _response = response; 242 } 243 244 public void setLog(Log log) 245 { 246 _log = log; 247 } 248 }