1 16 package org.apache.myfaces.component.html.util; 17 18 import java.io.IOException ; 19 import java.io.InputStream ; 20 import java.io.OutputStream ; 21 import java.io.PrintWriter ; 22 import java.text.ParseException ; 23 import java.text.SimpleDateFormat ; 24 import java.util.Calendar ; 25 import java.util.Date ; 26 import java.util.Iterator ; 27 import java.util.LinkedHashSet ; 28 import java.util.ResourceBundle ; 29 import java.util.Set ; 30 31 import javax.faces.context.FacesContext; 32 import javax.faces.context.ResponseWriter; 33 import javax.servlet.http.HttpServletRequest ; 34 import javax.servlet.http.HttpServletResponse ; 35 36 import org.apache.commons.logging.Log; 37 import org.apache.commons.logging.LogFactory; 38 import org.apache.myfaces.renderkit.html.HTML; 39 40 125 public class AddResource { 126 private static final Log log = LogFactory.getLog(AddResource.class); 127 128 private static final String COMPONENTS_PACKAGE = "org.apache.myfaces.custom."; 129 130 private static final String RESOURCE_VIRTUAL_PATH = "/faces/myFacesExtensionResource"; 131 132 private static final String ADDITIONAL_HEADER_INFO_REQUEST_ATTRUBITE_NAME = "myFacesHeaderResource2Render"; 133 134 136 139 public static void addJavaScriptHere(Class componentClass, String resourceFileName, FacesContext context) throws IOException { 140 ResponseWriter writer = context.getResponseWriter(); 141 142 writer.startElement(HTML.SCRIPT_ELEM,null); 143 writer.writeAttribute(HTML.SCRIPT_TYPE_ATTR,HTML.SCRIPT_TYPE_TEXT_JAVASCRIPT,null); 144 writer.writeURIAttribute(HTML.SRC_ATTR, 145 getResourceMappedPath(componentClass, resourceFileName, context), 146 null); 147 writer.endElement(HTML.SCRIPT_ELEM); 148 } 149 150 154 public static void addJavaScriptToHeader(Class componentClass, String resourceFileName, FacesContext context){ 155 addJavaScriptToHeader(componentClass, resourceFileName, false, context); 156 } 157 158 162 public static void addJavaScriptToHeader(Class componentClass, String resourceFileName, boolean defer, FacesContext context){ 163 AdditionalHeaderInfoToRender jsInfo = 164 new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_JS, componentClass, resourceFileName, defer); 165 addAdditionalHeaderInfoToRender(context, jsInfo ); 166 } 167 168 172 public static void addStyleSheet(Class componentClass, String resourceFileName, FacesContext context){ 173 AdditionalHeaderInfoToRender cssInfo = 174 new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_CSS, componentClass, resourceFileName); 175 addAdditionalHeaderInfoToRender(context, cssInfo ); 176 } 177 178 182 public static void addInlineStyleToHeader(String inlineStyle, FacesContext context){ 183 AdditionalHeaderInfoToRender cssInfo = 184 new AdditionalHeaderInfoToRender(AdditionalHeaderInfoToRender.TYPE_CSS_INLINE, inlineStyle); 185 addAdditionalHeaderInfoToRender(context, cssInfo ); 186 } 187 188 199 public static String getResourceMappedPath(Class componentClass, String resourceFileName, FacesContext context){ 200 HttpServletRequest request = null; 201 if( context != null ) 202 request = (HttpServletRequest )context.getExternalContext().getRequest(); 203 204 return getResourceMappedPath( 205 getComponentName(componentClass), 206 resourceFileName, 207 request); 208 } 209 210 private static String getResourceMappedPath(String componentName, String resourceFileName, HttpServletRequest request){ 211 String contextPath = ""; 212 if( request != null ) 213 contextPath = request.getContextPath(); 214 return contextPath+RESOURCE_VIRTUAL_PATH+"/"+componentName+'/'+getCacheKey()+'/'+resourceFileName; 215 } 216 217 private static long getCacheKey(){ 218 return getLastModified(); 219 } 220 221 private static Date lastModified = null; 222 private static long getLastModified(){ 223 if( lastModified == null ){ 224 final String format = "yyyy-MM-dd HH:mm:ss Z"; final String bundleName = AddResource.class.getName(); 226 ResourceBundle resources = ResourceBundle.getBundle( bundleName ); 227 String sLastModified = resources.getString("lastModified"); 228 try { 229 lastModified = new SimpleDateFormat (format).parse( sLastModified ); 230 } catch (ParseException e) { 231 lastModified = new Date (); 232 log.error("Unparsable lastModified : "+sLastModified); 233 } 234 } 235 236 return lastModified.getTime(); 237 } 238 239 public static boolean isResourceMappedPath(HttpServletRequest request){ 240 return request.getRequestURI().indexOf( RESOURCE_VIRTUAL_PATH ) != -1; 241 } 242 243 248 private static String [] getResourceInfoFromPath(HttpServletRequest request){ 249 String uri = request.getRequestURI(); 250 String componentNameStartsAfter = RESOURCE_VIRTUAL_PATH+'/'; 251 252 int posStartComponentName = uri.indexOf( componentNameStartsAfter )+componentNameStartsAfter.length(); 253 int posEndComponentName = uri.indexOf("/", posStartComponentName); 254 String componentName = uri.substring(posStartComponentName, posEndComponentName); 255 256 int posStartResourceFileName = uri.indexOf("/", posEndComponentName+1)+1; 258 259 String resourceFileName = uri.substring(posStartResourceFileName); 260 261 return new String []{componentName, resourceFileName}; 262 } 263 264 private static String getComponentName(Class componentClass){ 265 String name = componentClass.getName(); 266 if( ! name.startsWith(COMPONENTS_PACKAGE) ){ 267 log.error("getComponentName called for non extension component : "+name+"\n"+ 268 "For security reasons, only components member of the "+COMPONENTS_PACKAGE+" are allowed to add ressources."); 269 return null; 270 } 271 272 name = name.substring( COMPONENTS_PACKAGE.length() ); 273 274 return name; 275 } 276 277 static Class getComponent(String componentName) throws ClassNotFoundException { 278 return Class.forName( COMPONENTS_PACKAGE+componentName ); 279 } 280 281 static private InputStream getResource(String componentName, String resourceFileName) { 282 Class component; 283 try { 284 component = getComponent(componentName); 285 } catch (ClassNotFoundException e) { 286 log.error("Class not found for component "+componentName); 287 return null; 288 } 289 while( resourceFileName.startsWith(".") || resourceFileName.startsWith("/") || resourceFileName.startsWith("\\") ) 290 resourceFileName = resourceFileName.substring(1); 291 292 return component.getResourceAsStream( "resource/"+resourceFileName ); 293 } 294 295 static public void serveResource(HttpServletRequest request, HttpServletResponse response) throws IOException { 296 String [] resourceInfo = getResourceInfoFromPath(request); 297 String componentName = resourceInfo[0]; 298 String resourceFileName = resourceInfo[1]; 299 300 log.debug("Serving resource "+resourceFileName+" for component "+componentName); 301 302 String lcResourceFileName = resourceFileName.toLowerCase(); 303 304 if( lcResourceFileName.endsWith(".js") ) 305 response.setContentType("text/javascript"); 306 else if( lcResourceFileName.endsWith(".css") ) 307 response.setContentType("text/css"); 308 else if( lcResourceFileName.endsWith(".gif") ) 309 response.setContentType("image/gif"); 310 else if( lcResourceFileName.endsWith(".png") ) 311 response.setContentType("image/png"); 312 else if( lcResourceFileName.endsWith(".jpg") || lcResourceFileName.endsWith(".jpeg") ) 313 response.setContentType("image/jpeg"); 314 else if( lcResourceFileName.endsWith(".xml") || lcResourceFileName.endsWith(".xsl") ) 315 response.setContentType("text/xml"); 317 InputStream is = getResource(componentName, resourceFileName); 318 if( is == null ){ 319 throw new IOException ("Unable to find resource "+resourceFileName+" for component "+componentName+ 320 ". Check that this file is available in the classpath in sub-directory /resource of the component-directory."); 321 } 322 323 response.setDateHeader("Last-Modified", getLastModified()); 324 325 Calendar expires = Calendar.getInstance(); 328 expires.add(Calendar.DAY_OF_YEAR, 7); 329 response.setDateHeader("Expires", expires.getTimeInMillis()); 330 331 OutputStream os = response.getOutputStream(); 332 int c; 333 while ((c = is.read()) != -1) 334 os.write(c); 335 336 os.close(); 337 } 338 339 341 private static Set getAdditionalHeaderInfoToRender(HttpServletRequest request){ 342 Set set = (Set ) request.getAttribute(ADDITIONAL_HEADER_INFO_REQUEST_ATTRUBITE_NAME); 343 if( set == null ){ 344 set = new LinkedHashSet (); 345 request.setAttribute(ADDITIONAL_HEADER_INFO_REQUEST_ATTRUBITE_NAME, set); 346 } 347 348 return set; 349 } 350 351 private static void addAdditionalHeaderInfoToRender(FacesContext context, AdditionalHeaderInfoToRender info){ 352 HttpServletRequest request = (HttpServletRequest ) context.getExternalContext().getRequest(); 353 Set set = getAdditionalHeaderInfoToRender( request ); 354 set.add( info ); 355 } 356 357 static public boolean hasAdditionalHeaderInfoToRender(HttpServletRequest request){ 358 return request.getAttribute(ADDITIONAL_HEADER_INFO_REQUEST_ATTRUBITE_NAME) != null; 359 } 360 361 static public void writeWithFullHeader(HttpServletRequest request, 362 ExtensionsResponseWrapper responseWrapper, 363 HttpServletResponse response) throws IOException { 364 365 String originalResponse = responseWrapper.toString(); 366 367 boolean addHeaderTags = false; 369 int insertPosition = originalResponse.indexOf( "</head>" ); 370 371 if( insertPosition < 0 ){ 372 insertPosition = originalResponse.indexOf( "</HEAD>" ); 373 374 if( insertPosition < 0 ){ 375 insertPosition = originalResponse.indexOf( "<body" ); 376 addHeaderTags = true; 377 378 if( insertPosition < 0 ){ 379 insertPosition = originalResponse.indexOf( "<BODY" ); 380 addHeaderTags = true; 381 382 if( insertPosition < 0 ){ 383 String lowerCase = originalResponse.toLowerCase(response.getLocale()); 385 insertPosition = lowerCase.indexOf( "</head>" ); 386 387 if( insertPosition < 0 ){ 388 insertPosition = lowerCase.indexOf( "<body" ); 389 addHeaderTags = true; 390 } 391 } 392 } 393 } 394 395 if( insertPosition < 0 ){ 396 log.warn("Response has no <head> or <body> tag:\n"+originalResponse); 397 insertPosition = 0; 398 } 399 } 400 401 PrintWriter writer = response.getWriter(); 402 403 if( insertPosition > 0 ) 404 writer.write( originalResponse.substring(0, insertPosition) ); 405 if( addHeaderTags ) 406 writer.write("<head>"); 407 408 for(Iterator i = getAdditionalHeaderInfoToRender(request).iterator(); i.hasNext() ;){ 409 AdditionalHeaderInfoToRender headerInfo = (AdditionalHeaderInfoToRender) i.next(); 410 writer.write( headerInfo.getString(request) ); 411 } 412 413 if( addHeaderTags ) 414 writer.write("</head>"); 415 416 writer.write( originalResponse.substring(insertPosition) ); 417 } 418 419 private static class AdditionalHeaderInfoToRender{ 420 static final int TYPE_JS = 0; 421 static final int TYPE_CSS = 1; 422 static final int TYPE_CSS_INLINE = 2; 423 424 public int type; 425 public boolean deferJS = false; 426 public String componentName; 427 public String resourceFileName; 428 public String inlineText; 429 430 public AdditionalHeaderInfoToRender(int infoType, Class componentClass, String resourceFileName) { 431 this.type = infoType; 432 this.componentName = getComponentName(componentClass); 433 this.resourceFileName = resourceFileName; 434 } 435 436 public AdditionalHeaderInfoToRender(int infoType, Class componentClass, String resourceFileName, boolean defer) { 437 if( defer && infoType != TYPE_JS ) 438 log.error("Defer can only be used for scripts."); 439 this.type = infoType; 440 this.componentName = getComponentName(componentClass); 441 this.resourceFileName = resourceFileName; 442 this.deferJS = defer; 443 } 444 445 public AdditionalHeaderInfoToRender(int infoType, String inlineText) { 446 if( infoType != TYPE_CSS_INLINE ) 447 log.error("This constructor only supports TYPE_CSS_INLINE"); 448 this.type = infoType; 449 this.inlineText = inlineText; 450 } 451 452 public int hashCode() { 453 return (componentName+((char)7) 454 +resourceFileName+((char)7) 455 +(type+""+((char)7)) 456 +(inlineText+""+((char)7)) 457 +(deferJS+"")).hashCode(); 458 } 459 460 public boolean equals(Object obj) { 461 if( !(obj instanceof AdditionalHeaderInfoToRender) ) 462 return false; 463 AdditionalHeaderInfoToRender toCompare = (AdditionalHeaderInfoToRender) obj; 464 465 if( type != toCompare.type || deferJS != toCompare.deferJS ) 466 return false; 467 468 if( componentName == null ){ 469 if( toCompare.componentName != null ) 470 return false; 471 }else if( ! componentName.equals(toCompare.componentName) ) 472 return false; 473 474 if( resourceFileName == null ){ 475 if( toCompare.resourceFileName != null ) 476 return false; 477 }else if( ! resourceFileName.equals(toCompare.resourceFileName) ) 478 return false; 479 480 if( inlineText == null ) 481 return toCompare.inlineText == null; 482 483 return inlineText.equals(toCompare.inlineText); 484 } 485 486 public String getString(HttpServletRequest request){ 487 switch (type) { 488 case TYPE_JS: 489 return "<script " 490 +"src=\""+getResourceMappedPath(componentName, resourceFileName, request)+"\" " 491 +(deferJS ? "defer=\"true\" " : "") 492 +"type=\"text/javascript\"" 493 +">" 494 +"</script>\n"; 495 case TYPE_CSS: 496 return "<link rel=\"stylesheet\" " 497 +"href=\""+getResourceMappedPath(componentName, resourceFileName, request)+"\" " 498 +"type=\"text/css\"/>\n"; 499 case TYPE_CSS_INLINE: 500 return "<style type=\"text/css\">"+inlineText+"</style>\n"; 501 default: 502 log.warn("Unknown type:"+type); 503 return "<link HREF=\""+"\"/>\n"; 504 } 505 } 506 } 507 } | Popular Tags |