<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6064095763580990</id><updated>2012-01-24T22:34:34.970-07:00</updated><category term='CXF'/><category term='AOP'/><category term='logging'/><category term='javascript'/><category term='Selenium'/><category term='maven'/><category term='myBatis'/><category term='design-patterns'/><category term='HTTP'/><category term='firefox'/><category term='PesterCat'/><category term='Ivy'/><category term='testng'/><category term='agile'/><category term='jetty'/><category term='servlet'/><category term='windows'/><category term='iBatis'/><category term='JMS'/><category term='SSL'/><category term='JUnit'/><category term='Spring'/><category term='Apache'/><category term='JSON'/><category term='ACID'/><category term='jmx'/><category term='database'/><category term='utility'/><category term='dependency injection'/><category term='acegi'/><category term='JBoss'/><category term='snippet'/><category term='soap'/><category term='cygwin'/><category term='REST'/><category term='process'/><category term='IceFaces'/><category term='webservices'/><category term='security'/><category term='ActiveMQ'/><category term='JSTL'/><category term='portfolio web-design'/><category term='BASE'/><category term='intellij'/><category term='Java'/><category term='NetBeans'/><category term='concurrency'/><category term='LDAP'/><category term='log4j'/><category term='observer'/><category term='Tellurium'/><category term='unix'/><category term='generics'/><category term='GWT RPC'/><category term='html'/><category term='design'/><category term='JSF'/><category term='caching'/><category term='testing'/><category term='cheatsheet'/><category term='Facelets'/><title type='text'>Perimeter Sweep</title><subtitle type='html'>Software Development: Tips and How-to's</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.temposwc.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>83</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4602167745541340761</id><published>2011-11-16T09:55:00.005-07:00</published><updated>2011-11-16T10:06:15.634-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='caching'/><category scheme='http://www.blogger.com/atom/ns#' term='HTTP'/><title type='text'>HTTP Caching 101</title><content type='html'>&lt;span class="Apple-style-span" style="color: #333333; font-family: Arial, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 17px;"&gt;This captures a basic learning curve around HTTP Caching, actually just a selected subset of a rather large topic. Another post driven by laziness - written in terse talking-point style, with most information either paraphrased or copied explicitly from the resources listed at end - with a touch of editorializing on my part. My thanks to those authors upfront.&lt;/span&gt;&lt;br /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;/div&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6064095763580990" name="HTTPCaching-Overview"&gt;&lt;/a&gt;Overview&lt;/h4&gt;&lt;hr style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; color: #cccccc; height: 0px;" /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Benefits&lt;/b&gt;: save bandwidth, reduce latency, lighten server load&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Types&lt;/b&gt;&amp;nbsp;of caches (server's responses can be cached by these)&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;gateway cache (shared): middle-man, facade in front of a server (for any client). This caches responses from one server for many clients.&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;browser cache (private): for BACK button, images, etc. This caches responses from many servers for one particular client.&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;proxy cache (shared): forward-facing towards web, client-side. This caches responses from many servers for many clients.&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Here's some decent&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.slideshare.net/rtomayko/https-bestkept-secret-caching" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;slideware&lt;/a&gt;&lt;/span&gt;&amp;nbsp;with boxes and lines illustrating the above, and with some sequence diagrams illustrating the protocols discussed below.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Mechanisms&lt;/b&gt;&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Expiration, Validation and Invalidation&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;note that caching is applied only to GET and HEAD&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;...this is yet another reason not to tunnel operations thru GET, etc abuse - requests won't make it to server w/proxy cache in play&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;and, another reason to use all verbs as intended: POST, PUT and DELETE&amp;nbsp;&lt;em&gt;should&lt;/em&gt;&amp;nbsp;not ever be cached&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;in fact, POST, PUT and DELETE&amp;nbsp;&lt;em&gt;should&lt;/em&gt;&amp;nbsp;result in cached content being&amp;nbsp;&lt;em&gt;invalidated&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Pragma&lt;/b&gt;: no-cache - don't bother;&amp;nbsp;from&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://palisade.plynt.com/issues/2008Jul/cache-control-attributes/" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://palisade.plynt.com/issues/2008Jul/cache-control-attributes/&lt;/a&gt;&lt;/span&gt;:&lt;/div&gt;&lt;div class="panel" style="background-attachment: initial; background-clip: initial; background-color: #f0f0f0; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(187, 187, 187); border-bottom-left-radius: 5px 5px; border-bottom-right-radius: 5px 5px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-top-style: solid; border-top-width: 1px; color: black; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: hidden; overflow-y: hidden; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div class="panelContent" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 0.95em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 10px; padding-right: 10px; padding-top: 0px; text-align: left;"&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left;"&gt;“pragma: no-cache” is linked to requests, and not responses. The RFC did not specify the behavior of this directive for&amp;nbsp;responses. Hence, this directive does NOT instruct the browser not to cache a page. We often see this tag being misused&amp;nbsp;when a page is served to the browser. Developers mistakenly set this directive expecting that the page will not be cached&amp;nbsp;on the browser.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span class="Apple-style-span" style="color: #333333; font-size: 10pt; font-weight: normal; line-height: 17px;"&gt;The sections of the HTTP 1.1 spec (&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.w3.org/Protocols/rfc2616/rfc2616.html" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.w3.org/Protocols/rfc2616/rfc2616.html&lt;/a&gt;&lt;/span&gt;) that discuss caches include 13, 14.9, 14.19, 14.21, and 14.24-29.&lt;/span&gt;&lt;/h4&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6064095763580990" name="HTTPCaching-TargetProtocols"&gt;&lt;/a&gt;Cache Control&lt;/h4&gt;&lt;hr style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; color: #cccccc; height: 0px;" /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Here's a subset of the cache-control mechanism to be discussed here:&lt;/div&gt;&lt;div class="code panel" style="background-attachment: initial; background-clip: initial; background-color: white; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(187, 187, 187); border-bottom-left-radius: 5px 5px; border-bottom-right-radius: 5px 5px; border-bottom-style: dashed; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: dashed; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: dashed; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-top-style: dashed; border-top-width: 1px; color: black; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: auto; overflow-y: auto; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div class="codeContent panelContent" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 0.95em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 10px; padding-right: 10px; padding-top: 0px; text-align: left;"&gt;&lt;div&gt;&lt;div class="syntaxhighlighter nogutter  java" id="highlighter_264513" style="background-color: white !important; font-size: 1em !important; margin-bottom: 1em !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 1em !important; overflow-x: auto !important; overflow-y: auto !important; position: relative !important; width: 923px;"&gt;&lt;table border="0" cellpadding="0" cellspacing="0" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; clear: left; color: #333333; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: 923px;"&gt;&lt;tbody style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;tr style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: #333333; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;&lt;td class="code" style="background-attachment: initial !important; background-clip: initial !important; background-color: white; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-style: dashed; border-bottom-width: 0px !important; border-color: initial !important; border-left-style: dashed; border-left-width: 0px !important; border-right-style: dashed; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-style: dashed; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: #333333; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: 923px;"&gt;&lt;div class="container" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 0px !important; padding-top: 0px !important; position: relative !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;" title="Hint: double-click to select code"&gt;&lt;div class="line number1 index0 alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;non-cacheable:&lt;/code&gt;&lt;/div&gt;&lt;div class="line number2 index1 alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;HTTP/&lt;/code&gt;&lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;1.1&lt;/code&gt; &lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;200&lt;/code&gt; &lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;OK&lt;/code&gt;&lt;/div&gt;&lt;div class="line number3 index2 alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Cache-Control: no-cache,no-store&lt;/code&gt;&lt;/div&gt;&lt;div class="line number4 index3 alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;/div&gt;&lt;div class="line number5 index4 alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;user-specific &lt;/code&gt;&lt;code class="java keyword" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(127, 0, 85) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: bold !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;private&lt;/code&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;:&lt;/code&gt;&lt;/div&gt;&lt;div class="line number6 index5 alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;HTTP/&lt;/code&gt;&lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;1.1&lt;/code&gt; &lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;200&lt;/code&gt; &lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;OK&lt;/code&gt;&lt;/div&gt;&lt;div class="line number7 index6 alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Cache-Control: &lt;/code&gt;&lt;code class="java keyword" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(127, 0, 85) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: bold !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;private&lt;/code&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;,no-cache,no-store&lt;/code&gt;&lt;/div&gt;&lt;div class="line number8 index7 alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;/div&gt;&lt;div class="line number9 index8 alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;cacheable:&lt;/code&gt;&lt;/div&gt;&lt;div class="line number10 index9 alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;HTTP/&lt;/code&gt;&lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;1.1&lt;/code&gt; &lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;200&lt;/code&gt; &lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;OK&lt;/code&gt;&lt;/div&gt;&lt;div class="line number11 index10 alt2" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Cache-Control: max-age=&lt;/code&gt;&lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;3600&lt;/code&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;,must-revalidate&lt;/code&gt;&lt;/div&gt;&lt;div class="line number12 index11 alt1" style="background-attachment: initial !important; background-clip: initial !important; background-color: white !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0em !important; padding-right: 1em !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; white-space: pre-wrap !important; width: auto !important;"&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Date: Sun &lt;/code&gt;&lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;16&lt;/code&gt; &lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;Oct &lt;/code&gt;&lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;2011&lt;/code&gt; &lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;15&lt;/code&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;:&lt;/code&gt;&lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;30&lt;/code&gt;&lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;:&lt;/code&gt;&lt;code class="java value" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: rgb(0, 153, 0) !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;00&lt;/code&gt; &lt;code class="java plain" style="background-attachment: initial !important; background-clip: initial !important; background-color: initial !important; background-image: none !important; background-origin: initial !important; background-position: initial initial !important; background-repeat: initial initial !important; border-bottom-left-radius: 0px 0px !important; border-bottom-right-radius: 0px 0px !important; border-bottom-width: 0px !important; border-color: initial !important; border-left-width: 0px !important; border-right-width: 0px !important; border-style: initial !important; border-top-left-radius: 0px 0px !important; border-top-right-radius: 0px 0px !important; border-top-width: 0px !important; bottom: auto !important; box-sizing: content-box !important; color: black !important; float: none !important; font-family: Consolas, 'Bitstream Vera Sans Mono', 'Courier New', Courier, monospace !important; font-size: 1em !important; font-style: normal !important; font-weight: normal !important; height: auto !important; left: auto !important; line-height: 1.2em !important; margin-bottom: 0px !important; margin-left: 0px !important; margin-right: 0px !important; margin-top: 0px !important; min-height: inherit !important; outline-color: initial !important; outline-style: initial !important; outline-width: 0px !important; overflow-x: visible !important; overflow-y: visible !important; padding-bottom: 0px !important; padding-left: 0px !important; padding-right: 0px !important; padding-top: 0px !important; position: static !important; right: auto !important; text-align: left !important; top: auto !important; vertical-align: baseline !important; width: auto !important;"&gt;GMT&lt;/code&gt;&lt;/div&gt;&lt;/div&gt;&lt;/td&gt;&lt;/tr&gt;&lt;/tbody&gt;&lt;/table&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Here's a summary on the semantics of the header values listed above ( the first explanation section of each&amp;nbsp;directive comes from&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.mnot.net/cache_docs/#CACHE-CONTROL" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.mnot.net/cache_docs/#CACHE-CONTROL&lt;/a&gt;&lt;/span&gt;, with subsequent text coming from section 14.9 of&amp;nbsp;the HTTP 1.1 spec):&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;no-cache&lt;/b&gt;&amp;nbsp;— forces caches to submit the request to the origin server for validation before releasing a cached copy,&amp;nbsp;every time. This is useful to assure that authentication is respected (in combination with public), or to maintain&amp;nbsp;rigid freshness, without sacrificing all of the benefits of caching. From the spec: This allows an origin server to&amp;nbsp;prevent caching even by caches that have been configured to return stale responses to client requests. (Note that it's&amp;nbsp;possible to specify field-names for this header that can apply more fine-grained meaning to the intended behavior;&amp;nbsp;see section 14.9.1 for details.). However, a point of interest from&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://palisade.plynt.com/issues/2008Jul/cache-control-attributes" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;this article&lt;/a&gt;&lt;/span&gt;:&lt;/div&gt;&lt;div class="panel" style="background-attachment: initial; background-clip: initial; background-color: #f0f0f0; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(187, 187, 187); border-bottom-left-radius: 5px 5px; border-bottom-right-radius: 5px 5px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-top-style: solid; border-top-width: 1px; color: black; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: hidden; overflow-y: hidden; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div class="panelContent" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 0.95em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 10px; padding-right: 10px; padding-top: 0px; text-align: left;"&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left;"&gt;“no-cache” directive, according to the RFC, tells the browser that it should revalidate with the server before serving the page from the cache..."no-store" tells the browser not only not to cache the page, but also not to even store the page in its cache folder. Whenever you’re serving a sensitive page, this is the cache control directive to use. Notice that of late, “cache-control: no-cache” has also started behaving like the “no-store” directive. To be on the safer side, we recommend that you use both “no-cache” and “no-store” when serving sensitive pages.&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;no-store&lt;/b&gt;&amp;nbsp;— instructs caches not to keep a copy of the representation under any conditions.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;private&lt;/b&gt;&amp;nbsp;— allows caches that are specific to one user (e.g., in a browser) to store the response; shared caches&amp;nbsp;(e.g., in a proxy) may not.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;max-age=&lt;/b&gt;&lt;b&gt;&lt;seconds&gt;&lt;/seconds&gt;&lt;/b&gt;&amp;nbsp;--- specifies the maximum amount of time that a representation will be considered fresh. Similar to&amp;nbsp;Expires, this directive is relative to the time of the request, rather than absolute. &lt;seconds&gt;&amp;nbsp;is the number of&amp;nbsp;seconds from the time of the request you wish the representation to be fresh for. From the spec: implies that the&amp;nbsp;response is cacheable (i.e., "public") unless some other, more restrictive cache directive is also present. If a&amp;nbsp;response includes both an Expires header and a max-age directive, the max-age directive overrides the Expires header,&amp;nbsp;even if the Expires header is more restrictive. (Note that max-age can also be specified in a request from the&amp;nbsp;client; the meaning here is, from section 14.9.3, that the client is willing to accept a response whose age is no&amp;nbsp;greater than the specified time in seconds. There are also max-stale and max-fresh directives that further refine the&amp;nbsp;user-agent's intentions; see section 14.9.3 for details.). Note that setting max-age to zero ensures that a page is never served from cache, but is always re-validated against the server.&lt;/seconds&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;must-revalidate&lt;/b&gt;&amp;nbsp;— tells caches that they must obey any freshness information you give them about a representation.&amp;nbsp;HTTP allows caches to serve stale representations under special conditions; by specifying this header, you’re telling&amp;nbsp;the cache that you want it to strictly follow your rules.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;What follows here is a general discussion of the Expiration and Validation models.&lt;/div&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6064095763580990" name="HTTPCaching-Expiration"&gt;&lt;/a&gt;Expiration&lt;/h4&gt;&lt;hr style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; color: #cccccc; height: 0px;" /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;With expiration, you tell the cache how long should a response be considered "fresh", i.e. on expiration it becomes "stale". This is&amp;nbsp;more efficient than validation since cache need not go back to origin server until staleness.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Expires&lt;/b&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;This is useful for long-lived static images, and things that change on regular schedule. It's not without its&amp;nbsp;problems:&amp;nbsp;&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;server/client clocks must be in sync&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;times are in one-second resolution&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;you might forget to update date after expiration&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;spec prohibits Expires date more than one year in future&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;it's not useful for dynamic content&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Cache-Control&lt;/b&gt;&amp;nbsp;(i.e. max-age, s-max-age (shared caches only))&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;to address limitations of Expires&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;relative vs absolute time frames&lt;/li&gt;&lt;/ul&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6064095763580990" name="HTTPCaching-Validation"&gt;&lt;/a&gt;Validation&lt;/h4&gt;&lt;hr style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; color: #cccccc; height: 0px;" /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;With the validation model, the cache asks the origin server on each request if its cached response is still valid. This is probably the&amp;nbsp;best option for dynamic content.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;ETag/If-None-Match&lt;/em&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;You can think of ETag as a hash, fingerprint, etc. unique ID for a given page, resource, etc. Use this when it's&amp;nbsp;inconvenient to store modification dates, or when the 1-second resolution of HTTP expiration model is not good enough, etc.&amp;nbsp;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;An ETag can be&amp;nbsp;&lt;em&gt;strong&lt;/em&gt;&amp;nbsp;or&amp;nbsp;&lt;em&gt;weak&lt;/em&gt;&amp;nbsp;- in a nutshell, strong means the response can be used to compose a larger response, i.e. it's valid as a partial piece of content; whereas weak means you should expect to use the response to reliably compose larger pages (etc.). If a request contains an If-None-Match header with a value equal to the ETag for the given resource, the response simply contains a 304-Not Modified response status with an empty body; this is useful for bandwidth savings. If any intermediary cache must go back to the origin server for validation, a useful implementation to consider on that origin server involves saving the ETag (in database, key-value store, in-memory hashmap, etc.) on a per-URL (etc.) basis, to additionally save on CPU (i.e. the ETag need not be recomputed on each request).&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Note that there is a fair amount more to the ETag-based validation model than discussed here. Please consult the RFC for more details.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;Last-Modified/If-Modified-Since&lt;/em&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;A date-based approach to validation can be done with the Last-Modified/If-Modified-Since protocol. Similar to ETag, the server might save the modification dates for a given resource (URL, etc.) and compare that with any request header value for If-Modified-Since, again returning 304-Not Modified if the resource has not been modified since that given date. Again, savings in bandwidth can be gained here.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;Combining ETag with Last-Modified&lt;/em&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;There's a fair amount of ambiguity in blogs and web articles around what the behavior should be when requests send both types of validation headers and the server side supports both. To make things worse, the RFC 2616 is not the easiest specification to understand (in fact, there's an effort underway to improve that with a&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://tools.ietf.org/wg/httpbis/" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;rewrite&lt;/a&gt;&lt;/span&gt;). I'm basing my interpretation on my own careful read of RFC 2616; you are invited to correct me if you believe I've got it wrong - note in the following that "server must execute the method" is similar to saying there was a "cache miss":&lt;/div&gt;&lt;div class="panel" style="background-attachment: initial; background-clip: initial; background-color: #f0f0f0; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(187, 187, 187); border-bottom-left-radius: 5px 5px; border-bottom-right-radius: 5px 5px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-top-style: solid; border-top-width: 1px; color: black; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: hidden; overflow-y: hidden; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div class="panelContent" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 0.95em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 10px; padding-right: 10px; padding-top: 0px; text-align: left;"&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left;"&gt;- etag matches, date is fresh: return status 304&lt;br /&gt;- etag matches, but date is stale: origin server must execute the method and return that response&lt;br /&gt;- etag does not match:&amp;nbsp;origin server must execute the method and return that response,&amp;nbsp;&lt;em&gt;regardless&lt;/em&gt;&amp;nbsp;of any date-based validation information in request&lt;br /&gt;- etag matches, and there is no date-based header:&amp;nbsp;return status 304&lt;br /&gt;- no etag in request; date is fresh:&amp;nbsp;return status 304&lt;br /&gt;-&amp;nbsp;no etag in request; date is&amp;nbsp;stale:&amp;nbsp;origin server must execute the method and return that response&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span style="color: #333333;"&gt;For reference,&lt;/span&gt;&amp;nbsp;&lt;span style="color: #333333;"&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13.3.4" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;here&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;span style="color: #333333;"&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.25" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;are&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;span style="color: #333333;"&gt;the&lt;/span&gt;&amp;nbsp;&lt;span style="color: #333333;"&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.26" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;sections&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&amp;nbsp;&lt;span style="color: #333333;"&gt;of the RFC that I used to determine the above recipe.&lt;/span&gt;&lt;/div&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6064095763580990" name="HTTPCaching-Invalidation"&gt;&lt;/a&gt;Invalidation&lt;/h4&gt;&lt;hr style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; color: #cccccc; height: 0px;" /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;The third mechanism supporting caching (beyond Expiration and Validation) is&amp;nbsp;&lt;em&gt;invalidation&lt;/em&gt;. However, here you shouldn't need to do anything special with your web application -- it should be handled implicitly by any gateway or proxy cache that is in place. What you&amp;nbsp;&lt;em&gt;should&lt;/em&gt;&amp;nbsp;do is return ETag and Last-Modified information in any response for a given resource (including GET, POST, PUT, DELETE) - such that any intermediary cache can not only cache as needed (for GETs), but also evict/update/otherwise-deal-with URLs that have been changed (via POST, PUT and DELETE).&lt;/div&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6064095763580990" name="HTTPCaching-DesignDiscussion"&gt;&lt;/a&gt;Design Discussion&lt;/h4&gt;&lt;hr style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; color: #cccccc; height: 0px;" /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Checking for ETag alone is not as effective a caching solution as might be accomplished with use of If-Modified-Since.&amp;nbsp;This is because the cache must validate the value of the ETag before returning a 304. In the case of a&amp;nbsp;dynamic system where state changes frequently or unpredictably, however, ETag might be the best approach.&amp;nbsp;In other cases where it is known e.g. that state won't change until a given time, use of If-Modified-Since is likely&amp;nbsp;to be the most scalable solution, since the application can offload requests to reverse proxies.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Use of the Expires header can also be used in systems where it is known that state will change at precise times;&amp;nbsp;however, this calls for client and server clocks to be sync, and requires explicit updates to the Expires header&amp;nbsp;value when expiration occurs (which, if forgotten, leads to permanent staleness and zero benefit from a cache).&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Note that date-based validation is limited to one-second precision. So it's feasible that a client could fetch a resource, saving the last-modified date in the response,&amp;nbsp;then change the resource (or some other client could change it) and subsequently request it again immediately, but receive a 304&amp;nbsp;response since all of this took place within the same second. As such, I'd&amp;nbsp;endorse use of the ETag as the preferred caching protocol if your service expects anything resembling high concurrency and requires strict correctness.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span style="color: #333333;"&gt;If proxy/reverse caches are expected to be part of the ecosystem and you want to control their behavior&amp;nbsp;independently, consider using these headers (see RFC 2616, section 14.9.3 for details):&lt;/span&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span style="color: #333333;"&gt;&lt;b&gt;s-maxage=&lt;/b&gt;&lt;/span&gt;&lt;span style="color: #333333;"&gt;&lt;b&gt;&lt;seconds&gt;&lt;/seconds&gt;&lt;/b&gt;&lt;/span&gt;&lt;span style="color: #333333;"&gt;&lt;seconds&gt; - similar to max-age, except that it only applies to shared caches.&lt;/seconds&gt;&lt;/span&gt;&lt;br /&gt;&lt;span style="color: #333333;"&gt;&lt;b&gt;proxy-revalidate&lt;/b&gt;&lt;/span&gt;&lt;span style="color: #333333;"&gt;&amp;nbsp;- similar to must-revalidate, except that it only applies to proxy caches. From&lt;/span&gt;&amp;nbsp;&lt;span style="color: #333333;"&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://palisade.plynt.com/issues/2008Jul/cache-control-attributes" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;this article&lt;/a&gt;&lt;/span&gt;&lt;/span&gt;&lt;span style="color: #333333;"&gt;:&lt;/span&gt;&lt;/div&gt;&lt;div class="panel" style="background-attachment: initial; background-clip: initial; background-color: #f0f0f0; background-image: initial; background-origin: initial; background-position: initial initial; background-repeat: initial initial; border-bottom-color: rgb(187, 187, 187); border-bottom-left-radius: 5px 5px; border-bottom-right-radius: 5px 5px; border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(187, 187, 187); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(187, 187, 187); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(187, 187, 187); border-top-left-radius: 5px 5px; border-top-right-radius: 5px 5px; border-top-style: solid; border-top-width: 1px; color: black; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 0px; overflow-x: hidden; overflow-y: hidden; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;div class="panelContent" style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 0.95em; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 10px; padding-right: 10px; padding-top: 0px; text-align: left;"&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px; text-align: left;"&gt;&lt;span style="color: #333333;"&gt;...useful when an authenticated page is being cached&amp;nbsp;in the browser. You don’t want the proxy to cache and serve the page, whereas it’s fine for your browser to cache and&amp;nbsp;serve the page.&lt;/span&gt;&lt;/div&gt;&lt;/div&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span style="color: #333333;"&gt;From my understanding of the protocol so far, I'd also suggest adding the&lt;/span&gt;&amp;nbsp;&lt;span style="color: #333333;"&gt;&lt;em&gt;public&lt;/em&gt;&lt;/span&gt;&amp;nbsp;&lt;span style="color: #333333;"&gt;header value to responses intended to&amp;nbsp;be cacheable if HTTP authentication is involved:&lt;/span&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span style="color: #333333;"&gt;&lt;b&gt;public&lt;/b&gt;&lt;/span&gt;&lt;span style="color: #333333;"&gt;&amp;nbsp;--- marks authenticated responses as cacheable; normally, if HTTP authentication is required, responses are&amp;nbsp;automatically private. From HTTP 1.1 spec: Indicates that the response MAY be cached by any cache, even if it would&amp;nbsp;normally be non-cacheable or cacheable only within a non- shared cache.&lt;/span&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span style="color: #333333;"&gt;Here's additional information from HTTP 1.1. spec, section 14.8:&lt;/span&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span style="color: #333333;"&gt;A user agent that wishes to authenticate itself with a server – usually, but not necessarily, after receiving a 401&amp;nbsp;response – does so by including an Authorization request-header field with the request.&amp;nbsp;When a shared cache (see section 13.7) receives a request containing an Authorization field, it MUST NOT return the&amp;nbsp;corresponding response as a reply to any other request, except that it MAY use the response in a subsequent request&amp;nbsp;if s-maxage, must-revalidate or public headers are included in the response (see section 13.7 for details).&lt;/span&gt;&lt;/div&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6064095763580990" name="HTTPCaching-Other"&gt;&lt;/a&gt;Other&lt;/h4&gt;&lt;hr style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; color: #cccccc; height: 0px;" /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Here are some things you can do to make your website more&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.mnot.net/cache_docs/#TIPS" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;&lt;span class="Apple-style-span" style="color: #006daf;"&gt;&lt;span class="Apple-style-span" style="outline-color: initial; outline-width: initial;"&gt;cache-friendly&lt;/span&gt;&lt;/span&gt;&lt;/a&gt;&lt;/span&gt;&amp;nbsp;and&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.mnot.net/cache_docs/#SCRIPT" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;cache-aware&lt;/a&gt;&lt;/span&gt;. Not all of these will necessarily apply to your context, especially when dealing with dynamic content.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Note that one assumption made in this writeup is that there is only one representation for any given URL. This may not be the case, however; representations may vary as per JSON vs XML encoding, use of compression or not, special markup for a given browser (or other user agent), and etc. This is where the Vary header comes into play; here's a nice&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://symfony.com/doc/2.0/book/http_cache.html#varying-the-response" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;explanation&lt;/a&gt;&lt;/span&gt;&amp;nbsp;of this pattern.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;There are many open source/commercial proxy and reverse-proxy caches available, notably&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.squid-cache.org/" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;Squid&lt;/a&gt;&lt;/span&gt;&amp;nbsp;and&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="https://www.varnish-cache.org/" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;Varnish&lt;/a&gt;&lt;/span&gt;&amp;nbsp;(let alone&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://httpd.apache.org/" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;Apache&lt;/a&gt;&lt;/span&gt;,&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://wiki.nginx.org/" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;Nginx&lt;/a&gt;&lt;/span&gt;, ... etc.).&amp;nbsp;&lt;/div&gt;&lt;h4 style="color: black; font-size: 1.2em; font-weight: bold; line-height: normal; margin-bottom: 0.3em; margin-left: 0px; margin-right: 0px; margin-top: 1.2em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="http://www.blogger.com/post-create.g?blogID=6064095763580990" name="HTTPCaching-Resources"&gt;&lt;/a&gt;Resources&lt;/h4&gt;&lt;hr style="border-bottom-color: rgb(204, 204, 204); border-bottom-style: solid; border-bottom-width: 1px; border-left-color: rgb(204, 204, 204); border-left-style: solid; border-left-width: 1px; border-right-color: rgb(204, 204, 204); border-right-style: solid; border-right-width: 1px; border-top-color: rgb(204, 204, 204); border-top-style: solid; border-top-width: 1px; color: #cccccc; height: 0px;" /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://en.wikipedia.org/wiki/Web_cache" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://en.wikipedia.org/wiki/Web_cache&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.peej.co.uk/articles/http-caching.html" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.peej.co.uk/articles/http-caching.html&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://tomayko.com/writings/things-caches-do" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://tomayko.com/writings/things-caches-do&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.web-caching.com/welcome.html" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.web-caching.com/welcome.html&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.mnot.net/cache_docs/" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.mnot.net/cache_docs/&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.visolve.com/squid/whitepapers/ViSolve_Web_Caching.pdf" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.visolve.com/squid/whitepapers/ViSolve_Web_Caching.pdf&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://stackoverflow.com/questions/4360283/http-cache-control" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://stackoverflow.com/questions/4360283/http-cache-control&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec13.html#sec13" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.w3.org/Protocols/rfc2616/rfc2616.html&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://wiki.squid-cache.org/" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://wiki.squid-cache.org&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://symfony.com/doc/2.0/book/http_cache.html" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://symfony.com/doc/2.0/book/http_cache.html&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://hc.apache.org/user-docs.html" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://hc.apache.org/user-docs.html&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.slideshare.net/rtomayko/https-bestkept-secret-caching" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.slideshare.net/rtomayko/https-bestkept-secret-caching&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4602167745541340761?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4602167745541340761/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/11/this-captures-basic-learning-curve.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4602167745541340761'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4602167745541340761'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/11/this-captures-basic-learning-curve.html' title='HTTP Caching 101'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-3753994086992140494</id><published>2011-11-16T09:44:00.000-07:00</published><updated>2011-11-16T09:44:37.753-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='design'/><category scheme='http://www.blogger.com/atom/ns#' term='BASE'/><title type='text'>RESTful Design - Benefits, Patterns</title><content type='html'>&lt;span class="Apple-style-span" style="color: #333333; font-family: Arial, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 17px;"&gt;This captures odds-n-ends around RESTful design - why bother, what are the benefits, what are some patterns, etc. This is written in a terse "talking points" style, with most content either paraphrased or explicitly copied from the footnoted links at end of post; I've added some of my thoughts here and there.&lt;/span&gt;&lt;span class="Apple-style-span" style="color: #333333; font-family: Arial, Helvetica, FreeSans, sans-serif; font-size: 13px; line-height: 17px;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;An opening thought around what might be the benefit to understanding and leveraging aspects of RESTful design: since REST describes the way the web works, and the web is the single most scalable application ever known, we might do well to understand and embrace aspects of RESTful style.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;REST describes a&amp;nbsp;&lt;b&gt;Resource-Oriented Architecture (ROA):&lt;/b&gt;&amp;nbsp;&lt;em&gt;the web is based on resource exchange, not on sending commands&lt;/em&gt;.&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Selected excerpts from Roy Fielding's thesis&lt;/b&gt;[1]:&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;REST provides a set of architectural constraints that, when applied as a whole, emphasizes scalability of component interactions, generality of interfaces, independent deployment of components, and intermediary components to reduce interaction latency, enforce security, and encapsulate legacy systems.&lt;/em&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;The central feature that distinguishes the REST architectural style from other network-based styles is its emphasis on a uniform interface between components. By applying the software engineering principle of generality to the component interface, the overall system architecture is simplified and the visibility of interactions is improved.&lt;/em&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;What makes HTTP significantly different from RPC is that the requests are directed to resources using a generic interface with standard semantics that can be interpreted by intermediaries almost as well as by the machines that originate services. The result is an application that allows for layers of transformation and indirection that are independent of the information origin, which is very useful for an Internet-scale, multi-organization, anarchically scalable information system. RPC mechanisms, in contrast, are defined in terms of language APIs, not network-based applications.&lt;/em&gt;&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;HTTP is not designed to be a transport protocol. It is a transfer protocol in which the messages reflect the semantics of the Web architecture by performing actions on resources through the transfer and manipulation of representations of those resources. It is possible to achieve a wide range of functionality using this very simple interface, but following the interface is required in order for HTTP semantics to remain visible to intermediaries.&lt;/em&gt;&lt;/div&gt;&lt;h5 style="color: black; font-size: 1.1em; font-weight: bold; line-height: normal; margin-bottom: 0.1em; margin-left: 0px; margin-right: 0px; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="" name="RESTfulDesign-Benefits%2CPatterns-%7B%7D%7B%7DPrinciples%7B%7D%7B%7D"&gt;&lt;/a&gt;&lt;b&gt;&lt;em&gt;&lt;ins&gt;Principles&lt;/ins&gt;&lt;/em&gt;&lt;/b&gt;&lt;/h5&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;as per Tilkov&lt;/em&gt;[2]:&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Everything has its own URI as an identifier&lt;/b&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Link things together&lt;/b&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Use a set of standard methods (in the manner they're intended)&lt;/b&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Resources have multiple representations&lt;/b&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Assume stateless&lt;/b&gt;&amp;nbsp;&lt;b&gt;&lt;em&gt;communication&lt;/em&gt;&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h6 style="color: black; font-size: 1em; font-weight: bold; line-height: normal; margin-bottom: 0.1em; margin-left: 0px; margin-right: 0px; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="" name="RESTfulDesign-Benefits%2CPatterns-BenefitsfromPrinciples"&gt;&lt;/a&gt;&lt;em&gt;Benefits from Principles&lt;/em&gt;&lt;/h6&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Identifiers:&amp;nbsp;&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;flexibility, extensibility&lt;/em&gt;: bookmarkable, pass between different apps, facilitate new mashups; support versioning (version # is part of URI)&lt;br /&gt;&lt;em&gt;lowered dev costs, extensibility&lt;/em&gt;: familiar programming model (ala browser); apply web-centric security constraints; leverage HTTP redirects; apply different rules to different URIs for logging, statistics, auditing, etc.&lt;br /&gt;&lt;em&gt;ease of evolution&lt;/em&gt;: retrofit things like auditing, diagnostics, recovery, undo operations, ...&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Linking:&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;scalability&lt;/em&gt;: paging via links in the face of many results&lt;br /&gt;&lt;em&gt;lowered dev costs&lt;/em&gt;: symmetric, consistent, understandable, maintainable, extensible codebase; familiar programming model (as browser). Clients can "discover" the entire information space dynamically, no need to hardwire drill-down URLs that will later break.&lt;br /&gt;&lt;em&gt;lowered dev costs, extensibility&lt;/em&gt;: guide "next valid transitions"; encapsulate URI details via rel (relations) attribute, no need for out-of-band document (WADL, WSDL)&lt;br /&gt;&lt;em&gt;ease of evolution&lt;/em&gt;: self-describing server can evolve without breaking clients&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Standard Verbs ("Uniform Interface"):&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;scalability&lt;/em&gt;: can leverage (and not get burned by) existing web infrastructure (proxies, gateways, etc...crawlers, etc.)&lt;br /&gt;&lt;em&gt;reliability&lt;/em&gt;: GET and HEAD are safe; PUT and DELETE are idempotent - thus clients can resend requests as needed (except for POST - but see patterns below for workarounds). Cache intermediaries can "determine the cacheability of a response because the interface is generic rather than specific to each resource. By default, the response to a retrieval request is cacheable and the responses to other requests are non-cacheable." (Fielding, sec. 5.2.2)&lt;br /&gt;&lt;em&gt;lowered dev costs&lt;/em&gt;: no need to invent a new protocol for every application (ala WS).&lt;br /&gt;&lt;em&gt;extensibility&lt;/em&gt;: re-use of testing tools/techniques, interoperability between new apps and existing clients, etc.&lt;br /&gt;&lt;em&gt;value-add&lt;/em&gt;: facilitate intranet with searchable resources (i.e. crawlers can index GETs without deleting your database...).&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Multiple Representations:&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;ease of evolution&lt;/em&gt;: support versioning for backwards compatibility (e.g. application-custom MIME types)&lt;br /&gt;&lt;em&gt;flexibility&lt;/em&gt;: client references are not coupled to a particular representation&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;Stateless:&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;em&gt;scalability&lt;/em&gt;: facilitates load balancing, distributed caching, clustering, parallel processing and pipelining&lt;br /&gt;&lt;em&gt;reliability&lt;/em&gt;: failover&lt;br /&gt;&lt;em&gt;flexibility, extensibility&lt;/em&gt;: decoupled from clients&lt;br /&gt;&lt;em&gt;visibility&lt;/em&gt;: diagnostics are transparent since each request is self-contained&lt;/div&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;All of above used together&lt;/b&gt;&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;easier to combine different services (interop)&lt;br /&gt;more consistent coding patterns, better redundancy, faster training/learning curves, faster evolution&lt;br /&gt;symmetric, understandable, maintainable codebase&lt;br /&gt;From Fielding, section 5.3.1: "&lt;em&gt;(RESTful constraints) allow intermediaries - proxies, gateways, and firewalls - to be introduced at various points in the communication without changing the interfaces between (client and server), thus allowing them to assist in communication translation or improve performance via large-scale, shared caching. REST enables intermediate processing by constraining messages to be self-descriptive: interaction is stateless between requests, standard methods and media types are used to indicate semantics and exchange information, and responses explicitly indicate cacheability&lt;/em&gt;."&lt;/div&gt;&lt;h3 style="color: black; font-size: 1.4em; font-weight: bold; line-height: normal; margin-bottom: 0.5em; margin-left: 0px; margin-right: 0px; margin-top: 1.5em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="" name="RESTfulDesign-Benefits%2CPatterns-Patterns"&gt;&lt;/a&gt;&lt;ins&gt;Patterns&lt;/ins&gt;&lt;/h3&gt;&lt;h5 style="color: black; font-size: 1.1em; font-weight: bold; line-height: normal; margin-bottom: 0.1em; margin-left: 0px; margin-right: 0px; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="" name="RESTfulDesign-Benefits%2CPatterns-Scalability"&gt;&lt;/a&gt;&lt;em&gt;Scalability&lt;/em&gt;&lt;/h5&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Caching - leverage validation, expiration, etc so response transfers data only when it's changed; server-side caching to minimize repeating expensive computations and/or to handle increased demand from multiple clients&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Cache control - server specifies which responses are cacheable; client has option to re-use the cacheable data&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Response code 409 - to leverage optimistic locking patterns&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Asynch request pattern - POST a query that is costly on server side; client receives a "future" in Location header, later does a GET to that URI (404 means not done yet...)&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Header information - specify what encoding is acceptable, server can compress e.g. into gzip to save bandwidth&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Instead of sessions (impacts scalability), make the shopping cart a &lt;i&gt;resource&lt;/i&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Provide "collection" resources - coarse-grained interactions&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Provide paging of large results - e.g. 20 at a time - with links on NEXT and PREVIOUS, etc.&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;provide this link in a header to enable linking from non-text media types, e.g. an image[7]&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;use this "header link" pattern for other linking needs, e.g. providing the "next valid state transitions" (i.e. what valid things can client do from here)&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Transactional behavior&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;make the txn a resource. GET txn, do stuff to it, finally PUT it at the very end&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;use BASE and compensating txns (PUT, DELETE, POST) as needed&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;server provides links that facilitate compensating actions&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Conditional GET: response has ETag and/or Last-Modified set; client later requests same resource with header including Etag and/or Last-Modified value as value for If-None-Match&amp;nbsp;and/or&amp;nbsp;If-Modified-Since, respectively; then server decides if resend is needed ("validation"). If not, response code is 304.&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;If you must use cookies, store all app state on client side (i.e. don't use a session ID pointing to data on server) - else, you'll sacrifice scalability&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Leverage intermediaries - using HTTP as intended in a RESTful style facilitates interoperation with network components that provide load balancing, caching, security policies, etc. As per Fielding:&amp;nbsp;&lt;em&gt;Within REST, intermediary components can actively transform the content of messages because the messages are self-descriptive and their semantics are visible to intermediaries.&lt;/em&gt;&lt;/li&gt;&lt;/ul&gt;&lt;h5 style="color: black; font-size: 1.1em; font-weight: bold; line-height: normal; margin-bottom: 0.1em; margin-left: 0px; margin-right: 0px; margin-top: 1em; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;a href="" name="RESTfulDesign-Benefits%2CPatterns-Design%2FCoding"&gt;&lt;/a&gt;&lt;em&gt;Design/Coding&lt;/em&gt;&lt;/h5&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;New URIs are created by server, returned to server via the Location header after a POST creates it&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Version the service with URI - /v1/service/resource, or even as part of the host - v1.myservice.twc.com, v2.myservice.twc.com, etc.&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Keep in mind that HTML5 will support PUT/DELETE. But not all firewalls allow these through...so, tunnel the method using header or hidden form field, or just use XHR&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Provide canonical representations (text/plain, HTML) - supports easier debugging, scraping&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Get around idempotence of POST:&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;instead of retry when uncertain about success, do a PUT&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Post-Once-Exactly: to get around non-repeatable POSTs - GET returns a server-side link representing a resource not yet created, then client POSTs to that URL to create new resource; subsequent POSTs to the same resource URL return 405 (not allowed).&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ul&gt;&lt;ul style="font-size: 10pt; line-height: 13pt; list-style-type: disc;"&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;"Conditional PUT (POST)": before submitting a large amount of information to server that might not be able to handle it at the moment - PUT without resource but include Content-Length and Expect headers. If response has same code as Expect value, client proceeds, else it does not.&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Use POST to support large queries, to get around length limits imposed by servers, clients and proxies. However this results in loss of cacheability if response is sent synchronously; instead, use the async request pattern from above - server returns new resource with 201 response code, client then GETs the answer to the request.&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Content negotiation - client uses accept, accept-encoding, accept-language headers; vary and/or location headers in response. This supports "late binding" in determining content representation as function of request.&lt;/li&gt;&lt;li style="font-size: 10pt; line-height: 13pt; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;Evaluate extensibility vs visibility if considering "code on demand" (applets, javascript) - while this simplifies clients (extensible at runtime), it reduces visibility (maintenance, understandability, diagnostics, ...)&lt;/li&gt;&lt;/ul&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;&lt;b&gt;References&lt;/b&gt;:&lt;/div&gt;&lt;div style="background-attachment: initial; background-clip: initial; background-color: initial; background-image: none; background-origin: initial; background-position: initial initial; background-repeat: initial initial; color: #333333; font-size: 10pt; font-weight: normal; line-height: 13pt; margin-bottom: 10px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;[1] Fielding, Roy Thomas. Architectural Styles and the Design of Network-based Software Architectures. Doctoral dissertation, University of California, Irvine, 2000:&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm&lt;sup&gt;&lt;img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://mystropedia.corp.mystrotv.com/images/icons/linkext7.gif" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial;" width="7" /&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;[2] Stefan Tilkov presentation (video):&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://www.parleys.com/#st=5&amp;amp;id=1397" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://www.parleys.com/#st=5&amp;amp;id=1397&lt;sup&gt;&lt;img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://mystropedia.corp.mystrotv.com/images/icons/linkext7.gif" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial;" width="7" /&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;[3] Richardson, L. &amp;amp; Ruby, S. (2007)&amp;nbsp;&lt;em&gt;RESTful Web Services&lt;/em&gt;. Sebastopol, CA:O'Reilly Media, Inc.&lt;br /&gt;[4] Allamaraju, S. (2010)&amp;nbsp;&lt;em&gt;RESTful Web Services Cookbook&lt;/em&gt;. Sebastopol, CA:O'Reilly Media, Inc.&lt;br /&gt;[5] HATEOAS, the scary acronym&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://css.dzone.com/articles/hateoas-scary-acronym" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;: http://css.dzone.com/articles/hateoas-scary-acronym&lt;sup&gt;&lt;img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://mystropedia.corp.mystrotv.com/images/icons/linkext7.gif" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial;" width="7" /&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;[6] RESTful Web Services:&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://imyousuf-tech.blogs.smartitengineering.com/2011/02/restful-web-services.html" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://imyousuf-tech.blogs.smartitengineering.com/2011/02/restful-web-services.html&lt;sup&gt;&lt;img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://mystropedia.corp.mystrotv.com/images/icons/linkext7.gif" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial;" width="7" /&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/span&gt;&lt;br /&gt;[7] IETF RFC 5988 - Web Linking:&amp;nbsp;&lt;span class="nobr" style="white-space: nowrap;"&gt;&lt;a class="external-link" href="http://tools.ietf.org/html/rfc5988" rel="nofollow" style="color: #006daf; outline-color: initial; outline-style: none; outline-width: initial; text-decoration: none;"&gt;http://tools.ietf.org/html/rfc5988&lt;sup&gt;&lt;img align="absmiddle" alt="" border="0" class="rendericon" height="7" src="http://mystropedia.corp.mystrotv.com/images/icons/linkext7.gif" style="border-bottom-style: none; border-color: initial; border-left-style: none; border-right-style: none; border-top-style: none; border-width: initial;" width="7" /&gt;&lt;/sup&gt;&lt;/a&gt;&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-3753994086992140494?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/3753994086992140494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/11/restful-design-benefits-patterns.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3753994086992140494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3753994086992140494'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/11/restful-design-benefits-patterns.html' title='RESTful Design - Benefits, Patterns'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-430150990845222944</id><published>2011-10-27T15:37:00.004-06:00</published><updated>2011-10-28T10:04:09.330-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jmx'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Create and Detect Thread Deadlocks</title><content type='html'>Just the code:&lt;br /&gt;&lt;pre class="brush:java"&gt;import java.lang.management.ManagementFactory;&lt;br /&gt;import java.lang.management.ThreadMXBean;&lt;br /&gt;import java.util.Date;&lt;br /&gt;import java.util.Scanner;&lt;br /&gt;import java.util.concurrent.locks.Lock;&lt;br /&gt;import java.util.concurrent.locks.ReentrantLock;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Example of how a deadlock might be created, and how to detect it. Two threads&lt;br /&gt; * are launched, each acquiring the same two resources but in a different order.&lt;br /&gt; * A delay is introduced in the first thread before acquiring the second&lt;br /&gt; * resource to set up a race condition such that a deadlock *may* be introduced.&lt;br /&gt; * The deadlock is not guaranteed to occur; it depends on the length of the&lt;br /&gt; * delay, which is controlled by the user executing the program. Empirically, on&lt;br /&gt; * my desktop, I've found that values of 100 sometimes are adequate, while&lt;br /&gt; * values of 1000+ become increasingly reliable.&lt;br /&gt; * &lt;br /&gt; * Note that looking for deadlocks with the JMX mechanism used here (ThreadMXBean) is&lt;br /&gt; * described in the javadocs as an operation that "might be expensive". As such,&lt;br /&gt; * unless you're confident that the cost is not high...I'd recommend not putting&lt;br /&gt; * this into operation, rather using it in dev/test environment when trying to&lt;br /&gt; * reproduce a situation observed in production where a deadlock occurred. Note&lt;br /&gt; * that in production, issuing SIGQUIT (ctl-backslash) will provoke a thread&lt;br /&gt; * dump (without killing the process), in which information on deadlocked&lt;br /&gt; * threads will also appear.&lt;br /&gt; */&lt;br /&gt;public class DeadlockedThreadFinder&lt;br /&gt;{&lt;br /&gt;   // done becomes true when either thread completes; note that if one thread &lt;br /&gt;   // completes, then the other one will also&lt;br /&gt;   public boolean done;&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    * Launch two threads, each acquiring a lock on two different resources in a&lt;br /&gt;    * different order. The first thread pauses some time T as a function of the&lt;br /&gt;    * given delay.&lt;br /&gt;    *&lt;br /&gt;    * @param delay is the given delay&lt;br /&gt;    */&lt;br /&gt;   public void createDeadlock(final int delay)&lt;br /&gt;   {&lt;br /&gt;      System.out.println("First thread will pause for time T as function of " +&lt;br /&gt;         delay);&lt;br /&gt;&lt;br /&gt;      // the resources in contention - if threads use the synchronized keyword, &lt;br /&gt;      // you could use something like the two Object declared (and commented &lt;br /&gt;      // out) here; else, use the ReentrantLock if in java.util.concurrent &lt;br /&gt;      // land.&lt;br /&gt;      //      final Object resource1 = new Object();&lt;br /&gt;      //      final Object resource2 = new Object();&lt;br /&gt;      final Lock lock1 = new ReentrantLock();&lt;br /&gt;      final Lock lock2 = new ReentrantLock();&lt;br /&gt;&lt;br /&gt;      // first thread&lt;br /&gt;      Thread t1 = new Thread()&lt;br /&gt;      {&lt;br /&gt;         public void run()&lt;br /&gt;         {&lt;br /&gt;            long id = this.getId();&lt;br /&gt;            lock1.lock(); // alternately use synchronized (resource1) without &lt;br /&gt;            // the try-finally block&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;               System.out.println(id + ": obtained first lock");&lt;br /&gt;               // instead of using Thread.sleep(), just create a whole bunch &lt;br /&gt;               // of expensive objects&lt;br /&gt;               for (int i = 0; i &lt; delay; i++)&lt;br /&gt;               {&lt;br /&gt;                  new Date();&lt;br /&gt;               }&lt;br /&gt;&lt;br /&gt;               System.out.println(id + " waiting for 2nd lock...");&lt;br /&gt;               lock2.lock(); // or use synchronized (resource2)&lt;br /&gt;               try&lt;br /&gt;               {&lt;br /&gt;                  System.out.println(id + ": obtained 2nd lock");&lt;br /&gt;               }&lt;br /&gt;               finally&lt;br /&gt;               {&lt;br /&gt;                  lock2.unlock();&lt;br /&gt;               }&lt;br /&gt;               System.out.println(id + " is done!");&lt;br /&gt;               done = true;&lt;br /&gt;            }&lt;br /&gt;            finally&lt;br /&gt;            {&lt;br /&gt;               lock1.unlock();&lt;br /&gt;            }&lt;br /&gt;         }&lt;br /&gt;      };&lt;br /&gt;&lt;br /&gt;      // second thread&lt;br /&gt;      Thread t2 = new Thread()&lt;br /&gt;      {&lt;br /&gt;         public void run()&lt;br /&gt;         {&lt;br /&gt;            long id = this.getId();&lt;br /&gt;            lock2.lock(); // alternately use synchronized (resource2) without &lt;br /&gt;            // the try-finally block&lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;               System.out.println(id + ": obtained 2nd lock");&lt;br /&gt;               System.out.println(id + " waiting for first lock...");&lt;br /&gt;               lock1.lock(); // or use synchronized (resource1)&lt;br /&gt;               try&lt;br /&gt;               {&lt;br /&gt;                  System.out.println(id + ": obtained first lock");&lt;br /&gt;               }&lt;br /&gt;               finally&lt;br /&gt;               {&lt;br /&gt;                  lock1.unlock();&lt;br /&gt;               }&lt;br /&gt;               System.out.println(id + " is done!");&lt;br /&gt;               done = true;&lt;br /&gt;            }&lt;br /&gt;            finally&lt;br /&gt;            {&lt;br /&gt;               lock2.unlock();&lt;br /&gt;            }&lt;br /&gt;         }&lt;br /&gt;      };&lt;br /&gt;&lt;br /&gt;      t1.start();&lt;br /&gt;      t2.start();&lt;br /&gt;   }&lt;br /&gt;&lt;br /&gt;   /**&lt;br /&gt;    * User must enter an integer that specifies the delay. Values of 10000+ are&lt;br /&gt;    * likely to cause a deadlock. The larger the value entered, the more likely&lt;br /&gt;    * the program will loop a few times detecting no deadlock, and then, once&lt;br /&gt;    * the first thread has satisfied the delay, the program will detect a&lt;br /&gt;    * deadlock until the end of time or user termination of program, whichever&lt;br /&gt;    * comes first.&lt;br /&gt;    */&lt;br /&gt;   public static void main(String[] args) throws Exception&lt;br /&gt;   {&lt;br /&gt;      // the JMX deadlock finder&lt;br /&gt;      ThreadMXBean mxbean = ManagementFactory.getThreadMXBean();&lt;br /&gt;&lt;br /&gt;      // user specifies the delay&lt;br /&gt;      System.out.print("Enter delay: ");&lt;br /&gt;      Scanner sc = new Scanner(System.in);&lt;br /&gt;&lt;br /&gt;      // launch the threads&lt;br /&gt;      DeadlockedThreadFinder finder = new DeadlockedThreadFinder();&lt;br /&gt;      finder.createDeadlock(sc.nextInt());&lt;br /&gt;&lt;br /&gt;      System.out.println("Waiting for either thread to be done...");&lt;br /&gt;      do&lt;br /&gt;      {&lt;br /&gt;         // the JMX deadlock finder will return null if no deadlock detected. &lt;br /&gt;         // Note that if the threads are using traditional synchronization (i.e. &lt;br /&gt;         // using the synchronized keyword), then you could also use the &lt;br /&gt;         // findMonitorDeadlockedThreads method here; but since the threads &lt;br /&gt;         // might uplevel to use java.util.concurrent, it's advised to instead&lt;br /&gt;         // always use findDeadlockedThreads, which will work for either situation.&lt;br /&gt;         long[] deadlocked = mxbean.findDeadlockedThreads();&lt;br /&gt;         if (deadlocked != null)&lt;br /&gt;         {&lt;br /&gt;            System.out.print("Deadlocked threads: ");&lt;br /&gt;            String comma = "";&lt;br /&gt;            for (long l : deadlocked)&lt;br /&gt;            {&lt;br /&gt;               System.out.print(comma + l);&lt;br /&gt;               comma = ",";&lt;br /&gt;            }&lt;br /&gt;            System.out.println();&lt;br /&gt;         }&lt;br /&gt;         else&lt;br /&gt;         {&lt;br /&gt;            System.out.println("No deadlock detected");&lt;br /&gt;         }&lt;br /&gt;         Thread.sleep(1000);&lt;br /&gt;      }&lt;br /&gt;      while (!finder.done);&lt;br /&gt;&lt;br /&gt;      // uh, done&lt;br /&gt;      System.out.println("Done");&lt;br /&gt;   }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-430150990845222944?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/430150990845222944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/10/create-and-detect-thread-deadlocks.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/430150990845222944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/430150990845222944'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/10/create-and-detect-thread-deadlocks.html' title='Create and Detect Thread Deadlocks'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-1435002138548125532</id><published>2011-03-08T15:16:00.001-07:00</published><updated>2011-03-08T15:21:35.061-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design-patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='utility'/><title type='text'>Create and Initialize a Utility Class with Spring</title><content type='html'>&lt;p&gt;Let's say we want to encapsulate a collection of read-only configuration parameters into a class (so, we want immutability). There are numerous parameters in the configuration, perhaps dozens - so we don't want to provide some ugly multi-parameter constructor or factory method. In fact, we want to provide clients with the convenience of static getters, so we know this will be a utility class (i.e. no instances needed or desired). While we could initialize a list of static fields with their configuration values to facilitate this, we would rather factor these value out into some external file.&lt;/p&gt;&lt;p&gt;We'd like to use Spring - but Spring works on instances, and we don't have a constructor for Spring to use, nor do we want or need an instance anyway, nor do we want to provide setters on this class (since we would like to stay immutable). So, how to use Spring?&lt;/p&gt;&lt;p&gt;One solution is to use a variation on the &lt;a href="http://blog.temposwc.com/2010/12/notes-on-builder-pattern.html"&gt;Builder pattern&lt;/a&gt; - the recipe goes like this:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;create the utilty class with static fields for each configuration parameter&lt;/li&gt;&lt;li&gt;give it a private constructor&lt;/li&gt;&lt;li&gt;provide static getters for each parameter&lt;/li&gt;&lt;li&gt;add a static inner class with the same fields, although these will be instance fields (not static)&lt;/li&gt;&lt;li&gt;this inner class also has a private constructor&lt;/li&gt;&lt;li&gt;provide setters for each field - although here we deviate from the standard Builder pattern: these are true setters, returning void&lt;/li&gt;&lt;li&gt;the utility class now provides a static "init" method that accepts an instance of the inner class as an argument - and this behaves just like the regular Builder pattern, where the top-level class initializes its fields from the correspond fields in the argument object&lt;/li&gt;&lt;li&gt;the inner class provides its own "init" method which calls the utility class init method with itself&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Spring wires all of this together like this:&lt;/p&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    &amp;lt;bean id=&amp;quot;builder-variant&amp;quot; class=&amp;quot;com.mybiz.MyUtilityClass$MyInnerClass&amp;quot;&lt;br /&gt;            init-method=&amp;quot;initUtilityClass&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;foo&amp;quot; value=&amp;quot;bar&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;bar&amp;quot; value=&amp;quot;foo&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Using this, Spring will create an instance of the inner class, setting its properties as given, and then call the inner class' initializer. As mentioned in the above recipe, that initializer will then call the utility class' initializer with itself as the argument. That facilitates initializing the utility class properties, and we're done.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-1435002138548125532?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/1435002138548125532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/03/create-and-initialize-utility-class.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/1435002138548125532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/1435002138548125532'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/03/create-and-initialize-utility-class.html' title='Create and Initialize a Utility Class with Spring'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7977439352302534878</id><published>2011-02-17T13:39:00.000-07:00</published><updated>2011-02-21T07:39:21.905-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='myBatis'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit'/><title type='text'>Integrate MyBatis 3.x with Spring 3.x</title><content type='html'>&lt;p&gt;Since last I wrote about &lt;a href="http://blog.temposwc.com/2009/07/spring-30-cheatsheet-integration-with_09.html"&gt;integrating Spring with iBatis&lt;/a&gt;, and about using the very useful &lt;a href="http://blog.temposwc.com/2009/09/spring-test-framework-cheatsheet.html"&gt;auto-rollback in Spring Test Framework&lt;/a&gt;, things have changed. The iBatis project has shifted to Google and is now called &lt;a href="http://www.mybatis.org/"&gt;MyBatis&lt;/a&gt;, and includes numerous API changes. But, this was done after Spring 3.0 was released - so full integration with Spring was left undone at that time. However, a &lt;a href="http://mybatis.googlecode.com/files/mybatis-spring-1.0.0-reference.pdf"&gt;Spring-MyBatis integration component&lt;/a&gt; is now available, and here I&amp;#39;ll write up how to use it to very simply use MyBatis, preserving the auto-rollback feature.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/02/integrate-mybatis-3x-with-spring-3x.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7977439352302534878?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7977439352302534878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/integrate-mybatis-3x-with-spring-3x.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7977439352302534878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7977439352302534878'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/integrate-mybatis-3x-with-spring-3x.html' title='Integrate MyBatis 3.x with Spring 3.x'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-8003139612356468777</id><published>2011-02-09T14:44:00.001-07:00</published><updated>2011-02-09T14:44:15.944-07:00</updated><title type='text'>Get a Random Value Within a Given Range</title><content type='html'>&lt;p&gt;Just the code:&lt;/p&gt;&lt;pre class="brush:java"&gt;&lt;br /&gt;    public int getRandomValue(long min, long max) {&lt;br /&gt;        return (int) Math.max(min, (int) (Math.random() * max) + 1);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;p&gt;Use this to (obviously) generate an integer value between (inclusively) the given minimum and maximum - which is useful also to fetch random elements e.g. from a List.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-8003139612356468777?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/8003139612356468777/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/get-random-value-within-given-range.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8003139612356468777'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8003139612356468777'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/get-random-value-within-given-range.html' title='Get a Random Value Within a Given Range'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-9162298146343893479</id><published>2011-02-08T12:30:00.002-07:00</published><updated>2011-02-21T08:10:18.049-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design-patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Getting Around the Double-Checked Locking Problem</title><content type='html'>&lt;p&gt;This is just a summary of alternatives to the &lt;a href="http://en.wikipedia.org/wiki/Double-checked_locking"&gt;double-checked locking pattern&lt;/a&gt;, which has been shown to have potential problems. The alternatives are taken from &lt;a href="https://www.securecoding.cert.org/confluence/display/java/LCK10-J.+Do+not+use+incorrect+forms+of+the+double-checked+locking+idiom"&gt;a CERT wiki article&lt;/a&gt;. The motivations for the pattern come from three concerns:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;avoid constructing an object that is expensive to construct, unless-until it is needed&lt;/li&gt;&lt;li&gt;avoid more than one instantiation of that object, and&lt;/li&gt;&lt;li&gt;avoid the overhead of synchronization for the more common case of fetching the object.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/02/getting-around-double-checked-locking_08.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-9162298146343893479?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/9162298146343893479/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/getting-around-double-checked-locking_08.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/9162298146343893479'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/9162298146343893479'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/getting-around-double-checked-locking_08.html' title='Getting Around the Double-Checked Locking Problem'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4877548861789290125</id><published>2011-02-08T12:30:00.001-07:00</published><updated>2011-02-21T08:10:32.388-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design-patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Getting Around the Double-Checked Locking Problem</title><content type='html'>&lt;p&gt;This is just a summary of alternatives to the &lt;a href="http://en.wikipedia.org/wiki/Double-checked_locking"&gt;double-checked locking pattern&lt;/a&gt;, which has been shown to have potential problems. The alternatives are taken from &lt;a href="https://www.securecoding.cert.org/confluence/display/java/LCK10-J.+Do+not+use+incorrect+forms+of+the+double-checked+locking+idiom"&gt;a CERT wiki article&lt;/a&gt;. The motivations for the pattern come from three concerns:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;avoid constructing an object that is expensive to construct, unless-until it is needed&lt;/li&gt;&lt;li&gt;avoid more than one instantiation of that object, and&lt;/li&gt;&lt;li&gt;avoid the overhead of synchronization for the more common case of fetching the object.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/02/getting-around-double-checked-locking.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4877548861789290125?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4877548861789290125/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/getting-around-double-checked-locking.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4877548861789290125'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4877548861789290125'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/getting-around-double-checked-locking.html' title='Getting Around the Double-Checked Locking Problem'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4656917143906785539</id><published>2011-02-08T10:27:00.001-07:00</published><updated>2011-02-21T08:10:52.754-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design-patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Thread-Safe Singleton Using ThreadLocal</title><content type='html'>&lt;p&gt;Let&amp;#39;s say a given component in your application has these constraints:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;it needs a reference to an object that is expensive to construct&lt;/li&gt;&lt;li&gt;the expensive object state can be set just once, i.e. it never changes&lt;/li&gt;&lt;li&gt;it executes in a multi-threaded context&lt;/li&gt;&lt;li&gt;maximum throughput is required&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Constraint #1 seems to call for an instance variable - i.e. you don&amp;#39;t want to use a method-local field since the object construction is expensive, as such you don&amp;#39;t want to do it each time the object is needed. Constraint #2 implies that a singleton will serve our needs. However, constraint #3 means the singleton must be used in a thread-safe manner - and constraint #4 tells us that we want to avoid synchronization if possible.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/02/thread-safe-singleton-using-threadlocal.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4656917143906785539?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4656917143906785539/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/thread-safe-singleton-using-threadlocal.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4656917143906785539'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4656917143906785539'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/thread-safe-singleton-using-threadlocal.html' title='Thread-Safe Singleton Using ThreadLocal'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-8400804981544894559</id><published>2011-02-02T17:42:00.000-07:00</published><updated>2011-02-21T08:11:31.247-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design-patterns'/><category scheme='http://www.blogger.com/atom/ns#' term='observer'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Get Notified When Your Periodic Task Throws an Exception</title><content type='html'>&lt;p&gt;I&amp;#39;ve written up a how-to on a &lt;a href="http://blog.temposwc.com/2011/01/periodic-task-and-watchdog-reusable-and.html"&gt;periodic task with a watchdog&lt;/a&gt;; now I want to handle exceptions and notify interested observers when exceptions occur. The watchdog is in place in case exceptions don&amp;#39;t get handled at all, in which case subsequent executions of the scheduled thread &lt;a href="http://download.oracle.com/javase/6/docs/api/java/util/concurrent/ScheduledThreadPoolExecutor.html#scheduleAtFixedRate(java.lang.Runnable, long, long, java.util.concurrent.TimeUnit)"&gt;will be suppressed&lt;/a&gt;. in this case, the watchdog knows that some problem occurred - but it doesn&amp;#39;t know just what that was. As an alternative (or in addition) to the watchdog, we simply catch exceptions in the Runnable passed to our &lt;a href="http://download.oracle.com/javase/6/docs/api/java/util/concurrent/Executor.html"&gt;Executor&lt;/a&gt;, handling it by both logging the information and notifying interested listeners. This helps pass along exception information from a different stack of execution, since the problem occurs in a different thread:&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/02/get-notified-when-your-periodic-task.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-8400804981544894559?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/8400804981544894559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/get-notified-when-your-periodic-task.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8400804981544894559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8400804981544894559'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/get-notified-when-your-periodic-task.html' title='Get Notified When Your Periodic Task Throws an Exception'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-3284688508230549237</id><published>2011-02-02T17:18:00.000-07:00</published><updated>2011-02-21T08:12:00.203-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><title type='text'>Use Spring to Wire Up Varargs</title><content type='html'>&lt;p&gt;This describes a technique to provide N objects to a constructor using Spring. Nothing real new or innovative here; just an idiom I wanted to save for future reference. Let&amp;#39;s say we have a constructor that uses &lt;a href="http://download.oracle.com/javase/1.5.0/docs/guide/language/varargs.html"&gt;varargs&lt;/a&gt;:&lt;/p&gt;&lt;pre class="brush:java"&gt;&lt;br&gt;    public MockDao(SummaryData...summaries) {&lt;br&gt;        // save the summary data for downstream use...&lt;br&gt;    }&lt;br&gt;&lt;/pre&gt;&lt;p&gt;And, we want to use Spring to provide test summary data to our mocked-out DAO. &lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/02/use-spring-to-wire-up-varargs.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-3284688508230549237?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/3284688508230549237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/use-spring-to-wire-up-varargs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3284688508230549237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3284688508230549237'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/use-spring-to-wire-up-varargs.html' title='Use Spring to Wire Up Varargs'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-5743642347486052120</id><published>2011-02-02T13:27:00.001-07:00</published><updated>2011-02-21T08:12:45.898-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>Encapsulate SSL TrustStore Configuration</title><content type='html'>&lt;p&gt;Here I&amp;#39;ll present the steps of a recent exercise I completed, around configuring SSL on the client side, encapsulating the configuration for transparency. For some background on SSL and the topics discussed in this article, here are some resources:&lt;/p&gt;&lt;p&gt;&lt;a href="http://download.oracle.com/javase/1.5.0/docs/guide/security/jsse/JSSERefGuide.html#SSLOverview"&gt;SSL Protocol Overview&lt;/a&gt;&lt;br&gt;&lt;a href="http://www.tldp.org/HOWTO/SSL-Certificates-HOWTO"&gt;SSL Certificates HOWTO&lt;/a&gt;&lt;br&gt;&lt;a href="http://en.wikipedia.org/wiki/Secure_Sockets_Layer"&gt;Transport Layer Security&lt;/a&gt;&lt;/p&gt;&lt;p&gt;The &lt;a href="http://www.grc.com/securitynow.htm"&gt;Security Now podcast series&lt;/a&gt; also has some excellent deep-dives into SSL and many other security-related topics.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/02/encapsulate-ssl-truststore.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-5743642347486052120?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/5743642347486052120/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/encapsulate-ssl-truststore.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5743642347486052120'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5743642347486052120'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/encapsulate-ssl-truststore.html' title='Encapsulate SSL TrustStore Configuration'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7588795187208515271</id><published>2011-02-02T10:18:00.000-07:00</published><updated>2011-02-02T10:18:02.426-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='intellij'/><category scheme='http://www.blogger.com/atom/ns#' term='testng'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit'/><title type='text'>Enable Assertions with JUnit Tests in IDEA</title><content type='html'>&lt;p&gt;My test cases have recently been converted from &lt;a href="http://testng.org/"&gt;TestNG&lt;/a&gt; to &lt;a href="http://junit.org/"&gt;JUnit4&lt;/a&gt;, and to my surprise every single test I wrote from that point forward passed without a problem - if I ran the tests in my &lt;a href="http://www.jetbrains.com/idea/"&gt;Intellij IDEA&lt;/a&gt; 9.0.4 IDE. Mysteriously, executing them via &lt;a href="http://maven.apache.org/plugins/maven-surefire-plugin/"&gt;Maven&lt;/a&gt; did not have that same result (whether from command line or from IDEA). As it turns out - apparently &lt;a href="http://download.oracle.com/javase/1.4.2/docs/guide/lang/assert.html"&gt;JDK assertions&lt;/a&gt; are enabled by default when I use TestNG in IDEA, but not so much when using JUnit.&lt;/p&gt;&lt;p&gt;Fix this problem by setting the &lt;a href="http://download.oracle.com/javase/1.4.2/docs/guide/lang/assert.html#enable-disable"&gt;-ea VM argument&lt;/a&gt; for all existing tests, and setting that as a default for JUnit tests created from this point forward.&lt;/p&gt;&lt;p&gt;Arrrggghhhh.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7588795187208515271?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7588795187208515271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/02/enable-assertions-with-junit-tests-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7588795187208515271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7588795187208515271'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/02/enable-assertions-with-junit-tests-in.html' title='Enable Assertions with JUnit Tests in IDEA'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6887458408826815807</id><published>2011-01-28T11:41:00.000-07:00</published><updated>2011-02-21T08:13:07.280-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>Basic Authentication with Apache HttpClient</title><content type='html'>&lt;p&gt;This is the general idiom used for &lt;a href="http://hc.apache.org/httpclient-legacy/authentication.html"&gt;BASIC authentication with Apache&amp;#39;s HttpClient&lt;/a&gt;, version 3.1. &lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/01/basic-authentication-with-apache.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-6887458408826815807?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/6887458408826815807/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/01/basic-authentication-with-apache.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6887458408826815807'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6887458408826815807'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/01/basic-authentication-with-apache.html' title='Basic Authentication with Apache HttpClient'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7153195878565192690</id><published>2011-01-27T13:31:00.000-07:00</published><updated>2011-02-21T08:13:15.298-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='generics'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='JUnit'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Periodic Task and Watchdog - Reusable and Testable Versions</title><content type='html'>&lt;p&gt;In my &lt;a href="http://blog.temposwc.com/2011/01/schedule-periodic-task-with-watchdog.html"&gt;previous post&lt;/a&gt;, I presented a simple utility for a periodically executing task and a watchdog to make sure it runs forever. Now I&amp;#39;ll refactor that prototype code to make things generic and reusable.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/01/periodic-task-and-watchdog-reusable-and.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7153195878565192690?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7153195878565192690/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/01/periodic-task-and-watchdog-reusable-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7153195878565192690'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7153195878565192690'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/01/periodic-task-and-watchdog-reusable-and.html' title='Periodic Task and Watchdog - Reusable and Testable Versions'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7909878025798322132</id><published>2011-01-27T11:36:00.000-07:00</published><updated>2011-02-21T08:13:49.851-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='utility'/><category scheme='http://www.blogger.com/atom/ns#' term='concurrency'/><title type='text'>Schedule a Periodic Task with a Watchdog</title><content type='html'>&lt;p&gt;This demonstrates a simple mechanism to start up a thread that executes some task periodically, with a watchdog to ensure it keeps running. It is assumed that the periodic task involves some kind of I/O or remote communication that can result in an exception. The watchdog is needed because the &lt;span style="font-family: &amp;#39;courier new&amp;#39;, courier;"&gt;&lt;a title="java.util.concurrent javadoc" href="http://download.oracle.com/javase/7/docs/api/java/util/concurrent/package-summary.html"&gt;java.util.concurrent&lt;/a&gt;&lt;/span&gt; &lt;a title="ScheduledExecutorService javadoc" href="http://download.oracle.com/javase/7/docs/api/java/util/concurrent/ScheduledExecutorService.html"&gt;class used for the task&lt;/a&gt; will not proceed with subsequent executions after one of its executions encounters an exception. &lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/01/schedule-periodic-task-with-watchdog.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7909878025798322132?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7909878025798322132/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/01/schedule-periodic-task-with-watchdog.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7909878025798322132'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7909878025798322132'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/01/schedule-periodic-task-with-watchdog.html' title='Schedule a Periodic Task with a Watchdog'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7663784550458269706</id><published>2011-01-14T15:14:00.000-07:00</published><updated>2011-02-21T08:13:57.465-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CXF'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='webservices'/><title type='text'>Test Your CXF REST Service by Examining Application Object State</title><content type='html'>&lt;p&gt;While I&amp;#39;ve previously described using &lt;a href="http://cxf.apache.org/"&gt;CXF&lt;/a&gt; to &lt;a href="http://blog.temposwc.com/2011/01/unit-test-your-rest-service.html"&gt;unit test a REST service&lt;/a&gt;, it&amp;#39;s a limited technique since the object returned from the HTTP request is a response string - and any assertions you could make against that string are going to be brittle, since it relies on the decisions around how the encoding (XML or JSON) is done. What we really want is a more object-oriented mechanism for testing against application object state...which means you need a REST client that calls the service and can then look at the returned application object, as opposed to a Response object.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/01/test-your-cxf-rest-service-by-examining.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7663784550458269706?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7663784550458269706/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/01/test-your-cxf-rest-service-by-examining.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7663784550458269706'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7663784550458269706'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/01/test-your-cxf-rest-service-by-examining.html' title='Test Your CXF REST Service by Examining Application Object State'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-3082316974634622165</id><published>2011-01-10T09:31:00.002-07:00</published><updated>2011-02-21T08:14:22.179-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='portfolio web-design'/><title type='text'>Portfolio Entry #1: Massage Business in Erie, Colorado</title><content type='html'>&lt;p&gt;Tempo Software and Consulting (my LLC) happily announces the publishing of its first website, for my client running a massage therapy business in Erie, Colorado. Please have a look - comments and feedback are invited.&lt;/p&gt;&lt;p&gt;And if in fact you live in the Boulder County area, Aimee is a very gifted therapist who has taken care of my aches and pains for many years. Should you be looking for a massage therapist, I would very sincerely recommend her.&lt;/p&gt;&lt;p&gt;Full disclosure: Aimee is my wife, but I submit that my endorsement remains objective - since she was my massage therapist of choice for many years before we even started dating. And, yes, I billed her at a discounted rate.&lt;/p&gt;&lt;p&gt;Her business is &lt;a href="http://www.handsonharmonymassage.com/"&gt;Hands On Harmony Massage&lt;/a&gt;. Enjoy.&lt;/p&gt;&lt;p&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-3082316974634622165?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/3082316974634622165/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/01/portfolio-entry-1-massage-business-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3082316974634622165'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3082316974634622165'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/01/portfolio-entry-1-massage-business-in.html' title='Portfolio Entry #1: Massage Business in Erie, Colorado'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-3317056738845130225</id><published>2011-01-06T17:01:00.000-07:00</published><updated>2011-02-21T08:14:28.854-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JSON'/><category scheme='http://www.blogger.com/atom/ns#' term='CXF'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='webservices'/><title type='text'>Versioning XML and JSON Models in a CXF REST Service</title><content type='html'>&lt;p&gt;Following up on my &lt;a href="http://blog.temposwc.com/2011/01/add-json-support-to-your-rest-service.html"&gt;previous post&lt;/a&gt; around how simple it was to add JSON support to my CXF-based REST service...now I&amp;#39;ll add versioning support to my XML-annotated model and see what happens.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/01/versioning-xml-and-json-models-in-cxf.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-3317056738845130225?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/3317056738845130225/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/01/versioning-xml-and-json-models-in-cxf.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3317056738845130225'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3317056738845130225'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/01/versioning-xml-and-json-models-in-cxf.html' title='Versioning XML and JSON Models in a CXF REST Service'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-2756431121725412454</id><published>2011-01-04T15:47:00.000-07:00</published><updated>2011-02-21T08:14:42.875-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JSON'/><category scheme='http://www.blogger.com/atom/ns#' term='CXF'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='webservices'/><title type='text'>Add JSON Support to your REST Service</title><content type='html'>&lt;p&gt;Continuing from my previous post about &lt;a href="http://blog.temposwc.com/2011/01/unit-test-your-rest-service.html"&gt;a standalone unit-test mechanism for your RESTful service&lt;/a&gt;, now I&amp;#39;ll add JSON support to that service, which already gives you XML. This turns out to be a pretty simple thing - all we need is:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Tell the RESTful service to produce both XML and JSON&lt;/li&gt;&lt;li&gt;Add the runtime dependency you&amp;#39;ll need &lt;/li&gt;&lt;li&gt;Add some tests to confirm things&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/01/add-json-support-to-your-rest-service.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-2756431121725412454?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/2756431121725412454/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/01/add-json-support-to-your-rest-service.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/2756431121725412454'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/2756431121725412454'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/01/add-json-support-to-your-rest-service.html' title='Add JSON Support to your REST Service'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-8599183847146127937</id><published>2011-01-04T15:19:00.001-07:00</published><updated>2011-02-21T08:15:00.915-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='CXF'/><category scheme='http://www.blogger.com/atom/ns#' term='testng'/><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><category scheme='http://www.blogger.com/atom/ns#' term='webservices'/><title type='text'>Unit Test Your REST Service</title><content type='html'>&lt;p&gt;Here&amp;#39;s an approach for unit-testing your RESTful service. &lt;/p&gt;&lt;a href="http://blog.temposwc.com/2011/01/unit-test-your-rest-service.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-8599183847146127937?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/8599183847146127937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2011/01/unit-test-your-rest-service.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8599183847146127937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8599183847146127937'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2011/01/unit-test-your-rest-service.html' title='Unit Test Your REST Service'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7707361724157083239</id><published>2010-12-16T10:33:00.001-07:00</published><updated>2011-02-21T08:15:07.640-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iBatis'/><category scheme='http://www.blogger.com/atom/ns#' term='design-patterns'/><title type='text'>Populate Immutable Objects with iBatis Mapping</title><content type='html'>&lt;p&gt;In a &lt;a href="http://blog.temposwc.com/2010/08/ibatis-mapping-for-one-to-many.html"&gt;previous post&lt;/a&gt;, I provided iBatis configuration and mapping that supported populating Java objects on a per-property basis, i.e. the classes in use had to provide setters for this approach. Here I describe another iBatis pattern that populates &lt;i&gt;immutable&lt;/i&gt; objects with more than a few properties.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2010/12/populate-immutable-objects-with-ibatis.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7707361724157083239?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7707361724157083239/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/12/populate-immutable-objects-with-ibatis.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7707361724157083239'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7707361724157083239'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/12/populate-immutable-objects-with-ibatis.html' title='Populate Immutable Objects with iBatis Mapping'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4831374848807886781</id><published>2010-12-16T09:49:00.000-07:00</published><updated>2011-02-21T08:15:18.802-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='design-patterns'/><title type='text'>Notes on the Builder Pattern</title><content type='html'>&lt;p&gt;This post simply describes the &lt;a href="http://en.wikipedia.org/wiki/Builder_pattern"&gt;Builder Pattern&lt;/a&gt;, for my own future reference. This pattern is useful in constructing immutable objects that have numerous properties, some required and some optional, without using numerous constructors to support various permutations. And, of course, since the object is immutable, there are no setters or non-private fields to support post-construction alterations. With the Builder pattern, one can ensure objects are not only immutable, but have completed state initialization before use.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2010/12/notes-on-builder-pattern.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4831374848807886781?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4831374848807886781/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/12/notes-on-builder-pattern.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4831374848807886781'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4831374848807886781'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/12/notes-on-builder-pattern.html' title='Notes on the Builder Pattern'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7123639025479095349</id><published>2010-11-17T14:40:00.003-07:00</published><updated>2011-02-21T08:15:24.674-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='process'/><title type='text'>Running an Effective Agile Retrospective</title><content type='html'>&lt;p&gt;This is a summary of a presentation given by Brad Swanson of &lt;a href="http://properosolutions.com/"&gt;Propero Solutions &lt;/a&gt;on November 9, 2010, with permission from Brad. The topic was around effectively leading an &lt;a href="http://www.agilegamedevelopment.com/2008/06/when-we-talk-about-agile-we-use-phrase.html"&gt;agile retrospective meeting&lt;/a&gt;.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2010/11/running-effective-agile-retrospective.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7123639025479095349?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7123639025479095349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/11/running-effective-agile-retrospective.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7123639025479095349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7123639025479095349'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/11/running-effective-agile-retrospective.html' title='Running an Effective Agile Retrospective'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6617550959654250635</id><published>2010-11-03T14:11:00.001-06:00</published><updated>2011-02-21T08:15:35.326-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='REST'/><category scheme='http://www.blogger.com/atom/ns#' term='soap'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='webservices'/><title type='text'>Features Gained with SOAP UI Pro Version</title><content type='html'>&lt;p&gt;This documents differences between the free and Pro version of SOAP  UI, as determined solely from browsing the &lt;a href="http://www.soapui.org/"&gt;SOAP UI website&lt;/a&gt;. This might be  useful in determining if the &lt;a href="http://eviware.com/soapUI-Pro-14-day-free-trial.html"&gt;free 14-day trial&lt;/a&gt; of the Pro version is worth your time.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2010/11/features-gained-with-soap-ui-pro.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-6617550959654250635?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/6617550959654250635/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/11/features-gained-with-soap-ui-pro.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6617550959654250635'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6617550959654250635'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/11/features-gained-with-soap-ui-pro.html' title='Features Gained with SOAP UI Pro Version'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-8711984857481493869</id><published>2010-10-25T13:46:00.004-06:00</published><updated>2011-02-21T08:15:43.239-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Specify Database Connection via Maven Resource Filtering</title><content type='html'>&lt;p&gt;This is an example usage of &lt;a href="http://maven.apache.org/plugins/maven-resources-plugin/examples/filter.html"&gt;Maven&amp;#39;s resource filtering&lt;/a&gt; facility. While &lt;a href="http://www.manydesigns.com/documentation/tutorials/using-maven-profiles-and-resource-filtering.html"&gt;Paolo Predonzani&amp;#39;s excellent article&lt;/a&gt; got me pointed in the right direction, it didn&amp;#39;t quite work out for me. Here is what I did to get what I needed.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2010/10/specify-database-connection-via-maven.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-8711984857481493869?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/8711984857481493869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/10/specify-database-connection-via-maven.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8711984857481493869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8711984857481493869'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/10/specify-database-connection-via-maven.html' title='Specify Database Connection via Maven Resource Filtering'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4433036700158927984</id><published>2010-10-01T15:12:00.002-06:00</published><updated>2011-02-21T08:15:49.258-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GWT RPC'/><title type='text'>Basic GWT-RPC Application</title><content type='html'>&lt;p&gt;After stepping through &lt;a href="http://roberthanson.blogspot.com/2006/06/trivial-gwt-example.html"&gt;this introduction&lt;/a&gt; to a simple GWT-RPC example, I had to experiment a bit to (1) get it running and (2) get some clarity around the naming hooks. What I&amp;#39;ll present here is my own variation on that article&amp;#39;s code examples, and the understanding I gained around what to keep straight around the names of the various classes and files.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2010/10/basic-gwt-rpc-application.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4433036700158927984?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4433036700158927984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/10/basic-gwt-rpc-application.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4433036700158927984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4433036700158927984'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/10/basic-gwt-rpc-application.html' title='Basic GWT-RPC Application'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_ejClviH_0ls/TKZI_rONA8I/AAAAAAAAADU/Gu5_8tbo11s/s72-c/proto-gwt-rpc-idea.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4173514923781743911</id><published>2010-10-01T13:43:00.001-06:00</published><updated>2011-02-21T08:16:03.660-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GWT RPC'/><title type='text'>Tip on Specifying GWT-RPC Remote Service Relative Path</title><content type='html'>&lt;p&gt;Robert Hanson&amp;#39;s &lt;a href="http://roberthanson.blogspot.com/2006/06/trivial-gwt-example.html"&gt;excellent introduction&lt;/a&gt; to a GWT-RPC application got me started, but I bumped into this:&lt;/p&gt;&lt;pre class="brush:xml"&gt;&lt;br&gt;com.google.gwt.user.client.rpc.StatusCodeException:&lt;br&gt;HTTP ERROR: 404&lt;br&gt;&lt;br&gt;NOT_FOUND&lt;br&gt;&lt;br&gt;RequestURI=/relative-path&lt;br&gt;&lt;/pre&gt;&lt;p&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2010/10/tip-on-specifying-gwt-remote-service.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4173514923781743911?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4173514923781743911/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/10/tip-on-specifying-gwt-remote-service.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4173514923781743911'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4173514923781743911'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/10/tip-on-specifying-gwt-remote-service.html' title='Tip on Specifying GWT-RPC Remote Service Relative Path'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-8201217305483739711</id><published>2010-08-26T15:04:00.001-06:00</published><updated>2011-02-21T08:16:11.700-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iBatis'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>iBatis Mapping for One-to-Many Relationship</title><content type='html'>&lt;p&gt;Perhaps you had a problem, like I did, understanding the &lt;a href="http://ibatisnet.sourceforge.net/DevGuide.html#d0e1036"&gt;iBatis writeup&lt;/a&gt; on loading complex collection properties, i.e. populating a list-based field in a domain object where that list is the many-side of a one-to-many relationship. I&amp;#39;ll admit it took me a few iterations to totally grasp what was being said, so for my future reference and yours, I&amp;#39;ll write down my notes here.&lt;br&gt;&lt;/p&gt;&lt;a href="http://blog.temposwc.com/2010/08/ibatis-mapping-for-one-to-many.html#more"&gt;Read more »&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-8201217305483739711?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/8201217305483739711/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/08/ibatis-mapping-for-one-to-many.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8201217305483739711'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8201217305483739711'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/08/ibatis-mapping-for-one-to-many.html' title='iBatis Mapping for One-to-Many Relationship'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7533502622126437722</id><published>2010-08-23T09:58:00.001-06:00</published><updated>2010-09-02T15:44:25.078-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='observer'/><category scheme='http://www.blogger.com/atom/ns#' term='utility'/><title type='text'>Monitor Changes to a File</title><content type='html'>&lt;p&gt;There are undoubtedly many options out there for file-system change monitors - i.e. a component that will notify your Java object when a given file or directory has changed, among many others I'm sure:&amp;nbsp;&lt;a href="http://jnotify.sourceforge.net/"&gt;jnotify&lt;/a&gt;&amp;nbsp;and&amp;nbsp;&lt;a href="http://jpathwatch.wordpress.com/"&gt;jpathwatch&lt;/a&gt;, the latter of which is based on &lt;a href="http://jcp.org/en/jsr/detail?id=203"&gt;upcoming Java 7 NIO enhancements&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;I gave jpathwatch a try recently, at a time when I was under a tight deadline and didn't want to reinvent a wheel. This presents a perfectly fine API that worked out quite well and quite quickly for me in my Windows environment, but alas when I deployed to Linux, I bumped into an unsatisfied link error. The problem was around&amp;nbsp;libc.so.6 and GLIBC_2.4, and I gave it a reasonable first effort to try quickly finding the resolution - assuming I'd deployed incorrectly, or my Linux box was out of date, etc. It was neither of these - OK, in fairness, it &lt;em&gt;could&lt;/em&gt;&amp;nbsp;be an out-of-date Linux box, but my experiment was to just try it out on our customers' target system - and the same problem occurred. So out-of-date becomes a moot point.&lt;/p&gt;&lt;p&gt;As I mentioned, I was under a tight deadline, so I began some quick prototyping to see if I could reinvent something but without relying on native libraries (as jpathwatch did). That would give me the added advantage of a smaller runtime footprint, which was another customer requirement. Since we are really talking about an &lt;a href="http://en.wikipedia.org/wiki/Observer_pattern"&gt;Observer pattern&lt;/a&gt;, here's how I started:&lt;/p&gt;&lt;pre class="brush:java"&gt;public interface FileChangeListener {&lt;br /&gt;&lt;br /&gt;    void fileModified(String file);&lt;br /&gt;&lt;br /&gt;    void fileDeleted(String file);&lt;br /&gt;&lt;br /&gt;    void fileCreated(String file);&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;That specifies the observer. Here's a simple monitor interface:&lt;/p&gt;&lt;pre class="brush:java"&gt;public interface AbstractFileChangeMonitor {&lt;br /&gt;&lt;br /&gt;    void watch(FileChangeListener listener, final String filename);&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;This one is a bit limited, since it supports just one listener (observer) for one file. But my goal is not (yet) to provide a full-featured framework - I just need to knock out the problem at hand without any gold-plating. I'm a firm believer in doing the least I have to for a given problem - first make it work, then make it fast, then extend it, ... etc., but only if subsequent steps are called for. In either event, here's an implementation of the monitor - this one polls the file in question to detect changes, running in a thread so the client process isn't blocked:&lt;/p&gt;&lt;pre class="brush:java"&gt;public class PollingFileChangeMonitor implements AbstractFileChangeMonitor&lt;br /&gt;{&lt;br /&gt;    private final Logger logger = LoggerFactory.getLogger(getClass().getSimpleName());&lt;br /&gt;    private FileChangeListener listener;&lt;br /&gt;    private boolean done;&lt;br /&gt;    private Thread watch;&lt;br /&gt;    private int pollingInterval;&lt;br /&gt;&lt;br /&gt;    public PollingFileChangeMonitor(int interval) {&lt;br /&gt;        pollingInterval = interval;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void watch(FileChangeListener theListener, final String filename)&lt;br /&gt;    {&lt;br /&gt;        if (watch == null) {&lt;br /&gt;            listener = theListener;&lt;br /&gt;            watch = new Thread() {&lt;br /&gt;                public void run() {&lt;br /&gt;                    try  {&lt;br /&gt;                        init(filename);&lt;br /&gt;                    } catch (Exception e) {&lt;br /&gt;                        throw new IllegalStateException(e);&lt;br /&gt;                    }&lt;br /&gt;                 }&lt;br /&gt;             };&lt;br /&gt;            watch.start();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private void init(String filename) throws Exception&lt;br /&gt;    {&lt;br /&gt;        boolean exists = false;&lt;br /&gt;        long modTime = -1;&lt;br /&gt;        File file = new File(filename);&lt;br /&gt;        if (file.exists())&lt;br /&gt;        {&lt;br /&gt;            exists = true;&lt;br /&gt;            modTime = file.lastModified();&lt;br /&gt;            logger.info("====&amp;gt; File '" + filename + "' exists; change monitor is running...");&lt;br /&gt;        } else&lt;br /&gt;        {&lt;br /&gt;            logger.info("====&amp;gt; File '" + filename + "' does NOT exist; change monitor is running...");&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        while (!done) {&lt;br /&gt;            try {&lt;br /&gt;                watch.sleep(pollingInterval);&lt;br /&gt;            } catch (InterruptedException e) {&lt;br /&gt;                // ignore for now&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            if (!exists &amp;amp;&amp;amp; file.exists()) {&lt;br /&gt;                exists = true;&lt;br /&gt;                logger.info("====&amp;gt; File '" + filename + "' has been created; notify listener...");&lt;br /&gt;                listener.fileCreated(filename);&lt;br /&gt;            } else if (exists &amp;amp;&amp;amp; !file.exists())  {&lt;br /&gt;                exists = false;&lt;br /&gt;                logger.info("====&amp;gt; File '" + filename + "' has been deleted; notify listener...");&lt;br /&gt;                listener.fileDeleted(filename);&lt;br /&gt;            } else if (exists &amp;amp;&amp;amp; file.exists()) {&lt;br /&gt;                long timestamp = file.lastModified();&lt;br /&gt;                if (timestamp &amp;gt; modTime) {&lt;br /&gt;                    modTime = timestamp;&lt;br /&gt;                    logger.info("====&amp;gt; File '" + filename + "' has been modified; notify listener...");&lt;br /&gt;                    listener.fileModified(filename);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;What the listener does when notified is not really important in the context of this post; it can be anything. While I've to a certain extent "reinvented" something here, I've gotten away from reliance on native code and the potential deployment headaches around that, I've reduced my runtime footprint, and for that matter I've solved the problem with about the same amount of code I needed for the &lt;a href="http://jpathwatch.wordpress.com/documentation/getting-started/"&gt;boiler-plate suggested by jpathwatch&lt;/a&gt;.&lt;/p&gt;&lt;p&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7533502622126437722?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7533502622126437722/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/08/monitor-changes-to-file.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7533502622126437722'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7533502622126437722'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/08/monitor-changes-to-file.html' title='Monitor Changes to a File'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-143734556607918474</id><published>2010-08-23T09:58:00.000-06:00</published><updated>2010-08-23T09:58:25.946-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iBatis'/><category scheme='http://www.blogger.com/atom/ns#' term='ACID'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='BASE'/><title type='text'>Handling Partially Failed Transactions</title><content type='html'>Requirements for a web application I've developed include submitting a form that results in many database operations, including a mix-and-match of updates, inserts and deletes. Since there can be hundreds of such operations, my initial approach was to execute them in a single batch for a reasonable response time. Now, if what you want is fast execution and truly an all-or-nothing transactional behavior, using batch execution is a good idea. However, on iteration with my customers, the requirements evolved into something a bit more sophisticated: if any operations in the submitted set fails - e.g. attempting to insert a duplicate row with a unique index - we want all other operations to succeed, and we want to identify the operations that did not succeed.&lt;br /&gt;&lt;br /&gt;What's I've described of course is not strictly "transactional" - i.e. the all-or-nothing constraint is not in play, but for the application's needs, this is perfectly acceptable. And since what I describe involves removing the batch execution, the overall set of operations will take longer to execute - but again, this is acceptable for the application's goals. You won't always find these trade-offs to be acceptable, but when you do, I'll describe one mechanism for making it work - my strategy includes the following:&lt;br /&gt; &lt;br /&gt;1 - Execute the collection of operations in a single transaction, albeit without batch&lt;br /&gt;2 - Catch and deal with exceptions around operations that fail&lt;br /&gt;3 - Provide return values that includes lists of successes and failures&lt;br /&gt;&lt;br /&gt;Here are some snippets that provides these things. I start with an enumeration of the operation types I care about:&lt;br /&gt;&lt;pre class="brush:java"&gt;    private enum OP {add, update, delete}&lt;br /&gt;&lt;/pre&gt;Next I provide a result object that can be examined by the client to identify successes and failures. I use generics for type-safety - the &lt;tt&gt;Key&lt;/tt&gt; and &lt;tt&gt;Value&lt;/tt&gt; types represent your application-specific database key and object classes:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class OperationResult {&lt;br /&gt;    private Map&amp;lt;Key, Value&amp;gt; successes = new HashMap();&lt;br /&gt;    private Map&amp;lt;Key, Exception&amp;gt; failures = new HashMap();&lt;br /&gt;&lt;br /&gt;    public OperationResult(Map&amp;lt;Key, Value&amp;gt; succeeded, Map&amp;lt;Key, Value&amp;gt; failed) {&lt;br /&gt;        successes.putAll(succeeded != null? succeeded : new HashMap());&lt;br /&gt;        failures.putAll(failed != null ? failed : new HashMap());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Map&amp;lt;Key, Value&amp;gt; getSuccesses() {&lt;br /&gt;        return successes;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public Map&amp;lt;Key, Exception&amp;gt; getFailures() {&lt;br /&gt;        return failures;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Finally I provide the workhorse method that executes all operations, capturing exception details in the face of any failures, and proceeding in either event so it can commit as many successes as possible. Due to the way this API is set up, clients can execute only one operation type at a time:&lt;br /&gt;&lt;pre class="brush:java"&gt;    private OperationResult execute(Map&amp;lt;Key, Value&amp;gt; map, String query, OP operation) {&lt;br /&gt;        Map&amp;lt;Key, Value&amp;gt; successes = new HashMap();&lt;br /&gt;        Map&amp;lt;Key, Exception&amp;gt; failures = new HashMap();&lt;br /&gt;        try {&lt;br /&gt;            try {&lt;br /&gt;                sqlMapClient.startTransaction();&lt;br /&gt;            } catch (SQLException e) {&lt;br /&gt;                throw new IllegalStateException(&amp;quot;None of the operations succeeded - txn could not be started&amp;quot;,&lt;br /&gt;                    e);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            for (Map.Entry&amp;lt;Key, Value&amp;gt; entry : map.entrySet()) {&lt;br /&gt;                try {&lt;br /&gt;                    switch (operation) {&lt;br /&gt;                        case add:&lt;br /&gt;                            return sqlMapClient.add(query, entry.getValue()); break;&lt;br /&gt;                        case update:&lt;br /&gt;                            return sqlMapClient.update(query, entry.getValue()); break;&lt;br /&gt;                        case delete:&lt;br /&gt;                            return sqlMapClient.delete(query, entry.getKey()); break;&lt;br /&gt;                    }&lt;br /&gt;                    successes.put(entry.getKey(), entry.getValue());&lt;br /&gt;                } catch (Exception e) {&lt;br /&gt;                    failures.put(entry.getKey(), e);&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            try {&lt;br /&gt;                sqlMapClient.commitTransaction();&lt;br /&gt;            } catch (SQLException e) {&lt;br /&gt;                throw new IllegalStateException(&amp;quot;None of the operations succeeded - txn could not be committed&amp;quot;,&lt;br /&gt;                    e);&lt;br /&gt;            }&lt;br /&gt;        } finally {&lt;br /&gt;            try {&lt;br /&gt;                sqlMapClient.endTransaction();&lt;br /&gt;            } catch (SQLException e) {&lt;br /&gt;                throw new IllegalStateException(&amp;quot;Problem ending the txn - you should check whether the operations succeeded or not&amp;quot;,&lt;br /&gt;                    e);&lt;br /&gt;            }&lt;br /&gt;&lt;br /&gt;            return new OperationResult(successes, failures);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;I'm using iBatis 2.x, as you might be able to guess from the SQL Map Client reference; it is assumed that the client passes in the correct query ID for the given operation.&lt;br /&gt;&lt;br /&gt;One guideline from my Computer Science education that always stuck with me is "make it right first, then make it fast". The solution provided here is, as noted, not going to be as fast as batch execution - but it does meet the requirements correctly. If there is still a need to make it "faster", we could consider using an asynchronous API so that the response time from the user's point of view is improved. This type of approach is in fact used in many enterprise settings, leveraging so called &lt;a href="http://devblog.streamy.com/tag/eventual-consistency/"&gt;BASE&lt;/a&gt; &lt;a href="http://en.wikipedia.org/wiki/Eventual_consistency"&gt;behavior&lt;/a&gt; instead of the traditional ACID approach.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-143734556607918474?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/143734556607918474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/08/handling-partially-failed-transactions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/143734556607918474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/143734556607918474'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/08/handling-partially-failed-transactions.html' title='Handling Partially Failed Transactions'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4090423415180192302</id><published>2010-08-10T13:34:00.001-06:00</published><updated>2010-08-10T13:36:52.014-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IceFaces'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>Open a PDF Resource in Browser Tab</title><content type='html'>Though the title refers to a PDF, the MIME type of the resource is not crucial to this. My goal was to open a PDF file in a new browser tab, and with IceFaces I initially tried the &lt;a href="http://www.icefaces.org/docs/v1_8_0/tld/ice/outputResource.html"&gt;outputResource&lt;/a&gt; widget. This seemed like the right way to go, and according to the TLD I should have been able to open the resource directly in the browser as opposed to simply downloading it - but I could not get that to work as expected. &lt;br /&gt;&lt;br /&gt;Instead, I backed off to a more straightforward link approach, i.e. use of the &lt;a href="http://www.icefaces.org/docs/v1_8_0/tld/ice/outputLink.html"&gt;outputLink&lt;/a&gt; control, as follows: &lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;ice:form&amp;gt;&lt;br /&gt;    &amp;lt;ice:outputLink value=&amp;quot;doc/some-PDF-file.pdf&amp;quot;&lt;br /&gt;                    target=&amp;quot;_blank&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;ice:outputText value=&amp;quot;Open PDF&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/ice:outputLink&amp;gt;&lt;br /&gt;&amp;lt;/ice:form&amp;gt;&lt;br /&gt;&lt;/pre&gt;The plain vanilla HTML is dirt-simple:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;a target=&amp;quot;_blank&amp;quot; href=&amp;quot;doc/some-PDF-file.pdf&amp;quot;&amp;gt;Open PDF&amp;lt;/a&amp;gt;&lt;br /&gt;&lt;/pre&gt;This assumes the "doc" directory location is under your webapp root context.&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4090423415180192302?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4090423415180192302/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/08/open-pdf-resource-in-browser-tab.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4090423415180192302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4090423415180192302'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/08/open-pdf-resource-in-browser-tab.html' title='Open a PDF Resource in Browser Tab'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6907385664704327542</id><published>2010-08-10T13:19:00.000-06:00</published><updated>2010-08-10T13:19:06.380-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>SELECT Menu Doesn't Grey-Out When Disabled in Firefox 3.6.8</title><content type='html'>The title of this post pretty much says it all, just in case you've been Google'ing it to find some kind of reference to this. Since I found nothing, and since I solved my problem by manually changing the background-color on my SELECT item, I write it up here. &lt;br /&gt;&lt;br /&gt;The problem does not occur in Firefox 3.5.11, and only occurs in 3.6.8 when you specify the SIZE attribute in a SELECT menu with MULTIPLE selectable items. Leave off the SIZE attribute, and the desired greying-out happens just fine, albeit with a 20-item window into your menu items. If that's not acceptable, you can use some JavaScript to convince the menu to grey out and then come back to normal as you disable and enable it. For example, given some HTML that defines some menu items, which should be disabled when a given checkbox is selected:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;input type=&amp;quot;checkbox&amp;quot; name=&amp;quot;items&amp;quot; onclick=&amp;quot;enableDisable(this, formname)&amp;quot; .../&amp;gt;&lt;br /&gt;....&lt;br /&gt;&amp;lt;select id=&amp;quot;menu&amp;quot; multiple size=&amp;quot;4&amp;quot;&amp;gt;&lt;br /&gt;....&lt;br /&gt;&amp;lt;/select&amp;gt;&lt;br /&gt;&lt;/pre&gt;...here's the JS that can be used:&lt;br /&gt;&lt;pre class="brush:java"&gt;function enableDisable(input, formName) {&lt;br /&gt;    if (input.checked) {&lt;br /&gt;        disable(formName);&lt;br /&gt;    } else {&lt;br /&gt;        enable(formName);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;function disable(theForm) {   &lt;br /&gt;    if (theForm.menu != null) {&lt;br /&gt;        theForm.menu.disabled = true;&lt;br /&gt;        // address the Firefox problem - grey it out manually:&lt;br /&gt;        theForm.menu.style.backgroundColor="#DBDBDB";&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;function enable(theForm)&lt;br /&gt;{&lt;br /&gt;    if (document.forms[theForm].menu != null) {&lt;br /&gt;        document.forms[theForm].menu.disabled = false;&lt;br /&gt;        // address the Firefox problem - reset to normal color, manually:&lt;br /&gt;        document.forms[theForm].menu.style.backgroundColor="#FFFFFF";&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;i&gt;Disclaimer&lt;/i&gt;: As is typical with my code snippet posts, I've extracted the relevant statements from my production code, manually editing field names, method names, etc. to protect my client's IP. Since I've not taken the time to confirm the extracted code (I'm lazy, I'm too busy, it's left as a reader exercise, etc.), I can't guarantee that your copy-paste exercise will "just work" - but I do believe I've given you the basic building blocks to make it work for your application.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-6907385664704327542?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/6907385664704327542/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/08/select-menu-doesnt-grey-out-when.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6907385664704327542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6907385664704327542'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/08/select-menu-doesnt-grey-out-when.html' title='SELECT Menu Doesn&apos;t Grey-Out When Disabled in Firefox 3.6.8'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6518267410989801501</id><published>2010-08-10T12:48:00.000-06:00</published><updated>2010-08-10T12:48:35.994-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='IceFaces'/><category scheme='http://www.blogger.com/atom/ns#' term='servlet'/><title type='text'>Get Full Filepath from IceFaces Webapp</title><content type='html'>Just a quick little snippet - if you need the "&lt;a href="http://download.oracle.com/javaee/5/api/javax/servlet/ServletContext.html"&gt;real path&lt;/a&gt;" for a resource residing within your webapp directory tree, here's how I've done it in &lt;a href="http://www.icefaces.org"&gt;IceFaces&lt;/a&gt;:&lt;br /&gt;&lt;pre class="brush:java"&gt;        FacesContext fc = FacesContext.getCurrentInstance();&lt;br /&gt;        ExternalContext ec = fc.getExternalContext();&lt;br /&gt;        ServletContext servletCtx = (ServletContext)ec.getContext();&lt;br /&gt;        String filePath = servletCtx.getRealPath("/directory-under-webapp-root/somefile.pdf");&lt;br /&gt;&lt;/pre&gt;In principle the same idea applies regardless of IceFaces or any other web framework - you just need to get the &lt;tt&gt;ServletContext&lt;/tt&gt;. That said, it appears that J2EE servers are not required to return anything beyond &lt;tt&gt;null&lt;/tt&gt; from the &lt;tt&gt;getRealPath&lt;/tt&gt; method.&lt;br /&gt;&lt;br /&gt;My setting is with an exploded WAR - but if your webapp remains unexploded, &lt;a href="http://www.jguru.com/forums/view.jsp?EID=1038798"&gt;here&lt;/a&gt; are some &lt;a href="http://www.coderanch.com/t/211165/JSF/java/getRealPath-Depricated"&gt;tips &lt;/a&gt;to get around that. &lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-6518267410989801501?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/6518267410989801501/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/08/get-full-filepath-from-icefaces-webapp.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6518267410989801501'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6518267410989801501'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/08/get-full-filepath-from-icefaces-webapp.html' title='Get Full Filepath from IceFaces Webapp'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-8355310719127393513</id><published>2010-07-28T11:54:00.001-06:00</published><updated>2010-07-28T11:56:56.038-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iBatis'/><category scheme='http://www.blogger.com/atom/ns#' term='logging'/><category scheme='http://www.blogger.com/atom/ns#' term='log4j'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>Log SQL Statements with iBatis</title><content type='html'>Here's what I've done to enable logging of SQL statements in an iBatis context. This adds to the &lt;a href="http://stackoverflow.com/questions/3014318/how-to-do-ibatis-version-of-show-sql"&gt;various&lt;/a&gt; &lt;a href="http://stackoverflow.com/questions/2635058/ibatis-get-executed-sql"&gt;bits&lt;/a&gt; of &lt;a href="http://www.apachebookstore.com/confluence/oss/display/IBATIS/How+do+I+get+SqlMapClient+to+log+SQL+statements"&gt;advice&lt;/a&gt; I've found on the web.&lt;br /&gt;&lt;br /&gt;Perhaps it's due to the environment I'm using (Java 1.6, iBatis 2.3.4, SLF4J 1.5.11, Log4J 1.2.15), but the &lt;a href="http://ibatisnet.sourceforge.net/DevGuide.html#d0e2600"&gt;guidance offered in the iBatis Dev Guide&lt;/a&gt; is, in my experience, not quite right. In fairness, something must have changed since last I tried that advice (was I using Java 1.5 back then? I'm not sure), since, as I recall, it resulted in the SQL logging I needed. However, at the moment I find that simply configuring this:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;logger name=&amp;quot;java.sql.PreparedStatement&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;level value=&amp;quot;debug&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/logger&amp;gt;&lt;br /&gt;&lt;/pre&gt;...yields no SQL output at all. &lt;br /&gt;&lt;br /&gt;On target is the &lt;a href="http://www.apachebookstore.com/confluence/oss/display/IBATIS/How+do+I+get+SqlMapClient+to+log+SQL+statements"&gt;advice given in the Apache Bookstore&lt;/a&gt;, i.e. to specify this:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;logger name=&amp;quot;java.sql&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;level value=&amp;quot;debug&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/logger&amp;gt;&lt;br /&gt;&lt;/pre&gt;This yields all of connection information, prepared statements, values inserted as prepared statement parameters and result sets, something like this:&lt;br /&gt;&lt;pre class="brush:xml"&gt;Connection&lt;br /&gt;{conn-100008} Preparing Statement:  select name, description  from mytable where name like ?      &lt;br /&gt;{pstm-100009} Executing Statement:  select name, description  from mytable  where name like ?               &lt;br /&gt;{pstm-100009} Parameters: [name%]&lt;br /&gt;{pstm-100009} Types: []&lt;br /&gt;{rset-100010} ResultSet&lt;br /&gt;{rset-100010} Header: [NAME, DESCRIPTION]&lt;br /&gt;{rset-100010} Result: [name2, Desc for name2]&lt;br /&gt;{rset-100010} Result: [name3, Desc for name3]&lt;br /&gt;&lt;/pre&gt;You'll possibly get even more output, depending on your app - since the &lt;tt&gt;java.sql&lt;/tt&gt; namespace is more than just these things. If this is a concern, use the following:&lt;br /&gt;&lt;pre class="brush:xml"&gt;    &amp;lt;logger name=&amp;quot;java.sql.Connection&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;level value=&amp;quot;debug&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/logger&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;logger name=&amp;quot;java.sql.PreparedStatement&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;level value=&amp;quot;debug&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/logger&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;logger name=&amp;quot;java.sql.ResultSet&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;level value=&amp;quot;debug&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/logger&amp;gt;&lt;br /&gt;&lt;/pre&gt;I've personally found that the output from &lt;tt&gt;PreparedStatement&lt;/tt&gt; and &lt;tt&gt;ResultSet&lt;/tt&gt; are the most useful - but omitting &lt;tt&gt;Connection&lt;/tt&gt; appears to preclude these log statements from appearing. The &lt;tt&gt;Connection&lt;/tt&gt; logger also appears to control the output level of &lt;tt&gt;PreparedStatement&lt;/tt&gt; and &lt;tt&gt;ResultSet&lt;/tt&gt; - i.e., setting &lt;tt&gt;Connection&lt;/tt&gt; to INFO results in zero output from any of them. I begin to suspect that &lt;tt&gt;Connection&lt;/tt&gt; has a &lt;a href="http://logging.apache.org/log4j/1.2/manual.html"&gt;parent relationship&lt;/a&gt; to the other two; perhaps &lt;i&gt;this&lt;/i&gt; is what's changed since last I did this.&lt;br /&gt;&lt;br /&gt;In either event, here are some SQL-interceptor tools recommended by various posts (though I've yet to try either one):&lt;br /&gt;&lt;br /&gt;&lt;b&gt;p6spy&lt;/b&gt;: &lt;a href="http://www.p6spy.com/"&gt;http://www.p6spy.com/&lt;/a&gt;&lt;br /&gt;&lt;b&gt;jdbcdslog&lt;/b&gt;: &lt;a href="http://code.google.com/p/jdbcdslog/"&gt;http://code.google.com/p/jdbcdslog/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And here are all the web references I used for this post:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Stack Overflow&lt;/b&gt;: &lt;br /&gt;&lt;a href="http://stackoverflow.com/questions/3014318/how-to-do-ibatis-version-of-show-sql"&gt;http://stackoverflow.com/questions/3014318/how-to-do-ibatis-version-of-show-sql&lt;/a&gt;&lt;br /&gt;&lt;a href="http://stackoverflow.com/questions/2635058/ibatis-get-executed-sql"&gt;http://stackoverflow.com/questions/2635058/ibatis-get-executed-sql&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Apache Bookstore&lt;/b&gt;: &lt;a href="http://www.apachebookstore.com/confluence/oss/display/IBATIS/How+do+I+get+SqlMapClient+to+log+SQL+statements"&gt;http://www.apachebookstore.com/confluence/oss/display/IBATIS/How+do+I+get+SqlMapClient+to+log+SQL+statements&lt;/a&gt;&lt;br /&gt;&lt;b&gt;iBatis Dev Guide&lt;/b&gt;: &lt;a href="http://ibatisnet.sourceforge.net/DevGuide.html#d0e2600"&gt;http://ibatisnet.sourceforge.net/DevGuide.html#d0e2600&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-8355310719127393513?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/8355310719127393513/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/log-sql-statements-with-ibatis.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8355310719127393513'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8355310719127393513'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/log-sql-statements-with-ibatis.html' title='Log SQL Statements with iBatis'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-1181643662684994324</id><published>2010-07-27T10:53:00.001-06:00</published><updated>2011-01-25T14:51:14.805-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='generics'/><title type='text'>Generic Conversion Between List&lt;T&gt; and String</title><content type='html'>&lt;p&gt;Here's a reusable generic "converter" interface for converting between comma-separated strings and &lt;tt&gt;List&lt;/tt&gt;s of a given type:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;pre class="brush:java"&gt;/**&lt;br /&gt; * Converts between comma-separated string and lists of type T.&lt;br /&gt; */&lt;br /&gt;public abstract class AbstractConverter&amp;lt;T&amp;gt; {&lt;br /&gt;&lt;br /&gt;    protected abstract T getInstance(String s);&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Converts a list of items into a comma-separated string&lt;br /&gt;     */&lt;br /&gt;    public String toString(List&amp;lt;T&amp;gt; items)&lt;br /&gt;    {&lt;br /&gt;        String s= "";&lt;br /&gt;        String comma = "";&lt;br /&gt;        for (T item : items) {&lt;br /&gt;            s += comma + item;&lt;br /&gt;            comma = ",";&lt;br /&gt;        }&lt;br /&gt;        return s;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Converts a comma-separated string into a list of items.&lt;br /&gt;     */&lt;br /&gt;    public List&amp;lt;T&amp;gt; toList(String csv)&lt;br /&gt;    {&lt;br /&gt;        if (csv == null)&lt;br /&gt;        {&lt;br /&gt;            return new ArrayList&amp;lt;T&amp;gt;();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        String[] items = csv.split(",");&lt;br /&gt;        List&amp;lt;T&amp;gt; theList = new ArrayList&amp;lt;T&amp;gt;();&lt;br /&gt;        for (String item : items) {&lt;br /&gt;            T thisItem = getInstance(item); &lt;br /&gt;            theList.add(thisItem);&lt;br /&gt;        }&lt;br /&gt;        return theList;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /**&lt;br /&gt;     * Converts a comma-separated string into a list of items, excluding any items in the given&lt;br /&gt;     * exclude list.&lt;br /&gt;     */&lt;br /&gt;    public List&amp;lt;T&amp;gt; toList(String csv, List&amp;lt;T&amp;gt; excludes)&lt;br /&gt;    {&lt;br /&gt;        if (csv == null)&lt;br /&gt;        {&lt;br /&gt;            return new ArrayList&amp;lt;T&amp;gt;();&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        String[] items = csv.split(",");&lt;br /&gt;        List&amp;lt;T&amp;gt; theList = new ArrayList();&lt;br /&gt;        for (String item : items) {&lt;br /&gt;            T thisItem = getInstance(item);&lt;br /&gt;            if (!excludes.contains(thisItem))&lt;br /&gt;            {&lt;br /&gt;                theList.add(thisItem);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;        return theList;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;I can now provide concrete converter classes of types as needed, for example:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;pre class="brush:java"&gt;/**&lt;br /&gt; * Converts between comma-separated string and lists of MyType objects.&lt;br /&gt; */&lt;br /&gt;public class MyConverter extends AbstractConverter&amp;lt;MyType&amp;gt; {&lt;br /&gt;&lt;br /&gt;    // provide an instance of the custom type&lt;br /&gt;    protected MyType getInstance(String s)&lt;br /&gt;    {&lt;br /&gt;        return new MyType(s);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // optionally enforce a singleton, if needed&lt;br /&gt;    private MyConverter() {&lt;br /&gt;        // EMPTY&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // provide a convenience for static utility classes&lt;br /&gt;    private static AbstractConverter&amp;lt;MyType&amp;gt; cvt = new MyConverter();&lt;br /&gt;    public static AbstractConverter&amp;lt;MyType&amp;gt; inst()&lt;br /&gt;    {&lt;br /&gt;        return cvt;&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Since I want to make things convenient for clients of this, I now provide a static utility class that precludes the need to instantiate anything:&lt;br /&gt;&lt;br /&gt;&lt;/p&gt;&lt;pre class="brush:java"&gt;public class MyHelper {&lt;br /&gt;&lt;br /&gt;    private static AbstractConverter cvt = MyConverter.inst();&lt;br /&gt;&lt;br /&gt;    public static String toString(List&amp;lt;MyType&amp;gt; items)&lt;br /&gt;    {&lt;br /&gt;        return cvt.toString(items);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static List&amp;lt;MyType&amp;gt; toList(String csv)&lt;br /&gt;    {&lt;br /&gt;        return cvt.toList(csv);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static List&amp;lt;MyType&amp;gt; toList(String csv, List&amp;lt;MyType&amp;gt; excludes)&lt;br /&gt;    {&lt;br /&gt;        return cvt.toList(csv, excludes);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;p&gt;Now my clients can call e.g. &lt;tt&gt;MyHelper.toList("x, y, z")&lt;/tt&gt; instead of &lt;tt&gt;MyConverter.inst().toList("x, y, z")&lt;/tt&gt;. A small advantage, but it does help with readability and reinforces the notion that this is a collection of utility functions.&lt;/p&gt;&lt;p&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-1181643662684994324?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/1181643662684994324/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/convert-between-list-and-string.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/1181643662684994324'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/1181643662684994324'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/convert-between-list-and-string.html' title='Generic Conversion Between List&amp;lt;T&amp;gt; and String'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-2074220760533347517</id><published>2010-07-23T10:03:00.003-06:00</published><updated>2010-07-23T10:06:48.016-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='logging'/><category scheme='http://www.blogger.com/atom/ns#' term='log4j'/><title type='text'>When Log4j Logs Messages Twice...</title><content type='html'>Since my Google'ing &lt;a href="http://tapestry-users.832.n2.nabble.com/T5-logging-statements-logged-twice-using-Log4j-td4597599.html#a4597599"&gt;only gave me a clue&lt;/a&gt; instead of the complete answer on this one, I post it here for future reference.&lt;br /&gt;&lt;br /&gt;When a given &lt;tt&gt;logger&lt;/tt&gt; in a log4j configuration is outputting messages twice to your appender, you might try adding the &lt;tt&gt;additivity&lt;/tt&gt; attribute as a fix:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;logger name=&amp;quot;myLogger&amp;quot; additivity=&amp;quot;false&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;level value=&amp;quot;info&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;appender-ref ref=&amp;quot;myFile&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/logger&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Same idea for &lt;tt&gt;logger&lt;/tt&gt; or for &lt;tt&gt;category&lt;/tt&gt;. From the DTD (I'm using version 1.2.15):&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;!ELEMENT category (param*,(priority|level)?,appender-ref*)&amp;gt;&lt;br /&gt;&amp;lt;!ATTLIST category&lt;br /&gt;  class         CDATA   #IMPLIED&lt;br /&gt;  name  CDATA #REQUIRED&lt;br /&gt;  additivity (true|false) &amp;quot;true&amp;quot;  &lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;!ELEMENT logger (level?,appender-ref*)&amp;gt;&lt;br /&gt;&amp;lt;!ATTLIST logger&lt;br /&gt;  name  CDATA #REQUIRED&lt;br /&gt;  additivity (true|false) &amp;quot;true&amp;quot;  &lt;br /&gt;&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This worked for me. As you can see, the &lt;tt&gt;additivity&lt;/tt&gt; defaults to &lt;tt&gt;true&lt;/tt&gt;. There may be other fixes, but I didn't dig any further after fixing my problem.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-2074220760533347517?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/2074220760533347517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/when-log4j-logs-messages-twice.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/2074220760533347517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/2074220760533347517'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/when-log4j-logs-messages-twice.html' title='When Log4j Logs Messages Twice...'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-3733208605858165532</id><published>2010-07-22T14:26:00.000-06:00</published><updated>2010-07-22T14:26:48.096-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><title type='text'>Determine Which Row is Selected in a JSF Table</title><content type='html'>Let's say you have a JSF table with numerous rows, and that when one of them is "selected" you'd like to know which one it is. Let's assume a JSF event is fired off when you click on the row, resulting in a callback to your application. In this callback, you want to determine the identity of the row in a way that correlates to your application, such that you can proceed with whatever action is needed. Here's the sequence:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Table is constructed with rows representing application objects A, B and C&lt;/li&gt;&lt;li&gt;User clicks somewhere in row B, e.g. in an input text box&lt;/li&gt;&lt;li&gt;JSF event fires and your application is called back&lt;/li&gt;&lt;li&gt;The application determines that B has been selected and processes that application object as needed&lt;/li&gt;&lt;/ol&gt;Now the obvious way to "determine that B has been selected" would be to simply set the ID attribute for each row to an identifying value at table construction time. But, using such variables for the ID attribute is not allowed in JSF. Don't ask me why. Here's one way to get around this:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Add a parameter to the &lt;tt&gt;UIComponent&lt;/tt&gt; with the identity as needed&lt;/li&gt;&lt;li&gt;Register an interest in the JSF event that will get fired when the &lt;tt&gt;UIComponent&lt;/tt&gt; is selected&lt;/li&gt;&lt;li&gt;Examine the event received in your application callback to find the &lt;tt&gt;UIParameter&lt;/tt&gt; child&lt;/li&gt;&lt;li&gt;Examine the&amp;nbsp;&lt;tt&gt;UIParameter&lt;/tt&gt;&amp;nbsp;to extract the identity of the row&lt;/li&gt;&lt;/ol&gt;The code snippets, first for the JSF markup addressing step #1 and #2. Note that I'm using IceFaces, setting the per-row variable for use in each &lt;tt&gt;UIComponent&lt;/tt&gt; comprising each row:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;ice:dataTable &lt;br /&gt;     var="thisRow"&lt;br /&gt;     value="#{app.allRows}"&lt;br /&gt;    ....&lt;br /&gt;&amp;gt;&lt;br /&gt;....&lt;br /&gt;    &amp;lt;ice:inputText valueChangeListener="#{app.callback}"&amp;gt;&lt;br /&gt;        &amp;lt;f:param name="name" value="#{thisRow.id}"/&amp;gt;&lt;br /&gt;    &amp;lt;/ice:inputText&amp;gt;&lt;br /&gt;....&lt;br /&gt;&amp;lt;/ice:dataTable&amp;gt;&lt;br /&gt;&lt;/pre&gt;Here are steps #3 and #4, the application callback:&lt;br /&gt;&lt;pre class="brush:java"&gt;public void callback(ValueChangeEvent event) { &lt;br /&gt;&lt;br /&gt;    for (UIComponent child : event.getComponent().getChildren()) {&lt;br /&gt;        if (child instanceof UIParameter) {&lt;br /&gt;            UIParameter param = (UIParameter) child;&lt;br /&gt;            if ("name".equals(param.getName())) {&lt;br /&gt;                String thisID = (String)param.getValue();&lt;br /&gt;                if (thisID.equals(theIDofInterest)) {&lt;br /&gt;                    // process as needed&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-3733208605858165532?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/3733208605858165532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/determine-which-row-is-selected-in-jsf.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3733208605858165532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3733208605858165532'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/determine-which-row-is-selected-in-jsf.html' title='Determine Which Row is Selected in a JSF Table'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-1351558237409538444</id><published>2010-07-20T17:25:00.000-06:00</published><updated>2010-07-20T17:25:10.523-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><title type='text'>Using JSF Conversion with Custom Objects</title><content type='html'>My goal is to provide a user-facing input text box that accepts a comma-separated list of values, and converts these to an application-specific object. Since I'm using JSF, I'll take advantage of their &lt;a href="http://download.oracle.com/docs/cd/E17477_01/javaee/5/tutorial/doc/bnatt.html"&gt;built-in conversion facility&lt;/a&gt; - but I want to remain as decoupled from JSF as possible. This means I'll implement their Converter class, but I'll do so only with very minimal high-level business logic (as opposed to emulating their example usage in the &lt;a href="http://download.oracle.com/docs/cd/E17477_01/javaee/5/tutorial/doc/bnaus.html"&gt;JEE Tutorial example&lt;/a&gt;). That way, I can reuse my conversion logic in other frameworks, for other clients, etc.&lt;br /&gt;&lt;br /&gt;My application object is a collection of name objects. The name objects look something like this:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class MyName {   &lt;br /&gt;&lt;br /&gt;    private String name;&lt;br /&gt;&lt;br /&gt;    public MyName(String name) {&lt;br /&gt;        this.name = name;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String getName() {&lt;br /&gt;        return name;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void setName(String name) {&lt;br /&gt;        this.name = name;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public boolean equals(Object obj) {&lt;br /&gt;        if (null == obj) {&lt;br /&gt;            return true;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        if ((obj == null) || (getClass() != obj.getClass())) {&lt;br /&gt;            return false;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        MyName other = (MyName) obj;&lt;br /&gt;        return new EqualsBuilder().append(this.name, other.name).isEquals();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public int hashCode() {&lt;br /&gt;        return new HashCodeBuilder(3, 13).append(this.name).toHashCode();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String toString() {&lt;br /&gt;        return name;&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Note that I've implemented &lt;tt&gt;toString&lt;/tt&gt; as well as the &lt;tt&gt;equals&lt;/tt&gt; and &lt;tt&gt;hashCode&lt;/tt&gt; methods. The builder classes are provided by Apache Commons Lang; I've blogged about them &lt;a href="http://blog.temposwc.com/2009/06/conveniences-for-equals-hashcode-and.html"&gt;here&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The collection object uses the Java &lt;tt&gt;List&lt;/tt&gt; to wrap the name objects:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class MyNameList {&lt;br /&gt;&lt;br /&gt;    private List&amp;lt;MyName&amp;gt; names = new ArrayList&amp;lt;MyName&amp;gt;();&lt;br /&gt;&lt;br /&gt;    public MyNameList (List&amp;lt;MyName&amp;gt; names) {&lt;br /&gt;        setNames(names);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static MyNameList getInstance(String csv) {&lt;br /&gt;        if (csv == null) {&lt;br /&gt;            return new MyNameList();&lt;br /&gt;        }&lt;br /&gt;        String[] names = csv.split(&amp;quot;,&amp;quot;);&lt;br /&gt;        List&amp;lt;MyName&amp;gt; nameList = new ArrayList();&lt;br /&gt;        for (String name : names) {&lt;br /&gt;            nameList.add(new MyName(name));&lt;br /&gt;        }&lt;br /&gt;        return new MyNameList(nameList );&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public List&amp;lt;MyName&amp;gt; getNames() {&lt;br /&gt;        return names;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void setNames(List&amp;lt;MyName&amp;gt; names) {&lt;br /&gt;        this.names = names;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String toString() {&lt;br /&gt;        StringBuffer sb = new StringBuffer();&lt;br /&gt;        String comma = &amp;quot;&amp;quot;;&lt;br /&gt;        for (MyName name: names) {&lt;br /&gt;            sb.append(comma).append(name);&lt;br /&gt;            comma = &amp;quot;,&amp;quot;;&lt;br /&gt;        }&lt;br /&gt;        return sb.toString();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;Here's what the JSF converter implementation might look like - there's not much there, as planned:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class MyNameListConverter implements Converter {&lt;br /&gt;&lt;br /&gt;    public Object getAsObject(FacesContext context,&lt;br /&gt;                              UIComponent component, String newValue)&lt;br /&gt;            throws ConverterException {&lt;br /&gt;&lt;br /&gt;        return MyNameList.getInstance(newValue);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public String getAsString(FacesContext context,&lt;br /&gt;                              UIComponent component, Object value)&lt;br /&gt;            throws ConverterException {&lt;br /&gt;&lt;br /&gt;        return value == null? "" : value.toString();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;I must register the converter with JSF:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;converter&amp;gt;&lt;br /&gt;    &amp;lt;description&amp;gt;&lt;br /&gt;        Converter for CSV list of name values&lt;br /&gt;    &amp;lt;/description&amp;gt;&lt;br /&gt;    &amp;lt;converter-id&amp;gt;MyNameListConverter&amp;lt;/converter-id&amp;gt;&lt;br /&gt;    &amp;lt;converter-class&amp;gt;&lt;br /&gt;        com.mybiz.MyNameListConverter&lt;br /&gt;    &amp;lt;/converter-class&amp;gt;&lt;br /&gt;&amp;lt;/converter&amp;gt;&lt;br /&gt;&lt;/pre&gt;Finally I reference the converter in my JSF page:&lt;br /&gt;&lt;pre class="brush:xml"&gt; &amp;lt;ice:inputText id=&amp;quot;nameValues&amp;quot; partialSubmit=&amp;quot;true&amp;quot;&lt;br /&gt;     converter=&amp;quot;MyNameListConverter&amp;quot;&lt;br /&gt;     value=&amp;quot;#{bean.nameList}&amp;quot;/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Lots of moving parts are needed when working with JSF. But, the more I can encapsulate, the less it will cost to migrate to a different web framework down the road.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-1351558237409538444?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/1351558237409538444/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/using-jsf-conversion-with-custom.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/1351558237409538444'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/1351558237409538444'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/using-jsf-conversion-with-custom.html' title='Using JSF Conversion with Custom Objects'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4537214284378738955</id><published>2010-07-19T13:55:00.000-06:00</published><updated>2010-07-28T11:29:02.698-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iBatis'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><title type='text'>What Database Am I Connecting To?</title><content type='html'>If you run your functional tests in different environments (your local box, the nightly build, etc.), in addition to running your components in a production setting, you might want to get feedback at startup time around just what database you're connecting to. Since I was recently bit by nightly tests that were wiping out my development-local database tables - since I'd hardwired the connection URL - I was motivated to put this kind of feedback in place. Here are some details around how I did this.&lt;br /&gt;&lt;br /&gt;Our nightly build has certain environment variables set:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;dbip=BuildHost&lt;br /&gt;dbinstance=TestDatabase&lt;br /&gt;dbuser=username&lt;br /&gt;dbpass=password&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;To simulate this environment, I set these same variables in my shell (Unix, Cygwin, ...), changing the values as needed to facilitate connecting to my local database. Next I set up a &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-factory-placeholderconfigurer"&gt;property configuration&lt;/a&gt; in my Spring startup file that uses these variables, replacing things per the environment:&lt;br /&gt;&lt;pre class="brush:xml"&gt;    &amp;lt;bean class=&amp;quot;org.springframework.beans.factory.config.PropertyPlaceholderConfigurer&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;properties&amp;quot;&amp;gt;&lt;br /&gt;            &amp;lt;value&amp;gt;&lt;br /&gt;                database.driver=oracle.jdbc.OracleDriver&lt;br /&gt;                database.url=jdbc:oracle:thin:@//${dbip}:1521/${dbinstance}&lt;br /&gt;                database.user=${dbuser}&lt;br /&gt;                database.password=${dbpass}&lt;br /&gt;            &amp;lt;/value&amp;gt;&lt;br /&gt;        &amp;lt;/property&amp;gt;&lt;br /&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;These Spring-generated properties are used to create a &lt;a href="http://commons.apache.org/dbcp/apidocs/org/apache/commons/dbcp/BasicDataSource.html"&gt;data source&lt;/a&gt;, which is used to configure an &lt;a href="http://ibatis.apache.org/"&gt;iBatis&lt;/a&gt; &lt;a href="http://ibatis.apache.org/docs/java/user/com/ibatis/sqlmap/client/SqlMapClient.html"&gt;SQLMapClient&lt;/a&gt;, which I then use in my DAO:&lt;br /&gt;&lt;pre class="brush:xml"&gt;    &amp;lt;bean id=&amp;quot;dataSource&amp;quot; class=&amp;quot;org.apache.commons.dbcp.BasicDataSource&amp;quot;&lt;br /&gt;          destroy-method=&amp;quot;close&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;driverClassName&amp;quot; value=&amp;quot;${database.driver}&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;url&amp;quot; value=&amp;quot;${database.url}&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;username&amp;quot; value=&amp;quot;${database.user}&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;password&amp;quot; value=&amp;quot;${database.password}&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;....&lt;br /&gt;    &amp;lt;bean id=&amp;quot;sqlMapClient&amp;quot; class=&amp;quot;org.springframework.orm.ibatis.SqlMapClientFactoryBean&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;configLocation&amp;quot; value=&amp;quot;classpath:SqlMapConfig.xml&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;dataSource&amp;quot; ref=&amp;quot;dataSource&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;....&lt;br /&gt;    &amp;lt;bean id=&amp;quot;ibatisDao&amp;quot; class=&amp;quot;com.mybiz.resource.db.ibatis.IbatisDao&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;property name=&amp;quot;sqlMapClient&amp;quot; ref=&amp;quot;sqlMapClient&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;The DAO implements &lt;tt&gt;&lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-factory-aware"&gt;ApplicationContextAware&lt;/a&gt;&lt;/tt&gt; so that it can examine the Spring context, in particular the properties of interest in the data source:&lt;br /&gt;&lt;pre class="brush:java"&gt;public class MyDao implements ApplicationContextAware&lt;br /&gt;{&lt;br /&gt;    private ApplicationContext context;&lt;br /&gt;&lt;br /&gt;    public void setApplicationContext(ApplicationContext ctx)&lt;br /&gt;    {&lt;br /&gt;        context = ctx;&lt;br /&gt;        dumpDataSource(ctx);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void dumpDataSource(ApplicationContext ctx)&lt;br /&gt;    {&lt;br /&gt;        BasicDataSource dataSource =  ctx.getBean(BasicDataSource.class);&lt;br /&gt;        logger.info("============&gt;&gt;&gt; Data Source: " + dataSource.getUrl()&lt;br /&gt;                + " (user " + dataSource.getUsername() + ")");&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Finally, I get myself into the habit of looking for that log output at the start of my test runs to be sure I'm connecting to the expected database. Once burned, twice shy.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4537214284378738955?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4537214284378738955/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/what-database-am-i-connecting-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4537214284378738955'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4537214284378738955'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/what-database-am-i-connecting-to.html' title='What Database Am I Connecting To?'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6185597362165239893</id><published>2010-07-16T14:16:00.001-06:00</published><updated>2010-07-16T14:23:39.271-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LDAP'/><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>JEE Authentication: Login Errors, Roles, Access Denied, and Logout</title><content type='html'>In a &lt;a href="http://blog.temposwc.com/2010/07/ldap-authentication-with-jetty.html"&gt;previous post&lt;/a&gt; around LDAP authentication using Jetty, I had some unfinished business. Here I'll deal with login errors, restrict the login to a given role, deal with subsequent access denied scenarios, display the currently logged-in user name, and provide log-out functionality.&lt;br /&gt;&lt;br /&gt;The web.xml I'd started with already specified a login-error page, but I was simply pointing it to the same page as the login form. This results in that page simply refreshing without any indication to the user of why that happened. What we want is some kind of message displayed that indicates the given username or password was not valid.&lt;br /&gt;&lt;br /&gt;Now, I could simply copy the login.jsp to another JSP, say login-error.jsp, add a message to that page and alter the web.xml to specify that new page on login error:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;login-config&amp;gt;&lt;br /&gt;    &amp;lt;auth-method&amp;gt;FORM&amp;lt;/auth-method&amp;gt;&lt;br /&gt;    &amp;lt;realm-name&amp;gt;ldap&amp;lt;/realm-name&amp;gt;&lt;br /&gt;    &amp;lt;form-login-config&amp;gt;&lt;br /&gt;        &amp;lt;form-login-page&amp;gt;/faces/login/login.jsp&amp;lt;/form-login-page&amp;gt;&lt;br /&gt;        &amp;lt;form-error-page&amp;gt;/faces/login/login-error.jsp&amp;lt;/form-error-page&amp;gt;&lt;br /&gt;    &amp;lt;/form-login-config&amp;gt;&lt;br /&gt;&amp;lt;/login-config&amp;gt;&lt;br /&gt;&lt;/pre&gt;But now both login.jsp and login-error.jsp contain the same FORM snippet. Now if I next want to specify yet another login page to which the user is directed on an "access denied" error (which we'll deal with momentarily), and I'm still afflicted with copy-paste fever, I'll have the same login form in three places. Let's factor it out instead into a JSP snippet named loginform.jsp:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;form method=post action=&amp;quot;j_security_check&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;label for=&amp;quot;j_username&amp;quot; style=&amp;quot;font-weight:bold&amp;quot;&amp;gt;Username&amp;lt;/label&amp;gt;&lt;br /&gt;    &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;j_username&amp;quot; id=&amp;quot;j_username&amp;quot; style=&amp;quot;margin-left:10px&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;label for=&amp;quot;j_password&amp;quot; style=&amp;quot;font-weight:bold&amp;quot;&amp;gt;Password&amp;lt;/label&amp;gt;&lt;br /&gt;    &amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;j_password&amp;quot; id=&amp;quot;j_password&amp;quot; style=&amp;quot;margin-left:10px&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Log In&amp;quot;/&amp;gt;&lt;br /&gt;&amp;lt;/form&amp;gt;&lt;br /&gt;&lt;/pre&gt;The usual login page now looks like this:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;div style=&amp;quot;margin-top:25px; margin-left:25%&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;h2 style=&amp;quot;text-decoration:underline; color:blue;margin-left:-10px&amp;quot;&amp;gt;Management App&amp;lt;/h2&amp;gt;&lt;br /&gt;    &amp;lt;h3&amp;gt;Please Log In&amp;lt;/h3&amp;gt;&lt;br /&gt;    &amp;lt;%@include file=&amp;quot;loginform.jsp&amp;quot;%&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;And I'll provide an "access denied" page that looks like this:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;div style=&amp;quot;margin-top:25px; margin-left:25%&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;h2 style=&amp;quot;text-decoration:underline; color:blue;margin-left:-10px&amp;quot;&amp;gt;Management App&amp;lt;/h2&amp;gt;&lt;br /&gt;    &amp;lt;h3&amp;gt;Please Log In&amp;lt;/h3&amp;gt;&lt;br /&gt;    &amp;lt;div style=&amp;quot;color:red;font-weight:bold;&amp;quot;&amp;gt;Authentication failed. User is not in Required Role.&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;%@include file=&amp;quot;loginform.jsp&amp;quot;%&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;Configuring HTTP-403 responses (i.e. access denied) to navigate to this page is done like so:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;error-page&amp;gt;&lt;br /&gt;    &amp;lt;error-code&amp;gt;403&amp;lt;/error-code&amp;gt;&lt;br /&gt;    &amp;lt;location&amp;gt;/login/accessDenied.jsp&amp;lt;/location&amp;gt;&lt;br /&gt;&amp;lt;/error-page&amp;gt;&lt;br /&gt;&lt;/pre&gt;Access denied problems will occur if a given user is not in the expected role. So far, the web.xml has granted authorization to &lt;i&gt;all&lt;/i&gt; roles by virtue of the wild-card for the role-name. We can restrict that by naming a role instead:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;security-constraint&amp;gt;&lt;br /&gt;    &amp;lt;web-resource-collection&amp;gt;&lt;br /&gt;        &amp;lt;web-resource-name&amp;gt;Protected Resources&amp;lt;/web-resource-name&amp;gt;&lt;br /&gt;        &amp;lt;url-pattern&amp;gt;*.iface&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;    &amp;lt;/web-resource-collection&amp;gt;&lt;br /&gt;    &amp;lt;auth-constraint&amp;gt;&lt;br /&gt;        &amp;lt;role-name&amp;gt;admin&amp;lt;/role-name&amp;gt;&lt;br /&gt;    &amp;lt;/auth-constraint&amp;gt;&lt;br /&gt;    &amp;lt;user-data-constraint&amp;gt;&lt;br /&gt;        &amp;lt;transport-guarantee&amp;gt;&lt;br /&gt;            CONFIDENTIAL&lt;br /&gt;        &amp;lt;/transport-guarantee&amp;gt;&lt;br /&gt;    &amp;lt;/user-data-constraint&amp;gt;&lt;br /&gt;&amp;lt;/security-constraint&amp;gt;&lt;br /&gt;&lt;/pre&gt;Now, once a user provides his/her credentials at the login form, these are first checked by the LDAP module (configured as a Jetty realm, as per the previous post); if those are valid, the user is next confirmed to be assigned the &lt;tt&gt;admin&lt;/tt&gt; role. If that is the case, all is well and navigation will proceed as configured by the JSF navigation rule (again, please see the previous post). If the credentials are not valid, the user will be redirected to the login-error page, this time with an informative error message about the login problem. If the credentials are good but the user is not assigned the &lt;tt&gt;admin&lt;/tt&gt; role, the user will be redirected to the access-denied page, again with a informative message.&lt;br /&gt;&lt;br /&gt;Displaying the current username is a simple matter of leveraging the built-in &lt;tt&gt;getRemoteUser()&lt;/tt&gt;, provided by &lt;tt&gt;HttpServletRequest&lt;/tt&gt;. Since, after logging in, I've transitioned into a JSF application - and because I'm adverse to using JSP scriptlets to accomplish use of that getter once I'm in JSF - I simply provide a getter in one of my JSF managed beans that fetches the HTTP request and returns the user name:&lt;br /&gt;&lt;pre class="brush:java"&gt;public String getUserName() {&lt;br /&gt;    return getServletRequest().getRemoteUser();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;...referencing it, as usual, with the JSF expression language:&lt;br /&gt;&lt;tt&gt;&lt;br /&gt;User: #{svh.userName}&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;Finally, I'll provide log-out functionality. First, a command link (done with the IceFaces framework):&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;ice:commandLink &lt;br /&gt;    action=&amp;quot;logout&amp;quot; immediate=&amp;quot;true&amp;quot; value=&amp;quot;Logout&amp;quot; &lt;br /&gt;    style=&amp;quot;margin-left:5px;color:blue;font-size:medium&amp;quot;/&amp;gt;&lt;br /&gt;&lt;/pre&gt;The &lt;tt&gt;logout&lt;/tt&gt; action is mapped with a navigation rule:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;navigation-rule&amp;gt;&lt;br /&gt;    &amp;lt;description&amp;gt;Logout&amp;lt;/description&amp;gt;&lt;br /&gt;    &amp;lt;from-view-id&amp;gt;/*&amp;lt;/from-view-id&amp;gt;&lt;br /&gt;    &amp;lt;navigation-case&amp;gt;&lt;br /&gt;        &amp;lt;from-outcome&amp;gt;logout&amp;lt;/from-outcome&amp;gt;&lt;br /&gt;        &amp;lt;to-view-id&amp;gt;/login/logout.jsp&amp;lt;/to-view-id&amp;gt;&lt;br /&gt;        &amp;lt;redirect/&amp;gt;&lt;br /&gt;    &amp;lt;/navigation-case&amp;gt;&lt;br /&gt;&amp;lt;/navigation-rule&amp;gt;&lt;br /&gt;&lt;/pre&gt;...taking us to the logout.jsp page, which invalidates the session and invites the user to log back in:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;% session.invalidate(); %&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;div style=&amp;quot;margin-top:5px; margin-left:25%&amp;quot;&amp;gt;&lt;br /&gt;    &amp;lt;h2 style=&amp;quot;text-decoration:underline; color:blue;margin-left:-10px&amp;quot;&amp;gt;Management App&amp;lt;/h2&amp;gt;&lt;br /&gt;    &amp;lt;h3&amp;gt;Logout Succeeded&amp;lt;/h3&amp;gt;&lt;br /&gt;    &amp;lt;p&amp;gt;&lt;br /&gt;        You are now logged out of the Management UI.&lt;br /&gt;    &amp;lt;/p&amp;gt;&lt;br /&gt;    &amp;lt;a href=&amp;quot;/index.jsp&amp;quot; style=&amp;quot;text-decoration:underline&amp;quot;&amp;gt;Return to Login page.&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;br /&gt;&lt;/pre&gt;The index.jsp redirects to the application's JSF-based home page:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;html&amp;gt;&lt;br /&gt;    &amp;lt;head&amp;gt;&lt;br /&gt;        &amp;lt;title&amp;gt;Management UI&amp;lt;/title&amp;gt;&lt;br /&gt;    &amp;lt;/head&amp;gt;&lt;br /&gt;    &amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;%&lt;br /&gt;        String redirectURL = &amp;quot;./index.iface&amp;quot;;&lt;br /&gt;        response.sendRedirect(redirectURL);&lt;br /&gt;    %&amp;gt;&lt;br /&gt;    &amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/pre&gt;And, as mentioned in the first post, all &lt;tt&gt;iface&lt;/tt&gt; resources are protected by a security constraint, so this will redirect to the login page.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-6185597362165239893?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/6185597362165239893/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/jee-authentication-login-errors-roles.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6185597362165239893'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6185597362165239893'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/jee-authentication-login-errors-roles.html' title='JEE Authentication: Login Errors, Roles, Access Denied, and Logout'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-3944027023966958365</id><published>2010-07-14T15:20:00.000-06:00</published><updated>2010-07-16T14:20:21.516-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LDAP'/><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>LDAP Authentication with Jetty</title><content type='html'>If you noticed nothing else but the titles of my &lt;a href="http://blog.temposwc.com/2010/07/tip-provide-spring-security.html"&gt;last&lt;/a&gt; &lt;a href="http://blog.temposwc.com/2010/07/tip-package-name-correction-for-jetty.html"&gt;two&lt;/a&gt; posts, you might suspect that I went down the &lt;a href="http://static.springsource.org/spring-security/site/index.html"&gt;Spring-Security&lt;/a&gt; road and back-pedaled to a standard &lt;a href="http://en.wikipedia.org/wiki/Java_Authentication_and_Authorization_Service"&gt;JAAS&lt;/a&gt; approach. You would be correct.&lt;br /&gt;&lt;br /&gt;As it turned out - to my surprise and with great disappointment - I found Spring Security to be impenetrable. Now, Spring makes a lot of things in my life easier, and in fact &lt;i&gt;that's the only reason I use it&lt;/i&gt;. But when it becomes a tangled snarl of undocumented opaqueness, and especially when I read that &lt;i&gt;&lt;a href="http://www.mularien.com/blog/2008/07/07/5-minute-guide-to-spring-security/"&gt;even an expert in Acegi had trouble with it&lt;/a&gt;&lt;/i&gt; (see the Wrap-Up in that article), I decide to find another way. &lt;br /&gt;&lt;br /&gt;Here are my basic building blocks for a JAAS approach with a JSF application in Jetty. First, I configure my web.xml with an authorization constraint:&lt;br /&gt;&lt;pre class="brush:xml"&gt;    &amp;lt;security-constraint&amp;gt;&lt;br /&gt;        &amp;lt;web-resource-collection&amp;gt;&lt;br /&gt;            &amp;lt;web-resource-name&amp;gt;Protected Resources&amp;lt;/web-resource-name&amp;gt;&lt;br /&gt;            &amp;lt;url-pattern&amp;gt;*.iface&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;        &amp;lt;/web-resource-collection&amp;gt;&lt;br /&gt;        &amp;lt;auth-constraint&amp;gt;&lt;br /&gt;            &amp;lt;role-name&amp;gt;*&amp;lt;/role-name&amp;gt;&lt;br /&gt;        &amp;lt;/auth-constraint&amp;gt;&lt;br /&gt;        &amp;lt;user-data-constraint&amp;gt;&lt;br /&gt;            &amp;lt;transport-guarantee&amp;gt;&lt;br /&gt;                CONFIDENTIAL&lt;br /&gt;            &amp;lt;/transport-guarantee&amp;gt;&lt;br /&gt;        &amp;lt;/user-data-constraint&amp;gt;&lt;br /&gt;    &amp;lt;/security-constraint&amp;gt;&lt;br /&gt;&lt;/pre&gt;My web.xml has already mapped an IceFaces &lt;tt&gt;PersistentFacesServlet&lt;/tt&gt; to &lt;tt&gt;*.iface&lt;/tt&gt;, so that's what the URL pattern is about. My initial naive attempt was to use a URL pattern of &lt;tt&gt;/*&lt;/tt&gt;, but that's much too broad - it will preclude e.g. loading image resources as part of your login page. That login page is also configured in the web.xml:&lt;br /&gt;&lt;pre class="brush:xml"&gt;    &amp;lt;login-config&amp;gt;&lt;br /&gt;        &amp;lt;auth-method&amp;gt;FORM&amp;lt;/auth-method&amp;gt;&lt;br /&gt;        &amp;lt;realm-name&amp;gt;ldap&amp;lt;/realm-name&amp;gt;&lt;br /&gt;        &amp;lt;form-login-config&amp;gt;&lt;br /&gt;            &amp;lt;form-login-page&amp;gt;/faces/login/login.jsp&amp;lt;/form-login-page&amp;gt;&lt;br /&gt;            &amp;lt;form-error-page&amp;gt;/faces/login/login.jsp&amp;lt;/form-error-page&amp;gt;&lt;br /&gt;        &amp;lt;/form-login-config&amp;gt;&lt;br /&gt;    &amp;lt;/login-config&amp;gt;&lt;br /&gt;&lt;/pre&gt;The login page (&lt;tt&gt;./login/login.jsp&lt;/tt&gt;) is dirt-simple so far. I'm only working on basic functionality at this point, and there's nothing pretty about it:&lt;br /&gt;&lt;pre class="brush:xml"&gt;    &amp;lt;form method=post action=&amp;quot;j_security_check&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;label for=&amp;quot;j_username&amp;quot;&amp;gt;Username&amp;lt;/label&amp;gt;&lt;br /&gt;        &amp;lt;input type=&amp;quot;text&amp;quot; name=&amp;quot;j_username&amp;quot; id=&amp;quot;j_username&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;br/&amp;gt;&lt;br /&gt;        &amp;lt;label for=&amp;quot;j_password&amp;quot;&amp;gt;Password&amp;lt;/label&amp;gt;&lt;br /&gt;        &amp;lt;input type=&amp;quot;password&amp;quot; name=&amp;quot;j_password&amp;quot; id=&amp;quot;j_password&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;br/&amp;gt;&lt;br /&gt;        &amp;lt;input type=&amp;quot;submit&amp;quot; value=&amp;quot;Login&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;/form&amp;gt;&lt;br /&gt;&lt;/pre&gt;The realm-name portion in the web.xml references the container environment; configuring the realm is delegated to the container in JEE. My container is (embedded) Jetty, and I configure the Jetty realm via a simple entry in my jetty.xml file:&lt;br /&gt;&lt;pre class="brush:xml"&gt;    &amp;lt;Call name=&amp;quot;addUserRealm&amp;quot;&amp;gt;&lt;br /&gt;        &amp;lt;Arg&amp;gt;&lt;br /&gt;            &amp;lt;New class=&amp;quot;org.mortbay.jetty.plus.jaas.JAASUserRealm&amp;quot;&amp;gt;&lt;br /&gt;                &amp;lt;Set name=&amp;quot;name&amp;quot;&amp;gt;ldap&amp;lt;/Set&amp;gt;&lt;br /&gt;                &amp;lt;Set name=&amp;quot;LoginModuleName&amp;quot;&amp;gt;ldapmodule&amp;lt;/Set&amp;gt;&lt;br /&gt;            &amp;lt;/New&amp;gt;&lt;br /&gt;        &amp;lt;/Arg&amp;gt;&lt;br /&gt;    &amp;lt;/Call&amp;gt;&lt;br /&gt;&lt;/pre&gt;By the way, I followed the &lt;a href="http://docs.codehaus.org/display/JETTY/JAAS"&gt;Jetty tutorial on JAAS&lt;/a&gt; to make all of this happen. My &lt;a href="http://blog.temposwc.com/2010/07/tip-package-name-correction-for-jetty.html"&gt;previous post&lt;/a&gt; mentioned a gotcha in that article around the LDAP Login Module package name. Depending on which version of Jetty you're using, you may need to make the change discussed there.&lt;br /&gt;&lt;br /&gt;In either event, the Jetty realm configuration references a LoginModuleName of "ldapmodule", and as per standard JAAS, this configuration is captured in a file (in my case, a file named &lt;tt&gt;./etc/ldap.conf&lt;/tt&gt;) referenced by the JVM argument &lt;tt&gt;-Djava.security.auth.login.config=etc/ldap.conf&lt;/tt&gt;. That file is basically a replica of the &lt;tt&gt;ldaploginmodule&lt;/tt&gt; example in the Jetty tutorial (again, except for the package name of the &lt;tt&gt;LdapLoginModule&lt;/tt&gt; class), configured of course with the proper schema references and credentials for my LDAP environment. &lt;br /&gt;&lt;br /&gt;Finally, I configure a navigation rule so JSF will take me to my target page after successful login:&lt;br /&gt;&lt;pre class="brush:xml"&gt;    &amp;lt;navigation-rule&amp;gt;&lt;br /&gt;        &amp;lt;description&amp;gt;After Login&amp;lt;/description&amp;gt;&lt;br /&gt;        &amp;lt;from-view-id&amp;gt;/login/login.jsp&amp;lt;/from-view-id&amp;gt;&lt;br /&gt;        &amp;lt;navigation-case&amp;gt;&lt;br /&gt;            &amp;lt;to-view-id&amp;gt;/index.jsp&amp;lt;/to-view-id&amp;gt;&lt;br /&gt;            &amp;lt;redirect/&amp;gt;&lt;br /&gt;        &amp;lt;/navigation-case&amp;gt;&lt;br /&gt;    &amp;lt;/navigation-rule&amp;gt;&lt;br /&gt;&lt;/pre&gt;Note that this a bare-bones scaffold for authentication. I have yet to deal with login failures, roles, JSF-messaging for the user, and the like.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-3944027023966958365?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/3944027023966958365/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/ldap-authentication-with-jetty.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3944027023966958365'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3944027023966958365'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/ldap-authentication-with-jetty.html' title='LDAP Authentication with Jetty'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-3888757816007221710</id><published>2010-07-14T14:00:00.003-06:00</published><updated>2010-07-16T14:19:44.789-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='LDAP'/><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>Tip: Package Name Correction for Jetty LdapLoginModule</title><content type='html'>If you're following the &lt;a href="http://docs.codehaus.org/display/JETTY/JAAS"&gt;Jetty tutorial&lt;/a&gt; for establishing JAAS authentication, and you have copied the sample configuration file for the LDAP module, you may come across this error message after you enter your username and password on your login page:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;javax.security.auth.login.LoginException: unable to find LoginModule class: org.mortbay.jetty.plus.jaas.spi.LdapLoginModule&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;The reason is that, at least for the version of Jetty I'm using (6.1.21), the package name has changed to &lt;tt&gt;org.mortbay.jetty.plus.jaas.&lt;b&gt;ldap&lt;/b&gt;.LdapLoginModule&lt;/tt&gt;. Make that change in the ldaploginmodule sample in that tutorial and you'll solve this problem (assuming of course you have all the needed dependencies).&lt;br /&gt;&lt;br /&gt;For your reference, here's the complete dependency list I'm using for a JSF-based app running with embedded Jetty 6.1.21:&lt;br /&gt;&lt;pre class="brush:xml"&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jetty&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;${jettyVersion}&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jetty-util&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;${jettyVersion}&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jsp-2.1-jetty&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;${jettyVersion}&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jetty-plus&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;${jettyVersion}&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jetty-ldap-jaas&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;${jettyVersion}&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-3888757816007221710?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/3888757816007221710/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/tip-package-name-correction-for-jetty.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3888757816007221710'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3888757816007221710'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/tip-package-name-correction-for-jetty.html' title='Tip: Package Name Correction for Jetty LdapLoginModule'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6858899134120666522</id><published>2010-07-14T13:31:00.002-06:00</published><updated>2010-07-14T13:36:23.193-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='acegi'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>Tip: Provide the Spring Security NamespaceHandler Explicitly</title><content type='html'>If you're working with Spring Security, you might start your pom with an entry like so:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;    &amp;lt;groupId&amp;gt;org.springframework.security&amp;lt;/groupId&amp;gt;&lt;br /&gt;    &amp;lt;artifactId&amp;gt;spring-security-web&amp;lt;/artifactId&amp;gt;&lt;br /&gt;    &amp;lt;version&amp;gt;3.0.0.RELEASE&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;This will bring in the spring-web, spring-security-core and commons-logging artifacts, and I would not fault you for thinking you're good to go. You're possibly following &lt;a href="http://ocpsoft.com/java/acegi-spring-security-jsf-login-page/"&gt;this tutorial&lt;/a&gt; or &lt;a href="http://www.mularien.com/blog/2008/07/07/5-minute-guide-to-spring-security/"&gt;another&lt;/a&gt; among the many out there, and are providing a Spring config that references the http://www.springframework.org/schema/security namespace - and, at least for me, here's where things did not go as expected. At runtime, the error message I received was this:&lt;br /&gt;&lt;tt&gt;&lt;br /&gt;Unable to locate Spring NamespaceHandler for XML schema namespace [http://www.springframework.org/schema/security]&lt;br /&gt;&lt;/tt&gt;&lt;br /&gt;After a fair amount of Google'ing and Stack-Overflow'ing, I found the problem - and I reproduce it here to save myself (and hopefully you) the headache next time around: there is a dependency missing that looks like this:&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt;    &amp;lt;groupId&amp;gt;org.springframework.security&amp;lt;/groupId&amp;gt;&lt;br /&gt;    &amp;lt;artifactId&amp;gt;spring-security-config&amp;lt;/artifactId&amp;gt;&lt;br /&gt;    &amp;lt;version&amp;gt;3.0.0.RELEASE&amp;lt;/version&amp;gt;&lt;br /&gt; &amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;i&gt;Now&lt;/i&gt; you should be good to go.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-6858899134120666522?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/6858899134120666522/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/tip-provide-spring-security.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6858899134120666522'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6858899134120666522'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/tip-provide-spring-security.html' title='Tip: Provide the Spring Security NamespaceHandler Explicitly'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6291401239763189240</id><published>2010-07-02T12:36:00.006-06:00</published><updated>2010-07-16T14:20:04.799-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSL'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>Using JDK keytool To Generate Keys and Certs</title><content type='html'>In &lt;a href="http://blog.temposwc.com/2010/06/using-keyman-to-generate-keys-and-certs.html"&gt;a previous post&lt;/a&gt;, I stepped through use of IBM's &lt;a href="http://www.alphaworks.ibm.com/tech/keyman"&gt;KeyMan&lt;/a&gt; GUI to generate SSL keys and certificates, placing them in KeyMan's "token", or as more commonly known, a keystore. I then attempted to use this keystore in both a Windows and Linux deployment of my webapp, and met with mixed success - since I used the Windows version of the KeyMan tool, this worked out OK for the Windows webapp, but not so much for the Linux deployment.&lt;br /&gt;&lt;br /&gt;My subsequent attempts to use KeyMan's Unix shell script (km) instead - under a Cygwin environment - were much the same: on deployment, an "Invalid keystore format" exception was issued. Clearly, this is about the difference between DOS and Unix file formats. The next obvious step was to simply execute the km script directly under Linux, foregoing any file encoding or translation issues that might be happening with Cygwin. Here, however, after appearing to generate the key pair, the KeyMan GUI went into some kind of blocking wait - or maybe an infinite loop? a deadlock? There was no way to tell; the GUI simply became unresponsive. Followup exercises to include setting KM_HOME in the environment, unpacking the native library support ZIP file and setting the LD_LIBRARY_PATH to point to them, and etc. all proved fruitless.&lt;br /&gt;&lt;br /&gt;Finally, I reverted to using the JDK &lt;a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/keytool.html"&gt;keytool &lt;/a&gt;utility, and - no surprise - this works out just fine in both Windows and Linux (i.e. in terms of generating a keystore that is recognized by the webserver). Here is the script I use to generate things, in both Linux and Cygwin:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;######################################&lt;br /&gt;#&lt;br /&gt;#  generate-keystore.sh - Generate key and certificate&lt;br /&gt;#&lt;br /&gt;######################################&lt;br /&gt;&lt;br /&gt;CN=MyKeystore&lt;br /&gt;OU='Web - Development'&lt;br /&gt;ORG='My Biz Inc.'&lt;br /&gt;COUNTRY=US&lt;br /&gt;ALIAS=MyBizKeystore&lt;br /&gt;PASS=password&lt;br /&gt;KEYSTORE=keystore&lt;br /&gt;CERTFILE=cert&lt;br /&gt;EXPIRY=730&lt;br /&gt;&lt;br /&gt;# remove it if it's there&lt;br /&gt;[ -f "$KEYSTORE" ] &amp;&amp; /bin/rm $KEYSTORE&lt;br /&gt;&lt;br /&gt;# generate the keystore with a self-signed cert and an RSA keypair&lt;br /&gt;$JAVA_HOME/jre/bin/keytool -genkeypair -keyalg RSA \&lt;br /&gt;-dname "cn=$CN, ou=$OU, o=$ORG, c=$COUNTRY" \&lt;br /&gt;-alias $ALIAS -keypass $PASS -keystore $KEYSTORE \&lt;br /&gt;-storepass $PASS -validity $EXPIRY&lt;br /&gt;&lt;br /&gt;# export the certificate so we can look at it&lt;br /&gt;$JAVA_HOME/jre/bin/keytool -exportcert -alias $ALIAS -file $CERTFILE -keystore $KEYSTORE -storepass $PASS&lt;br /&gt;&lt;br /&gt;# print the certificate&lt;br /&gt;$JAVA_HOME/jre/bin/keytool -printcert -file $CERTFILE&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;If you bump into this error message in the generate step:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;Incorrect AVA format&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;...you'll want to make sure you didn't embed any commas or other special characters in the values you provide. For example, I started out with an Organizational Unit (OU) of  'My Biz, Inc.' - but that provoked the error message. Embedded dashes and periods are apparently OK, but note that I've enclosed any values with embedded spaces in single quotes. That's more a shell issue than a keytool problem.&lt;br /&gt;&lt;br /&gt;If you bump into an error message something like this, in the print-certificate step:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;lengthTag=109, too big&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;...you might be trying to pass in the entire keystore to the printcert command; that's why I export the certificate first in the script above, using just that piece as the argument to print it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-6291401239763189240?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/6291401239763189240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/07/using-jdk-keytool-to-generate-keys-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6291401239763189240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6291401239763189240'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/07/using-jdk-keytool-to-generate-keys-and.html' title='Using JDK keytool To Generate Keys and Certs'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6625022070192647756</id><published>2010-06-30T16:58:00.004-06:00</published><updated>2010-06-30T17:05:04.567-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SSL'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>Using KeyMan To Generate Keys and Certs</title><content type='html'>&lt;span class="Apple-style-span" style="font-family: Arial; font-size: small;"&gt;&lt;span class="Apple-style-span" style="font-size: 13px;"&gt;&lt;span class="Apple-style-span" style="font-family: Times, 'Times New Roman', serif;"&gt;&lt;span class="Apple-style-span" style="font-size: medium;"&gt;This post continues my series around Jetty. Previous posts discuss setting up a &lt;a href="http://blog.temposwc.com/2010/06/basic-embedded-jetty-setup-jsf-12.html"&gt;basic embedded Jetty&lt;/a&gt; application, and then some adjustments to &lt;a href="http://blog.temposwc.com/2010/06/followup-basic-embedded-jetty-in-cygwin.html"&gt;repeat that exercise in a Cygwin environment&lt;/a&gt;. My next step is to configure SSL...now, I recall earlier adventures using &lt;a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html"&gt;Java's keytool&lt;/a&gt; to manage this, and have hoped for something better. &lt;a href="http://docs.codehaus.org/display/JETTY/How+to+configure+SSL"&gt;Jetty's documentation&lt;/a&gt; pointed me to &lt;a href="http://www.alphaworks.ibm.com/tech/keyman"&gt;KeyMan&lt;/a&gt;, which is by far a nicer way to go - it provides a decent intuitive GUI to help create, delete, and otherwise manage keys and certificates, among other things. Here's an outline of how to use KeyMan to create a PKCS#12 keystore with a self-signed certificate and public-private key pair:&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Download, install (unpack zip, etc.), read the README.txt. I did nothing with the km.setup file, but did edit the km.bat as instructed. Turns out that, since I'm on cygwin, that wasn't needed; instead, I execute the km program.&amp;nbsp;Click on the "New" icon to create a new "token" (i.e. repository for keys, certs, etc.):&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://2.bp.blogspot.com/_ejClviH_0ls/TCvFSQsHQ9I/AAAAAAAAACE/nFTDZzbr0mk/s1600/moz-screenshot-76.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_ejClviH_0ls/TCvFSQsHQ9I/AAAAAAAAACE/nFTDZzbr0mk/s320/moz-screenshot-76.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Choose the PKCS#12 Token from the next dialog, and hit the checkmark ("Complete Dialog") to proceed:&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://2.bp.blogspot.com/_ejClviH_0ls/TCvFyigKsCI/AAAAAAAAACM/_3ACoI9lwcQ/s1600/moz-screenshot-77.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://2.bp.blogspot.com/_ejClviH_0ls/TCvFyigKsCI/AAAAAAAAACM/_3ACoI9lwcQ/s320/moz-screenshot-77.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Next, you need to store a key and a certificate in this token. Select "Actions -&amp;gt; Generate Key" from the token management window that appears:&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://4.bp.blogspot.com/_ejClviH_0ls/TCvGr__vbaI/AAAAAAAAACU/-lbMxkcdiV4/s1600/moz-screenshot-78.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_ejClviH_0ls/TCvGr__vbaI/AAAAAAAAACU/-lbMxkcdiV4/s320/moz-screenshot-78.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The default algorithm is RSA-1024; that's strong enough for my needs. Click the Complete Dialog checkmark...this takes a second to complete, offering a cool little progress bar while you wait.&amp;nbsp;The new key shows up in the All Certificate Items viewport of the token management window; now we need a certificate to go with it. Click "Actions -&amp;gt; Create Certificate...".&amp;nbsp;Self-signed is good enough for my needs. Click checkmark and fill in the fields as needed (only "Your name" is required):&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://4.bp.blogspot.com/_ejClviH_0ls/TCvHU5MzM4I/AAAAAAAAACc/1Q3aZs37-KQ/s1600/moz-screenshot-81.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://4.bp.blogspot.com/_ejClviH_0ls/TCvHU5MzM4I/AAAAAAAAACc/1Q3aZs37-KQ/s320/moz-screenshot-81.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A verification appears when you check "Complete Dialog" here, with the option to label this certificate. Enter a label if you wish, and again move on with the checkmark:&lt;/li&gt;&lt;/ul&gt;&lt;div class="separator" style="clear: both; text-align: center;"&gt;&lt;br /&gt;&lt;/div&gt;&lt;a href="http://3.bp.blogspot.com/_ejClviH_0ls/TCvHdyp-5zI/AAAAAAAAACk/Vo9OxjdFjDk/s1600/moz-screenshot-82.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"&gt;&lt;img border="0" src="http://3.bp.blogspot.com/_ejClviH_0ls/TCvHdyp-5zI/AAAAAAAAACk/Vo9OxjdFjDk/s320/moz-screenshot-82.jpg" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Save the token to a file by selecting File -&amp;gt; Save. This first prompts you for a passphrase, then a file location.&lt;/li&gt;&lt;/ul&gt;Prove to yourself that the keystore (token, repository, whatever) is really there and that you can view it in human-friendly form by first exiting the program, restarting it and selecting the "Open existing..." icon, then "Local resource..." and "Open a file...". Browse to the file location you just saved to, enter the passphrase, and you should see your token listed in the Private Certificates category (in the dropdown). Click right on that item and you'll see all the informational details entered when you created the certificate.&lt;br /&gt;&lt;br /&gt;Next, I'll see about using that keystore for my Jetty SSL setup. Meanwhile, here are some useful links around KeyMan, SSL and Jetty's SSL instructions:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Solaris Keytool&lt;/b&gt;:&amp;nbsp;&lt;a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/keytool.html"&gt;http://java.sun.com/j2se/1.4.2/docs/tooldocs/solaris/keytool.html&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Windows Keytool&lt;/b&gt;:&amp;nbsp;&lt;a href="http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html"&gt;http://java.sun.com/j2se/1.4.2/docs/tooldocs/windows/keytool.html&lt;/a&gt;&lt;br /&gt;&lt;b&gt;KeyMan&lt;/b&gt;:&amp;nbsp;&lt;a href="http://www.alphaworks.ibm.com/tech/keyman"&gt;http://www.alphaworks.ibm.com/tech/keyman&lt;/a&gt;&lt;br /&gt;&lt;b&gt;OpenSSL&lt;/b&gt;:&amp;nbsp;&lt;a href="http://www.openssl.org/docs/HOWTO/"&gt;http://www.openssl.org/docs/HOWTO/&lt;/a&gt;&lt;br /&gt;&lt;b&gt;OpenSSL FAQ&lt;/b&gt;:&amp;nbsp;&lt;a href="http://www.openssl.org/support/faq.html"&gt;http://www.openssl.org/support/faq.html&lt;/a&gt;&lt;br /&gt;&lt;b&gt;Jetty SSL&lt;/b&gt;:&amp;nbsp;&lt;a href="http://docs.codehaus.org/display/JETTY/How+to+configure+SSL#HowtoconfigureSSL-step3"&gt;http://docs.codehaus.org/display/JETTY/How+to+configure+SSL&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-6625022070192647756?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/6625022070192647756/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/06/using-keyman-to-generate-keys-and-certs.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6625022070192647756'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/6625022070192647756'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/06/using-keyman-to-generate-keys-and-certs.html' title='Using KeyMan To Generate Keys and Certs'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_ejClviH_0ls/TCvFSQsHQ9I/AAAAAAAAACE/nFTDZzbr0mk/s72-c/moz-screenshot-76.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7001656445760265335</id><published>2010-06-30T12:07:00.006-06:00</published><updated>2010-06-30T12:13:03.610-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='cygwin'/><title type='text'>Followup: Basic Embedded Jetty in Cygwin</title><content type='html'>In &lt;a href="http://blog.temposwc.com/2010/06/basic-embedded-jetty-setup-jsf-12.html"&gt;a recent post&lt;/a&gt;, I described how to get a basic Jetty web application going, using the embedded approach. Since then, I've reproduced it in a Cygwin environment, and here I'll comment on that exercise.&lt;br /&gt;&lt;br /&gt;As it turns out, specifying this type of Java startup in Cygwin (or in Windows XP per se) will not work:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;java -server -Dbasedir=/usr/local/mywebapp/war -cp /usr/local/mywebapp/war/WEB-INF/lib/* com.mybiz.MyJettyWebServer&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;That's because the wild-card expression apparently is not supported in a DOS-based environment - even if I enclose the above classpath in quotes. Instead, I'd need to provide a semi-colon-delimited list (not colon-separated - I'm in XP) of all jars under ./WEB-INF/lib. This is not the kind of thing I'd like to do; maintaining that kind of list would be a headache as the webapp evolves. Additionally, keep in mind that I've told Jetty to start up with an exploded warfile location:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;        WebAppContext appContext = new WebAppContext();&lt;br /&gt;        File warPath = new File(System.getProperty("basedir"));&lt;br /&gt;        appContext.setWar(warPath.getAbsolutePath());&lt;br /&gt;        HandlerList handlers = new HandlerList();&lt;br /&gt;        handlers.setHandlers(new Handler[]{ appContext, new DefaultHandler() });&lt;br /&gt;        jetty.setHandler(handlers);&lt;br /&gt;        jetty.start();&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will result in a web-level classloader to load all the jars under WEB-INF/lib, which is arguably redundant, since I'm explicitly setting my classpath to the same thing. That in turn will cause loader constraint violations when running the webapp in an IDE such as Intellij, if the run configuration you're using there points to the same classpath (since that application-level classloader loads the classes first, and then the webapp-level classloader tries to do the same thing). I'll defer solving the Intellij problem for now, and just address basic command line startup.&lt;br /&gt;&lt;br /&gt;Given maintenance cost concerns, I'm motivated to load the minimal number of jars needed to get Jetty going, then allow it to load the rest of what it needs from WEB-INF/lib. In my particular setup, that minimal set includes my application jar and three Jetty jars:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;MyApp-1.0.jar&lt;br /&gt;jetty-6.1.21.jar&lt;br /&gt;jetty-util-6.1.21.jar&lt;br /&gt;servlet-api-2.5-20081211.jar&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;I figured out this minimal set by just trying to start up the WebServer class and seeing what classdef-not-found problems I had - then searching for the necessary jar by setting up a bash function that I can reuse:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;findclass () { find . -name '*.jar' -o -type f |xargs -i bash -c "jar -tvf {}| tr / . | grep -i "$@" &amp;&amp; echo {}"; }&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;...and subsequently invoking it like this:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;findclass &amp;lt;dot-delimited-classname&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Once I have all my dependencies figured out, I can invoke my Jetty program with that minimal set, and rely on the webapp-level classloader to do the rest when the embedded Jetty webserver starts:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;java -server -Dbasedir=/usr/local/mywebapp/war -cp "MyApp-1.0.jar;jetty-6.1.21.jar;jetty-util-6.1.21.jar;servlet-api-2.5-20081211.jar" com.mybiz.MyJettyWebServer&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Note that I've wrapped the classpath in quotes, and, as mentioned, used semi-colons instead of colons.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7001656445760265335?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7001656445760265335/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/06/followup-basic-embedded-jetty-in-cygwin.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7001656445760265335'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7001656445760265335'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/06/followup-basic-embedded-jetty-in-cygwin.html' title='Followup: Basic Embedded Jetty in Cygwin'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-3079337924967605876</id><published>2010-06-23T17:07:00.002-06:00</published><updated>2010-06-23T17:11:39.805-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='snippet'/><category scheme='http://www.blogger.com/atom/ns#' term='JMS'/><title type='text'>Send/Receive Notification About an Exception</title><content type='html'>From &lt;a href="http://www.books-by-isbn.com/0-596/0596522045-Java-Message-Service-Mark-Richards-Richard-Monson-Haefel-David-Chappell-0-596-52204-5.html"&gt;Java Message Service, Second Edition&lt;/a&gt;, by Mark Richards, Richard Monson-Haefel, and David A. Chappell - here's a lightweight mechanism for notifying interested consumers about an exception (in a JMS context):&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;try {&lt;br /&gt;    ...&lt;br /&gt;} catch (Exception up) {&lt;br /&gt;    Message message = session.createMessage();&lt;br /&gt;    message.setStringProperty("Exception", up.getMessage());&lt;br /&gt;    publisher.publish(message);&lt;br /&gt;    throw up;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Interested consumers can receive this notification like this:&lt;br /&gt;&lt;pre class="brush:java"&gt;public void onMessage(Message message) {&lt;br /&gt;    System.out.println("Exception: " + message.getStringProperty());&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This uses a simple &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;&lt;a href="http://download.oracle.com/docs/cd/E17477_01/javaee/5/api/javax/jms/Message.html"&gt;Message&lt;/a&gt;&amp;nbsp;&lt;/span&gt;&lt;/span&gt;object, which contains no payload - only JMS headers and properties. As noted in the book, when simple notification is all that is needed, use of the &lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;span class="Apple-style-span" style="font-size: small;"&gt;Message&lt;/span&gt;&lt;/span&gt; type is the most efficient means to do this.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-3079337924967605876?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/3079337924967605876/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/06/snippet-sendreceive-notification-about.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3079337924967605876'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/3079337924967605876'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/06/snippet-sendreceive-notification-about.html' title='Send/Receive Notification About an Exception'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-5276586817631632106</id><published>2010-06-23T16:04:00.005-06:00</published><updated>2010-06-30T12:03:25.867-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='IceFaces'/><title type='text'>Basic Embedded Jetty Setup: JSF 1.2 Webapp</title><content type='html'>Here are some basic code snippets I've used (to-date ... subject to change) to get an &lt;a href="http://www.icefaces.org/main/home/"&gt;IceFaces&lt;/a&gt;-based (&lt;a href="http://en.wikipedia.org/wiki/JavaServer_Faces"&gt;JSF &lt;/a&gt;1.2) webapp deployed as an embedded &lt;a href="http://docs.codehaus.org/display/JETTY/Jetty+Documentation"&gt;Jetty &lt;/a&gt;webapp - using &lt;a href="http://maven.apache.org/index.html"&gt;Maven&lt;/a&gt; for building. The artifacts include a &lt;a href="http://docs.codehaus.org/display/JETTY/jetty.xml"&gt;configuration file&lt;/a&gt;, a minimal bootstrap class, the pom and the file used as the &lt;a href="http://maven.apache.org/plugins/maven-assembly-plugin/"&gt;maven assembly-plugin&lt;/a&gt; descriptor.&lt;br /&gt;&lt;br /&gt;The configuration file is jetty.xml, and doesn't require much. It lives under the ./etc directory of my project:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;!DOCTYPE Configure PUBLIC "-//Mort Bay Consulting//DTD Configure//EN" "http://jetty.mortbay.org/configure.dtd"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Configure id="Server" class="org.mortbay.jetty.Server"&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;Set name="ThreadPool"&amp;gt;&lt;br /&gt;        &amp;lt;New class="org.mortbay.thread.QueuedThreadPool"&amp;gt;&lt;br /&gt;            &amp;lt;!-- initial threads set to 10 --&amp;gt;&lt;br /&gt;            &amp;lt;Set name="minThreads"&amp;gt;10&amp;lt;/Set&amp;gt;&lt;br /&gt;            &amp;lt;!-- the thread pool will grow only up to 200 --&amp;gt;&lt;br /&gt;            &amp;lt;Set name="maxThreads"&amp;gt;200&amp;lt;/Set&amp;gt;&lt;br /&gt;            &amp;lt;!-- indicates that having 20 and below, the pool will be considered low on threads --&amp;gt;&lt;br /&gt;            &amp;lt;Set name="lowThreads"&amp;gt;20&amp;lt;/Set&amp;gt;&lt;br /&gt;            &amp;lt;!-- The number of queued jobs (or idle threads) needed before the thread pool is grown (or shrunk) --&amp;gt;&lt;br /&gt;            &amp;lt;Set name="SpawnOrShrinkAt"&amp;gt;2&amp;lt;/Set&amp;gt;&lt;br /&gt;        &amp;lt;/New&amp;gt;&lt;br /&gt;    &amp;lt;/Set&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;Call name="addConnector"&amp;gt;&lt;br /&gt;        &amp;lt;Arg&amp;gt;&lt;br /&gt;            &amp;lt;New class="org.mortbay.jetty.nio.SelectChannelConnector"&amp;gt;&lt;br /&gt;                &amp;lt;!-- the ip address or domain to bind --&amp;gt;&lt;br /&gt;                &amp;lt;Set name="host"&amp;gt;&lt;br /&gt;                    &amp;lt;SystemProperty name="jetty.host"/&amp;gt;&lt;br /&gt;                &amp;lt;/Set&amp;gt;&lt;br /&gt;                &amp;lt;!-- the port to use/bind, defaults to 8080 if property not set --&amp;gt;&lt;br /&gt;                &amp;lt;Set name="port"&amp;gt;&lt;br /&gt;                    &amp;lt;SystemProperty name="jetty.port" default="8080"/&amp;gt;&lt;br /&gt;                &amp;lt;/Set&amp;gt;&lt;br /&gt;                &amp;lt;!-- the time in milliseconds when a connection is considered idle --&amp;gt;&lt;br /&gt;                &amp;lt;Set name="maxIdleTime"&amp;gt;300000&amp;lt;/Set&amp;gt;&lt;br /&gt;                &amp;lt;!-- the number of acceptors (their job is to accept the connection and dispatch to thread pool) --&amp;gt;&lt;br /&gt;                &amp;lt;Set name="Acceptors"&amp;gt;2&amp;lt;/Set&amp;gt;&lt;br /&gt;                &amp;lt;!-- should the connection statistics be turned on? (Not advisable in production) --&amp;gt;&lt;br /&gt;                &amp;lt;Set name="statsOn"&amp;gt;false&amp;lt;/Set&amp;gt;&lt;br /&gt;                &amp;lt;!-- the confidential port --&amp;gt;&lt;br /&gt;                &amp;lt;Set name="confidentialPort"&amp;gt;8443&amp;lt;/Set&amp;gt;&lt;br /&gt;                &amp;lt;!-- indicates the minimum number of connections when the server is considered low on resources --&amp;gt;&lt;br /&gt;                &amp;lt;Set name="lowResourcesConnections"&amp;gt;5000&amp;lt;/Set&amp;gt;&lt;br /&gt;                &amp;lt;!-- when low on resources, this indicates the maximum time a connection must be idle to not be closed --&amp;gt;&lt;br /&gt;                &amp;lt;Set name="lowResourcesMaxIdleTime"&amp;gt;5000&amp;lt;/Set&amp;gt;&lt;br /&gt;            &amp;lt;/New&amp;gt;&lt;br /&gt;        &amp;lt;/Arg&amp;gt;&lt;br /&gt;    &amp;lt;/Call&amp;gt;&lt;br /&gt;&lt;br /&gt;    &amp;lt;!-- Stops the server when ctrl+c is pressed (registers to Runtime.addShutdownHook) --&amp;gt;&lt;br /&gt;    &amp;lt;Set name="stopAtShutdown"&amp;gt;true&amp;lt;/Set&amp;gt;&lt;br /&gt;    &amp;lt;!-- send the server version in the response header? --&amp;gt;&lt;br /&gt;    &amp;lt;Set name="sendServerVersion"&amp;gt;true&amp;lt;/Set&amp;gt;&lt;br /&gt;    &amp;lt;!-- send the date header in the response header? --&amp;gt;&lt;br /&gt;    &amp;lt;Set name="sendDateHeader"&amp;gt;true&amp;lt;/Set&amp;gt;&lt;br /&gt;    &amp;lt;!-- allows requests(prior to shutdown) to finish gracefully --&amp;gt;&lt;br /&gt;    &amp;lt;Set name="gracefulShutdown"&amp;gt;1000&amp;lt;/Set&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/Configure&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This configuration file is referenced by the bootstrap class, using it to configure Jetty. This class also sets the context for the webapp, points to the top-level directory of the exploded WAR content, sets a webapp context and a default context as handlers for Jetty, and starts up the webserver:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;public class MyJettyWebServer {&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) throws Exception {&lt;br /&gt;&lt;br /&gt;        Server jetty = new Server();&lt;br /&gt;&lt;br /&gt;        // configure Jetty by pointing to config file(s)&lt;br /&gt;        String[] configFiles = { "etc/jetty.xml" };&lt;br /&gt;        for (String configFile : configFiles) {&lt;br /&gt;            XmlConfiguration configuration = new XmlConfiguration(new File(configFile).toURI().toURL());&lt;br /&gt;            configuration.configure(jetty);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        // set the context for the webapp&lt;br /&gt;        WebAppContext appContext = new WebAppContext();&lt;br /&gt;        appContext.setContextPath("/mycontext");&lt;br /&gt;&lt;br /&gt;        // point to the top-level directory of the exploded WAR content&lt;br /&gt;        File warPath = new File(System.getProperty("basedir"));&lt;br /&gt;        appContext.setWar(warPath.getAbsolutePath());&lt;br /&gt;&lt;br /&gt;        // set a webapp context and a default context as handlers for Jetty&lt;br /&gt;        HandlerList handlers = new HandlerList();&lt;br /&gt;        handlers.setHandlers(new Handler[]{ appContext, new DefaultHandler() });&lt;br /&gt;        jetty.setHandler(handlers);&lt;br /&gt;&lt;br /&gt;        // start up the webserver&lt;br /&gt;        jetty.start();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;The pom specifies IceFaces 1.8.2, being careful to exclude the EL API jar wherever that's brought in transitively, as per &lt;a href="http://www.icefaces.org/docs/v1_8_0/htmlguide/devguide/appendixA.html"&gt;http://www.icefaces.org/docs/v1_8_0/htmlguide/devguide/appendixA.html&lt;/a&gt;; and it specifies Jetty and Log4J artifacts. Note that JSP support is explicitly specified:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;    ....&lt;br /&gt;    &amp;lt;!-- use JAR packaging for embedded Jetty --&amp;gt;&lt;br /&gt;    &amp;lt;packaging&amp;gt;jar&amp;lt;/packaging&amp;gt;&lt;br /&gt;    ....&lt;br /&gt;    &amp;lt;dependencies&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.icefaces&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;icefaces&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;1.8.2&amp;lt;/version&amp;gt;&lt;br /&gt;            &amp;lt;exclusions&amp;gt;&lt;br /&gt;                &amp;lt;exclusion&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;javax.el&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;el-api&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                &amp;lt;/exclusion&amp;gt;&lt;br /&gt;            &amp;lt;/exclusions&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.icefaces&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;icefaces-comps&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;1.8.2&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.icefaces&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;icefaces-facelets&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;1.8.2&amp;lt;/version&amp;gt;&lt;br /&gt;            &amp;lt;exclusions&amp;gt;&lt;br /&gt;                &amp;lt;exclusion&amp;gt;&lt;br /&gt;                    &amp;lt;groupId&amp;gt;javax.el&amp;lt;/groupId&amp;gt;&lt;br /&gt;                    &amp;lt;artifactId&amp;gt;el-api&amp;lt;/artifactId&amp;gt;&lt;br /&gt;                &amp;lt;/exclusion&amp;gt;&lt;br /&gt;            &amp;lt;/exclusions&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;javax.faces&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jsf-api&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;1.2_12&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;javax.faces&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jsf-impl&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;1.2_12&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jetty&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;6.1.21&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jetty-util&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;6.1.21&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;jsp-2.1-jetty&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;6.1.21&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;log4j&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;log4j&amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;${log4jVersion}&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;/dependency&amp;gt;&lt;br /&gt;    &amp;lt;/dependencies&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The assembly-plugin descriptor has a few things worth mentioning. In the dependency sets, I lay down my dependencies in the standard webapp location, ./WEB-INF/lib:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;        &amp;lt;dependencySet&amp;gt;&lt;br /&gt;            &amp;lt;unpack&amp;gt;false&amp;lt;/unpack&amp;gt;&lt;br /&gt;            &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;&lt;br /&gt;            &amp;lt;outputDirectory&amp;gt;war/WEB-INF/lib&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;        &amp;lt;/dependencySet&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I also specify several destinations for my dev-time files to deal with Jetty-related stuff:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;        &amp;lt;!--&lt;br /&gt;        Jetty deployment: configuration file location&lt;br /&gt;        --&amp;gt;&lt;br /&gt;        &amp;lt;fileSet&amp;gt;&lt;br /&gt;            &amp;lt;directory&amp;gt;etc&amp;lt;/directory&amp;gt;&lt;br /&gt;            &amp;lt;outputDirectory&amp;gt;/usr/local/mywebapp/etc&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;            &amp;lt;fileMode&amp;gt;0644&amp;lt;/fileMode&amp;gt;&lt;br /&gt;        &amp;lt;/fileSet&amp;gt;&lt;br /&gt;        &amp;lt;!--&lt;br /&gt;        Jetty deployment: webapp deployment location - hmmm, this one might not be needed...&lt;br /&gt;        --&amp;gt;&lt;br /&gt;        &amp;lt;fileSet&amp;gt;&lt;br /&gt;            &amp;lt;directory&amp;gt;webapps&amp;lt;/directory&amp;gt;&lt;br /&gt;            &amp;lt;outputDirectory&amp;gt;/usr/local/mywebapp/webapps&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;            &amp;lt;fileMode&amp;gt;0644&amp;lt;/fileMode&amp;gt;&lt;br /&gt;        &amp;lt;/fileSet&amp;gt;&lt;br /&gt;        &amp;lt;!--&lt;br /&gt;        Jetty deployment: production-time exploded warfile location&lt;br /&gt;        --&amp;gt;&lt;br /&gt;        &amp;lt;fileSet&amp;gt;&lt;br /&gt;            &amp;lt;directory&amp;gt;src/main/webapp&amp;lt;/directory&amp;gt;&lt;br /&gt;            &amp;lt;outputDirectory&amp;gt;/usr/local/mywebapp/war&amp;lt;/outputDirectory&amp;gt;&lt;br /&gt;            &amp;lt;fileMode&amp;gt;0644&amp;lt;/fileMode&amp;gt;&lt;br /&gt;        &amp;lt;/fileSet&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, to start the program:&lt;br /&gt;&lt;code&gt;&lt;br /&gt;java -server -Dbasedir=/usr/local/mywebapp/war -cp /usr/local/mywebapp/war/WEB-INF/lib/* com.mybiz.MyJettyWebServer&lt;br /&gt;&lt;/code&gt;&lt;br /&gt;Browse to localhost:8080 and your webapp should appear.&lt;br /&gt;&lt;br /&gt;You'll notice I did not use the &lt;a href="http://docs.codehaus.org/display/JETTY/Maven+Jetty+Plugin"&gt;maven-jetty plugin&lt;/a&gt; (nor the &lt;a href="http://wiki.eclipse.org/Jetty/Feature/Jetty_Maven_Plugin"&gt;jetty-maven plugin&lt;/a&gt; - yes, there are two different ones, each with different names, schemas and behaviors). The good news about the plugin is that it shields you from much of the configuration/deployment exercises you'll need; that's also the bad news. I needed to understand explicitly what dependencies, etc. I'd need for production, so I chose to do things manually. The good news around this is that I've done most of the heavy lifting to grease the skids for future embedded-Jetty exercises.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;span class="Apple-style-span" style="font-size: larger;"&gt;References&lt;/span&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="http://docs.codehaus.org/display/JETTY/Jetty+Wiki"&gt;Jetty 6.x Wiki&lt;/a&gt;&lt;/b&gt;&lt;br /&gt;&lt;b&gt;&lt;a href="http://wiki.eclipse.org/Jetty/"&gt;Jetty 7x, 8.x Wiki&lt;/a&gt;&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-5276586817631632106?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/5276586817631632106/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/06/basic-embedded-jetty-setup-jsf-12.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5276586817631632106'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5276586817631632106'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/06/basic-embedded-jetty-setup-jsf-12.html' title='Basic Embedded Jetty Setup: JSF 1.2 Webapp'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-8938323263885984189</id><published>2010-06-22T12:29:00.001-06:00</published><updated>2010-06-22T12:31:17.009-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='JBoss'/><title type='text'>Ternary Expressions Problem with JSF 1.2</title><content type='html'>This is a followup on &lt;a href="http://blog.temposwc.com/2009/05/some-notes-on-jsf-related-fix.html"&gt;my previous post&lt;/a&gt; that offered a workaround to this error message, as seen when working with JSF 1.2:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;javax.el.ELException: Error Parsing: &lt;insert el="" expression="" here="" ternary="" with=""&gt;&lt;br /&gt;Caused by: com.sun.el.parser.ParseException: Encountered ":text"&lt;br /&gt;&lt;/insert&gt;&lt;/pre&gt;&lt;br /&gt;In my first post, I failed to find the root cause, so I took the path of least resistance in the interest of the project schedule (using the heavy-handed approach of two separate panel groups, backing the condition out to the "rendered" attribute of each group). As it turns out, the root cause is lack of white space around the colon, as explained in &lt;a href="http://www.oracle.com/technology/products/jdev/collateral/migration.html"&gt;this article from Oracle&lt;/a&gt;. That is, given an expression:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;#{isThisTrue?doThis:doThat}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;...and depending on what the deployment environment is, you'll either notice nothing or bump into this error message. When that first post was written, I was deploying to Glassfish 8.x - and I needed to use the workaround described above. Recently, I deployed to JBoss 4.x and later to 5.1, and here I did not notice anything - but then I redeployed to embedded Jetty 6.1.x, and the problem re-appeared. This is probably because JBoss shielded me from needing the magic combination of JSF, JSP, Facelets, JSTL and etc. to make things work, but vanilla Jetty does not.&lt;br /&gt;&lt;br /&gt;In either event, you might have better luck than I with determining the dependency mix, including &lt;a href="http://forums.sun.com/thread.jspa?threadID=787962&amp;amp;messageID=4478794"&gt;tweaking the web.xml&lt;/a&gt; to configure the correct expression factory, trying various permutations of Jetty's JSP libraries, and etc. (aka "time sink"); but, more simply, the fix is to change the problematic expression (well, actually it's a bug in JSF 1.2) to this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;#{isThisTrue?doThis : doThat}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This time, I googled for the right thing ("JSF 1.2 ternary parse exception") and, by now, the article from Oracle had finally been published (it wasn't there until after I originally bailed out with my workaround).&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-8938323263885984189?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/8938323263885984189/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/06/ternary-expressions-problem-with-jsf-12.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8938323263885984189'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/8938323263885984189'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/06/ternary-expressions-problem-with-jsf-12.html' title='Ternary Expressions Problem with JSF 1.2'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-4923737877464218140</id><published>2010-06-14T17:15:00.012-06:00</published><updated>2010-06-14T17:44:27.113-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JMS'/><category scheme='http://www.blogger.com/atom/ns#' term='ActiveMQ'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><title type='text'>Blocking Sends When ActiveMQ Broker is Down</title><content type='html'>As it turns out, even though you may have specified &lt;a href="http://activemq.apache.org/async-sends.html"&gt;useAsyncSend&lt;/a&gt; on an &lt;a href="http://activemq.apache.org/"&gt;ActiveMQ&lt;/a&gt; broker, your sends will block if that broker goes down (or if the broker was not running when the client first attempts a send). Don't waste any cycles (like I did) tweaking the &lt;a href="http://activemq.apache.org/activemq-performance-module-users-manual.html"&gt;delivery mode&lt;/a&gt;, whether or not the &lt;a href="http://activemq.apache.org/should-i-use-transactions.html"&gt;session is transacted&lt;/a&gt;, or other connection settings - the "problem" will occur with use of the&lt;a href="http://activemq.apache.org/failover-transport-reference.html"&gt; failover transport&lt;/a&gt;&amp;nbsp;(i.e., my broker URL is failover://(tcp://localhost:61616))&amp;nbsp;. This transport will automatically attempt reconnection until success, but during these attempts, your client will block. This may not be the behavior you want; I'm not able just yet to give you a satisfying solution, but I can give you some hooks to come up with something on your own (and if so, please let me know!).&lt;br /&gt;&lt;br /&gt;One so-called solution is of course to back off using the failover transport - e.g., use &lt;a href="http://activemq.apache.org/tcp-transport-reference.html"&gt;TCP&lt;/a&gt; instead. But this is heavy-handed and arguably overkill - the failover transport serves an excellent purpose in the face of broker failures, and I'm reluctant to let it go just to solve an exceptional condition. In either event, you'll receive something like a java.net.ConnectException with the TCP protocol when the broker is down; you can try-catch that and do whatever makes sense to you. But that's a questionable workaround. Instead, here's some &lt;a href="http://activemq.apache.org/failover-transport-reference.html:"&gt;advice&lt;/a&gt; from the ActiveMQ website:&lt;br /&gt;&lt;span class="Apple-style-span" style="font-family: verdana, arial, helvetica, sans-serif; font-size: 10px; line-height: 20px;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: verdana, arial, helvetica, sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 20px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;If you use failover, and a broker dies at some point, your sends will block by default. Using&amp;nbsp;TransportListener&amp;nbsp;can help with this regard. It is best to set the Listener directly on the&amp;nbsp;ActiveMQConnectionFactory&amp;nbsp;so that it is in place before any request that may require an network hop. Additionally you can use&amp;nbsp;&lt;em&gt;timeout&lt;/em&gt;&amp;nbsp;option which will cause your current send to fail after specified timeout. The following URL, for example&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: verdana, arial, helvetica, sans-serif; font-size: 10px; line-height: 20px;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;div class="preformatted panel" style="border-bottom-color: rgb(60, 120, 181); border-bottom-style: dashed; border-bottom-width: 1px; border-left-color: rgb(60, 120, 181); border-left-style: dashed; border-left-width: 1px; border-right-color: rgb(60, 120, 181); border-right-style: dashed; border-right-width: 1px; border-top-color: rgb(60, 120, 181); border-top-style: dashed; border-top-width: 1px; font-family: Courier; font-size: 11px; line-height: 13px; margin-bottom: 10px; margin-left: 10px; margin-right: 10px; margin-top: 0px;"&gt;&lt;div class="preformattedContent panelContent" style="background-color: #f0f0f0; padding: 5px;"&gt;&lt;pre style="margin: 5px 5px 5px 15px; padding: 0px; text-align: left;"&gt;failover:(tcp://primary:61616)?timeout=3000&lt;br /&gt;&lt;/pre&gt;&lt;/div&gt;&lt;/div&gt;&lt;span class="Apple-style-span" style="font-family: verdana, arial, helvetica, sans-serif; font-size: 10px; line-height: 20px;"&gt; &lt;/span&gt;&lt;br /&gt;&lt;div style="font-family: verdana, arial, helvetica, sans-serif; font-size: 14px; font-style: normal; font-weight: normal; line-height: 20px; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 10px; padding-bottom: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;"&gt;will cause send to fail after 3 seconds if the connection isn't established. The connection will not be killed, so you can try sending messages later at some point using the same connection (presumably some of your brokers will be available again).&amp;nbsp;&lt;/div&gt;&lt;br /&gt;Now, I first tried the timeout parameter approach - as it turns out, your application will get an exception at the timeout expiration. But this introduces its own problems around performance - if you have thousands (or, even dozens) or messages, each one timing out after say 2-3 seconds, your throughput will be hurt badly. So, I bailed out on this approach and took a closer look at the &lt;a href="http://activemq.apache.org/maven/activemq-core/apidocs/org/apache/activemq/transport/TransportListener.html"&gt;TransportListener&lt;/a&gt;. Here's what I implemented in my publisher class:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;    public void onCommand(Object command) { /* EMPTY */ }&lt;br /&gt;&lt;br /&gt;    public void onException(IOException error)&lt;br /&gt;    {    Logging.publisher.warn("Transport exception encountered", error);  }&lt;br /&gt;&lt;br /&gt;    public void transportInterupted()&lt;br /&gt;    {    Logging.publisher.warn("Transport interrupted! Is Broker down?");  }&lt;br /&gt;&lt;br /&gt;    public void transportResumed()&lt;br /&gt;    {    Logging.publisher.warn("Transport connectivity is restored");  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;My test program received callbacks for all of these methods, as expected, except for the onException. The ActiveMQ javadocs aren't much help here ("An unrecoverable exception has occured on the transport"). From this point, I tried combining the timeout parameter with the transport callback - e.g.&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;    public void transportInterupted()&lt;br /&gt;    {    this.amqFactory.setSendTimeout(100); }&lt;br /&gt;&lt;br /&gt;    public void transportResumed()&lt;br /&gt;    {    this.amqFactory.setSendTimeout(-1); }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;My strategy here is to minimize the timeout, and hence maximize the throughput in the face of dozens of sends, by reducing the timeout to a bare minimum (and then restoring it to the original timeout value when the broker comes back up). But, I had no success here; the timeout remained at the original value as set in the URL (which I consider to be too high in this context) even after transportInterupted (allegedly) changed it. I'm not willing to set my initial timeout to a low value either, since this may cause exceptions even under nominal operating conditions - not good.&lt;br /&gt;&lt;br /&gt;From here, I leave it to you to find some clever use of the transport listener that can deal with a broker that goes down. Good luck, and let me know.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-4923737877464218140?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/4923737877464218140/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/06/blocking-sends-when-activemq-broker-is.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4923737877464218140'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/4923737877464218140'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/06/blocking-sends-when-activemq-broker-is.html' title='Blocking Sends When ActiveMQ Broker is Down'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-5246111317057943477</id><published>2010-06-11T12:06:00.004-06:00</published><updated>2010-06-13T14:32:07.829-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='intellij'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='unix'/><title type='text'>Preserve Unix Line Endings with Windows Intellij</title><content type='html'>Just a quick note: If you're editing shell scripts and the like on a Windows platform with Intellij (my version is 9.0), here's how I succeeded preserving Unix-style line endings. First, to more accurately describe the problem, a given file has been saved in the Windows IDE, then checked in to a Perforce repository, and then sync'd onto a Unix server. When editing that file with vi, the settings &lt;b&gt;[dos] [noeol]&lt;/b&gt; appear - and I'd like to get rid of these. I do &lt;i&gt;not&lt;/i&gt; want a DOS format, and I &lt;i&gt;do&lt;/i&gt; want an end-of-line. Here are the steps I took:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;First, set the Intellij "Line separator" property to Unix - enter "Line separator" in the Settings search box, then choose the Code Style - General result. The Line separator property is towards the bottom of that panel. Yes, it does state "for new files", and not surprisingly I did not succeed simply editing an existing file - the DOS-style format remained after I saved and checked it in (Perforce). But, this does cover things going forward - at least this gets rid of the &lt;b&gt;[dos] &lt;/b&gt;problem on new files - however, on the subsequent edit of the new file on Unix, the &lt;b&gt;[noeol]&lt;/b&gt; is still there.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;To get rid of the &lt;b&gt;[noeol]&lt;/b&gt; problem, I set one more thing in Intellij: in IDE Settings - search for "Ensure blank", which will bring up the optional checkbox for "Ensure blank line before end of file on Save". Check that option, and from here it appears that newly created files in the IDE will no longer have the &lt;b&gt;[noeol]&lt;/b&gt; affliction. To address this problem on existing files, simply edit/save/check-in the file with the IDE. Probably, you could also just add a blank line at the end with vi on Unix - I didn't try this, but it sounds reasonable. &lt;br /&gt;&lt;/li&gt;&lt;li&gt;To fix the &lt;b&gt;[dos]&lt;/b&gt; problem on existing files, open the file in vi and type ":set ff=unix". Save, check in, and this problem should not reappear even after subsequent edits in the Windows IDE.&lt;/li&gt;&lt;/ol&gt;Don't forget, depending on what steps you take from above, that if you have set your CVS system to "revert unchanged" files (as I do with Perforce), that you &lt;i&gt;may&lt;/i&gt; need to actually change something that the CVS will notice before it will check it in. Huh, maybe adding some comments...&lt;br /&gt;&lt;br /&gt;Hope that helps. I'm sure I'll bump into this headache again, so I can also hope this helps &lt;i&gt;me&lt;/i&gt; when that happens.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-5246111317057943477?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/5246111317057943477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/06/preserve-unix-line-endings-with-windows.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5246111317057943477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5246111317057943477'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/06/preserve-unix-line-endings-with-windows.html' title='Preserve Unix Line Endings with Windows Intellij'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-5066237164296115700</id><published>2010-05-28T11:42:00.012-06:00</published><updated>2010-06-13T14:42:21.446-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='snippet'/><category scheme='http://www.blogger.com/atom/ns#' term='dependency injection'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='AOP'/><title type='text'>Get a Spring Aspect Working</title><content type='html'>After some time away from AOP, I've found myself forgetting the bare essentials of getting off the ground - i.e., &lt;i&gt;what's the least I have to do just to prove that my aspect is getting invoked?&lt;br /&gt;&lt;br /&gt;&lt;/i&gt;Assuming a Spring 3.0 environment, here is the summary:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create the aspect&lt;/li&gt;&lt;li&gt;Create a Spring context file that declares AOP&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Use Spring to instantiate both the aspect and the application object&lt;/li&gt;&lt;li&gt;Run your test case to prove it's working&lt;/li&gt;&lt;/ol&gt;Here's a bare-bones aspect:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package com.mybiz;&lt;br /&gt;import org.aspectj.lang.annotation.Aspect;&lt;br /&gt;&lt;br /&gt;@Aspect&lt;br /&gt;@Component&lt;br /&gt;public class MyAspect&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // Flag indicating that a given pointcut was in fact invoked.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public boolean madeIntercept = false;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Pointcut("execution(* com.mybiz.MyApp.myMethod(..))")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void myMethodPointcut() {}&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @After("myMethodPointcut()")&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void doSomething() { madeIntercept = true; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Here's a simple Spring file, named &lt;big&gt;&lt;tt&gt;spring-test-aspect.xml&lt;/tt&gt;&lt;/big&gt;:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: xml"&gt;&amp;lt;beans xmlns="http://www.springframework.org/schema/beans"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:aop="http://www.springframework.org/schema/aop"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:context="http://www.springframework.org/schema/context"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xsi:schemaLocation="http://www.springframework.org/schema/aop&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/aop/spring-aop-3.0.xsd&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/beans&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/beans/spring-beans.xsd&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/context&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/context/spring-context.xsd"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;context:annotation-config/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;context:component-scan base-package="com.mybiz"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;aop:aspectj-autoproxy/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The aspect will automatically get Spring-managed since it uses the @Component annotation, and component scanning declared above includes its package. Use the same approach with your application object:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package com.mybiz;&lt;br /&gt;@Component&lt;br /&gt;public class MyApp&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void myMethod() {}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Filling in the method is irrelevant; all we're trying to do is prove that if that method is invoked, that the aspect will&amp;nbsp; intercept the control flow afterwards. Here's a test case that does this for us:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush: java"&gt;package com.mybiz;&lt;br /&gt;&lt;br /&gt;@ContextConfiguration(locations ={"file:spring-test-aspect.xml"})&lt;br /&gt;public class MyAspectTest extends AbstractTestNGSpringContextTests&lt;br /&gt;{&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Resource&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private MyAspect myAspect;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Resource&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private MyApp myApp;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @PostConstruct&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void init() { assert myAspect != null; assert myApp != null; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; @Test&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void confirmInterception() &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; { &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; assert !myAspect.intercepted();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; myApp.myMethod(); &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; assert myAspect.intercepted();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;There's the proof-of-concept; from here, you'd add real functionality to your application class and decorate that functionality with the aspect. I should offer a fair warning: the above snippets have not been actually compiled and run, rather simply copy-pasted with various names changed to be more general purpose (as such, it might not be totally correct). So, if you bump into any problems - you have my apologies, but I'm sure the problems will be trivial.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-5066237164296115700?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/5066237164296115700/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/05/get-spring-aspect-working.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5066237164296115700'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5066237164296115700'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/05/get-spring-aspect-working.html' title='Get a Spring Aspect Working'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-7810703742705176717</id><published>2010-04-01T15:42:00.012-06:00</published><updated>2010-06-15T10:58:40.758-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JMS'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><title type='text'>Unit Test with a Reusable Spring-JMS Configuration</title><content type='html'>In my &lt;a href="http://blog.temposwc.com/2010/04/general-template-for-spring-jms.html"&gt;previous post&lt;/a&gt;, I introduced a general-purpose, allegedly reusable Spring configuration for an ActiveMQ-based JMS application. In this post, I'll expand on that by writing a unit test around a basic configuration that establishes an evolving prototype.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Test-driven_development"&gt;Writing tests&amp;nbsp;that fail&lt;/a&gt;, as it turns out, is pretty simple. Let's pick that low-hanging fruit:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms;&lt;br /&gt;&lt;br /&gt;import org.springframework.jms.core.JmsTemplate;&lt;br /&gt;import org.springframework.test.context.ContextConfiguration;&lt;br /&gt;import org.springframework.test.context.testng.AbstractTestNGSpringContextTests;&lt;br /&gt;import org.testng.annotations.BeforeClass;&lt;br /&gt;import org.testng.annotations.Test;&lt;br /&gt;&lt;br /&gt;import javax.annotation.Resource;&lt;br /&gt;import javax.jms.DeliveryMode;&lt;br /&gt;&lt;br /&gt;@ContextConfiguration(locations =&lt;br /&gt;{&lt;br /&gt;    "classpath:com/mybiz/jms/spring-jms-demo.xml"}&lt;br /&gt;)&lt;br /&gt;public class BasicConfigTest extends AbstractTestNGSpringContextTests&lt;br /&gt;{&lt;br /&gt;    @Resource&lt;br /&gt;    private JmsTemplate myTemplate;&lt;br /&gt;&lt;br /&gt;    @BeforeClass&lt;br /&gt;    public void setUp() throws Exception&lt;br /&gt;    {&lt;br /&gt;        System.out.println("Run BasicConfigTest...");&lt;br /&gt;        assert applicationContext != null;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Test&lt;br /&gt;    public void testConfig() throws Exception&lt;br /&gt;    {&lt;br /&gt;        System.out.println("testConfig...");&lt;br /&gt;        assert myTemplate.getDeliveryMode() == DeliveryMode.PERSISTENT;&lt;br /&gt;        assert myTemplate.getTimeToLive() == 10000;&lt;br /&gt;        assert myTemplate.isExplicitQosEnabled();&lt;br /&gt;        assert myTemplate.isPubSubDomain();&lt;br /&gt;        assert myTemplate.isSessionTransacted();&lt;br /&gt;        assert myTemplate.isPubSubNoLocal();&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I don't need to tell you that, without any other work, this test will fail; let's take on something a bit less trivial. The test assumes a Spring configuration which resides in the same logical directory tree as the class. Here's that&amp;nbsp;&lt;span style="font-family: 'Courier New', Courier, monospace;"&gt;&lt;small&gt;spring-jms-demo.xml&lt;/small&gt;&lt;/span&gt; configuration:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;beans xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;     &lt;br /&gt;       xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot; &lt;br /&gt;       xsi:schemalocation=&amp;quot;http://www.springframework.org/schema/beans&lt;br /&gt;       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd&amp;quot;&amp;gt;&lt;br /&gt;    &lt;br /&gt;    &amp;lt;import resource=&amp;quot;spring-context.xml&amp;quot;/&amp;gt;&lt;br /&gt;    &amp;lt;import resource=&amp;quot;spring-jms.xml&amp;quot;/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The first import establishes &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-annotation-config"&gt;context-based annotation&lt;/a&gt;,&amp;nbsp;&lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-classpath-scanning"&gt;scanning for&lt;/a&gt;&amp;nbsp;&lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-classpath-scanning"&gt;components&lt;/a&gt; and &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-factory-placeholderconfigurer"&gt;property placeholder&lt;/a&gt;&lt;br /&gt;&lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-factory-placeholderconfigurer"&gt; resolution&lt;/a&gt;, which will combine to help us reuse the generic Spring-JMS configuration (the second import). This&amp;nbsp;&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: 13px;"&gt;spring-context.xml&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size: 16px;"&gt;file is&amp;nbsp;&lt;/span&gt;&lt;span class="Apple-style-span" style="font-size: 13px;"&gt;&lt;big&gt;t&lt;/big&gt;&lt;/span&gt;he beginning of my application-specific configuration:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;beans xmlns:context=&amp;quot;http://www.springframework.org/schema/context&amp;quot;             &lt;br /&gt;       xmlns:xsi=&amp;quot;http://www.w3.org/2001/XMLSchema-instance&amp;quot;  &lt;br /&gt;       xmlns=&amp;quot;http://www.springframework.org/schema/beans&amp;quot;  &lt;br /&gt;       xsi:schemalocation=&amp;quot;http://www.springframework.org/schema/beans&lt;br /&gt;       http://www.springframework.org/schema/beans/spring-beans-3.0.xsd&lt;br /&gt;       http://www.springframework.org/schema/context&lt;br /&gt;       http://www.springframework.org/schema/context/spring-context-3.0.xsd&amp;quot;&amp;gt;&lt;br /&gt;       &lt;br /&gt;        &amp;lt;context:annotation-config&amp;gt;&lt;br /&gt;            &amp;lt;context:component-scan base-package=&amp;quot;com.mybiz.jms&amp;quot;/&amp;gt;&lt;br /&gt;            &amp;lt;context:property-placeholder &lt;br /&gt;                location=&amp;quot;com/mybiz/jms/spring-jms.properties&amp;quot; &lt;br /&gt;                system-properties-mode=&amp;quot;OVERRIDE&amp;quot;/&amp;gt;&lt;br /&gt;        &amp;lt;/context:annotation-config&amp;gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That file uses a properties file (&lt;span class="Apple-style-span" style="font-family: 'Courier New', Courier, monospace; font-size: 13px;"&gt;spring-jms.properties&lt;/span&gt;) that backfills the generic Spring-JMS configuration with more of our application specifics:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;# URL for connection to broker - use testing URL:&lt;br /&gt;brokerURL=vm://localhost?async=false&amp;amp;broker.persistent=false&lt;br /&gt;#&lt;br /&gt;# messages will remain in broker for 10 seconds&lt;br /&gt;timeToLive=10000&lt;br /&gt;#&lt;br /&gt;# 1 - non-persistent / 2 - persistent&lt;br /&gt;deliveryMode=2&lt;br /&gt;#&lt;br /&gt;# Specify concurrency of 40 for starters&lt;br /&gt;sessionCacheSize=40&lt;br /&gt;#&lt;br /&gt;# big perf boost, minimal risk (see comments in spring-jms.xml)&lt;br /&gt;useAsyncSend=true&lt;br /&gt;#&lt;br /&gt;# support for flexibility on per-send basis&lt;br /&gt;explicitQosEnabled=true&lt;br /&gt;#&lt;br /&gt;# concern is around a blocking consumer (see comments in spring-jms.xml)&lt;br /&gt;dispatchAsync=true&lt;br /&gt;#&lt;br /&gt;# txn around sends&lt;br /&gt;sessionTransacted=true&lt;br /&gt;#&lt;br /&gt;# just in case&lt;br /&gt;pubSubNoLocal=true&lt;br /&gt;#&lt;br /&gt;# must specify this if it's a topic; default is false&lt;br /&gt;pubSubDomain=true&lt;br /&gt;#&lt;br /&gt;# txn around receives&lt;br /&gt;acknowledge=transacted&lt;br /&gt;#&lt;br /&gt;# destination for topic&lt;br /&gt;destination=theTopic&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Finally, I need two listeners that are referenced by the Spring-JMS configuration:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms;&lt;br /&gt;&lt;br /&gt;import org.springframework.stereotype.Service;&lt;br /&gt;&lt;br /&gt;@Service&lt;br /&gt;public class MyListener {}  // works for now; but this will need some work&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms;&lt;br /&gt;&lt;br /&gt;import org.slf4j.Logger;&lt;br /&gt;import org.slf4j.LoggerFactory;&lt;br /&gt;import org.springframework.stereotype.Component;&lt;br /&gt;import javax.jms.ExceptionListener;&lt;br /&gt;import javax.jms.JMSException;&lt;br /&gt;&lt;br /&gt;@Component&lt;br /&gt;public class MyExceptionListener implements ExceptionListener&lt;br /&gt;{&lt;br /&gt;    private final Logger log = LoggerFactory.getLogger(getClass());&lt;br /&gt;    public void onException(JMSException e)&lt;br /&gt;    {&lt;br /&gt;        log.error("JMS error", e);&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now the unit test will run successfully; there's no (meaningful) output to show you, and for that matter the test and listeners are along the lines of "&lt;a href="http://en.wikiquote.org/wiki/Ward_Cunningham#The_Simplest_Thing_that_Could_Possibly_Work"&gt;what's the&lt;/a&gt;&amp;nbsp;&lt;a href="http://en.wikiquote.org/wiki/Ward_Cunningham#The_Simplest_Thing_that_Could_Possibly_Work"&gt;simplest&lt;/a&gt; &lt;a href="http://en.wikiquote.org/wiki/Ward_Cunningham#The_Simplest_Thing_that_Could_Possibly_Work"&gt;thing that&lt;/a&gt;&amp;nbsp;&lt;a href="http://en.wikiquote.org/wiki/Ward_Cunningham#The_Simplest_Thing_that_Could_Possibly_Work"&gt;would&lt;/a&gt; &lt;a href="http://en.wikiquote.org/wiki/Ward_Cunningham#The_Simplest_Thing_that_Could_Possibly_Work"&gt;work&lt;/a&gt;" - it's really only done to demonstrate that all the moving parts are in place and that I've used them correctly so far. In my next, I'll start adding on some target functionality and look for ways to test it.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-7810703742705176717?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/7810703742705176717/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/04/unit-test-with-reusable-spring-jms.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7810703742705176717'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/7810703742705176717'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/04/unit-test-with-reusable-spring-jms.html' title='Unit Test with a Reusable Spring-JMS Configuration'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-920592846148655809</id><published>2010-04-01T14:52:00.006-06:00</published><updated>2010-06-15T20:31:56.917-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JMS'/><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><title type='text'>A General Template for Spring-JMS Applications</title><content type='html'>Here I'll post a simple Spring setup that can be reused for specific JMS-based applications. It assumes the use of &lt;a href="http://activemq.apache.org/activemq-530-release.html"&gt;ActiveMQ 5.3.0&lt;/a&gt;, opting to use &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/api/org/springframework/jms/connection/CachingConnectionFactory.html"&gt;Spring's caching connection factory&lt;/a&gt; in lieu of &lt;a href="http://activemq.apache.org/maven/activemq-core/apidocs/org/apache/activemq/pool/PooledConnectionFactory.html"&gt;ActiveMQ's pooling version&lt;/a&gt;. It uses various &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#jms"&gt;Spring-JMS&lt;/a&gt; components, including one &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#jms-jmstemplate"&gt;JmsTemplate&lt;/a&gt; and one &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#jms-mdp"&gt;MessageListenerContainer&lt;/a&gt;, respectively intended for sending and receiving messages to-and-from a topic. In upcoming posts, I'll use this to prototype a JMS application.&lt;br /&gt;&lt;br /&gt;The connection factory and JMS template have most of their specific &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#beans-factory-placeholderconfigurer"&gt;configuration parameters externalized&lt;/a&gt; to facilitate better reuse.&lt;br /&gt;&lt;br /&gt;For the ActiveMQ connection factory, the externalized properties include the &lt;a href="http://activemq.apache.org/uri-protocols.html"&gt;broker URL&lt;/a&gt;, use of &lt;a href="http://activemq.apache.org/async-sends.html"&gt;asynchronous sends&lt;/a&gt; and choice around &lt;a href="http://activemq.apache.org/consumer-dispatch-async.html"&gt;dispatching asynchronously&lt;/a&gt;. The example XML template that follows includes some commentary around the pros/cons of configuring the last two. This factory is used by Spring's caching connection factory, externalizes &lt;a href="http://codedependents.com/2009/10/16/efficient-lightweight-jms-with-spring-and-activemq/"&gt;configuration of session cache size&lt;/a&gt;, and assumes the presence of an &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/api/index.html?org/springframework/jms/connection/CachingConnectionFactory.html"&gt;exception listener&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;The JMS template has the following properties that are configurable: &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bncfu.html#bncfy"&gt;delivery mode&lt;/a&gt;, &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bncfu.html#bncga"&gt;time-to-live&lt;/a&gt;, whether or not &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/api/org/springframework/jms/core/JmsTemplate.html#isExplicitQosEnabled%28%29"&gt;explicit quality of service&lt;/a&gt; is enabled, whether or not the &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/api/org/springframework/jms/support/JmsAccessor.html#setSessionTransacted%28boolean%29"&gt;session is transacted&lt;/a&gt;, whether or not a given connection should &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/api/org/springframework/jms/core/JmsTemplate.html#isPubSubNoLocal%28%29"&gt;listen for messages sent by that same connection&lt;/a&gt;, and whether or not the &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/api/org/springframework/jms/support/destination/JmsDestinationAccessor.html#isPubSubDomain%28%29"&gt;domain is pub-sub or point-to-point&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I would have externalized configuration for the listener container but, as it turns out, apparently the &lt;small&gt;&lt;span style="font-family: Courier New,Courier,monospace;"&gt;listener-container&lt;/span&gt;&lt;/small&gt; parent tag does not support this, so I've hardwired this to use the &lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html#jms-mdp-default"&gt;default listener container&lt;/a&gt;, listening on a topic within a transaction. &lt;br /&gt;&lt;br /&gt;Here's that template:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;!--&lt;br /&gt;A reusable template for Spring-JMS applications. The following must be configured externally, presumably&lt;br /&gt;via Spring's property-placeholder mechanism:&lt;br /&gt;&lt;br /&gt;brokerURL&lt;br /&gt;useAsyncSend&lt;br /&gt;dispatchAsync&lt;br /&gt;sessionCacheSize&lt;br /&gt;deliveryMode&lt;br /&gt;explicitQosEnabled&lt;br /&gt;sessionTransacted&lt;br /&gt;pubSubNoLocal&lt;br /&gt;pubSubDomain&lt;br /&gt;destination&lt;br /&gt;&lt;br /&gt;As well, the following is assumed:&lt;br /&gt;&lt;br /&gt;An exception listener that can be dereference by the name 'myExceptionListener' is assumed present &lt;br /&gt;in classpath.&lt;br /&gt;A message listener that can be dereference by the name 'myListener' is assumed present in classpath.&lt;br /&gt;--&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;beans xmlns="http://www.springframework.org/schema/beans"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:context="http://www.springframework.org/schema/context"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:jms="http://www.springframework.org/schema/jms"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xmlns:amq="http://activemq.apache.org/schema/core"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; xsi:schemaLocation="http://www.springframework.org/schema/beans&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/beans/spring-beans-3.0.xsd&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/context&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/context/spring-context-3.0.xsd&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://activemq.apache.org/schema/core&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://activemq.apache.org/schema/core/activemq-core-5.3.0.xsd&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/jms&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; http://www.springframework.org/schema/jms/spring-jms-3.0.xsd"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Configure connection factory&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;bean id="connectionFactory" class="org.apache.activemq.ActiveMQConnectionFactory"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; for testing, we want to use vm://localhost?async=false&amp;amp;broker.persistent=false.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; This does intra-JVM messaging using synchronous sends and doesn't bother with persisting&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; messages to cover provider failures. See http://activemq.apache.org/uri-protocols.html for&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; protocol options.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="brokerURL" value="${testUrl}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; See http://activemq.apache.org/async-sends.html - use async sends for better throughput but&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; only if you're able to tolerate some message loss. &lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; From the FUSE Tuning Guide (http://fusesource.com/wiki/display/ProdInfo/FUSE%20Message%20Broker%20Performance%20Tuning%20Guide):&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; If you are using persistent messaging and you don't want to use Async Sends (see below) then you should&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; use JMS transactions to batch up many operations inside a single transaction. If you enable Async Sends&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; then you will reduce the blocking in senders which increases throughput.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; The only downside of asynchronous sending is if the send fails for whatever reason (security exception&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; typically or some transport failure), then you don't get an exception thrown in the sender thread,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; since all the work is done asynchronously, though your ErrorListener will get notified.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; From the activemq.xsd:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Forces the use of Async Sends which adds a massive performance boost; but means that the send() method will&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return immediately whether the message has been sent or not which could lead to message loss.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="useAsyncSend" value="${useAsyncSend}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; From the activemq.xsd:&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Enables or disables the default setting of whether or not consumers have their messages dispatched&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; synchronously or asynchronously by the broker. For non-durable topics for example we typically dispatch&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; synchronously by default to minimize context switches which boost performance. However sometimes its better&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; to go slower to ensure that a single blocked consumer socket does not block delivery to other consumers.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; See http://activemq.apache.org/consumer-dispatch-async.html.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="dispatchAsync" value="${dispatchAsync}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Use the vanilla connection factory to configure a caching connection factory, using the Spring&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; version instead of ActiveMQ. Default session cache size for Spring factory is 1. An exception listener&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; that can be dereference by the name 'myExceptionListener' is assumed present in classpath.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;bean id="cachingConnectionFactory"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; class="org.springframework.jms.connection.CachingConnectionFactory"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; destroy-method="destroy"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="targetConnectionFactory" ref="connectionFactory"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="sessionCacheSize" value="${sessionCacheSize}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="exceptionListener" ref="myExceptionListener"/&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; The JMS template intended for sending of messages.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;bean id="myTemplate" class="org.springframework.jms.core.JmsTemplate"&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;constructor-arg ref="cachingConnectionFactory"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="deliveryMode" value="${deliveryMode}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="timeToLive" value="${timeToLive}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if explicit QoS is enabled, you can control deliveryMode, priority and TTL on a per-send basis&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="explicitQosEnabled" value="${explicitQosEnabled}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="sessionTransacted" value="${sessionTransacted}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="pubSubNoLocal" value="${pubSubNoLocal}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; override JMS template default domain type of PTP if you want pub-sub&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;property name="pubSubDomain" value="${pubSubDomain}"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/bean&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;!--&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; concurrency should be 1 for topic listeners. This is one attribute that apparently canNOT be&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; externalized into the properties file. Use transacted acknowledgment (another attribute that&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; cannot be externalized). A message listener that can be dereference by the name 'myListener' &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; is assumed present in classpath.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; --&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;jms:listener-container container-type="default"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; destination-type="topic"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; acknowledge="transacted"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; connection-factory="cachingConnectionFactory"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; concurrency="1"&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;jms:listener&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; destination="${myTopic}"&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ref="myListener"/&amp;gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; &amp;lt;/jms:listener-container&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/beans&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's a starting point; from here, I'll add configuration, listeners and a test case as the next step.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;big&gt;References&lt;/big&gt;&lt;/b&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://activemq.apache.org/"&gt;Apache ActiveMQ&lt;/a&gt;&lt;br /&gt;&lt;a href="p://static.springsource.org/spring/docs/3.0.0.RELEASE/api/"&gt;Spring 3.0.0 Javadoc&lt;/a&gt;&lt;br /&gt;&lt;a href="http://activemq.apache.org/maven/activemq-core/apidocs"&gt;ActiveMQ Javadoc&lt;/a&gt;&lt;br /&gt;&lt;a href="http://fusesource.com/wiki/display/ProdInfo/FUSE%20Message%20Broker%20Performance%20Tuning%20Guide"&gt;FUSE Tuning Guide&lt;/a&gt;&lt;br /&gt;&lt;a href="http://static.springsource.org/spring/docs/3.0.0.RELEASE/spring-framework-reference/htmlsingle/spring-framework-reference.html"&gt;Spring 3.0.0 Reference Documentation&lt;/a&gt;&lt;br /&gt;&lt;a href="http://codedependents.com/2009/10/16/efficient-lightweight-jms-with-spring-and-activemq/"&gt;Efficient Lightweight JMS with Spring and ActiveMQ&lt;br /&gt;&lt;/a&gt;&lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bncfu.html"&gt;Creating Robust JMS Applications (Java EE 5 Tutorial)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-920592846148655809?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/920592846148655809/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/04/general-template-for-spring-jms.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/920592846148655809'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/920592846148655809'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/04/general-template-for-spring-jms.html' title='A General Template for Spring-JMS Applications'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-80436372489451744</id><published>2010-03-25T13:50:00.008-06:00</published><updated>2010-06-16T20:14:40.838-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JMS'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><title type='text'>Asynchronous JMS Request/Reply</title><content type='html'>My recent previous posts have described a JMS-based request/reply &lt;a href="http://blog.temposwc.com/2010/03/jms-requestreply-part-1.html"&gt;client that uses blocking request semantics&lt;/a&gt;, and has no way of knowing if the service has thrown an exception and as such will never reply; and another that is marginally better, which &lt;a href="http://blog.temposwc.com/2010/03/jms-requestreply-part-2.html"&gt;listens on an invalid channel&lt;/a&gt; for messages about exceptions. That slight improvement still leaves a fair amount to be desired.&lt;br /&gt;&lt;br /&gt;To clarify the goal of this exercise, I'd like the following:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;JMS request-reply arrangement, i.e. issue a request to some service for some information or for a task to be performed, and receive a reply (either with the information or a status/confirmation about the task)&lt;/li&gt;&lt;li&gt;Receive information about an invalid request, or an exception that happened on the service side, in its attempt to fulfill the request&lt;br /&gt;&lt;/li&gt;&lt;li&gt;No blocking request is left hanging in the face of a service-side exception&lt;/li&gt;&lt;/ol&gt;So, to address #3, we already know we could use &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/MessageConsumer.html#receive%28long%29" rel="nofollow"&gt;block-without-wait&lt;/a&gt; or &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/MessageConsumer.html#receive%28long%29" rel="nofollow"&gt;block-with-timeout&lt;/a&gt;; or we can use an &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/MessageListener.html" rel="nofollow"&gt;asynchronous listener&lt;/a&gt;. Using the different flavors of blocking request means using one's own &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/MessageConsumer.html"&gt;MessageConsumer&lt;/a&gt; instead of the built-in &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/QueueRequestor.html"&gt;QueueRequestor&lt;/a&gt; (which is what I used in the first two parts of this series). From here, however, I'd prefer to just move on to an asynchronous idiom - but before leaving, check &lt;a href="http://codedependents.com/2010/03/04/synchronous-request-response-with-activemq-and-spring/"&gt;this article&lt;/a&gt; for some additional insights around synchronous request-reply.&lt;br /&gt;&lt;br /&gt;Now if we use our own asynchronous consumer, we no longer have the automatic temporary queue construction done via the QueueRequestor. We'll construct our own invalid queue, but we do &lt;i&gt;not &lt;/i&gt;want one for each potential requestor - construction of these is expensive. To be more precise, I'll quote the &lt;a href="http://activemq.apache.org/how-should-i-implement-request-response-with-jms.html"&gt;ActiveMQ recommendation&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;i&gt;The best way to implement request-response over JMS is to create a temporary queue and consumer per client on startup, set JMSReplyTo property on each message to the temporary queue and then use a correlationID on each message to correlate request messages to response messages. This avoids the overhead of creating and closing a consumer for each request (which is expensive). It also means you can share the same producer &amp;amp; consumer across many threads if you want (or pool them maybe).&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;This example will not do exactly that; instead, for simplicity (and as an excuse to demonstrate &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bnceh.html#bncer"&gt;message selection&lt;/a&gt;), I'll construct one shared invalid queue - but this presents the problem of how to return invalid information only to the requestor that should hear about it (i.e., if there are multiple queue-based clients, then only the first one to receive from a given queue will get that message). To address this problem, we can use message selection - but keep in mind, this relies on all requestors in a given system to be using the same filter. If for example one rogue client did no filtering with a message selector, it would be competing with the intended receiver for a message about exceptions - and if it got there first, the intended receiver would never know about it.&lt;br /&gt;&lt;br /&gt;We've already seen a remote service that sends messages about exceptions to an invalid queue; now we want to extend that to additionally set a property in the reply message that facilitates the intended message selection. Here's an example of that, where the service agrees to set a unique ID as a property in the reply message (see the previous posts for details around the &lt;small&gt;&lt;span style="font-family: Courier New,Courier,monospace;"&gt;RemoteService&lt;span style="font-family: Times New Roman,Times,serif;"&gt;,&lt;/span&gt;&lt;/span&gt;&lt;/small&gt; &lt;small&gt;&lt;span style="font-family: Courier New,Courier,monospace;"&gt;InvalidQueueRemoteService&lt;big&gt;&lt;span style="font-family: Times New Roman,Times,serif;"&gt;, etc.&lt;/span&gt;&lt;/big&gt;&lt;/span&gt;&lt;/small&gt;&lt;big&gt;&lt;span style="font-family: Times New Roman,Times,serif;"&gt;):&lt;/span&gt;&lt;/big&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms.activemq.server.requestreply.replier;&lt;br /&gt;&lt;br /&gt;import javax.jms.JMSException;&lt;br /&gt;import javax.jms.Message;&lt;br /&gt;import javax.jms.TextMessage;&lt;br /&gt;&lt;br /&gt;public class MessageSelectorRemoteService extends InvalidQueueRemoteService {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static final String OID_PROPERTY = "oid";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public MessageSelectorRemoteService(String theUrl, String serviceQueueName, String invalidQueueName)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throws JMSException {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; super(theUrl, serviceQueueName, invalidQueueName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println("&amp;nbsp;&amp;nbsp; ...service will now add OID property to reply...");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected TextMessage decorateMessage(Message requestMessage, TextMessage replyMsg)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; throws JMSException {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // include the OID_PROPERTY property&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; String oid = requestMessage.getStringProperty(OID_PROPERTY);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; replyMsg.setStringProperty(OID_PROPERTY, oid);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return replyMsg;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void main(String[] args) throws Exception {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RemoteService service = &lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new MessageSelectorRemoteService(url, serviceQueueName, invalidQueueName);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RemoteService.run(service);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;One consumer using this service could set up a single &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/MessageListener.html"&gt;MessageListener&lt;/a&gt; that receives both nominal and exceptional messages:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms.activemq.server.requestreply.requestor.async;&lt;br /&gt;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.AsyncConsumerConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.ConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.ProducerConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.replier.InvalidQueueRemoteService;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.replier.MessageSelectorRemoteService;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.replier.RemoteService;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.util.MessageUtil;&lt;br /&gt;&lt;br /&gt;import javax.jms.Message;&lt;br /&gt;import javax.jms.MessageListener;&lt;br /&gt;import javax.jms.TextMessage;&lt;br /&gt;&lt;br /&gt;// you'll see lots of protected access in fields and methods here - that's because I just happen&lt;br /&gt;// to know that I'll be extending this class, and protected qualifiers are "good enough" for&lt;br /&gt;// demo purposes:&lt;br /&gt;public class InvalidListener implements MessageListener {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // add message selector so queue listeners only gets their own messages,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // and so that no other clients receive those messages instead. This particular&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // OID can fail if two clients are started at exactly the same millisecond:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected static final String OID = MessageUtil.getDateTime();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected static final String MSG_SELECTOR =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MessageSelectorRemoteService.OID_PROPERTY + "='" + OID + "'";&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // keep track of number of message sent and received; if the gap between these grows&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // too much, log a message&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected static int numSent = 0, numReceived = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected static final int maxGap = 6;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // protect numSent and numReceived during concurrent access&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected static final Object mutex = new Object();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // this client uses message selectors so it receives only those messages intended&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // for it on the invalid queue, ftm no other clients will receive those messages. With&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // message selectors, the filtering is done by the provider so it's more efficient than&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; // asking each client to inspect each message for those that apply to it alone.&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void onMessage(Message msg) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; processReceived(msg);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected ConnectionStuff setupInvalidConnection() throws Exception {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // miracle occurs here: this sets the message listener to this object, so both&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // nominal and invalid messages will now arrive in the onMessage method:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new AsyncConsumerConnectionStuff(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RemoteService.url, InvalidQueueRemoteService.invalidQueueName,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; this, MSG_SELECTOR, true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected static void run(InvalidListener requestor) throws Exception {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; boolean shutdown = false;&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // set up producer that sends requests to the service queue. Messages to the queue should&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // have set the replyTo value to a temporary queue created manually.&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ProducerConnectionStuff requestorConnection =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new ProducerConnectionStuff(RemoteService.url, RemoteService.serviceQueueName, true);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // set up consumer that receives asynchronously at the temporary queue and start the connection&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ConnectionStuff consumerConnection =&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; new AsyncConsumerConnectionStuff(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RemoteService.url, "MyListener", requestor,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requestor.MSG_SELECTOR, true);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // factored out so it can be overridden, which will happen in next example requestor:&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ConnectionStuff invalidConnection = requestor.setupInvalidConnection();&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // send a message - async handler should get the reply&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; int msgNum = 0;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; while (!shutdown) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; TextMessage requestMsg = requestorConnection.getSession().createTextMessage();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if ((++msgNum % 3) == 0) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // every 3rd message is NULL, which we know will provoke an invalid queue message&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requestMsg.setText(null);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; } else {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requestMsg.setText("Async data request #" + msgNum);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // remote service agrees to set property in invalid replies matching any given&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // with RemoteService.OID_PROPERTY name&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requestMsg.setStringProperty(MessageSelectorRemoteService.OID_PROPERTY, requestor.OID);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requestorConnection.send(requestMsg, consumerConnection.getDestination());&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requestor.processSent(requestMsg);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; requestorConnection.getConnection().close();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; consumerConnection.getConnection().close();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; invalidConnection.getConnection().close();&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void main(String[] args) throws Exception {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; run(new InvalidListener());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private void processSent(Message requestMsg) throws Exception {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; synchronized (mutex) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; numSent++;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MessageUtil.examineSentRequest(requestMsg);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Thread.currentThread().sleep(3000);&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // confirms the number of messages received vs sent is within a specified threshold. If&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // the remote service goes down, this message will log continuously; but within a finite&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // amount of time (depending on how long the service was down), the gap will be made up&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // (from empirical observations)&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (numSent - numReceived &amp;gt; maxGap) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.err.println("WARNING: # sent = " + numSent&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; + ", # received = " + numReceived);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected void processReceived(Message msg) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; synchronized (mutex) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; ++numReceived;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; // confirms the message received has an OID property value as expected&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MessageUtil.process(msg, MessageSelectorRemoteService.OID_PROPERTY, OID);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;This requestor also monitors the gap between the number of messages sent vs the number received - if that starts getting too large, it will notice; this could indicate some kind of trouble around receiving the expected replies. As hoped, it now receives messages about exceptions, etc. - here's the startup of the service, and then the requestor:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Mar 25, 2010 1:27:25 PM org.apache.activemq.transport.failover.FailoverTransport doReconnect&lt;br /&gt;Service is waiting for a request...&lt;br /&gt;INFO: Successfully connected to tcp://localhost:61616&lt;br /&gt;&amp;nbsp;&amp;nbsp; ...service will now send replies to an INVALID queue...&lt;br /&gt;&amp;nbsp;&amp;nbsp; ...service will now add OID property to reply...&lt;/pre&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Mar 25, 2010 1:28:42 PM org.apache.activemq.transport.failover.FailoverTransport doReconnect&lt;br /&gt;INFO: Successfully connected to tcp://localhost:61616&lt;br /&gt;13:28:42.779 Request for service 'Async data request #1' has been sent...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34234-1269545322429-0:0:1:1:1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Correlation ID: null&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; queue://MyListener&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:28:42.217&lt;br /&gt;----------------------------------------------&lt;br /&gt;13:28:42.796 Received reply 'Normal Service Reply'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34222-1269545245163-0:0:1:2:1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Correlation ID: ID:localhost-34234-1269545322429-0:0:1:1:1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; null&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:28:42.217&lt;br /&gt;----------------------------------------------&lt;br /&gt;13:28:45.792 Request for service 'Async data request #2' has been sent...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34234-1269545322429-0:0:1:1:2&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Correlation ID: null&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; queue://MyListener&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:28:42.217&lt;br /&gt;----------------------------------------------&lt;br /&gt;13:28:45.819 Received reply 'Normal Service Reply'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34222-1269545245163-0:0:1:3:1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Correlation ID: ID:localhost-34234-1269545322429-0:0:1:1:2&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; null&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:28:42.217&lt;br /&gt;----------------------------------------------&lt;br /&gt;13:28:48.798 Request for service 'null' has been sent...&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34234-1269545322429-0:0:1:1:3&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Correlation ID: null&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; queue://MyListener&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:28:42.217&lt;br /&gt;----------------------------------------------&lt;br /&gt;13:28:48.815 Received reply 'Exception occurred: java.lang.IllegalStateException: java.lang.IllegalStateException: NULL message received'&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34222-1269545245163-0:0:2:2:1&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Correlation ID: ID:localhost-34234-1269545322429-0:0:1:1:3&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; null&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:28:42.217&lt;br /&gt;----------------------------------------------&lt;/pre&gt;&lt;br /&gt;As expected from the requestor logic, every 3rd message results in an exception, and the requestor is notified about this. So we've solved the problem around a request blocking, potentially "forever", if the service throws an exception; however, this particular flavor of consumer must examine each message received to determine whether or not it indicates such a problem. If you'd like to avoid putting that burden on every requestor (since &lt;i&gt;most&lt;/i&gt; of the time, the message will&lt;i&gt; not&lt;/i&gt; be about an exception - that's why they're called "exceptions"), here's an extension to this consumer that provides &lt;i&gt;two&lt;/i&gt; separate callbacks, one for nominal messages and another for invalid ones:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms.activemq.server.requestreply.requestor.async;&lt;br /&gt;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.AsyncConsumerConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.ConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.replier.InvalidQueueRemoteService;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.replier.RemoteService;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.util.MessageUtil;&lt;br /&gt;&lt;br /&gt;import javax.jms.Message;&lt;br /&gt;import javax.jms.MessageListener;&lt;br /&gt;&lt;br /&gt;public class DualListener extends InvalidListener implements AbstractInvalidNotifier {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void notifyInvalidRequest(Message notification) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; processReceived(notification, true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; protected ConnectionStuff setupInvalidConnection() throws Exception {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; MessageListener invalidQueueListener = new MessageListener() {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; public void onMessage(Message msg) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; notifyInvalidRequest(msg);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; };&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; return new AsyncConsumerConnectionStuff(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; RemoteService.url, InvalidQueueRemoteService.invalidQueueName,&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; invalidQueueListener, MSG_SELECTOR, true);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; public static void main(String[] args) throws Exception {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; run(new DualListener());&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; private void processReceived(Message msg, boolean invalid) {&lt;br /&gt;&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; if (invalid) {&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; System.out.println(&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; "----&amp;gt;&amp;gt;&amp;gt; " + MessageUtil.getDateTime() + " Notified about INVALID response &amp;lt;&amp;lt;&amp;lt;----");&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; processReceived(msg);&lt;br /&gt;&amp;nbsp;&amp;nbsp;&amp;nbsp; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Running this modified requestor confirms that invalid messages are received in a separate method, precluding the need to parse replies just to catch the occasional exception:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Mar 25, 2010 1:33:29 PM org.apache.activemq.transport.failover.FailoverTransport doReconnect       &lt;br /&gt;INFO: Successfully connected to tcp://localhost:61616       &lt;br /&gt;13:33:29.939 Request for service 'Async data request #1' has been sent...       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34245-1269545609591-0:0:1:1:1       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Correlation ID: null       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; queue://MyListener       &lt;br /&gt;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:33:29.348       &lt;br /&gt;----------------------------------------------       &lt;br /&gt;13:33:29.959 Received reply 'Normal Service Reply'       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34222-1269545245163-0:0:1:4:1       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Correlation ID: ID:localhost-34245-1269545609591-0:0:1:1:1       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; null       &lt;br /&gt;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:33:29.348       &lt;br /&gt;----------------------------------------------       &lt;br /&gt;13:33:32.951 Request for service 'Async data request #2' has been sent...       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34245-1269545609591-0:0:1:1:2       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Correlation ID: null       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; queue://MyListener       &lt;br /&gt;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:33:29.348       &lt;br /&gt;----------------------------------------------       &lt;br /&gt;13:33:32.954 Received reply 'Normal Service Reply'       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34222-1269545245163-0:0:1:5:1       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Correlation ID: ID:localhost-34245-1269545609591-0:0:1:1:2       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; null       &lt;br /&gt;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:33:29.348       &lt;br /&gt;----------------------------------------------       &lt;br /&gt;13:33:35.954 Request for service 'null' has been sent...       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34245-1269545609591-0:0:1:1:3       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Correlation ID: null       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; queue://MyListener       &lt;br /&gt;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:33:29.348       &lt;br /&gt;----------------------------------------------       &lt;br /&gt;----&amp;gt;&amp;gt;&amp;gt; 13:33:35.957 Notified about INVALID response &amp;lt;&amp;lt;&amp;lt;----       &lt;br /&gt;13:33:35.957 Received reply 'Exception occurred: java.lang.IllegalStateException: java.lang.IllegalStateException: NULL message received'       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Message ID: ID:localhost-34222-1269545245163-0:0:2:3:1       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Correlation ID: ID:localhost-34245-1269545609591-0:0:1:1:3       &lt;br /&gt;&amp;nbsp;&amp;nbsp; Reply to:&amp;nbsp;&amp;nbsp; null       &lt;br /&gt;&amp;nbsp;&amp;nbsp; OID property:&amp;nbsp;&amp;nbsp; 13:33:29.348       &lt;br /&gt;----------------------------------------------&lt;/pre&gt;&lt;br /&gt;The caveat mentioned above remains, however: since this is a shared queue destination, any consumers that are not using the same message selector as this one can compete to consume the invalid messages, subverting the intended behavior. The "correct" remedy here is to simply follow the ActiveMQ recommendation, noted above.&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-80436372489451744?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/80436372489451744/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/03/asynchronous-jms-requestreply_25.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/80436372489451744'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/80436372489451744'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/03/asynchronous-jms-requestreply_25.html' title='Asynchronous JMS Request/Reply'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-5869351063809815376</id><published>2010-03-23T15:18:00.011-06:00</published><updated>2010-06-18T20:13:16.506-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JMS'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><title type='text'>Sync Request w/Async Invalid Channel</title><content type='html'>The &lt;a href="http://blog.temposwc.com/2010/03/jms-requestreply-part-1.html" title="JMS Request-Reply, Part 1"&gt;first part&lt;/a&gt; of this series of writeups introduced a request-reply client that used the &lt;a href="http://java.sun.com/products/jms/docs.html" rel="nofollow"&gt;JMS 1.1&lt;/a&gt; &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/QueueRequestor.html" rel="nofollow"&gt;QueueRequestor&lt;/a&gt; to send the request, and a service that would throw an exception if the message was null or was not a &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/TextMessage.html" rel="nofollow"&gt;TextMessage&lt;/a&gt;. The client will not know that the exception occurred, and since it is using the QueueRequestor, it can block "forever". Even without exceptions, blocking clients consume resources and can compromise the responsiveness of a system; while a trivial remedy to this would be to simply use an &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/MessageListener.html" rel="nofollow"&gt;asynchronous listener&lt;/a&gt;, or to &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/MessageConsumer.html#receive%28long%29" rel="nofollow"&gt;block-without-wait&lt;/a&gt; or &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/MessageConsumer.html#receive%28long%29" rel="nofollow"&gt;block-with-timeout&lt;/a&gt;, in this case, again due to use of the QueueRequestor, a block-until-reply request is the only option. Before moving on to alternative client approaches, I'll first introduce a strategy that at least partially mitigates the problem with block-until-reply, and that can also be used with other request approaches as a mechanism for communicating exception information across the messaging middleware.&lt;br /&gt;&lt;br /&gt;Here's an example class that blocks "forever":&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms.activemq.server.requestreply.requestor.sync.broken;&lt;br /&gt;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.SyncRequestorConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.replier.RemoteService;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.util.MessageUtil;&lt;br /&gt;import javax.jms.JMSException;&lt;br /&gt;import javax.jms.Message;&lt;br /&gt;&lt;br /&gt;public class BlockForeverRequestor {&lt;br /&gt;&lt;br /&gt;    public void runBlocking(SyncRequestorConnectionStuff stuff) throws JMSException {&lt;br /&gt;&lt;br /&gt;        // create a null text message so as to provoke an exception. While the&lt;br /&gt;        // exception will indeed get thrown, the client using this approach to request&lt;br /&gt;        // something will never know it - it will block forever&lt;br /&gt;        Message oopsieMyBad = stuff.getSession().createTextMessage();&lt;br /&gt;&lt;br /&gt;        // this method calls use the QueueRequestor - a block-until-reply approach:&lt;br /&gt;        MessageUtil.processRequestAndReply(stuff.getRequestor(), oopsieMyBad);&lt;br /&gt;&lt;br /&gt;        // at this point, this client is going to block forever.&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) throws JMSException {&lt;br /&gt;&lt;br /&gt;        BlockForeverRequestor requestor = new BlockForeverRequestor();&lt;br /&gt;        SyncRequestorConnectionStuff serviceQueueStuff =&lt;br /&gt;                new SyncRequestorConnectionStuff(RemoteService.url, RemoteService.serviceQueueName);&lt;br /&gt;        requestor.runBlocking(serviceQueueStuff);&lt;br /&gt;        serviceQueueStuff.getConnection().close();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Continuing to use the plain-vanilla RemoteServer (introduced in Part 1) when running this client results in the following output:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Mar 23, 2010 10:26:11 AM org.apache.activemq.transport.failover.FailoverTransport doReconnect&lt;br /&gt;INFO: Successfully connected to tcp://localhost:61616&lt;br /&gt;10:26:11.514 Sending request for service 'null'...&lt;/pre&gt;&lt;br /&gt;And meanwhile, the service has issued this complaint, but the client will not be aware of it:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;SEVERE: ID:localhost-52606-1269361388958-0:0:1:1 Exception while processing message: java.lang.IllegalStateException: java.lang.IllegalStateException: NULL message received&lt;br /&gt;java.lang.IllegalStateException: java.lang.IllegalStateException: NULL message received&lt;br /&gt;   .....&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;One technique to remedy the client's ignorance of the exception is to listen on &lt;a href="http://www.eaipatterns.com/InvalidMessageChannel.html"&gt;a separate queue where messages about exceptions, invalid messages, and etc. can be posted&lt;/a&gt;. A service that posts such messages might look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms.activemq.server.requestreply.replier;&lt;br /&gt;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.ConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.ProducerConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.util.MessageUtil;&lt;br /&gt;import javax.jms.Connection;&lt;br /&gt;import javax.jms.JMSException;&lt;br /&gt;import javax.jms.Message;&lt;br /&gt;import javax.jms.TextMessage;&lt;br /&gt;&lt;br /&gt;// see Part 1 to examine the RemoteService class&lt;br /&gt;public class InvalidQueueRemoteService extends RemoteService {&lt;br /&gt;&lt;br /&gt;    public static final String invalidQueueName = "InvalidQueue";&lt;br /&gt;    private ConnectionStuff invalidStuff;&lt;br /&gt;&lt;br /&gt;    public InvalidQueueRemoteService() {}&lt;br /&gt;&lt;br /&gt;    public InvalidQueueRemoteService(String theUrl, String serviceQueueName, String invalidQueueName)&lt;br /&gt;            throws JMSException {&lt;br /&gt;&lt;br /&gt;        super(theUrl, serviceQueueName);&lt;br /&gt;        // use the same connection for the invalid queue as is used for the normal service replies.&lt;br /&gt;        // Connections are expensive, but you can get multiple sessions out of them.&lt;br /&gt;        invalidStuff = new ProducerConnectionStuff(getServiceQueueConnection(), invalidQueueName);&lt;br /&gt;        System.out.println("   ...service will now send replies to an INVALID queue...");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    private Connection getInvalidQueueConnection() {&lt;br /&gt;        return invalidStuff.getConnection();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void onMessage(Message requestMsg) {&lt;br /&gt;&lt;br /&gt;        try {&lt;br /&gt;            if (requestMsg instanceof TextMessage) {&lt;br /&gt;                super.onMessage(requestMsg);&lt;br /&gt;            } else {&lt;br /&gt;                MessageUtil.examineReceivedRequest(requestMsg);&lt;br /&gt;                // send a message back to an "invalid" queue. Any blocking requests&lt;br /&gt;                // would still block forever. At least in this case, that requestor gets some indication&lt;br /&gt;                // that something went wrong.&lt;br /&gt;                sendReply(invalidStuff, requestMsg, "Replied to Invalid Queue",&lt;br /&gt;                        invalidStuff.getDestination());&lt;br /&gt;            }&lt;br /&gt;        } catch (Exception e) {&lt;br /&gt;            try {&lt;br /&gt;                sendReply(invalidStuff, requestMsg, "Exception occurred: " + e,&lt;br /&gt;                        invalidStuff.getDestination());&lt;br /&gt;            } catch (JMSException e1) {&lt;br /&gt;                throw new IllegalStateException("Multiple exceptions occurred; first " + e&lt;br /&gt;                        + " -- and then " + e1);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected void shutdown() throws Exception {&lt;br /&gt;        super.shutdown();&lt;br /&gt;        getInvalidQueueConnection().close();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) throws Exception {&lt;br /&gt;        RemoteService service =&lt;br /&gt;                new InvalidQueueRemoteService(url, serviceQueueName, invalidQueueName);&lt;br /&gt;        RemoteService.run(service);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A base class that listens (asychronously) on this queue could look like this:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms.activemq.server.requestreply.requestor.sync;&lt;br /&gt;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.util.MessageUtil;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.AsyncConsumerConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.ConnectionStuff;&lt;br /&gt;import javax.jms.JMSException;&lt;br /&gt;import javax.jms.Message;&lt;br /&gt;import javax.jms.MessageListener;&lt;br /&gt;import javax.jms.MessageProducer;&lt;br /&gt;import javax.jms.TextMessage;&lt;br /&gt;&lt;br /&gt;public abstract class AbstractInvalidListener implements MessageListener {&lt;br /&gt;&lt;br /&gt;    public void onMessage(Message msg) {&lt;br /&gt;&lt;br /&gt;        try {&lt;br /&gt;            System.out.println("Received message on INVALID queue!");&lt;br /&gt;            MessageUtil.examineReceivedReply(msg);&lt;br /&gt;            msg.acknowledge();&lt;br /&gt;        } catch (JMSException e) {&lt;br /&gt;            e.printStackTrace();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected ConnectionStuff setUpInvalidQueue(ConnectionStuff stuff, String theQueueName) throws JMSException {&lt;br /&gt;&lt;br /&gt;        System.out.println("Setting up 'invalid queue' to listen for exceptions, etc...");&lt;br /&gt;        ConnectionStuff invalidStuff = new AsyncConsumerConnectionStuff(stuff.getConnection(), theQueueName, this);&lt;br /&gt;        // need to return a different "stuff" object here that reuses existing connection but encapsulates&lt;br /&gt;        // new session and destination; client should use that stuff object for subsequent invalid-queue&lt;br /&gt;        // messaging&lt;br /&gt;        return invalidStuff;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public void testInvalidQueue(ConnectionStuff stuff) throws JMSException {&lt;br /&gt;&lt;br /&gt;        TextMessage testMsg =&lt;br /&gt;                stuff.getSession().createTextMessage("Testing invalid queue");&lt;br /&gt;        testMsg.setText("Testing Invalid Queue");&lt;br /&gt;        testMsg.setJMSReplyTo(stuff.getDestination());&lt;br /&gt;        MessageUtil.examineRequestBeforeSend(testMsg);&lt;br /&gt;       &lt;br /&gt;        MessageProducer producer = stuff.getSession().createProducer(stuff.getDestination());&lt;br /&gt;        producer.send(testMsg);&lt;br /&gt;        MessageUtil.examineSentRequest(testMsg);&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;A concrete subclass that uses this could be something like this:&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.mybiz.jms.activemq.server.requestreply.requestor.sync.broken;&lt;br /&gt;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.ConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.connection.SyncRequestorConnectionStuff;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.replier.InvalidQueueRemoteService;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.replier.RemoteService;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.requestor.sync.AbstractInvalidListener;&lt;br /&gt;import com.mybiz.jms.activemq.server.requestreply.util.MessageUtil;&lt;br /&gt;import javax.jms.JMSException;&lt;br /&gt;import javax.jms.Message;&lt;br /&gt;&lt;br /&gt;public class InvalidListener extends AbstractInvalidListener {&lt;br /&gt;&lt;br /&gt;    public void runBlocking(SyncRequestorConnectionStuff stuff) throws JMSException {&lt;br /&gt;&lt;br /&gt;        // create a null text message so as to provoke an exception.&lt;br /&gt;        Message mistake = stuff.getSession().createTextMessage();&lt;br /&gt;        MessageUtil.processRequestAndReply(stuff.getRequestor(), mistake);&lt;br /&gt;&lt;br /&gt;        // Though this client now listens on the invalid queue, it is STILL going to block &lt;br /&gt;        // forever since the request still gets no reply - but at least this client gets &lt;br /&gt;        // notified since it's listening on the invalid queue. At that point, the client might &lt;br /&gt;        // be able to e.g. kill a separately-launched thread that is blocking, or otherwise &lt;br /&gt;        // deal with state as needed.&lt;br /&gt;&lt;br /&gt;        // Can we provoke a reply to the blocked service request from that point?&lt;br /&gt;        // That would be ideal, since that guarantees that the service reply comes AFTER the &lt;br /&gt;        // invalid reply. But that would involve knowing the name of the temporary queue that &lt;br /&gt;        // was set as the reply-to, but that isn't set until after the message is sent - which &lt;br /&gt;        // is too late - since it's a blocking request. Once the send is done, the requestor &lt;br /&gt;        // blocks for a reply. We need a better strategy.&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) throws JMSException {&lt;br /&gt;&lt;br /&gt;        InvalidListener requestor = new InvalidListener();&lt;br /&gt;        SyncRequestorConnectionStuff serviceQueueStuff =&lt;br /&gt;                new SyncRequestorConnectionStuff(RemoteService.url, &lt;br /&gt;                RemoteService.serviceQueueName);&lt;br /&gt;        ConnectionStuff invalidStuff =&lt;br /&gt;                requestor.setUpInvalidQueue(serviceQueueStuff, &lt;br /&gt;                     InvalidQueueRemoteService.invalidQueueName);&lt;br /&gt;        requestor.testInvalidQueue(invalidStuff);&lt;br /&gt;        requestor.runBlocking(serviceQueueStuff);&lt;br /&gt;        serviceQueueStuff.getConnection().close();&lt;br /&gt;    }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;Here's the console output when starting this new service:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Mar 23, 2010 10:59:57 AM org.apache.activemq.transport.failover.FailoverTransport doReconnect&lt;br /&gt;INFO: Successfully connected to tcp://localhost:61616&lt;br /&gt;Service is waiting for a request...&lt;br /&gt;   ...service will now send replies to an INVALID queue...&lt;/pre&gt;&lt;br /&gt;...and when running the requestor client:&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;Mar 23, 2010 11:00:28 AM org.apache.activemq.transport.failover.FailoverTransport doReconnect&lt;br /&gt;INFO: Successfully connected to tcp://localhost:61616&lt;br /&gt;Setting up 'invalid queue' to listen for exceptions, etc...&lt;br /&gt;11:00:29.104 Sending request for service 'Testing Invalid Queue'...&lt;br /&gt;Received message on INVALID queue!&lt;br /&gt;11:00:29.242 Request for service 'Testing Invalid Queue' has been sent...&lt;br /&gt;    Message ID: ID:localhost-52894-1269363628763-0:0:2:1:1&lt;br /&gt;    Correlation ID: null&lt;br /&gt;    Reply to:   queue://InvalidQueue&lt;br /&gt;----------------------------------------------&lt;br /&gt;11:00:29.236 Received reply 'Testing Invalid Queue'&lt;br /&gt;    Message ID: ID:localhost-52894-1269363628763-0:0:2:1:1&lt;br /&gt;    Correlation ID: null&lt;br /&gt;    Reply to:   queue://InvalidQueue&lt;br /&gt;----------------------------------------------&lt;br /&gt;11:00:29.243 Sending request for service 'null'...&lt;br /&gt;Received message on INVALID queue!&lt;br /&gt;11:00:29.275 Received reply 'Exception occurred: java.lang.IllegalStateException: java.lang.IllegalStateException: NULL message received'&lt;br /&gt;    Message ID: ID:localhost-52884-1269363597038-0:0:2:2:1&lt;br /&gt;    Correlation ID: ID:localhost-52894-1269363628763-0:0:1:1:1&lt;br /&gt;    Reply to:   null&lt;br /&gt;----------------------------------------------&lt;/pre&gt;&lt;br /&gt;So, we've succeeded in at least notifying the client that its service request resulted in an exception; this is marginally helpful in the context of using a block-until-reply requestor. Note that the &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/TopicRequestor.html" rel="nofollow"&gt;TopicRequestor&lt;/a&gt; is another JMS 1.1 concrete helper class, used again to facilitate a simple request-reply arrangement, but the API is the same - the request is blocking. Bottom line, we'd have better resilience - and be less coupled in either event, which is arguably one of the major benefits of a messaging approach - to use asynchronous messaging.&lt;br /&gt;&lt;br /&gt;The next article will continue with use of the separate queue for invalid messaging, using asynchronous messaging for both that and for service requests, and evolve to ensure that invalid messages are delivered only to the appropriate requestor (since the invalid queue is a system-wide resource, shared by all requestor clients).&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size: large;"&gt;&lt;b&gt;References&lt;/b&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blog.temposwc.com/2010/03/jms-requestreply-part-1.html"&gt;JMS Request/Reply, Part 1&lt;/a&gt;&lt;br /&gt;&lt;a href="http://java.sun.com/products/jms/" id="itjj" title="JMS HomePage"&gt;JMS Home Page&lt;/a&gt;&lt;br /&gt;&lt;a href="http://java.sun.com/products/jms/docs.html" id="n7li" title="JMSDownloads"&gt;JMS Downloads&lt;/a&gt;&lt;br /&gt;&lt;a href="http://java.sun.com/products/jms/faq.html" id="p.yh" title="JMSFAQ"&gt;JMS FAQ&lt;/a&gt;&lt;br /&gt;&lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bncdq.html" id="w2ff" title="JEE 5 JMS Tutorial"&gt;JEE 5 JMS Tutorial&lt;/a&gt;&lt;br /&gt;&lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bncgv.html" id="bpzx" title="JEE 5 JMS Tutorial - Example Code"&gt;JEE 5 JMS Tutorial -  Example Code&lt;/a&gt;&lt;br /&gt;&lt;a href="http://java.sun.com/javaee/5/docs/api/" id="jq92" title="JEE 5 Online Javadoc"&gt;JEE 5 Online Javadoc&lt;/a&gt;&lt;br /&gt;&lt;a href="http://java.sun.com/javaee/5/docs/api/index.html?javax/jms/package-summary.html" id="mene" title="JMS 1.1 Online Javadoc"&gt;JMS 1.1 Online Javadoc&lt;/a&gt;&lt;br /&gt;&lt;a href="http://activemq.apache.org/"&gt;Apache ActiveMQ&lt;/a&gt;&lt;br /&gt;&lt;a href="http://www.eaipatterns.com/Messaging.html"&gt;Enterprise Integration Patterns - Messaging&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;span class="post-author vcard"&gt; &lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6064095763580990-5869351063809815376?l=blog.temposwc.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.temposwc.com/feeds/5869351063809815376/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.temposwc.com/2010/03/jms-requestreply-part-2.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5869351063809815376'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6064095763580990/posts/default/5869351063809815376'/><link rel='alternate' type='text/html' href='http://blog.temposwc.com/2010/03/jms-requestreply-part-2.html' title='Sync Request w/Async Invalid Channel'/><author><name>Gary Horton</name><uri>http://www.blogger.com/profile/00737811225322795002</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='29' height='32' src='http://3.bp.blogspot.com/_ejClviH_0ls/SgiZxS_zdEI/AAAAAAAAAAM/yRomC7dZPcs/S220/starzineyes.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6064095763580990.post-6176678027815695099</id><published>2010-03-19T13:37:00.015-06:00</published><updated>2010-06-18T20:17:56.805-06:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JMS'/><category scheme='http://www.blogger.com/atom/ns#' term='Apache'/><title type='text'>JMS Synchronous Request/Reply</title><content type='html'>So far I've introduced some basics around JMS 1.1, including the &lt;a href="http://blog.temposwc.com/2010/03/jms-design-guidelines-part-1-common-vs.html" id="okx5" title="Common Interface"&gt;Common Interface&lt;/a&gt; and &lt;a href="http://blog.temposwc.com/2010/03/jms-design-part-2-security-concurrency.html" id="xwux" title="Security/Concurrency"&gt;Security/Concurrency&lt;/a&gt;. Continuing through  the JMS 1.1 &lt;a href="http://java.sun.com/products/jms/docs.html" id="wvzt" title="spec"&gt;spec&lt;/a&gt;, my next drill-down will be around the  Request/Reply idiom. Just  so you know, these articles track my own learning curve around JMS, so  you are invited to correct me and comment as needed. &lt;br /&gt;&lt;br /&gt;Section 2.10 of the spec describes  &lt;i&gt;Request/Reply&lt;/i&gt; as an arrangement where a client sends a &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bnceh.html#bnces" id="h_r4" title="message"&gt;message&lt;/a&gt; to a remote service of some kind,  expecting a reply. The request message &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bnceh.html#bncet" id="b8qe" title="header"&gt;header&lt;/a&gt; specifies a &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bnceh.html#bncel" id="w3rs" title="destination"&gt;destination&lt;/a&gt; to which the service can  (optionally!) respond with some information, or a confirmation that a  requested action has been performed, or...etc. A well-behaved service not only replies, but the reply includes the ID of the request  message (typically) so the requestor can correlate the reply with the  previous request. JMS additionally provides basic helper implementations  (&lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/QueueRequestor.html" id="i1lq" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" title="QueueRequestor"&gt;QueueRequestor&lt;/a&gt; and &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/TopicRequestor.html" id="ameq" style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;" title="TopicRequestor"&gt;TopicRequestor&lt;/a&gt;) that encapsulate  some of the details involved here, including creating the &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/TemporaryQueue.html" id="kjmh" title="temporary"&gt;temporary&lt;/a&gt; &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bncdx.html#bnceb" id="i4.d" title="queue"&gt;queue&lt;/a&gt; or &lt;a href="http://java.sun.com/javaee/5/docs/api/javax/jms/TemporaryTopic.html" id="tne2" title="temporary"&gt;temporary&lt;/a&gt; &lt;a href="http://java.sun.com/javaee/5/docs/tutorial/doc/bncdx.html#bnced" id="rv9r" title="topic"&gt;topic&lt;/a&gt; to which the reply is sent. In this  series of posts, I'll trace my own prototyping, starting with the  &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;QueueRequestor&lt;/span&gt; (which is a synchronous messaging style) and 
