tag:blogger.com,1999:blog-336517432024-03-08T04:25:27.990-05:00Checkmate TechnologiesA blog from members of a technology company which primarily services the printing industry.Dan Thiffaulthttp://www.blogger.com/profile/02959804356193147585noreply@blogger.comBlogger10125tag:blogger.com,1999:blog-33651743.post-7006744142727229432011-07-01T09:05:00.000-04:002011-07-01T09:06:25.761-04:00Java Generics and Covariance/Contravariance<div class="blogPrefix"></div><div>Suppose you have Java classes Animal, Mammal, and Giraffe, with the obvious relationships,<div class="blogSuffix" style="display: none;"></div></div><div><span class="Apple-style-span" style="font-family:'courier new';"><br /></span></div><div><span class="Apple-style-span" style="font-family:'courier new';">class Animal {…}</span></div><div><span class="Apple-style-span" style="font-family:'courier new';">class Mammal extends Animal {…}</span></div><div><span class="Apple-style-span" style="font-family:'courier new';">class Giraffe extends Mammal {…}</span></div><div><br /></div><div>A coworker asked me why, if he had a method that accepted a List<Mammal>, why couldn't he pass into it a List<Giraffe>? Surely if a method was okay with operating on Mammals, it should be okay to operate on Giraffes, right?</div><div><br /></div><div>Method declaration:</div><div><span class="Apple-style-span" style="font-family:'courier new';">void petMammals(final List<Mammal> mammalsToPet);</span></div><div>Usage:</div><div><span class="Apple-style-span" style="font-family:'courier new';">final List<Giraffe> myGiraffes = Collections.singletonList(new Giraffe());</span></div><div><span class="Apple-style-span" style="font-family:'courier new';"><i>petMammals(myGiraffes); // Complier doesn't let work</i></span></div><div><br /></div><div>The answer to this riddle is that Java is designed to work with all kinds of generic classes, not just those which are collection-like. For instance, Comparator is a generic class, but you can't take a Comparator<Giraffe> and pass it to a method requiring a Comparator<Mammal>, since it doesn't know how to compare all Mammals. But you probably could pass a Comparator<Animal> to a method requiring a Comparator<Mammal>, since if it can sort Animals, it can certainly sort Mammals too.</div><div><br /></div><div>So, since List and Comparator and every other generic class have to be treated the same way by the Java compiler, and it doesn't know for which uses it makes sense to allow for a larger type or smaller type to be passed in, you have to tell it each time. This requires learning the "wildcard type" syntax. Really understanding this syntax is key to writing methods that actually work the way you intend with your class hierarchy. For instance, our prior example really ought to be (with change in bold):</div><div><br /></div><div><div>Method declaration:</div><div><span class="Apple-style-span" style="font-family:'courier new';">void petMammals(final List<<b>? extends</b> Mammal> mammalsToPet);</span></div><div>Usage:</div><div><span class="Apple-style-span" style="font-family:'courier new';">final List<Giraffe> myGiraffes = Collections.singletonList(new Giraffe());</giraffe></span></div><div><span class="Apple-style-span" style="font-family:'courier new';">petMammals(myGiraffes); // <b>Now works!</b></span></div></div><div><span class="Apple-style-span" style="font-family:'courier new';"><i><br /></i></span></div><div>That is, if you tell Java that you're okay with getting a List of a subtype, then Java will allow a List of a subtype in there. Even more interestingly, suppose that sorting Mammals is a common operation in your application, then you might have a method like:</div><div><br /></div><div><span class="Apple-style-span" style="font-family:'courier new';">void sortMammals(final List<? extends Mammal> mammalsToSort, final Comparator<? super Mammal> howToSort);</span></div><div><br /></div><div>Which is quite clear that you can sort a List<Giraffe> with a Comparator<Animal>. A good example of a use of these wildcard types that's built into Java is the <a href="http://download.oracle.com/javase/6/docs/api/java/util/Collections.html#binarySearch(java.util.List,%20T,%20java.util.Comparator)">Collections.binarySearch</a> method, which is itself parameterized on a type T, and allows for Lists of T's subtypes and Comparators of T's supertypes.</div><div><br /></div><div>So, when writing a method that's using generics, it helps to take a second to think about what type you're really trying to use, and making your method actually have that type can solve a lot of confusion later on. Usually, classes which are collection-like can accept subclasses, and classes which are comparator-like can accept superclasses, but there are more reasons that one might be writing or using a generic class.</div><div><br /></div><div style="margin-botton: 0.3em;">For further reading:</div><div><ul><li>For those who love reading dry specifications of the internals of programming languages, <a href="http://java.sun.com/docs/books/jls/third_edition/html/typesValues.html#4.5.1">section 4.5.1 of the Java Language Specification</a> describes the details of how wildcard parameters work.</li><li>I'm constantly referring to <a href="http://www.angelikalanger.com/GenericsFAQ/JavaGenericsFAQ.html">Angelika Langer's Java Generics FAQs</a> when I have questions like these. It has a lot of practical information on using generics in Java, without assuming a Computer Science degree or a love for academic type theory.</li><li>Eric Lippert, a developer for Microsoft who works on the C# compiler, had a whole blog <a href="http://blogs.msdn.com/b/ericlippert/archive/tags/covariance+and+contravariance/">series on covariance and contravariance in C#</a>, explaining some of their thoughts as they were thinking about add some of these features to C#. I don't even develop in C#, but I found his explanations about the tricky parts of type systems very useful and interesting.</li></ul></div>Peter Cooper Jr.http://www.blogger.com/profile/07999652335340380274noreply@blogger.com0tag:blogger.com,1999:blog-33651743.post-36694006428127423642007-05-16T23:38:00.000-04:002007-05-17T01:03:18.040-04:00Generic Daos without all the xmlI was real happy when http://www-128.ibm.com/developerworks/java/library/j-genericdao.html came out. It seemed ideal until a few days and 50 daos later I had spent far too much time repeating the same xml over and over again. Finally with a little help from some new classes in spring 2.1 we can get rid of all the repetitous xml. First we need to scan for all the applicable dao interfaces. ClassPathScanningCandidateComponentProvider would be perfect except it specfically disallows returning interfaces. So the the first step was to create InterfaceComponentProvider which is essentially a copy of the original class except it will match and return interfaces.<br /><br />WIthin the spring core there are TypeFilters that match on all sorts of things. The best choice seemed to be AbstractTypeHierarchyTraversingFilter. DaoFilter extends this and looks for any class which extends/implements the GenericDao interface.<br /><br /><code><br />import genericdao.GenericDao;<br /><br />import org.springframework.core.typefilter.AbstractTypeHierarchyTraversingFilter;<br /><br />/**<br /> * TypeFilter used to match classes which implement the GenericDao interface<br /> * <br /> * @author Dan Thiffault <dthiffault@eInvite.com><br /> *<br /> */<br />public class DaoFilter extends AbstractTypeHierarchyTraversingFilter {<br /> private Class daoInterfaceClass = GenericDao.class;<br /> <br /> public DaoFilter() {<br /> super(true, true);<br /> }<br /><br /> @Override<br /> protected boolean matchInterfaceName(String arg0) {<br /> return daoInterfaceClass.getName().equals(arg0); <br /> }<br /><br /> public Class getDaoInterfaceClass() {<br /> return daoInterfaceClass;<br /> }<br /><br /> public void setDaoInterfaceClass(Class daoInterfaceClass) {<br /> this.daoInterfaceClass = daoInterfaceClass;<br /> }<br /><br />}<br /></code><br /><br />Now that we can discover all the appropriate beans we need a way to register them. First step is to extract the runtime type info from the discover beans.<br /><br /><code><br /><br />public void scan() {<br /> DaoFilter filter = new DaoFilter();<br /> componentProvider.addIncludeFilter(filter);<br /> <br /> Set<Class> candidateClasses = componentProvider.findCandidateComponents(); <br /> <br /> for(Class candidateDao : candidateClasses) { <br /> for(Type interfaceType : candidateDao.getGenericInterfaces()) {<br /> if(interfaceType instanceof ParameterizedType) {<br /> ParameterizedType pInterfaceType = (ParameterizedType) interfaceType;<br /> if(filter.getDaoInterfaceClass().equals(pInterfaceType.getRawType())) {<br /> Type genericType = pInterfaceType.getActualTypeArguments()[0];<br /> Assert.isInstanceOf(Class.class, genericType);<br /> registerDao(candidateDao, (Class) genericType); <br /> }<br /> } <br /> } <br /> } <br /> <br /> }<br /></code><br /><br />You may wonder what that registerDao method does. It creates the two required beans from the original Don't repeat the Dao article. Here's that method:<br /><br /><code><br />protected void registerDao(Class daoInterface, Class entityClass) {<br /> //Create the target child bean<br /> ConstructorArgumentValues cav = new ConstructorArgumentValues();<br /> cav.addGenericArgumentValue(entityClass);<br /> BeanDefinition targetBeanDefinition = new ChildBeanDefinition("abstractDaoTarget", cav, null);<br /> <br /> //Create the proxy child bean<br /> MutablePropertyValues propertyValues = new MutablePropertyValues();<br /> propertyValues.addPropertyValue("proxyInterfaces", daoInterface);<br /> propertyValues.addPropertyValue("target", targetBeanDefinition);<br /> BeanDefinition proxyBeanDefinition = new ChildBeanDefinition("abstractDao", propertyValues);<br /> <br /> registry.registerBeanDefinition(daoInterface.getSimpleName(), proxyBeanDefinition); <br /> }<br /></code><br /><br />For the last step our new class implements BeanFactoryPostProcessor so it automatically registers beans at startup. Now we just need one line of xml per application instead of many per dao. Get the full code from here:<br /><br />http://www.gspiral.com/autoDao.tgzDan Thiffaulthttp://www.blogger.com/profile/02959804356193147585noreply@blogger.com3tag:blogger.com,1999:blog-33651743.post-1166112004044778622006-12-14T10:59:00.000-05:002007-05-01T16:19:31.073-04:00Acegi/Spring Security with X509 and LDAP Authentication<div><a name="x509LdapWriteupTop"></a>We have chosen Acegi security for our security interface for our web applications. We have decided to use X509 certificates to obtain user’s credential and roles. If the certificate is not available or expired, we then show the login page having the user enter the user name and password. To accomplish this we used a special XML configuration and had to create 3 new classes.<br /><br /> <strong>EhCacheX509LdapUserCache.java</strong> (<a href="#EhCacheX509LdapUserCache.java">source</a>)<br />This class is used to search the cache for the user. We are using two different authenticator providers and because each provider implements the cache the differently, this class is used to provide a bridge between the two concepts by delegating methods to the underlying store.<br /><br /> <strong>SecurityContextHolderAwareRequestWrapperWrapper.java</strong> (<a href="#SecurityContextHolderAwareRequestWrapperWrapper.java">source</a>)<br />This class wraps SecurityContextHolderAwareRequestWrapper because the SecurityContextHolderAwareRequestFilter api requires both request and port resolver, however, Acegi’s SecurityContextHolderAwareRequestWrapper only supports request. Hopefully Acegi will fix this problem in the future, removing the need for this class.<br /><br /> <strong>X509LdapAuthoritiesPopulator.java</strong> (<a href="#X509LdapAuthoritiesPopulator.java">source</a>)<br />We are using 2 authorities populators so our class implements the X509 interface and delegates to the LDAP authority populator. We expect to obtain the userid from the certificate or login page while we obtain the roles from LDAP. One note of interest is that we use Perl to simplify the use of regular expressions because of the need to search the CN string for the user name.<br /><br /><strong>How they all tie together</strong><br />We have enabled these all by creating a <a href="#x509Ldap-servlet.xml">Spring configuration</a> that utilizes them. In addition, we needed a <a href="#LoginController.java">login controller</a> to redirect the user to their desired URI upon successful authentication (only necessary for form-based login). The last step is to make sure that your web.xml uses the correct servlet mappings, the following is how we organized ours:<br /><code><pre><br /> <servlet-mapping><br /> <servlet-name>Phoenix</servlet-name><br /> <url-pattern>/login</url-pattern><br /> </servlet-mapping><br /> <servlet-mapping><br /> <servlet-name>Phoenix</servlet-name><br /> <url-pattern>/login_error</url-pattern><br /> </servlet-mapping><br /> <servlet-mapping><br /> <servlet-name>Phoenix</servlet-name><br /> <url-pattern>/app/*</url-pattern><br /> </servlet-mapping><br /></pre></code></div><br /><br /><h2>Source</h2><br /> <a name="EhCacheX509LdapUserCache.java"></a><strong>EhCacheX509LdapUserCache.java</strong> (<a href="#x509LdapWriteupTop">top</a>)<br /><div style="font-size: .8em;"><code><font color="#7f0055"><b>package </b></font><font color="#000000">com.checkernet.security;</font><br /><font color="#ffffff"></font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">java.security.cert.X509Certificate;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">net.sf.ehcache.Cache;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">net.sf.ehcache.CacheException;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">net.sf.ehcache.Element;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.providers.dao.UserCache;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.providers.x509.X509UserCache;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.userdetails.UserDetails;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.commons.logging.Log;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.commons.logging.LogFactory;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.MalformedPatternException;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.MatchResult;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.Pattern;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.PatternMatcher;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.Perl5Compiler;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.Perl5Matcher;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.beans.factory.InitializingBean;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.dao.DataRetrievalFailureException;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.util.Assert;</font><br /><font color="#ffffff"></font><br /><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Notes:</font><font color="#7f7f9f"><br/></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Implements </font><font color="#3f3fbf">{@link org.acegisecurity.providers.x509.X509UserCache}</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* and </font><font color="#3f3fbf">{@link org.acegisecurity.providers.dao.UserCache} </font><font color="#7f7f9f"><br /></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Used to provide a bridge between the two concepts by delegating methods to the underlying store</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@author </font><font color="#3f5fbf">JoeS@checkernet.com</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@version </font><font color="#3f5fbf">1.0.0</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#646464">@SuppressWarnings</font><font color="#000000">({</font><font color="#2a00ff">"OverloadedMethodsWithSameNumberOfParameters"</font><font color="#000000">})</font><br /><font color="#7f0055"><b>public class </b></font><font color="#000000">EhCacheX509LdapUserCache </font><font color="#7f0055"><b>implements </b></font><font color="#000000">UserCache, X509UserCache, InitializingBean </font><font color="#000000">{</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private final </b></font><font color="#000000">Log logger = LogFactory.getLog</font><font color="#000000">(</font><font color="#000000">EhCacheX509LdapUserCache.</font><font color="#7f0055"><b>class</b></font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private </b></font><font color="#000000">Cache cache;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private </b></font><font color="#000000">Pattern subjectDNPattern;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private </b></font><font color="#000000">String subjectDNRegex = </font><font color="#2a00ff">"CN=(.*?),"</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">setCache</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">Cache cache</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>this</b></font><font color="#000000">.cache = cache;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Obtains a </font><font color="#3f3fbf">{@link UserDetails} </font><font color="#3f5fbf">from the cache.</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">username the </font><font color="#3f3fbf">{@link org.acegisecurity.userdetails.User#getUsername()} </font><font color="#3f5fbf">used to place the user in</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* the cache</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@return </font><font color="#3f5fbf">the populated </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">UserDetails</font><font color="#7f7f9f"></code> </font><font color="#3f5fbf">or </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">null</font><font color="#7f7f9f"></code> </font><font color="#3f5fbf">if</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* the user could not be found or if the cache entry has expired</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#000000">UserDetails getUserFromCache</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">String username</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">Element element;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>try </b></font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#000000">element = cache.get</font><font color="#000000">(</font><font color="#000000">username</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">} </font><font color="#7f0055"><b>catch </b></font><font color="#000000">(</font><font color="#000000">CacheException cacheException</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">//noinspection ThrowInsideCatchBlockWhichIgnoresCaughtException</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>throw new </b></font><font color="#000000">DataRetrievalFailureException</font><font color="#000000">(</font><font color="#2a00ff">"Cache failure: " </font><font color="#000000">+ cacheException.getMessage</font><font color="#000000">())</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">logger.isDebugEnabled</font><font color="#000000">()) {</font><br /><font color="#ffffff"> </font><font color="#000000">logger.debug</font><font color="#000000">(</font><font color="#2a00ff">"Cache hit for userName: " </font><font color="#000000">+ username</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">element == </font><font color="#7f0055"><b>null</b></font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">//noinspection ReturnOfNull</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return null</b></font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">} </font><font color="#7f0055"><b>else </b></font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return </b></font><font color="#000000">(</font><font color="#000000">UserDetails</font><font color="#000000">) </font><font color="#000000">element.getValue</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Obtains a </font><font color="#3f3fbf">{@link UserDetails} </font><font color="#3f5fbf">from the cache.</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">userCert the </font><font color="#3f3fbf">{@link X509Certificate} </font><font color="#3f5fbf">used to place the user in</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* the cache</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@return </font><font color="#3f5fbf">the populated </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">UserDetails</font><font color="#7f7f9f"></code> </font><font color="#3f5fbf">or </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">null</font><font color="#7f7f9f"></code> </font><font color="#3f5fbf">if</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* the user could not be found or if the cache entry has expired</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#000000">UserDetails getUserFromCache</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">X509Certificate userCert</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">userCert != </font><font color="#7f0055"><b>null </b></font><font color="#000000">&& userCert.getSubjectDN</font><font color="#000000">() </font><font color="#000000">!= </font><font color="#7f0055"><b>null</b></font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return </b></font><font color="#000000">getUserFromCache</font><font color="#000000">(</font><font color="#000000">extractUsernameFromCert</font><font color="#000000">(</font><font color="#000000">userCert</font><font color="#000000">))</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">} </font><font color="#7f0055"><b>else if </b></font><font color="#000000">(</font><font color="#000000">logger.isDebugEnabled</font><font color="#000000">()) {</font><br /><font color="#ffffff"> </font><font color="#000000">logger.debug</font><font color="#000000">(</font><font color="#2a00ff">"Certificate or user is null"</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f7f5f">//noinspection ReturnOfNull</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return null</b></font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Places a </font><font color="#3f3fbf">{@link UserDetails} </font><font color="#3f5fbf">in the cache. The </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">username</font><font color="#7f7f9f"></code> </font><font color="#3f5fbf">is</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* the key used to subsequently retrieve the </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">UserDetails</font><font color="#7f7f9f"></code></font><font color="#3f5fbf">.</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">user the fully populated </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">UserDetails</font><font color="#7f7f9f"></code> </font><font color="#3f5fbf">to place in the</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* cache</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">putUserInCache</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">UserDetails user</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#000000">Element element = </font><font color="#7f0055"><b>new </b></font><font color="#000000">Element</font><font color="#000000">(</font><font color="#000000">user.getUsername</font><font color="#000000">()</font><font color="#000000">, user</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">logger.isDebugEnabled</font><font color="#000000">()) {</font><br /><font color="#ffffff"> </font><font color="#000000">logger.debug</font><font color="#000000">(</font><font color="#2a00ff">"Cache put: " </font><font color="#000000">+ user.getUsername</font><font color="#000000">())</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#000000">cache.put</font><font color="#000000">(</font><font color="#000000">element</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* delegates to #putUserInCache(final UserDetails user)</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">userCert user's X509Certificate</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">user the fully populated </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">UserDetails</font><font color="#7f7f9f"></code> </font><font color="#3f5fbf">to place in the</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* cache</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">putUserInCache</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">X509Certificate userCert, </font><font color="#7f0055"><b>final </b></font><font color="#000000">UserDetails user</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#000000">putUserInCache</font><font color="#000000">(</font><font color="#000000">user</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Removes the specified user from the cache. The </font><font color="#7f7f9f"><code></font><font color="#3f5fbf">username</font><font color="#7f7f9f"></code> </font><font color="#3f5fbf">is</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* the key used to remove the user. If the user is not found, the method</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* should simply return (not thrown an exception).</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f7f9f"><p></p></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f7f9f"><p></font><font color="#3f5fbf">Some cache implementations may not support eviction from the cache, in</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* which case they should provide appropriate behaviour to alter the user</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* in either its documentation, via an exception, or through a log</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* message.</font><font color="#7f7f9f"></p></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">username to be evicted from the cache</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">removeUserFromCache</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">String username</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">logger.isDebugEnabled</font><font color="#000000">()) {</font><br /><font color="#ffffff"> </font><font color="#000000">logger.debug</font><font color="#000000">(</font><font color="#2a00ff">"Cache remove: " </font><font color="#000000">+ username</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#000000">cache.remove</font><font color="#000000">(</font><font color="#000000">username</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* delegates to #removeUserFromCache(final String username)</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">userCert user's X509Certificate</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">removeUserFromCache</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">X509Certificate userCert</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#000000">removeUserFromCache</font><font color="#000000">(</font><font color="#000000">extractUsernameFromCert</font><font color="#000000">(</font><font color="#000000">userCert</font><font color="#000000">))</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* {</font><font color="#7f9fbf">@inheritDoc</font><font color="#3f5fbf">}</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#646464">@SuppressWarnings</font><font color="#000000">({</font><font color="#2a00ff">"ProhibitedExceptionDeclared"</font><font color="#000000">})</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">afterPropertiesSet</font><font color="#000000">() </font><font color="#7f0055"><b>throws </b></font><font color="#000000">Exception </font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#000000">Assert.notNull</font><font color="#000000">(</font><font color="#000000">cache, </font><font color="#2a00ff">"cache is mandatory"</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">Perl5Compiler compiler = </font><font color="#7f0055"><b>new </b></font><font color="#000000">Perl5Compiler</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f7f5f">//noinspection UnusedCatchParameter</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>try </b></font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#000000">subjectDNPattern = compiler.compile</font><font color="#000000">(</font><font color="#000000">subjectDNRegex,</font><br /><font color="#ffffff"> </font><font color="#000000">Perl5Compiler.READ_ONLY_MASK</font><br /><font color="#ffffff"> </font><font color="#000000">| Perl5Compiler.CASE_INSENSITIVE_MASK</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">} </font><font color="#7f0055"><b>catch </b></font><font color="#000000">(</font><font color="#000000">MalformedPatternException mpe</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">//noinspection ThrowInsideCatchBlockWhichIgnoresCaughtException</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>throw new </b></font><font color="#000000">IllegalArgumentException</font><font color="#000000">(</font><font color="#2a00ff">"Malformed regular expression: "</font><br /><font color="#ffffff"> </font><font color="#000000">+ subjectDNRegex</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* extracts a username from a certificate's subjectDN</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">userCert X509Certificate</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@return </font><font color="#3f5fbf">String username</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private </b></font><font color="#000000">String extractUsernameFromCert</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">X509Certificate userCert</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">PatternMatcher matcher = </font><font color="#7f0055"><b>new </b></font><font color="#000000">Perl5Matcher</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">userCert == </font><font color="#7f0055"><b>null </b></font><font color="#000000">|| userCert.getSubjectDN</font><font color="#000000">() </font><font color="#000000">== </font><font color="#7f0055"><b>null</b></font><br /><font color="#ffffff"> </font><font color="#000000">|| !matcher.contains</font><font color="#000000">(</font><font color="#000000">userCert.getSubjectDN</font><font color="#000000">()</font><font color="#000000">.toString</font><font color="#000000">()</font><font color="#000000">, subjectDNPattern</font><font color="#000000">)) {</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">//noinspection ReturnOfNull</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return null</b></font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">MatchResult match = matcher.getMatch</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">match.groups</font><font color="#000000">() </font><font color="#000000">!= </font><font color="#990000">2</font><font color="#000000">) { </font><font color="#3f7f5f">// 2 = 1 + the entire match</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>throw new </b></font><font color="#000000">IllegalArgumentException</font><font color="#000000">(</font><br /><font color="#ffffff"> </font><font color="#2a00ff">"Regular expression must contain a single group "</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return </b></font><font color="#000000">match.group</font><font color="#000000">(</font><font color="#990000">1</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#000000">}</font></code></div><br /><br /> <a name="SecurityContextHolderAwareRequestWrapperWrapper.java"></a><strong>SecurityContextHolderAwareRequestWrapperWrapper.java</strong> (<a href="#x509LdapWriteupTop">top</a>)<br /><div style="font-size: .8em;"><code><font color="#7f0055"><b>package </b></font><font color="#000000">com.checkernet.security;</font><br /><font color="#ffffff"></font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">javax.servlet.http.HttpServletRequest;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.util.PortResolver;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.wrapper.SecurityContextHolderAwareRequestWrapper;</font><br /><font color="#ffffff"></font><br /><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Notes:</font><font color="#7f7f9f"><br /></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* This class acts as a wrapper for org.acegisecurity.wrapper.SecurityContextHolderAwareRequestWrapper because it does</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* not conform to the SecurityContextHolderAwareRequestFilter api. This class may be removed when Acegi fixes this bug.</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@author </font><font color="#3f5fbf">JoeS@checkernet.com</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@version </font><font color="#3f5fbf">1.0.0</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#646464">@SuppressWarnings</font><font color="#000000">({</font><font color="#2a00ff">"ClassNamePrefixedWithPackageName"</font><font color="#000000">, </font><font color="#2a00ff">"ClassNameSameAsAncestorName"</font><font color="#000000">})</font><br /><font color="#7f0055"><b>public class </b></font><font color="#000000">SecurityContextHolderAwareRequestWrapperWrapper</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>extends </b></font><font color="#000000">SecurityContextHolderAwareRequestWrapper </font><font color="#000000">{</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* conforming to SecurityContextHolderAwareRequestFilter api</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">request HttpServletRequest</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@param </font><font color="#3f5fbf">portResolver PortResolver (not currently used)</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#646464">@SuppressWarnings</font><font color="#000000">({</font><font color="#2a00ff">"UNUSED_SYMBOL"</font><font color="#000000">})</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#000000">SecurityContextHolderAwareRequestWrapperWrapper</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">HttpServletRequest request,</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">PortResolver portResolver</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>super</b></font><font color="#000000">(</font><font color="#000000">request</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#000000">}</font></code></div><br /><br /> <a name="X509LdapAuthoritiesPopulator.java"></a><strong>X509LdapAuthoritiesPopulator.java</strong> (<a href="#x509LdapWriteupTop">top</a>)<br /><div style="font-size: .8em;"><code><font color="#7f0055"><b>package </b></font><font color="#000000">com.checkernet.security;</font><br /><font color="#ffffff"></font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">java.security.cert.X509Certificate;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.BadCredentialsException;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.ldap.LdapUserSearch;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.providers.ldap.LdapAuthoritiesPopulator;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.providers.x509.X509AuthoritiesPopulator;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.userdetails.User;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.userdetails.UserDetails;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.userdetails.ldap.LdapUserDetails;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.MalformedPatternException;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.MatchResult;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.Pattern;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.PatternMatcher;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.Perl5Compiler;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.apache.oro.text.regex.Perl5Matcher;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.beans.factory.InitializingBean;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.util.Assert;</font><br /><font color="#ffffff"></font><br /><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Notes:</font><font color="#7f7f9f"><br/></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Implements the </font><font color="#3f3fbf">{@link X509AuthoritiesPopulator} </font><font color="#3f5fbf">while providing roles lookup from LDAP</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@author </font><font color="#3f5fbf">JoeS@checkernet.com</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@version </font><font color="#3f5fbf">1.0.0</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#646464">@SuppressWarnings</font><font color="#000000">({</font><font color="#2a00ff">"ClassNamingConvention"</font><font color="#000000">})</font><br /><font color="#7f0055"><b>public class </b></font><font color="#000000">X509LdapAuthoritiesPopulator </font><font color="#7f0055"><b>implements </b></font><font color="#000000">X509AuthoritiesPopulator, InitializingBean </font><font color="#000000">{</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private </b></font><font color="#000000">LdapUserSearch userSearch;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private </b></font><font color="#000000">LdapAuthoritiesPopulator authoritiesPopulator;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private </b></font><font color="#000000">Pattern subjectDNPattern;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>private </b></font><font color="#000000">String subjectDNRegex = </font><font color="#2a00ff">"CN=(.*?),"</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">setAuthoritiesPopulator</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">LdapAuthoritiesPopulator authoritiesPopulator</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>this</b></font><font color="#000000">.authoritiesPopulator = authoritiesPopulator;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">setUserSearch</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">LdapUserSearch userSearch</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>this</b></font><font color="#000000">.userSearch = userSearch;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* {</font><font color="#7f9fbf">@inheritDoc</font><font color="#3f5fbf">}</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#646464">@SuppressWarnings</font><font color="#000000">({</font><font color="#2a00ff">"ProhibitedExceptionDeclared"</font><font color="#000000">})</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">afterPropertiesSet</font><font color="#000000">() </font><font color="#7f0055"><b>throws </b></font><font color="#000000">Exception </font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#000000">Assert.notNull</font><font color="#000000">(</font><font color="#000000">userSearch, </font><font color="#2a00ff">"A userSearch must be set"</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">Assert.notNull</font><font color="#000000">(</font><font color="#000000">authoritiesPopulator, </font><font color="#2a00ff">"An authoritiesPopulator must be set"</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">Perl5Compiler compiler = </font><font color="#7f0055"><b>new </b></font><font color="#000000">Perl5Compiler</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f7f5f">//noinspection UnusedCatchParameter</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>try </b></font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#000000">subjectDNPattern = compiler.compile</font><font color="#000000">(</font><font color="#000000">subjectDNRegex,</font><br /><font color="#ffffff"> </font><font color="#000000">Perl5Compiler.READ_ONLY_MASK</font><br /><font color="#ffffff"> </font><font color="#000000">| Perl5Compiler.CASE_INSENSITIVE_MASK</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">} </font><font color="#7f0055"><b>catch </b></font><font color="#000000">(</font><font color="#000000">MalformedPatternException mpe</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">//noinspection ThrowInsideCatchBlockWhichIgnoresCaughtException</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>throw new </b></font><font color="#000000">IllegalArgumentException</font><font color="#000000">(</font><font color="#2a00ff">"Malformed regular expression: "</font><br /><font color="#ffffff"> </font><font color="#000000">+ subjectDNRegex</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* throws AuthenticationException</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* {</font><font color="#7f9fbf">@inheritDoc</font><font color="#3f5fbf">}</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#000000">UserDetails getUserDetails</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">X509Certificate userCertificate</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">String subjectDN = userCertificate.getSubjectDN</font><font color="#000000">()</font><font color="#000000">.getName</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">PatternMatcher matcher = </font><font color="#7f0055"><b>new </b></font><font color="#000000">Perl5Matcher</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">!matcher.contains</font><font color="#000000">(</font><font color="#000000">subjectDN, subjectDNPattern</font><font color="#000000">)) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>throw new </b></font><font color="#000000">BadCredentialsException</font><font color="#000000">(</font><font color="#2a00ff">"No matching pattern was found in subjectDN: {0}"</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">/*throw new BadCredentialsException(messages.getMessage(</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">"DaoX509AuthoritiesPopulator.noMatching",</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">new Object[] {subjectDN},</font><br /><font color="#ffffff"> </font><font color="#3f7f5f">"No matching pattern was found in subjectDN: {0}"));*/</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">MatchResult match = matcher.getMatch</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">match.groups</font><font color="#000000">() </font><font color="#000000">!= </font><font color="#990000">2</font><font color="#000000">) { </font><font color="#3f7f5f">// 2 = 1 + the entire match</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>throw new </b></font><font color="#000000">IllegalArgumentException</font><font color="#000000">(</font><br /><font color="#ffffff"> </font><font color="#2a00ff">"Regular expression must contain a single group "</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">String userName = match.group</font><font color="#000000">(</font><font color="#990000">1</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">LdapUserDetails userDetails = userSearch.searchForUser</font><font color="#000000">(</font><font color="#000000">userName</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return new </b></font><font color="#000000">User</font><font color="#000000">(</font><font color="#000000">userName, </font><font color="#2a00ff">"[PROTECTED]"</font><font color="#000000">, true, true, true, true,</font><br /><font color="#ffffff"> </font><font color="#000000">authoritiesPopulator.getGrantedAuthorities</font><font color="#000000">(</font><font color="#000000">userDetails</font><font color="#000000">))</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#000000">}</font></code></div><br /><br /> <a name="LoginController.java"></a><strong>LoginController.java</strong> (<a href="#x509LdapWriteupTop">top</a>)<br /><div style="font-size: .8em;"><code><font color="#7f0055"><b>package </b></font><font color="#000000">com.checkernet.controller;</font><br /><font color="#ffffff"></font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">javax.servlet.http.HttpServletRequest;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">javax.servlet.http.HttpServletResponse;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.Authentication;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.context.SecurityContextHolder;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.providers.anonymous.AnonymousAuthenticationToken;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.ui.AbstractProcessingFilter;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.ui.savedrequest.SavedRequest;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.acegisecurity.ui.webapp.AuthenticationProcessingFilter;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.beans.factory.InitializingBean;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.util.Assert;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.web.servlet.ModelAndView;</font><br /><font color="#7f0055"><b>import </b></font><font color="#000000">org.springframework.web.servlet.view.RedirectView;</font><br /><font color="#ffffff"></font><br /><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Notes:</font><font color="#7f7f9f"><br /></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@author </font><font color="#3f5fbf">JoeS@checkernet.com</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* </font><font color="#7f9fbf">@version </font><font color="#3f5fbf">1.0.0</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#7f0055"><b>public class </b></font><font color="#000000">LoginController </font><font color="#7f0055"><b>extends </b></font><font color="#000000">BaseController </font><font color="#7f0055"><b>implements </b></font><font color="#000000">InitializingBean </font><font color="#000000">{</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* user-defined loginPage view name</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>protected </b></font><font color="#000000">String loginPage;</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">setLoginPage</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">String loginPage</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>this</b></font><font color="#000000">.loginPage = loginPage;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* Login Page, redirects to saved request on authentication, otherwise goes to loginPage</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* {</font><font color="#7f9fbf">@inheritDoc</font><font color="#3f5fbf">}</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#646464">@SuppressWarnings</font><font color="#000000">({</font><font color="#2a00ff">"ProhibitedExceptionDeclared"</font><font color="#000000">})</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>protected </b></font><font color="#000000">ModelAndView handleRequestInternal</font><font color="#000000">(</font><font color="#7f0055"><b>final </b></font><font color="#000000">HttpServletRequest request, </font><font color="#7f0055"><b>final </b></font><font color="#000000">HttpServletResponse response</font><font color="#000000">)</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>throws </b></font><font color="#000000">Exception </font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">logger.isDebugEnabled</font><font color="#000000">()) {</font><br /><font color="#ffffff"> </font><font color="#000000">logger.debug</font><font color="#000000">(</font><font color="#2a00ff">"login attempt: "</font><br /><font color="#ffffff"> </font><font color="#000000">+ request.getParameter</font><font color="#000000">(</font><font color="#000000">AuthenticationProcessingFilter.ACEGI_SECURITY_FORM_USERNAME_KEY</font><font color="#000000">))</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">Authentication auth = SecurityContextHolder.getContext</font><font color="#000000">()</font><font color="#000000">.getAuthentication</font><font color="#000000">()</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">auth != </font><font color="#7f0055"><b>null </b></font><font color="#000000">&& auth.isAuthenticated</font><font color="#000000">() </font><font color="#000000">&& !</font><font color="#000000">(</font><font color="#000000">auth </font><font color="#7f0055"><b>instanceof </b></font><font color="#000000">AnonymousAuthenticationToken</font><font color="#000000">)) {</font><br /><font color="#ffffff"> </font><font color="#000000">logger.debug</font><font color="#000000">(</font><font color="#2a00ff">"redirecting to first request"</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>final </b></font><font color="#000000">Object savedRequest = request.getSession</font><font color="#000000">()</font><br /><font color="#ffffff"> </font><font color="#000000">.getAttribute</font><font color="#000000">(</font><font color="#000000">AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>if </b></font><font color="#000000">(</font><font color="#000000">savedRequest </font><font color="#7f0055"><b>instanceof </b></font><font color="#000000">SavedRequest</font><font color="#000000">) {</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return new </b></font><font color="#000000">ModelAndView</font><font color="#000000">(</font><font color="#7f0055"><b>new </b></font><font color="#000000">RedirectView</font><font color="#000000">(((</font><font color="#000000">SavedRequest</font><font color="#000000">) </font><font color="#000000">savedRequest</font><font color="#000000">)</font><font color="#000000">.getFullRequestUrl</font><font color="#000000">()))</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">} </font><font color="#7f0055"><b>else </b></font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#000000">logger.warn</font><font color="#000000">(</font><font color="#000000">AbstractProcessingFilter.ACEGI_SAVED_REQUEST_KEY</font><br /><font color="#ffffff"> </font><font color="#000000">+ </font><font color="#2a00ff">" found in session not of correct type returning to login page"</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>return new </b></font><font color="#000000">ModelAndView</font><font color="#000000">(</font><font color="#000000">loginPage</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#ffffff"></font><br /><font color="#ffffff"> </font><font color="#3f5fbf">/**</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">* {</font><font color="#7f9fbf">@inheritDoc</font><font color="#3f5fbf">}</font><br /><font color="#ffffff"> </font><font color="#3f5fbf">*/</font><br /><font color="#ffffff"> </font><font color="#646464">@SuppressWarnings</font><font color="#000000">({</font><font color="#2a00ff">"ProhibitedExceptionDeclared"</font><font color="#000000">})</font><br /><font color="#ffffff"> </font><font color="#7f0055"><b>public </b></font><font color="#7f0055"><b>void </b></font><font color="#000000">afterPropertiesSet</font><font color="#000000">() </font><font color="#7f0055"><b>throws </b></font><font color="#000000">Exception </font><font color="#000000">{</font><br /><font color="#ffffff"> </font><font color="#000000">Assert.notNull</font><font color="#000000">(</font><font color="#000000">loginPage, </font><font color="#2a00ff">"loginPage is required"</font><font color="#000000">)</font><font color="#000000">;</font><br /><font color="#ffffff"> </font><font color="#000000">}</font><br /><font color="#000000">}</font></code></div><br /><br /><a name="x509Ldap-servlet.xml"></a><strong>x509Ldap-servlet.xml</strong> (<a href="#x509LdapWriteupTop">top</a>)<br /><div style="font-size: .8em;"><code><pre><?xml version="1.0" encoding="UTF-8"?><br /><beans xmlns="http://www.springframework.org/schema/beans"<br /> xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"<br /> xmlns:aop="http://www.springframework.org/schema/aop"<br /> xmlns:tx="http://www.springframework.org/schema/tx"<br /> xsi:schemaLocation="http://www.springframework.org/schema/beans<br /> http://www.springframework.org/schema/beans/spring-beans.xsd<br /> http://www.springframework.org/schema/aop<br /> http://www.springframework.org/schema/aop/spring-aop.xsd<br /> http://www.springframework.org/schema/tx<br /> http://www.springframework.org/schema/tx/spring-tx.xsd"><br /><br /> <!-- This file describes standard concrete beans for security use,<br /> as well as provides abstract beans for bean defaults --><br /><br /> <!-- Automatically receives AuthenticationEvent messages --><br /> <bean id="loggerListener" class="org.acegisecurity.event.authentication.LoggerListener" /><br /><br /> <!--ldap configuration--><br /> <bean id="initialDirContextFactory" class="org.acegisecurity.ldap.DefaultInitialDirContextFactory"><br /> <constructor-arg value="ldap://checkernet.com:389" /><br /> <property name="managerDn"><br /> <value>cn=ReadOnlyUser,ou=webappgroup,dc=checkernet,dc=com</value><br /> </property><br /> <property name="managerPassword"><br /> <value>bogusPassword</value><br /> </property><br /> <property name="extraEnvVars"><br /> <map><br /> <entry key="java.naming.referral" value="follow" /><br /> </map><br /> </property><br /> </bean><br /><br /> <bean id="checkerboardLdapProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider"><br /> <constructor-arg><br /> <bean class="org.acegisecurity.providers.ldap.authenticator.BindAuthenticator"><br /> <constructor-arg><br /> <ref local="initialDirContextFactory" /><br /> </constructor-arg><br /> <property name="userSearch"><br /> <bean class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch"><br /> <constructor-arg><br /> <value>DC=checkernet,DC=com</value><br /> </constructor-arg><br /> <constructor-arg><br /> <value>(sAMAccountName={0})</value><br /> </constructor-arg><br /> <constructor-arg><br /> <ref local="initialDirContextFactory" /><br /> </constructor-arg><br /> <property name="searchSubtree" value="true" /><br /> </bean><br /> </property><br /> </bean><br /> </constructor-arg><br /> <constructor-arg ref="ldapAuthoritiesPopulator" /><br /> <!--you supply this part:--><br /> <!--<property name="userCache" ref="userCache" />--><br /> </bean><br /><br /> <bean id="ldapAuthoritiesPopulator"<br /> class="org.acegisecurity.providers.ldap.populator.DefaultLdapAuthoritiesPopulator"><br /> <constructor-arg><br /> <ref local="initialDirContextFactory" /><br /> </constructor-arg><br /> <constructor-arg><br /> <value>dc=checkernet,dc=com</value><br /> </constructor-arg><br /> <property name="groupSearchFilter"><br /> <value>(member={0})</value><br /> </property><br /> <property name="groupRoleAttribute"><br /> <value>CN</value><br /> </property><br /> <property name="searchSubtree" value="true" /><br /> </bean><br /><br /> <!--this bean specifies the special case of binding an x509 cert to an Ldap entry<br /> it requires a userCache to exist--><br /> <bean id="x509LdapAuthoritiesPopulator" class="com.checkernet.security.X509LdapAuthoritiesPopulator"><br /> <property name="userSearch"><br /> <bean class="org.acegisecurity.ldap.search.FilterBasedLdapUserSearch"><br /> <constructor-arg><br /> <value>DC=checkernet,DC=com</value><br /> </constructor-arg><br /> <constructor-arg><br /> <value>(cn={0})</value><br /> </constructor-arg><br /> <constructor-arg><br /> <ref local="initialDirContextFactory" /><br /> </constructor-arg><br /> <property name="searchSubtree" value="true" /><br /> </bean><br /> </property><br /> <property name="authoritiesPopulator"><br /> <ref local="ldapAuthoritiesPopulator" /><br /> </property><br /> </bean><br /><br /> <!-- An access decision voter that reads ROLE_* configuration settings --><br /> <bean id="roleVoter" class="org.acegisecurity.vote.RoleVoter" /><br /><br /> <!-- An access decision manager used by the business objects --><br /> <bean id="accessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"><br /> <property name="allowIfAllAbstainDecisions" value="false" /><br /> <property name="decisionVoters"><br /> <list><br /> <ref local="roleVoter" /><br /> </list><br /> </property><br /> </bean><br /><br /> <!-- ================= METHOD INVOCATION AUTHORIZATION ==================== --><br /><br /> <!--<br /> NOTE:<br /> users need to already be authenticated before invoking this security check<br /> --><br /> <bean id="securityAttributes" class="org.acegisecurity.annotation.SecurityAnnotationAttributes" /><br /><br /> <!--AOP security config--><br /> <bean id="securityInterceptor" class="org.acegisecurity.intercept.method.aopalliance.MethodSecurityInterceptor"><br /> <property name="validateConfigAttributes" value="true" /><br /> <property name="authenticationManager" ref="authenticationManager" /><br /> <property name="accessDecisionManager" ref="accessDecisionManager" /><br /> <property name="objectDefinitionSource"><br /> <bean class="org.acegisecurity.intercept.method.MethodDefinitionAttributes"><br /> <property name="attributes" ref="securityAttributes" /><br /> </bean><br /> </property><br /> </bean><br /><br /> <!--AspectJ security config--><br /> <bean id="securityInterceptorAspectJ" class="org.acegisecurity.intercept.method.aspectj.AspectJSecurityInterceptor"><br /> <property name="validateConfigAttributes" value="true" /><br /> <property name="authenticationManager" ref="authenticationManager" /><br /> <property name="accessDecisionManager" ref="accessDecisionManager" /><br /> <property name="objectDefinitionSource"><br /> <bean class="org.acegisecurity.intercept.method.MethodDefinitionAttributes"><br /> <property name="attributes" ref="securityAttributes" /><br /> </bean><br /> </property><br /> </bean><br /><br /> <!-- ======================== FILTER CHAIN ======================= --><br /><br /> <!-- If you wish to use channel security, add "channelProcessingFilter," in front<br /> of "httpSessionContextIntegrationFilter" in the list below --><br /> <bean id="filterChainProxy" class="org.acegisecurity.util.FilterChainProxy"><br /> <property name="filterInvocationDefinitionSource"><br /> <value><br /> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br /> PATTERN_TYPE_APACHE_ANT<br /> /**=httpSessionContextIntegrationFilter,logoutFilter,securityContextHolderAwareRequestFilter,x509ProcessingFilter,authenticationProcessingFilter,anonymousProcessingFilter,exceptionTranslationFilter,filterInvocationInterceptor<br /> </value><br /> </property><br /> </bean><br /><br /> <!-- ======================== AUTHENTICATION ======================= --><br /><br /> <bean id="authenticationManager" class="org.acegisecurity.providers.ProviderManager"><br /> <property name="providers"><br /> <list><br /> <ref bean="x509AuthenticationProvider" /><br /> <ref bean="ldapAuthenticationProvider" /><br /> <ref bean="anonymousAuthenticationProvider" /><br /> </list><br /> </property><br /> </bean><br /><br /> <bean id="anonymousAuthenticationProvider"<br /> class="org.acegisecurity.providers.anonymous.AnonymousAuthenticationProvider"><br /> <property name="key" value="fakeAnonymousKey" /><br /> <!--must match anonymousProcessingFilter key--><br /> </bean><br /><br /> <bean id="x509AuthenticationProvider" class="org.acegisecurity.providers.x509.X509AuthenticationProvider"><br /> <property name="x509AuthoritiesPopulator" ref="x509LdapAuthoritiesPopulator" /><br /> <property name="x509UserCache" ref="userCache" /><br /> </bean><br /><br /> <bean id="ldapAuthenticationProvider" class="org.acegisecurity.providers.ldap.LdapAuthenticationProvider"<br /> parent="checkerboardLdapProvider"><br /> <property name="userCache" ref="userCache" /><br /> </bean><br /><br /> <bean id="cacheManager" class="org.springframework.cache.ehcache.EhCacheManagerFactoryBean" /><br /><br /> <bean id="userCache" class="com.checkernet.security.EhCacheX509LdapUserCache"><br /> <property name="cache"><br /> <bean class="org.springframework.cache.ehcache.EhCacheFactoryBean"><br /> <property name="cacheManager" ref="cacheManager" /><br /> <property name="cacheName" value="x509Cache" /><br /> </bean><br /> </property><br /> </bean><br /><br /> <bean id="httpSessionContextIntegrationFilter"<br /> class="org.acegisecurity.context.HttpSessionContextIntegrationFilter"><br /> </bean><br /> <bean id="securityContextHolderAwareRequestFilter"<br /> class="org.acegisecurity.wrapper.SecurityContextHolderAwareRequestFilter"><br /> <property name="wrapperClass" value="com.checkernet.security.SecurityContextHolderAwareRequestWrapperWrapper" /><br /> </bean><br /><br /> <bean id="logoutFilter" class="org.acegisecurity.ui.logout.LogoutFilter"><br /> <constructor-arg value="/" /><br /> <constructor-arg><br /> <list><br /> <ref bean="securityContextLogoutHandler" /><br /> <bean class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler" /><br /> </list><br /> </constructor-arg><br /> <property name="filterProcessesUrl" value="/app/logout" /><br /> </bean><br /> <bean id="securityContextLogoutHandler"<br /> class="org.acegisecurity.ui.logout.SecurityContextLogoutHandler"><br /> </bean><br /><br /><br /> <!-- ===================== HTTP REQUEST SECURITY ==================== --><br /><br /> <bean id="anonymousProcessingFilter" class="org.acegisecurity.providers.anonymous.AnonymousProcessingFilter"><br /> <property name="key" value="fakeAnonymousKey" /><br /> <!--must match anonymousAuthenticationProvider key--><br /> <property name="userAttribute" value="anonymousUser,ROLE_ANONYMOUS" /><br /> </bean><br /><br /> <bean id="exceptionTranslationFilter" class="org.acegisecurity.ui.ExceptionTranslationFilter"><br /> <property name="authenticationEntryPoint"><br /> <bean class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilterEntryPoint"><br /> <property name="loginFormUrl"><br /> <value>/login</value><br /> </property><br /> <property name="forceHttps" value="true" /><br /> </bean><br /> </property><br /> </bean><br /><br /> <bean id="x509ProcessingFilter" class="org.acegisecurity.ui.x509.X509ProcessingFilter"><br /> <property name="authenticationManager" ref="authenticationManager" /><br /> </bean><br /><br /> <bean id="authenticationProcessingFilter" class="org.acegisecurity.ui.webapp.AuthenticationProcessingFilter"><br /> <property name="authenticationManager" ref="authenticationManager" /><br /> <property name="authenticationFailureUrl"><br /> <value>/login_error</value><br /> </property><br /> <property name="defaultTargetUrl"><br /> <value>/</value><br /> </property><br /> <property name="filterProcessesUrl"><br /> <value>/login/check</value><br /> </property><br /> </bean><br /><br /> <bean id="httpRequestAccessDecisionManager" class="org.acegisecurity.vote.AffirmativeBased"><br /> <property name="allowIfAllAbstainDecisions" value="false" /><br /> <property name="decisionVoters"><br /> <list><br /> <ref local="roleVoter" /><br /> </list><br /> </property><br /> </bean><br /><br /> <!-- Note the order that entries are placed against the objectDefinitionSource is critical.<br /> The FilterSecurityInterceptor will work from the top of the list down to the FIRST pattern that matches the request URL.<br /> Accordingly, you should place MOST SPECIFIC (ie a/b/c/d.*) expressions first, with LEAST SPECIFIC (ie a/.*) expressions last --><br /> <bean id="filterInvocationInterceptor" class="org.acegisecurity.intercept.web.FilterSecurityInterceptor"><br /> <property name="authenticationManager" ref="authenticationManager" /><br /> <property name="accessDecisionManager" ref="httpRequestAccessDecisionManager" /><br /> <property name="objectDefinitionSource"><br /> <value><br /> CONVERT_URL_TO_LOWERCASE_BEFORE_COMPARISON<br /> \A/login.*\Z=ROLE_ANONYMOUS,ROLE_PHOENIX<br /> \A/app/SecuredPage.*\Z=ROLE_SUPERUSER<br /> \A/app/AnotherSecuredPage.*\Z=ROLE_SUPERUSER<br /> \A/.*\Z=ROLE_PHOENIX<br /> </value><br /> </property><br /> </bean><br /></beans></pre></code></div>Joe Scalisehttp://www.blogger.com/profile/05974749541058701201noreply@blogger.com0tag:blogger.com,1999:blog-33651743.post-1163786312224402052006-11-17T12:16:00.000-05:002006-11-17T12:58:32.246-05:00Dan Thiffaulthttp://www.blogger.com/profile/02959804356193147585noreply@blogger.com0tag:blogger.com,1999:blog-33651743.post-1163719464788940962006-11-16T17:53:00.000-05:002007-02-16T12:54:00.066-05:00Auto Discovering Hibernate EntitiesWe're a good portion of the way through a rewrite of an Order Entry system using spring & hibernate and all and all love the technology. There a few places, in particular with configuration, that have forced us to have repetitive code.<br /><br />One of these places is the annotated class list that we provide to spring's <a href="http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/orm/hibernate3/AbstractSessionFactoryBean.html">AnnotationSessionFactoryBean</a> in order to let Hibernate know which classes it should persist. This seems repetitive since (almost, more on that later) every class we'd like to have on this list is marked by the @Entity annotation. The session factory bean just needs a list of classes so I thought why not make a factory bean to generate this list for me. This seemed a good place to start a little cleanup so I wrote the following test:<br /><br /><blockquote><pre><br />/**<br />* Test that we find annotated classes given a package<br />*/<br />public void testLoadWithPackageName() throws Exception {<br /> AnnotatedClassDiscoveryBean discoveryBean = new AnnotatedClassDiscoveryBean();<br /> discoveryBean.setAnnotation("com.checkernet.util.testpackage.Test");<br /> discoveryBean.setPkg("com.checkernet.util.testpackage");<br /> Object object = discoveryBean.getObject();<br /> assertTrue("Returned object incorrect type", object instanceof List );<br /> List classes = (List) object;<br /> assertEquals("Incorrect number of classes found", 3, classes.size());<br />}<br /></pre><br /></blockquote><br /><br />Then I created a test package, @Test annotation, and popped in a few classes. Two of these classes used the @Test annotation. Now it was time to create an AnnotatedClassDiscoveryBean.<br /><br />For starters it implemented <a href="http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/beans/factory/FactoryBean.html">FactoryBean</a>. After that I knew I needed to find a way to walk through a package and get all of the classes to see if they had the annotation or not. A quick search on google lead me to a discussion on topic. Between the code on page two and spring's included <a href="http://static.springframework.org/spring/docs/2.0.x/api/org/springframework/aop/support/annotation/AnnotationClassFilter.html">AnnotationClassFilter</a> I had everything I needed to make my test green.<br /><br />With that out of the way I decided to add some more functionality. If you're in the <a href="http://domaindrivendesign.org/">DomainDrivenDesign</a> camp or just don't like to organize your packages by architectural function, it would be nice to give the discovery bean a top level package and recurse/iterate through to find entity classes in sub-packages. So I added a second test:<br /><br /><blockquote><pre><br />/**<br />* Test that we can find annotations in packages which are descendants of the<br />* supplied package<br />*/<br />public void testParentPackage() throws Exception {<br /> AnnotatedClassDiscoveryBean discoveryBean = new AnnotatedClassDiscoveryBean();<br /> discoveryBean.setAnnotation("com.checkernet.util.testpackage.Test");<br /> discoveryBean.setPkg("com.checkernet");<br /> Object object = discoveryBean.getObject();<br /> assertTrue("Returned object incorrect type", object instanceof List );<br /> List classes = (List) object;<br /> assertEquals("Incorrect number of classes found", 2, classes.size());<br />}<br /></blockquote></pre><br /><br />As you could have guessed it didn't work. Looking at the code from that thread, it was a good starting point but:<br /><br />1. It wasn't that clean<br />2. It didn't help me pass my second test<br /><br />It looked like I needed to pull the code that generated a list of class apart from the code which checked for the annotation. I did this and tried to give the code a quick pass (although it is still pretty rough) but now it passes both tests. In my session factory spring configuration the annotatedClasses property referred to a list (bean) with an id of annotated classes. It used to look like this:<br /><blockquote><pre><br /><util:list id="annotatedClassList" list-class="java.util.ArrayList"><br /> com.checkernet.model.ActiveShipmentHold<br /> com.checkernet.model.Carrier<br /> com.checkernet.model.CarrierMethod<br /> com.checkernet.model.CatalogPageEntry<br /> ... (and many many more)<br /></util:list id="annotatedClassList" list-class="java.util.ArrayList"><br /></blockquote></pre><br /><br />so I removed that bean and replaced it with:<br /><br /><blockquote><pre><br /><bean id="annotatedClassList" class="com.checkernet.util.AnnotatedClassDiscoveryBean"> <br /> <property name="pkg" value="com.checkernet.model" /><br /> <property name="annotation" value="javax.persistence.Entity" /><br /></bean><br /></blockquote></pre><br /><br /><br /><br /><br />re-ran my tests and viola no more duplication.<br /><br />Here is the current (notice I didn't say final) code for the AnnotatedClassDiscoveryBean:<br /><br /><br />This code could be used for things other than hiberate.<br /><br /><blockquote><pre><br />package com.checkernet.util;<br /><br />import java.io.File;<br />import java.io.IOException;<br />import java.io.UnsupportedEncodingException;<br />import java.lang.annotation.Annotation;<br />import java.net.URL;<br />import static java.net.URLDecoder.decode;<br />import java.util.ArrayList;<br />import java.util.Enumeration;<br />import java.util.List;<br />import java.util.jar.JarEntry;<br />import java.util.jar.JarFile;<br />import org.apache.commons.logging.Log;<br />import org.apache.commons.logging.LogFactory;<br />import org.springframework.aop.support.annotation.AnnotationClassFilter;<br />import org.springframework.beans.factory.FactoryBean;<br />import org.springframework.beans.factory.annotation.Required;<br />import org.springframework.util.Assert;<br /><br /><br />/**<br /> * Notes: A factory bean which given a package name and fully qualified annotation class name will<br /> * Return a list of classes. Developed for use with Spring for dynamic configuration of annotated classes<br /> * properties (like the one in AbstractSessionFactoryBean)<br /> *<br /> * @author Dan Thiffault<br /> * @version 0.0.1<br /> */<br />public class AnnotatedClassDiscoveryBean implements FactoryBean {<br /><br /> private final Log logger = LogFactory.getLog(AnnotatedClassDiscoveryBean.class);<br /><br /> private Class<? extends Annotation> annotation;<br /><br /> private String packageName;<br /><br /> private static final int CLASS_LIST_SIZE = 50;<br /><br /> private static final char PACKAGE_SEPARATOR = '.';<br /><br /> /**<br /> * this is specified by ClassLoader.getResources()<br /> */<br /> @SuppressWarnings({"HardcodedFileSeparator"})<br /> private static final char PATH_SEPARATOR = '/';<br /><br /> /**<br /> * Set the annotation to search for<br /> *<br /> * @param annotationName of the Annotation class<br /> * @throws ClassNotFoundException if that annotation is not on the classpath<br /> */<br /> @Required<br /> public void setAnnotation(final String annotationName) throws ClassNotFoundException {<br /> //noinspection unchecked<br /> annotation = (Class<? extends Annotation>) Thread.currentThread().getContextClassLoader()<br /> .loadClass(annotationName);<br /> }<br /><br /> /**<br /> * Set the base package name to search through. Sub-packages will be searched<br /> *<br /> * @param packageName to search through<br /> */<br /> @Required<br /> public void setPkg(final String packageName) {<br /> this.packageName = packageName;<br /> }<br /><br /> /**<br /> * Returns a list of classes which are in the specified package, or<br /> * one of its descedant packages, that have the specified annotation.<br /> *<br /> * @return List<Class> of classes found<br /> */<br /> public final Object getObject() {<br /> final List<Class> classes;<br /><br /> List<Class> allClasses = getClassesForPackage(packageName);<br /> classes = new ArrayList<Class>(allClasses.size());<br /> logger.debug("Found ".concat(Integer.toString(allClasses.size())).concat(" classes in package"));<br /> AnnotationClassFilter filter = new AnnotationClassFilter(annotation);<br /> for (Class<?> clazz : allClasses) {<br /> if (filter.matches(clazz)) {<br /> logger.debug("Discovered class: ".concat(clazz.toString()));<br /> classes.add(clazz);<br /> }<br /> }<br /><br /><br /> return classes;<br /> }<br /><br /> /**<br /> * Returns a list class. This is the type the calls to getObject are<br /> * guaranteed to return.<br /> *<br /> * @return Class of type List<br /> */<br /> public Class<?> getObjectType() {<br /> return List.class;<br /> }<br /><br /> /**<br /> * {@inheritDoc}<br /> */<br /> public boolean isSingleton() {<br /> return false;<br /> }<br /><br /> /**<br /> * Attempts to list all the classes in the specified package as determined<br /> * by the context class loader<br /> *<br /> * @param pckgname the package name to search<br /> * @return a list of classes that exist within that package<br /> */<br /> @SuppressWarnings({"MethodWithMultipleLoops"})<br /> private static List<Class> getClassesForPackage(String pckgname) {<br /> String path = pckgname.replace(PACKAGE_SEPARATOR, PATH_SEPARATOR);<br /> // This will hold a list of directories matching the pckgname. There may be more than one if a package is split over multiple jars/paths<br /><br /> List<File> directories = new ArrayList<File>(CLASS_LIST_SIZE);<br /> List<String> jars = new ArrayList<String>(CLASS_LIST_SIZE);<br /><br /> List<String> classFiles = new ArrayList<String>(CLASS_LIST_SIZE);<br /><br /> //noinspection ProhibitedExceptionCaught<br /> try {<br /> ClassLoader cld = Thread.currentThread().getContextClassLoader();<br /> if (cld == null) {<br /> //noinspection ProhibitedExceptionThrown<br /> throw new RuntimeException("Can't get class loader.");<br /> }<br /><br /> // Ask for all resources for the path<br /> Enumeration<URL> resources = cld.getResources(path);<br /> //noinspection MethodCallInLoopCondition<br /> while (resources.hasMoreElements()) {<br /> //noinspection ObjectAllocationInLoop<br /> String decodedPath = decode(resources.nextElement().getPath(), "UTF-8");<br /> if (decodedPath.contains(".jar!")) {<br /> String jarPathName = decodedPath.substring(5, decodedPath.indexOf("!"));<br /> classFiles.addAll(getClassesInJar(new JarFile(jarPathName)));<br /> } else {<br /> if (!decodedPath.contains(".zip!")) {<br /> directories.add(new File(decodedPath));<br /> }<br /> }<br /> }<br /> } catch (NullPointerException x) {<br /> throw new IllegalArgumentException(<br /> pckgname + " does not appear to be a valid package", x);<br /> } catch (UnsupportedEncodingException encex) {<br /> throw new IllegalArgumentException(<br /> pckgname + " does not appear to be a valid package (Unsupported encoding)", encex);<br /> } catch (IOException ioex) {<br /> //noinspection ProhibitedExceptionThrown<br /> throw new RuntimeException("IOException was thrown when trying to get all resources for " + pckgname, ioex);<br /><br /> } catch (Exception e) {<br /> throw new RuntimeException("Something terrible happened", e);<br /> }<br /><br /> // For every directory identified capture all the .class files<br /> for (File directory : directories) {<br /> if (directory.exists()) {<br /> classFiles.addAll(getFilesInDirectory(directory, path));<br /> } else {<br /> throw new IllegalArgumentException(<br /> pckgname + " (" + directory.getPath() + ") does not appear to be a valid package");<br /> }<br /> }<br /><br /><br /> List<Class> classes = new ArrayList<Class>(classFiles.size());<br /><br /> for (String fileName : classFiles) {<br /> try { <br /> classes.add(Class.forName(<br /> fileName.substring(0, fileName.length() - 6).replace(File.separatorChar, PACKAGE_SEPARATOR).replace(PATH_SEPARATOR, PACKAGE_SEPARATOR)));<br /><br /> } catch (ClassNotFoundException e) {<br /> //noinspection ProhibitedExceptionThrown<br /> throw new RuntimeException("Couldn't load class", e);<br /> }<br /> }<br /><br /> return classes;<br /><br /> }<br /><br /> private static List<String> getFilesInDirectory(File directory, final String currentDirectory) {<br /> Assert.isTrue(directory.isDirectory(), "File passed in was not a directory: ".concat(directory.getName()));<br /> List<String> files = new ArrayList<String>(CLASS_LIST_SIZE);<br /><br /> for (File file : directory.listFiles()) {<br /> String fileName = file.getName();<br /> if (file.isFile() && fileName.endsWith(".class")) {<br /> files.add(currentDirectory.concat(File.separator).concat(fileName));<br /> } else if (file.isDirectory()) {<br /> files.addAll(getFilesInDirectory(file, currentDirectory.concat(File.separator).concat(fileName)));<br /> }<br /> }<br /><br /> return files;<br /> }<br /><br /> private static List<String> getClassesInJar(JarFile jf) {<br /> List<String> classNames = new ArrayList<String>(CLASS_LIST_SIZE);<br /> Enumeration<JarEntry> e = jf.entries();<br /> while (e.hasMoreElements()) {<br /> JarEntry jarEntry = e.nextElement();<br /> if (jarEntry.getName().endsWith(".class")) {<br /> classNames.add(jarEntry.getName());<br /> }<br /><br /> }<br /><br /> return classNames;<br /> }<br /><br />}<br /><br /></blockquote></pre><br /><br /><br />Updated: Fixed to work with jars.<br />Update 2: Based on Joe's comment, updated the code to work on non-BSD based systems (like windows)Dan Thiffaulthttp://www.blogger.com/profile/02959804356193147585noreply@blogger.com7tag:blogger.com,1999:blog-33651743.post-1163712852554777492006-11-16T15:58:00.000-05:002007-02-08T11:29:30.100-05:00Trac Wiki Macro API FunHere is a small snippet of Python code:<br /><div style="margin-left:-80px"><br /><blockquote><pre><br />import datetime<br />import time<br /><br />def execute(hdf, txt, env):<br /> event, when = txt.split(',', 1)<br /> today = datetime.date.today()<br /> future = datetime.date(*time.strptime(when, '%m/%d/%Y')[0:3])<br /> difference = future - today<br /> days = difference.days<br /> if days <= 0:<br /> return event + " has already happened! (" + str(abs(days)) + " days ago.)"<br /> proper = "days"<br /> if days == "1":<br /> proper = "day"<br /> return event + " in " + str(days) + " " + proper + "!"<br /></pre></blockquote><br /></div><br />Small and simple. Dan asked me to write a macro for our Trac wiki that would take in a date (in a string format) and an event name and create a small warning on the page. I've never written any sort of macro for any sort of wiki before, but the macros are written in Python, so I suspected it would be fun and easy. It was both.<br /><br />The first thing I did was edit the front page of our wiki, adding a call to my soon-to-be macro:<br /><code><br />[[CountDown()]]<br /></code><br />I saved the changes to the page, and got an error when the page reloaded, which was exactly what I wanted... the api says that you need to restart the webserver, which seemed sort of suspect since you don't need to restart after installing plugins.<br /><br />I read just enough of the docs to figure out where the macros directory was. Turns out that the macros reside in the rather obvious directory: "/usr/local/share/trac/wiki-macros". I created CountDown.py there and loaded it into vi.<br /><br />Back to the docs to check out the api. Only function needed in a macro is execute, which takes in an hdf, some text, and an environment. A bare-bones macro looks like this:<br /><blockquote><pre><br />def execute(hdf, txt, env):<br /> return "Testing..."<br /></pre></blockquote><br />I saved the above and reloaded the page. It worked. Now, for input. Input is passed into execute via the argument txt. Changed the macro call to:<br /><code><br />[[CountDown(Tomorrow,11/17/2006)]]<br /></code><br />and then changed the macro to:<br /><blockquote><pre><br />def execute(hdf, txt, env):<br /> return txt<br /></pre></blockquote><br />It returned "Tomorrow,11/17/2006" on the page. I poked around the module docs for datetime and finished the macro as it appears at the top of this post. It was just so painless. Trac has impressed me again.Myles Forresthttp://www.blogger.com/profile/01906954188376411486noreply@blogger.com0tag:blogger.com,1999:blog-33651743.post-1160693788243797562006-10-12T18:46:00.000-04:002006-10-13T13:22:53.243-04:00Making shell calls from JavaOne of our clients needs to tie an ancient DOS application into one of their web processes. Unfortunately, this application isn't <span style="font-style: italic;">really</span> designed for use in an unattended process. It works, but occasionally (usually in error states), it will request user input. Not so spectacular if there's no user and the applications output isn't even being sent to a display. In addition, it has some serious concurrency issues and every now and again it will just fail for no discernible reason whatsoever. I was faced with three major issues here: first, how to efficiently make system calls from Java (not as trivial as it sounds!), second, how to ensure that this application isn't called by more than a certain number of threads at a time and third, how to identify those random error states and retry the operation. Just so this post doesn't drag on forever, I'll just address the first of these here, and leave the others for another day.<br /><br />So, why is making shell calls from Java so difficult? All you have to do is call Runtime.getRuntime().exec() right? Well, sort of... Let me explain... round-aboutly. First off, our shell command execution needs to have some sort of timeout (to deal with those cases when the shell command hangs waiting for input). So, we need to execute the command in another thread and have a loop that waits until the process says it's done. Something that looks kind of like this perhaps (try/catch blocks and such elided for simplicity sake):<br /><br /><blockquote><pre>ExternalExecutionThread thread = <br /> new ExternalExecutionThread();<br />thread.setCommand(command);<br />long start = System.currentTimeMillis();<br />thread.start();<br />while (!thread.isFinished()) {<br /> if (System.currentTimeMillis() - start > timeout) {<br /> thread.interrupt();<br /> }<br />}<br /><br />class ExternalExecutionThread extends Thread {<br /> private boolean finished;<br /> private int exitCode;<br /> private ResultListener listener;<br /><br /> ... getters and setters<br /> <br /> public void run() {<br /> Process process = Runtime.getRuntime().exec(cmd);<br /> process.waitFor();<br /> exitCode = process.exitValue();<br /> }<br />}</pre></blockquote><br /><br />In addition to simply executing the command you specify, exec() provides StdErr and StdOut as streams for your monitoring convenience. A nice feature, especially when dealing with a legacy application as we are here. So, first problem. What if the command you're running doesn't output anything and then hangs? You're now stuck in a read loop, with nothing coming in. So, we need to spawn some more threads to gobble up the command output as it comes, so that our timeout will continue to work. Something more or less like this:<br /><br /><blockquote><pre>class StreamReadingThread extends Thread {<br /> private OutputStream outputStream;<br /> private InputStream inputStream;<br /> private boolean finishedWriting;<br /><br /> ... getters and setters<br /><br /> public void run() {<br /> BufferedInputStream bis = <br /> new BufferedInputStream(inputStream);<br /> while (!finishedWriting) {<br /> byte[] temp = new byte[bis.available()];<br /> int bytesRead = bis.read(temp, 0, temp.length);<br /> if (bytesRead == -1) {<br /> finishedWriting = true;<br /> } else {<br /> outputStream.write(temp, 0, bytesRead);<br /> }<br /> }<br /> }<br />}</pre></blockquote><br /><br />Now, we modify our ExternalExecutionThread to create a couple of these StreamReadingThreads, feed them the InputStreams from the Process we started and send them on their merry way. When the process ends (or times out) we call finishWriting() on the threads and they exit gracefully. Now, I can hear the clamor, "That's a whole lot of trouble to go through to read the output! What if I don't even need the output?" Well, I can't argue with you, it is a royal pain. There's a catch here though: if you don't read the output of the command, the stream buffer will eventually fill up (assuming your command has a significant amount of output) and it will deadlock your whole thread. Gross. <br /><br />At the end of the day, we've got a couple of custom classes and three new threads spawned every time we want to make a shell call. Is there a simpler way? I certainly hope so, but I've been unable to find it. I'd certainly love to be proven wrong...Unknownnoreply@blogger.com0tag:blogger.com,1999:blog-33651743.post-1160583393061619182006-10-11T12:09:00.000-04:002007-05-01T16:19:31.073-04:00My Experience Integrating Load Time Weaving<p>I use <a href="http://www.jetbrains.com/">JetBrains</a>’ <a href="http://www.jetbrains.com/idea/">IntelliJ IDEA</a> to develop Java web applications. Because of the lack of <a href="http://www.eclipse.org/aspectj/">AspectJ</a> support, I was forced to find my own solution. Reluctant to give up my highly adored <a href="http://www.jetbrains.com/idea/features/refactoring.html">refactorings</a> and productivity tools for an IDE like <a href="http://www.eclipse.org/eclipse/">Eclipse</a> that integrates AspectJ, I set out to find a workaround. At first I tried using LTW because it seemed like the quickest, easiest, and least invasive solution. However, I was met with disaster: stack traces a mile long, resulting in a heap exception. I searched Google for solutions to the LTW stack explosion, but to no avail. Accustomed to hitting seemingly insurmountable roadblocks (after all, I am a Software Engineer), I pressed on. Next I turned to the AJC compiler; “surely I can change which compiler I use,” I thought. Quite to the contrary; for reasons unknown to me, IDEA only lets you choose between two compilers: javac and jikes. I even looked inside the .bat file and properties files for IDEA. To solve this problem I turned to using an ant script that runs post-compilation to weave my classes. However, you can only have one post-compilation task and I had more than one module that I wanted to weave. My natural reaction at this point was to hit the Google searches. After an hour of searching on how to change my compiler in IDEA to AJC I decided to reinvestigate why LTW was croaking. After a mere fifteen minutes I found a mailing list posting by <a href="http://www.interface21.com/people/adrian.html">Adrian Colyer</a>, explaining what had happened: <a href="http://www.mail-archive.com/aspectj-users@eclipse.org/msg00326.html">aspectj-users@eclipse.org/msg0032</a>. Apparently there was a bug in the 1.5.2 release that caused an issue with pointcuts that match based on annotations (which Spring 2.0 does), and there was now a 1.5.2a release to address the issue. Downloading the bugfix release and plopping it into my libraries folder fixed the problem.</p><br /><p>The forehead slap occurred when I realized that including the spring-aspects.jar implies that you want to weave both the AnnotationBeanConfigurerAspect and AnnotationTransactionAspect aspects because Spring includes an aop.xml in the META-INF inside their jar. Simply placing <code><exclude within="org.springframework.transaction.aspectj.AnnotationTransactionAspect" /></code> in your aop.xml will tell AspectJ not to use the <code>@Transactional</code> support from spring-aspects.jar</p><br /><p>Using IDEA, the only change I had to make was to add the following to my JVM args in the Run/Debug dialog: <code>-javaagent:lib/aspectjweaver.jar</code>. See a great blog post by Adrian: <a href="http://www.aspectprogrammer.org/blogs/adrian/2006/02/a_practical_gui_2.html">A Practical Guide to Using an Aspect Library (part 1)</a>. Next, I needed to configure Tomcat for LTW in order for my webapp to function. This was as simple as adding the same JVM arg (changing the path to the jar) to my startup script. I can now sleep soundly at night, not worrying about having to abandon my beloved IDE. Now if only they’d let me write AspectJ code natively… I’ll save that one for another post.</p>Joe Scalisehttp://www.blogger.com/profile/05974749541058701201noreply@blogger.com0tag:blogger.com,1999:blog-33651743.post-1160572197998869432006-10-11T09:25:00.000-04:002011-07-01T08:18:01.536-04:00XSLT: Use of the self:: axis<p>I'm Peter Cooper, one of the software engineers working with Checkmate Technologies, and a lot of my job right now involves using <a href="http://www.w3.org/XML/">XML</a> and <a href="http://www.w3.org/Style/XSL/">XSLT.</a> One of the things that's kind of confusing when you first look at XSLT is all the axes that you can use, and why you'd want to use them. Things like child::, descendent::, and parent:: make a lot of sense, but what about self::? Doesn't it seem kind of pointless to have an axis that just gives you nodes where you already are?</p><p>However, after working with XSLT for many years, we've found a few places where the self:: axis is really a lifesaver. The biggest example is when you want to exclude a particular node when selecting a nodeset. For instance, let's say that you have input XML in a format like this:</p><p><code style="white-space: pre"><line><br/> <text>This is a line of text to be printed</text><br/> <fontsize>14</fontsize><br/> <justification>Center</justification><br/> ...<br/></line></code></p><p>The idea is that the <code><text></code> element is the only one that's required, and there are a bunch of optional tags you can add to change that Line from the default.</p><p>The problem we're trying to solve is that if there are any of these optional tags present, we want to send the user to the<br />"advanced" edit page, whereas if there are just lines of text without fancy<br />formatting on a particular line, we just want to show the "normal" edit page.</p><p>So our mission is to write a select statement that sees if there are any <code><line></code>s with any child elements other than <code><text></code>. The first thing that might come to your mind is something like this:</p><p><code><xsl:variable name="AdvancedNodes" select="line/*[not(name() = 'text')]" /></code></p><p>A statement like this kind of works, and it'd get the job done, but doing comparisons based on the name seems kind of sloppy. It also gets a lot more tricky if you're using XML Namespaces, because then you need to check the local-name() and namespace-uri(). So, the much more elegant approach is something like this:</p><p><code><xsl:variable name="AdvancedNodes" select="line/*[not(self::text)]" /></code></p><p>In this manner, the self:: axis lets you start with a very broad selection of elements (all children elements) and then narrow it down further very easily. You can also use techniques like this if you're writing XSLT that primarily copies elements, but you want to exclude some of the elements in the copy.</p>Peter Cooper Jr.http://www.blogger.com/profile/07999652335340380274noreply@blogger.com0tag:blogger.com,1999:blog-33651743.post-1157577800839893762006-09-06T17:19:00.000-04:002006-10-11T12:05:55.440-04:00Checkmate Technologies<p>Here's a little intro to what this blog is all about. Checkmate Technologies is a technical consulting company that specializes in dealing with clients from the printing industry. We produce forward thinking solutions based on cutting edge technologies including Spring 2, Hibernate, and Java 5.</p><p>We have written a fair amount of glue code to pull these technologies together that we will be posting as the months pass so keep checking in.</p>Dan Thiffaulthttp://www.blogger.com/profile/02959804356193147585noreply@blogger.com0