<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	>

<channel>
	<title>m i k e  b r e v o o r t &#187; Technology</title>
	<atom:link href="http://mike.brevoort.com/category/technology/feed/" rel="self" type="application/rss+xml" />
	<link>http://mike.brevoort.com</link>
	<description>life technology cycling etc</description>
	<pubDate>Wed, 07 May 2008 04:00:00 +0000</pubDate>
	<generator>http://wordpress.org/?v=2.5</generator>
	<language>en</language>
			<item>
		<title>Expanding on Jira&#8217;s LDAP Integration</title>
		<link>http://mike.brevoort.com/2008/04/08/syncing-jira-with-ldap/</link>
		<comments>http://mike.brevoort.com/2008/04/08/syncing-jira-with-ldap/#comments</comments>
		<pubDate>Tue, 08 Apr 2008 22:23:32 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Technology]]></category>

		<category><![CDATA[code]]></category>

		<category><![CDATA[active directory]]></category>

		<category><![CDATA[Jira]]></category>

		<category><![CDATA[LDAP]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/?p=123</guid>
		<description><![CDATA[I recently had the opportunity to do a fresh installation of Atlassian Jira and was faced again with how to integrate Jira with LDAP (in this case Active Directory).  Jira does support simple authentication integration with LDAP via OSUser but requires that each LDAP user exist in Jira first.  So if an LDAP [...]]]></description>
			<content:encoded><![CDATA[<p>I recently had the opportunity to do a fresh installation of <a href="http://www.atlassian.com">Atlassian</a> <a href="http://www.atlassian.com/software/jira/">Jira</a> and was faced again with how to integrate Jira with LDAP (in this case Active Directory).  Jira does support <a href="http://www.atlassian.com/software/jira/docs/latest/ldap.html">simple authentication integration</a> with LDAP via <a href="http://www.opensymphony.com/osuser/">OSUser</a> but requires that each LDAP user exist in Jira first.  So if an LDAP user needs access to Jira, I have to go to Jira and create a new corresponding user record with the same user id as LDAP (sAMAccountName in this case).  Ideally I would like Jira to synchronize LDAP users based on some query filter.</p>
<p>Luckily Jira has a perfect way to address this &#8220;opportunity&#8221;: <a href="http://www.atlassian.com/software/jira/docs/latest/services.html">Services</a>.  Jira Services are asynchronous scheduled units of work configured to run within Jira.  I created a new service using JNDI called LDAPUserSyncService that can periodically query LDAP user objects based on configurable filter and create new Jira users for new users it finds in LDAP.  I also added the ability to put a subset of users in a group called &#8220;internal-users&#8221; for users whose email matched a particular domain.</p>
<p>Its pretty simple.  When the service runs, it queries LDAP, iterates through each user checking if the user already exists in LDAP, if not it created the user and puts it in the jira-users group and then checks if the email address contains myco.com (fictitious domain for this example) and if so put the user in the internal group as well.  I just realized as I&#8217;m writing this though that I didn&#8217;t do anything to handle paging or results if the number of users is greater than the max returned by LDAP.  Oh well, maybe another time&#8230;</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
public class LDAPUserSyncService extends AbstractService {

    private static org.apache.log4j.Category log =
            org.apache.log4j.Category.getInstance(LDAPUserSyncService.class);

    public static String INITCTX = "com.sun.jndi.ldap.LdapCtxFactory";
    public static String GROUP_INTERNAL =  "Internal-Users";
    public static String LDAP_USER_ID_ATTRIBUTE = "sAMAccountName";

    public void run() {
        try {
            log.debug("Running com.myco.jira.service.LDAPUserSyncService");
            String LDAP_HOST = getProperty("LDAP_HOST");
            String LDAP_USER = getProperty("LDAP_USER");
            String LDAP_PASSWORD = getProperty("LDAP_PASSWORD");
            String LDAP_SEARCHBASE = getProperty("LDAP_SEARCHBASE");
            String LDAP_FILTER = getProperty("LDAP_FILTER");   

            Hashtable env = new Hashtable();
            env.put(Context.INITIAL_CONTEXT_FACTORY, INITCTX);
            env.put(Context.PROVIDER_URL, LDAP_HOST);
            env.put(Context.SECURITY_AUTHENTICATION, "simple");
            env.put(Context.SECURITY_PRINCIPAL, LDAP_USER);
            env.put(Context.SECURITY_CREDENTIALS, LDAP_PASSWORD);

            DirContext ctx = new InitialDirContext(env);

            SearchControls constraints = new SearchControls();
            constraints.setSearchScope(SearchControls.SUBTREE_SCOPE);

            NamingEnumeration results = ctx.search(LDAP_SEARCHBASE, LDAP_FILTER, constraints);

            while(results != null &#038;&#038; results.hasMore()) {
                SearchResult sr = (SearchResult) results.next();
                Attributes attrs = sr.getAttributes();
                log.debug("Processing " + attrs.get("dn"));

                // need sn, givenName, mail
                String first = (attrs.get("givenName") != null) ? (String) attrs.get("givenName").get() : null;
                String last = (attrs.get("sn") != null) ? (String) attrs.get("sn").get() : null;
                String mail = (attrs.get("mail") != null) ? (String) attrs.get("mail").get() : null;
                String userId = (attrs.get(LDAP_USER_ID_ATTRIBUTE) != null) ? (String) attrs.get(LDAP_USER_ID_ATTRIBUTE).get() : null;

                if(first != null &#038;&#038; last != null &#038;&#038; mail != null &#038;&#038; userId != null) {

                    // check if user is in Jira
                    if(!userExists(userId.toLowerCase())) {

                        // create user, if a myco company user then add to internal group
                        if(createJiraUser(userId, first, last, mail) != null) {
                            if(mail.contains("myco.com"))
                                addUserGroup(userId, GROUP_INTERNAL);
                        } else {
                            log.error("LDAP User " + userId + " could not be created");
                        }
                    }
                }
            }
        } catch (NamingException nex) {
            log.error("Caught exception trying to Synch LDAP Users: " + nex.toString());
        } catch (ObjectConfigurationException oce) {
            log.error("Caught exception trying to Synch LDAP Users.  Configuration setup failed: " + oce.toString());
        }
    }

    private User createJiraUser(String userId, String fname, String lname, String email) {

        UserManager userMgr = UserManager.getInstance();
        User osUser = null;
        try {
            osUser = userMgr.createUser(userId.toLowerCase());
            osUser.setFullName(fname + " " + lname);
            osUser.setEmail(email);

            Group jiraUserGroup = userMgr.getGroup("jira-users");
            osUser.addToGroup(jiraUserGroup);
            osUser.store();
            log.debug("Successfully added LDAP user "+ userId);
        } catch (ImmutableException e) {
            log.error("Error creating User in Jira: " + userId, e);
        } catch (DuplicateEntityException e) {
            log.error("Error creating User in Jira: " + userId, e);
        } catch (EntityNotFoundException e) {
            log.error("Could not find group jira-users.  Error creating User in Jira: " + userId, e);
        }

        // to be sure the user was created, return the reloaded user from Jira
        return osUser;

    }

    private boolean userExists(String userId) {
        UserManager userMgr = UserManager.getInstance();
        boolean exists = false;
        try {
            if(userMgr.getUser(userId) != null)
                exists = true;
        } catch (EntityNotFoundException e) {
            exists = false;
        }

        return exists;
    }

    private void addUserGroup(String userId, String groupName){
        UserManager userMgr = UserManager.getInstance();

        try {
            User osUser = userMgr.getUser(userId);

            Group group = userMgr.getGroup(groupName);
            osUser.addToGroup(group);
            osUser.store();
            log.debug("Successfully added LDAP user " + userId + " to group: " + groupName);
        } catch (ImmutableException e) {
            log.error("Error adding User to acis-users group: " + userId, e);
        } catch (EntityNotFoundException e) {
            log.error("Could not find group acis-users.  Error adding User to acis-users group:  " + userId, e);
        }
    }

    public ObjectConfiguration getObjectConfiguration() throws ObjectConfigurationException {
        return getObjectConfiguration("LDAPUSERSYNCSERVICE", "com/myco/jira/service/LDAPUserSyncService.xml", null);
    }
}
</pre>
<p>To configure a service within Jira navigate to Services in the System section of the Administration interface:<br />
<center><img src="/wp-content/uploads/2008/04/jira-services-menu.png" alt="" title="jira-services-menu" width="189" height="272" class="aligncenter size-full wp-image-124" /></center></p>
<p>Here I&#8217;ve added the service you can see the parameters that can be customized for the service:<br />
<img src="http://mike.brevoort.com/wp-content/uploads/2008/04/jira-service-ldap.png" alt="" title="jira-service-ldap" width="500" height="103" class="aligncenter size-full wp-image-125" /></p>
<p>The parameters are configured in an XML file that is referenced by the getObjectConfiguration() method in the service code above: </p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
&lt;debugservice id="LDAPUserSyncService"&gt;
    &lt;description&gt;
    &lt;/description&gt;
    &lt;properties&gt;

        &lt;property&gt;
            &lt;key&gt;LDAP_HOST&lt;/key&gt;
            &lt;name&gt;LDAP Host (format: ldap://myco.com:389)&lt;/name&gt;
            &lt;type&gt;string&lt;/type&gt;
        &lt;/property&gt;
        &lt;property&gt;
            &lt;key&gt;LDAP_USER&lt;/key&gt;
            &lt;name&gt;LDAP User DN&lt;/name&gt;
            &lt;type&gt;string&lt;/type&gt;
        &lt;/property&gt;
                &lt;property&gt;
            &lt;key&gt;LDAP_PASSWORD&lt;/key&gt;
            &lt;name&gt;LDAP Password)&lt;/name&gt;
            &lt;type&gt;string&lt;/type&gt;
        &lt;/property&gt;
        &lt;property&gt;
            &lt;key&gt;LDAP_SEARCHBASE&lt;/key&gt;
            &lt;name&gt;LDAP Search Base DN&lt;/name&gt;
            &lt;type&gt;string&lt;/type&gt;
        &lt;/property&gt;
        &lt;property&gt;
            &lt;key&gt;LDAP_FILTER&lt;/key&gt;
            &lt;name&gt;LDAP Search Filter&lt;/name&gt;
            &lt;type&gt;string&lt;/type&gt;
        &lt;/property&gt;

    &lt;/properties&gt;
&lt;/debugservice&gt;
</pre>
<p>And that&#8217;s it.  New users are automatically created in Jira within 30 minutes of being created in LDAP.  Ooo Rah.</p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2008/04/08/syncing-jira-with-ldap/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Still Here</title>
		<link>http://mike.brevoort.com/2008/04/04/still-here/</link>
		<comments>http://mike.brevoort.com/2008/04/04/still-here/#comments</comments>
		<pubDate>Fri, 04 Apr 2008 23:18:47 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Cycling]]></category>

		<category><![CDATA[Personal]]></category>

		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2008/04/04/still-here/</guid>
		<description><![CDATA[I&#8217;d love to see stats on what percentage of all blog posts are an apology for not posting often enough.  I have to assume the number is lower than I may expect since all the apologizing slackers (myself included), well, don&#8217;t post often enough.  Anyway, that&#8217;s my passive aggressive apology.
I&#8217;ve been incredibly busy [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;d love to see stats on what percentage of all blog posts are an apology for not posting often enough.  I have to assume the number is lower than I may expect since all the apologizing slackers (myself included), well, don&#8217;t post often enough.  Anyway, that&#8217;s my passive aggressive apology.</p>
<p>I&#8217;ve been incredibly busy and have since transitioned back into consulting, taking a full time position with <a href="http://avalonconsult.com">Avalon Consulting</a>.  I&#8217;m excited about my new role at Avalon, and the impact I&#8217;ll have there.  I&#8217;ve recently had the opportunity to do some more work with <a href="http://www.atlassian.com/jira">Jira </a>and set up a new <a href="http://subversion.tigris.org/">Subversion</a> environment.  I should have some stuff to share there soon as well as pick back up on <a href="http://grails.codehaus.org/">Grails</a>.</p>
<p>On the cycling front, the first Cherry Creek time trial is Wednesday.  I&#8217;m excited to see where my fitness is compared to last year at the same time.  I&#8217;m about 13 lbs lighter if that&#8217;s any indication&#8230;</p>
<p>Hopefully more soon.</p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2008/04/04/still-here/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Colorado Springs Open Source Software Meetup Group</title>
		<link>http://mike.brevoort.com/2008/02/29/colorado-springs-open-source-software-meetup-group/</link>
		<comments>http://mike.brevoort.com/2008/02/29/colorado-springs-open-source-software-meetup-group/#comments</comments>
		<pubDate>Fri, 29 Feb 2008 06:37:39 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Grails]]></category>

		<category><![CDATA[Technology]]></category>

		<category><![CDATA[open source]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2008/02/29/colorado-springs-open-source-software-meetup-group/</guid>
		<description><![CDATA[Tonight I headed down to the Springs for the first meeting of the Colorado Springs Open Source Software Meetup Group.  I learned about the group from this post on the Atlassian developer blog.  Though it was a bit slow tonight, overall it was a good event and I think it will develop into [...]]]></description>
			<content:encoded><![CDATA[<p>Tonight I headed down to the Springs for the first meeting of the <a href="http://opensource.meetup.com/88/">Colorado Springs Open Source Software Meetup Group</a>.  I learned about the group from <a href="http://blogs.atlassian.com/developer/2008/02/atlassian_wants_to_sponsor_you.html">this post</a> on the <a href="http://www.atlassian.com">Atlassian</a> <a href="http://blogs.atlassian.com/developer/">developer blog</a>.  Though it was a bit slow tonight, overall it was a good event and I think it will develop into a solid user group.  My only concern is that the topic is too broad to successfully satisfy everyone&#8217;s interest.  Tonight&#8217;s speaker was Kirstan Vandersluis who demoed the open source data integration platform <a href="http://www.xaware.com/">XAware</a>.  There seems to be a lot of momentum in the commercially backed open source BI/EAI space lately with others including <a href="http://www.pentaho.com/">Pentaho</a> and <a href="http://www.apatar.com/">Apatar</a>.</p>
<p>I walked away tonight with a free license of Intellij IDEA so thanks to Dave Booth from <a href="http://www.jetbrains.com/">Jetbrains</a> for the free swag!  Definitely my favorite IDE, and I was actually in need of a license!  And thanks <a href="http://www.teksystems.com/">TEKsystems</a> for the food and the other sponsors as well!</p>
<p>I&#8217;m scheduled to present on <a href="http://grails.org">Grails</a> in May so if you&#8217;re in the area feel free to come heckle!  Wow, four sentences ending in exclamation points (make that five)!</p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2008/02/29/colorado-springs-open-source-software-meetup-group/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Part 2: Terracotta: 1, Grails Searchable Plugin+Me: 1</title>
		<link>http://mike.brevoort.com/2008/01/29/terracotta-1-grails-searchable-pluginme-1/</link>
		<comments>http://mike.brevoort.com/2008/01/29/terracotta-1-grails-searchable-pluginme-1/#comments</comments>
		<pubDate>Wed, 30 Jan 2008 00:36:56 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Grails]]></category>

		<category><![CDATA[Technology]]></category>

		<category><![CDATA[code]]></category>

		<category><![CDATA[compass]]></category>

		<category><![CDATA[terracotta]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2008/01/29/terracotta-1-grails-searchable-pluginme-1/</guid>
		<description><![CDATA[Round Two of my quest to cluster the Grails Searchable Plugin index with Terracotta!  In my first post I outlined my intent to use Terracotta to cluster the Grails Searchable Plugin (Lucene + Compass) using the Lucene RAMDirectory approach.  Incidentally, my first attempt failed miserably.
As a quick confidence booster before setting out again, [...]]]></description>
			<content:encoded><![CDATA[<p>Round Two of my quest to cluster the <a href="http://grails.codehaus.org/Searchable+Plugin">Grails Searchable Plugin</a> index with Terracotta!  In <a href="http://mike.brevoort.com/2008/01/22/terracotta-1-grails-searchable-pluginme-0/">my first post</a> I outlined my intent to use <a href="http://www.terracotta.org">Terracotta</a> to cluster the Grails Searchable Plugin (<a href="http://www.google.com/url?sa=t&#038;ct=res&#038;cd=2&#038;url=http%3A%2F%2Flucene.apache.org%2F&#038;ei=F72fR6mANY-4gQTJweS1Bw&#038;usg=AFQjCNFlWzRUABueiSQ0PYLo1g0KjXCBRw&#038;sig2=TXtJGiDzcEqX2_NScUhT1Q">Lucene</a> + <a href="http://www.google.com/url?sa=t&#038;ct=res&#038;cd=1&#038;url=http%3A%2F%2Fwww.opensymphony.com%2Fcompass%2F&#038;ei=Br2fR6f-EabMgQSgrP27Bw&#038;usg=AFQjCNFWrf7kReBpKrslhJMYYfgcC03KXQ&#038;sig2=Zggi_LhkubznLIr6dGQwzA">Compass</a>) using the <a href="http://www.terracotta.org/confluence/display/integrations/Lucene">Lucene RAMDirectory</a> approach.  Incidentally, my first attempt failed miserably.</p>
<p>As a quick confidence booster before setting out again, I <a href="http://mike.brevoort.com/2008/01/24/using-jdbcdirectory-with-the-grails-searable-plugin/">successfully clustered the Grails Searchable Plugin using a JDBCDirectory store</a> for the index.  Since my last attempt I received some <a href="http://www.nabble.com/Compass%2C-Lucene-and-Grails-Searchable-Plugin-td15021230.html">guidance from the guys at Terracotta</a> and learned that <a href="http://www.kimchy.org/">Shay Banon</a> is working with Terrocotta to develop a Compass module for Terracotta.  Good stuff&#8230; check out the details of that <a href="http://www.nabble.com/FYI-on-Compass---Lucene-Integration-td15094091.html">here</a>. </p>
<p>The first hurdle I needed to overcome was the classloader error:</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
Classloader name not set, instances defined from this
loader not supported in Terracotta
(loader: org.codehaus.groovy.grails.cli.support.GrailsRootLoader)
</pre>
<p>Before an object can be shared by Terracotta, the ClassLoader that loaded the object&#8217;s class has to be known to Terracotta. For core java and supported frameworks/container Terracotta takes care of registering relevant classloaders.  For the GrailsRootLoader I can do this one of two ways: </p>
<ol>
<li>hack the grails source where the class loader is instantiated, registering it with Terracotta</li>
<li>this can be converted into a config module that will inject the proper byte code at runtime so as to avoid any code tweaking.  </li>
</ol>
<p>The guys at Terracotta offered to help with #2 if I could get #1 working.</p>
<p>So I checked out the Grail trunk from SVN, and added these lines after the GrailsRootLoader is created:</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
 // create loader and execute main class
GrailsRootLoader loader = new GrailsRootLoader(lc);
((com.tc.object.loaders.NamedClassLoader) loader).__tc_setClassLoaderName("Grail Classloader");
com.tc.object.bytecode.hook.impl.ClassProcessorHelper.registerGlobalLoader((com.tc.object.loaders.NamedClassLoader) loader);
</pre>
<p>I built the Grails jars and replaced them in my grails-1.0-final-SNAPSHOT/dist directory.  That resolved my classloader error!  Next I went through about 100 iterations of root and lock configuration for my tc-config.xml as I dug and spun my way around the Compass source.  I finally ended up with this for my tc-config.xml (keep in mind this can surely be optimized and is pretty brute force):</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;

&lt;tc:tc-config xmlns:tc="http://www.terracotta.org/config"&gt;
  &lt;system&gt;
    &lt;configuration-model&gt;development&lt;/configuration-model&gt;
  &lt;/system&gt;

  &lt;servers&gt;
    &lt;server name="localhost" /&gt;
  &lt;/servers&gt;

  &lt;clients&gt;
    &lt;logs&gt;%d/client-logs-%h&lt;/logs&gt;
    &lt;dso&gt;

       &lt;debugging&gt;
        &lt;runtime-logging&gt;
          &lt;lock-debug&gt;true&lt;/lock-debug&gt;
        &lt;/runtime-logging&gt;
        &lt;runtime-output-options&gt;
          &lt;full-stack&gt;true&lt;/full-stack&gt;
        &lt;/runtime-output-options&gt;
      &lt;/debugging&gt;

    &lt;/dso&gt;
    &lt;modules&gt;
      &lt;module name="clustered-lucene-2.0.0" version="2.5.0"/&gt;
    &lt;/modules&gt;
  &lt;/clients&gt;

  &lt;application&gt;
    &lt;dso&gt;
      &lt;instrumented-classes&gt;
        &lt;include&gt;
          &lt;class-expression&gt;org.compass.core.lucene.engine..*&lt;/class-expression&gt;
        &lt;/include&gt;
        &lt;include&gt;
          &lt;class-expression&gt;org.apache.lucene..*&lt;/class-expression&gt;
        &lt;/include&gt;
      &lt;/instrumented-classes&gt;
      &lt;roots&gt;
        &lt;root&gt;
          &lt;field-name&gt;org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore.ramIndexes&lt;/field-name&gt;
        &lt;/root&gt;
      &lt;/roots&gt;
      &lt;locks&gt;
	&lt;named-lock&gt;
          &lt;method-expression&gt;* org.compass.core.lucene.engine.manager.DefaultLuceneSearchEngineIndexManager.*(..)&lt;/method-expression&gt;
          &lt;lock-level&gt;write&lt;/lock-level&gt;
          &lt;lock-name&gt;theLockName&lt;/lock-name&gt;
        &lt;/named-lock&gt;
        &lt;/locks&gt;
     &lt;/dso&gt;
  &lt;/application&gt;
&lt;/tc:tc-config&gt;
</pre>
<p>It worked!  I was able to test with the Terracotta server running, and two Grails apps running in separate JVMs that all of the Domain class data that the first JVM indexed, could be searched by the second JVM and they both returned identical results.  I also verified the existence of the objects under the root in the Terracotta Admin GUI.  And finally, I shut both Grails app JVMs down and brought them back up and my index was still available.  </p>
<p>What&#8217;s next?  Work to get a config module for Terracotta and optimize my root and locking configuration.  I&#8217;m very interested in what will come of the promised Compass/Terracotta integration.  Also Clustering Grails is a much taller mountain.  I was told that past attempts to cluster Groovy with Terracotta had not been too successful.  For now I&#8217;m happy just to have climbed my little hill&#8230;</p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2008/01/29/terracotta-1-grails-searchable-pluginme-1/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Using JDBCDirectory with the Grails Searable Plugin</title>
		<link>http://mike.brevoort.com/2008/01/24/using-jdbcdirectory-with-the-grails-searable-plugin/</link>
		<comments>http://mike.brevoort.com/2008/01/24/using-jdbcdirectory-with-the-grails-searable-plugin/#comments</comments>
		<pubDate>Fri, 25 Jan 2008 04:05:34 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Grails]]></category>

		<category><![CDATA[Technology]]></category>

		<category><![CDATA[code]]></category>

		<category><![CDATA[compass]]></category>

		<category><![CDATA[lucene]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2008/01/24/using-jdbcdirectory-with-the-grails-searable-plugin/</guid>
		<description><![CDATA[In my last post I mentioned that one of the options I was considering for clustering the Grails Searchable plugin, though not the preferred option, was to use the JDBCDirectory implementation of Lucene.  I had this working in just 15 minutes and though it&#8217;s probably not the optimal approach, it is by far the [...]]]></description>
			<content:encoded><![CDATA[<p>In my <a href="http://mike.brevoort.com/2008/01/22/terracotta-1-grails-searchable-pluginme-0/">last post</a> I mentioned that one of the options I was considering for clustering the Grails Searchable plugin, though not the preferred option, was to use the <a href="http://www.opensymphony.com/compass/versions/0.9.0/api/org/apache/lucene/store/jdbc/JdbcDirectory.html">JDBCDirectory</a> implementation of Lucene.  I had this working in just 15 minutes and though it&#8217;s probably not the optimal approach, it is by far the simplest.</p>
<p>Just to set the stage, I&#8217;m running Grails RC3, Groovy 1.5.1, JDK 1.6, grails-searchable-0.4-SNAPSHOT and MySQL 5.0.45.  I will configure the <a href="http://www.opensymphony.com/compass/">Compass</a> to use the JdbcDirectory connection instead of file or ram and test that it can be updated and read by two instances of my Grails application.</p>
<p>First I created compass.cfg.xml in grails-app/conf (just needs to be somewhere in the classpath) with jdbc connection details:</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
&lt;compass-core-config xmlns="http://www.opensymphony.com/compass/schema/core-config"
 xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
 xsi:schemaLocation="http://www.opensymphony.com/compass/schema/core-config
          http://www.opensymphony.com/compass/schema/compass-core-config.xsd"&gt;
   &lt;compass name="default"&gt;
     &lt;connection&gt;
         &lt;jdbc dialect="org.apache.lucene.store.jdbc.dialect.MySQLDialect"&gt;
             &lt;dataSourceProvider&gt;
                 &lt;driverManager url="jdbc:mysql://localhost/search?emulateLocators=true"
                                      username="root" password="password"
                                      driverClass="com.mysql.jdbc.Driver" /&gt;
             &lt;/dataSourceProvider&gt;
             &lt;fileEntries&gt;
                 &lt;fileEntry name="__default__"&gt;
                     &lt;indexInput bufferSize="4096" /&gt;
                     &lt;indexOutput bufferSize="4096" /&gt;
                 &lt;/fileEntry&gt;
             &lt;/fileEntries&gt;
         &lt;/jdbc&gt;
     &lt;/connection&gt;
   &lt;/compass&gt;
&lt;/compass-core-config&gt;
</pre>
<p>One thing I missed at first was the dialect setting in the jdbc tag.  Make sure you select the correct dialect for your database.  I used <a href="http://static.compassframework.org/docs/latest/jdbcdirectory.html">this as a reference</a>.<br />
In SearchableConfiguration.groovy I set compassConnection to null</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
String compassConnection = null
</pre>
<p>I started my grails app and hit just one more snag, I got a &#8220;packet too large&#8221; error while it was building the index.  Turns out the default max_allowed_packet in MySQL on the server is 1M and since Lucene stores parts of the index in BLOBs I needed to up the max.  I found <a href="http://dev.mysql.com/doc/refman/5.0/en/packet-too-large.html">this article</a> and set the max_allowed_packet setting to 16M.  I did this in my.ini:</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
[mysqld]
max_allowed_packet=16M
</pre>
<p>That was it!  I tested by running two instances of my Grails application simultaneously and I modified one of my Domain class instances and executed a search on the other instance and within a few seconds, the new value was returned in my search.</p>
<p>The last thing I considered was configuring a <a href="http://www.opensymphony.com/compass/versions/1.2/html/core-connection.html#core-connection-localcache">Local Directory Cache</a> in Compass.  This seemed like it could offer some good optimization though it was unclear from the documentation how much control you have over the size of the cache, what gets cached, the TTL, etc.  Also, I soon realized that this was introduced in the latest version of Compass (1.2) and isn&#8217;t  the version that is bundled in the Searchable plugin.  </p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2008/01/24/using-jdbcdirectory-with-the-grails-searable-plugin/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Terracotta: 1, Grails Searchable Plugin+Me: 0</title>
		<link>http://mike.brevoort.com/2008/01/22/terracotta-1-grails-searchable-pluginme-0/</link>
		<comments>http://mike.brevoort.com/2008/01/22/terracotta-1-grails-searchable-pluginme-0/#comments</comments>
		<pubDate>Wed, 23 Jan 2008 04:46:53 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Grails]]></category>

		<category><![CDATA[Technology]]></category>

		<category><![CDATA[compass]]></category>

		<category><![CDATA[terracotta]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2008/01/22/terracotta-1-grails-searchable-pluginme-0/</guid>
		<description><![CDATA[I&#8217;m on a quest to figure out how to cluster the Grails Searchable plugin that&#8217;s based on Lucene/Compass across multiple nodes with as little intrusion and in a way that&#8217;s as turnkey as possible.  I&#8217;m in immediate need of a solution, and I think it would be a good contribution to the Searchable plugin.
To [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m on a quest to figure out how to cluster the <a href="http://grails.org/Searchable+Plugin">Grails Searchable plugin</a> that&#8217;s based on<a href="http://lucene.apache.org/"> Lucene</a>/<a href="http://www.opensymphony.com/compass/">Compass</a> across multiple nodes with as little intrusion and in a way that&#8217;s as turnkey as possible.  I&#8217;m in immediate need of a solution, and I think it would be a good contribution to the Searchable plugin.</p>
<p>To start here&#8217;s what I&#8217;m considering:</p>
<ol>
<li> Manage a local index per node.  Probably disable mirrorChanges via Compass::GPS and rebuild the index on someinterval. <i>This is not very desirable since it would eliminate the Compass:GPS capability, and there just must be a more elegant way&#8230;</i></li>
<li>Use the <a href="http://www.opensymphony.com/compass/versions/0.9.0/api/org/apache/lucene/store/jdbc/JdbcDirectory.html">JDBCDirectory</a> implementation of Lucene and store the index in a sql database. <i>This option is viable but has obvious performance implications that somewhat defeat the purpose.  The configuration would be fairly clean using the <a href="http://www.opensymphony.com/compass/versions/1.1/html/core-configuration.html#core-configuration-xml">native Compass XML config</a>.  This maybe a good fall back option to do some A/B testing against. </i></li>
<li>Use <a href="http://www.terracotta.org/confluence/display/integrations/Lucene">Terracotta and RAMDirectory</a> to handle synchronization of the index across nodes. <i>This has a lot of promise and will be the path I head down first.  Though I have no experience whatsoever with Terracotta</i></li>
</ol>
<p>Another possibility is to look at <a href="http://www.kimchy.org/compasslucene-and-datagrids/">Compass&#8217; new support for Gigaspaces</a>.   Thanks <a href="http://marcospereira.wordpress.com">Marcos</a> for passing that on.  This sounds interesting but &#8220;feels&#8221; fairly heavy compared to the others, though I know nothing about <a href="http://www.gigaspaces.com/">GigaSpaces</a> or <a href="http://www.oracle.com/technology/products/coherence/">Coherence</a> so I&#8217;m just talking out of my arse at this point!</p>
<p>I made my first attempt with Terracotta and Compass today.  I dug deep enough into Compass to find that it manages the RAMDirectory objects in this class: org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore and manages each subindex RAMDirectory in a java.util.HashMap called ramIndexes.  The first issue I had to overcome was that HashMap isn&#8217;t a synchronized data structure, and I was having problems configuring the locking in Terracotta for ramIdexes.  I kept getting  UnlockedSharedObjectException exceptions not matter what I tried.</p>
<p>After a little help from the Terracotta users list I ended up with a named lock.  My tc-config.xml looked like this:</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
&lt;?xml version="1.0" encoding="UTF-8" ?&gt;
&lt;tc:tc-config xmlns:tc="http://www.terracotta.org/config"&gt;
  &lt;system&gt;
    &lt;configuration-model&gt;development&lt;/configuration-model&gt;
  &lt;/system&gt;

  &lt;servers&gt;
    &lt;server name="localhost" /&gt;
  &lt;/servers&gt;

  &lt;clients&gt;
    &lt;logs&gt;%d/client-logs-%h&lt;/logs&gt;
    &lt;dso&gt;
      &lt;debugging&gt;
        &lt;runtime-logging&gt;
          &lt;lock-debug&gt;true&lt;/lock-debug&gt;
        &lt;/runtime-logging&gt;
        &lt;runtime-output-options&gt;
          &lt;full-stack&gt;true&lt;/full-stack&gt;
        &lt;/runtime-output-options&gt;
      &lt;/debugging&gt;
    &lt;/dso&gt;
    &lt;modules&gt;
      &lt;module name="clustered-lucene-2.0.0" version="2.5.0"/&gt;
    &lt;/modules&gt;
  &lt;/clients&gt;

  &lt;application&gt;
    &lt;dso&gt;
      &lt;instrumented-classes&gt;
        &lt;include&gt;
          &lt;class-expression&gt;org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore&lt;/class-expression&gt;
        &lt;/include&gt;
      &lt;/instrumented-classes&gt;
      &lt;roots&gt;
        &lt;root&gt;
          &lt;field-name&gt;org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore.ramIndexes&lt;/field-name&gt;
        &lt;/root&gt;
      &lt;/roots&gt;
      &lt;locks&gt;
	&lt;named-lock&gt;
          &lt;method-expression&gt;* org.compass.core.lucene.engine.store.RAMLuceneSearchEngineStore.*(..)&lt;/method-expression&gt;
          &lt;lock-level&gt;write&lt;/lock-level&gt;
          &lt;lock-name&gt;theLockName&lt;/lock-name&gt;
        &lt;/named-lock&gt;
      &lt;/locks&gt;
     &lt;/dso&gt;
  &lt;/application&gt;
&lt;/tc:tc-config&gt;
</pre>
<p>That at least got me past the UnlockSharedObjectException, but tonight I hit a bigger roadblock.  Terracotta is choking on Grails custom ClassLoader.  I did a preliminary search on the error message below but came up short.  I followed up on the Terracotta users&#8217; list but I thought I&#8217;d share my progress and my intentions here in case anyone out there has any ideas&#8230;</p>
<pre class="prettyprint" style="line-height:normal;width:500px;font-size:11px;overflow:auto">
Error creating bean with name 'compassGps': Cannot resolve reference to
bean'compass' while setting bean property 'compass'; nested exception is
org.springframework.beans.factory.BeanCreationException: Error creating
bean with name 'compass': FactoryBean threw exception on object creation;
nested exception is java.lang.IllegalStateException: Classloader name not set,
instances defined from this loader not supported in Terracotta (loader:
org.codehaus.groovy.grails.cli.support.GrailsRootLoader):
java.lang.IllegalStateException: Classloader name not set, instances
defined from this loader not supported in Terracotta (loader:
org.codehaus.groovy.grails.cli.support.GrailsRootLoader)
</pre>
<p>Stay tuned&#8230;</p>
<p><b>Update (Jan 29, 2008):</b> <a href="http://mike.brevoort.com/2008/01/29/terracotta-1-grails-searchable-pluginme-1/">See part 2 here</a></p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2008/01/22/terracotta-1-grails-searchable-pluginme-0/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Building Grails Applications with Hudson</title>
		<link>http://mike.brevoort.com/2008/01/21/building-grails-applications-with-hudson/</link>
		<comments>http://mike.brevoort.com/2008/01/21/building-grails-applications-with-hudson/#comments</comments>
		<pubDate>Mon, 21 Jan 2008 18:33:10 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Grails]]></category>

		<category><![CDATA[Technology]]></category>

		<category><![CDATA[code]]></category>

		<category><![CDATA[continuous integration]]></category>

		<category><![CDATA[hudson]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2008/01/21/building-grails-applications-with-hudson/</guid>
		<description><![CDATA[I&#8217;m never surprised by the shear magnitude of stuff I don&#8217;t know about.  Last week I stumbled upon Hudson as I was searching for help to configure Cruise Control to build and execute tests for my  a Grails application.  I found this post that introduced me to Hudson and got me started.
Let [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;m never surprised by the shear magnitude of stuff I don&#8217;t know about.  Last week I stumbled upon <a href="https://hudson.dev.java.net/">Hudson</a> as I was searching for help to configure <a href="http://cruisecontrol.sourceforge.net/">Cruise Control</a> to build and execute tests for my  a <a href="http://grails.codehaus.org/">Grails</a> application.  I found <a href="http://www.giolist.com/2008_01_01_giolist-archive.html">this post</a> that introduced me to Hudson and got me started.</p>
<p>Let me first say that I was off to find a free solution.  If I had the luxury of spending a little cash at this point I would have used <a href="http://www.atlassian.com/software/bamboo/">Bamboo</a>.  I&#8217;m a big fan of <a href="http://www.atlassian.com">Atlassian</a> products and love what they are doing to integrate Jira, Fisheye, Bamboo, Crucible, Clover and Confluence.  The traceability of code to issues to change sets to builds and tests is really cool.  Anyway I digress.  </p>
<p>So I found Hudson, and I was pleasantly surprised how easy it was to set up.  I&#8217;m running on Windows with Tomcat 6 and JDK1.6.  I simply deployed the Hudson WAR, and I was off to the races.  Hudson&#8217;s configuration is all done via a web interface and the interface itself is very clean, especially compared to Cruise Control.  </p>
<p>In Hudson I created a new job, selected SVN in the source code management section and entered the URL to my Grails app in the repository.  Hudson also lets you configure a repository browser (ViewSVN, Fisheye, WebSVN or Sventon).  Since I was already running Tomcat, I chose Sventon and quickly downloaded and deployed the WAR for <a href="http://www.sventon.org/">Sventon</a>.  I set Hudson to poll SVN for changes every minute and for the build itself I told Hudson to use Ant (you need to set up ANT first in Hudsson) and pointed to the Grails build.xml.  </p>
<p>I created two new targets in the build.xml: &#8216;all&#8217; and &#8216;deploy&#8217;.  The all target chains the depended targets together.  I had originally had Hudson call test, war, deploy and doc separately but each invoked a compile separately.</p>
<pre class="prettyprint" style="width:500px;font-size:11px;overflow:auto">
&lt;target name="all" depends="war, test, deploy, doc"
  description="build, test, deploy and build the javadoc"/&gt;
</pre>
<p>I also created a deploy target that takes the war and deploys to Tomcat and property to specify the grails_env.</p>
<p>Under Post-build Actions I set the following properties:</p>
<ul>
<li>Files to Archive = &#8220;**/*.war&#8221;</li>
<li>Javadoc Directory = &#8220;GRAILS-APP/docs/gapi&#8221;</li>
<li>Test Reports XMLs = &#8220;GRAILS-APP/test/reports/*.xml&#8221;</li>
</ul>
<p>So in about an hour I had Hudson continually building, running my test cases, generating javadoc and deploying my WAR to Tomcat, and it was all relatively painless.  Here&#8217;s what the job status screen looks like:<br />
<img src='http://mike.brevoort.com/wp-content/uploads/2008/01/hudson_sh.png' alt='Hudson Screenshot' /></p>
<p>Going forward I&#8217;ll probably investigate using <a href="http://weblogs.java.net/blog/kohsuke/archive/2007/07/hudson_gant_plu.html">GANT instead of ANT</a>, otherwise it&#8217;s serving the purpose.</p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2008/01/21/building-grails-applications-with-hudson/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Active Directory Authentication Part I</title>
		<link>http://mike.brevoort.com/2007/09/25/active-directory-authentication/</link>
		<comments>http://mike.brevoort.com/2007/09/25/active-directory-authentication/#comments</comments>
		<pubDate>Wed, 26 Sep 2007 05:14:14 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Technology]]></category>

		<category><![CDATA[code]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2007/09/25/active-directory-authentication/</guid>
		<description><![CDATA[One of the main reasons I started this blog was so that I would have a place to &#8220;give back&#8221; to the software development community.  For those that read my blog for more personal updates, you&#8217;ll want to stop here.  My daily work life depends heavily on open source projects, discussion forums and [...]]]></description>
			<content:encoded><![CDATA[<p>One of the main reasons I started this blog was so that I would have a place to &#8220;give back&#8221; to the software development community.  For those that read my blog for more personal updates, you&#8217;ll want to stop here.  My daily work life depends heavily on open source projects, discussion forums and blogs posts like this one.  In this post I&#8217;ll be sharing what I&#8217;ve learned regarding programmatic authentication to Active Directory for a custom website authentication scheme. </p>
<p>I recently redeveloped a centralized web-based authentication service for a large corporate intranet that leveraged <a href="http://www.rsa.com/node.aspx?id=1186">RSA Access Manager</a> as a web single sign-on (SSO) provider.  The authentication service validates credentials against Active Directory and handles cases of expired password resets and locked accounts.  Once a credential has been validated, the user Id is asserted to RSA Access Manager, creating a new session and then passing control over to RSA and redirecting onto the requested resource.  </p>
<p>A few key points:</p>
<li>RSA Access Manager is a Java based application and all of the intranet applications are Java applications</li>
<li>The Active Directory forest has 42 child domains organized by business unit, all de-centrally administered, spread across the US in 18 states and connected by a wide area network with relatively high latency.</li>
<li>Across the 42 domains, there are well over 100 domain controllers, some Windows 2003, others Windows 2000</li>
<p>As I am a Java developer with no previous .Net experience, the first attempt to externalize authentication from RSA Access Manager was in Java via JAAS/GSS-API and then JNDI.  Without going into detail, this turned out to be difficult given the diversity of the AD infrastructure.  </p>
<p>Next, since we had other needs to interface with Active Directory from our applications, I decided on a new approach.  I developed a .Net Web Services API in C# to interface with our Active Directory infrastructure.  The Web Services API encapsulated the complexity of the environment and allowed the caller to search, retrieve, and manipulate AD users and group information.  This is now how we integrate our Java applications with AD.  As part of this implementation in the same ASP.Net web application, I implemented the centralized authentication HTML service.  Next I&#8217;ll dive into three iterations of the authentication functionality.  I&#8217;ll save anything outside of the authentication portion for other posts if there&#8217;s interest.</p>
<p>The first iteration leveraged Integrated Windows Authentication (IWA) via IIS.  I won&#8217;t go into detail on this but will say we abandoned that approach because of the regular use of shared workstations and auto-login accounts.</p>
<p>The second iteration used LDAP via ADSI:<br />
Given a domain, samAccountName and password, attempt to bind to the root of the domain of the user.  If successful, continue. If the bind failed, one of these cases is true: the credential was invalid, the account password is expired (including &#8220;must change password on next login&#8221; situations, the account is locked (too many bad attempts) or the account is disabled.  Therefore, bind with a known system credential to check the account status.  If the account password is expired, notify the user and prompt the user to reset their password.  Otherwise, if the account is locked, disabled, or looks OK, return that the authentication was unsuccessful.</p>
<p>Below are several snippets of code:</p>
<pre class="prettyprint" style="width:500px;font-size:11px;overflow:auto">
public static AuthenticationResult authAndStatus(string domain, string samAccountName,
    string password, String adminUsername, String adminPassword)
{
    String domainAndUsername = domain + "\\\\" + samAccountName;
    DirectoryEntry de = getDirectoryEntry(domain, domainAndUsername, password);
    AuthenticationResult result = new AuthenticationResult();
    DirectoryEntry userDE = null;

    try
    {
        // Bind to the native AdsObject to force authentication.
        Object obj = de.NativeObject;
        result.authResult = AuthenticationResult.SUCCESSFUL;

    }
    catch (Exception ex)
    {
        // the bind failed
        result.authResult = AuthenticationResult.FAILED;
    }

    try
    {
        // get the account status
        de = getDirectoryEntry(domain, adminUsername, adminPassword);
        userDE = ADUtilities.getUserDE(de, "samAccountName", samAccountName,
            adminUsername, adminPassword);
        if (userDE != null)
        {
            AccountStatus accountStatus = new AccountStatus(userDE);
            result.accountStatus = accountStatus;
        }
        else
        {
            result.authResult = AuthenticationResult.INVALID;
        }
    }
    catch (Exception ex)
    {
        // need to trap, basically don't let the status check break
        //the authentication
    }

    return result;
}
</pre>
<pre class="prettyprint"  style="width:500px;font-size:11px">
    public static DirectoryEntry getUserDE(DirectoryEntry de, String propertyName, String propertyValue, String adminUsername, String adminPassword)
    {
        DirectorySearcher search = new DirectorySearcher(de);
        search.Filter = "(" + propertyName + "=" + propertyValue + ")";

        SearchResult result = search.FindOne();
        if (result != null) {
            return new DirectoryEntry(result.Path, adminUsername, adminPassword);
        } else {
            return null;
        }
    }

public static DirectoryEntry getDirectoryEntry(String domain, String adminUsername, String adminPassword)
{
    String dePath = null;

    String adForestName = (String)System.AppDomain.CurrentDomain.GetData("adForestName");
    String forestDomainName = adForestName.Substring(0, adForestName.IndexOf('.'));
    String gcDNS = (String)System.AppDomain.CurrentDomain.GetData("adForestPreferredGlobalCatalog");

    if (domain != null &#038;&#038; domain.ToUpper().Contains(".") &#038;&#038; domain.ToUpper().Contains(adForestName.ToUpper()))
    {
        domain = (domain.Split(new char[] { '.' }))[0];
    }

    // if the domain is not null and not All
    if (domain != null &#038;&#038; !domain.Trim().Equals("") &#038;&#038; !domain.Equals("All"))
    {
        String fqdn = domain + "." + adForestName;

        // check if this is the root domain and if so rest the fqdn to the forest name
        if (domain.ToUpper().Equals(forestDomainName.ToUpper()))
            fqdn = adForestName;

        String ldapDCs = getLDAPDCs(fqdn);
        dePath = "LDAP://" + fqdn + "/" + ldapDCs;

    }
    else
    {
        String ldapDCs = getLDAPDCs(adForestName);
        // if the domain is null assume the global catalog
        dePath = "LDAP://" + gcDNS + ":3268/" + ldapDCs;
    }

    DirectoryEntry de = new DirectoryEntry(dePath, adminUsername, adminPassword, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
    return de;

}

public static DirectoryEntry getGCDirectoryEntryForDomain(String domain, String adminUsername, String adminPassword)
{
    String dePath = null;

    String adForestName = (String)System.AppDomain.CurrentDomain.GetData("adForestName");
    String forestDomainName = adForestName.Substring(0, adForestName.IndexOf('.'));
    String gcDNS = (String)System.AppDomain.CurrentDomain.GetData("adForestPreferredGlobalCatalog");

    dePath = "LDAP://" + gcDNS + ":3268/" + getLDAPDCs(domain + "." + adForestName);

    DirectoryEntry de = new DirectoryEntry(dePath, adminUsername, adminPassword, AuthenticationTypes.Secure | AuthenticationTypes.Sealing);
    return de;

}

public static String getLDAPDCs(String fqdn)
{
    String ldapDCs = "";

    if (fqdn != null) {
        char[] separator = new char[1];
        separator[0] = '.';
        String[] parts = fqdn.Split(separator);

        for (int i = 0; i < parts.Length; i++) {
            ldapDCs = ldapDCs + "dc=" + parts[i];

            if (i + 1 != parts.Length)
                ldapDCs = ldapDCs + ",";
        }
    }

    return ldapDCs;
}
</pre>
<p>In my next post I describe how well this approach actually worked and the issues that prompted the search for another solution.  Then I&#8217;ll go into using SSPI and then the use of the LogonUser native function call&#8230;</p>
<p>To be Continued.</p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2007/09/25/active-directory-authentication/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Broken Part III</title>
		<link>http://mike.brevoort.com/2007/08/28/broken-part-iii/</link>
		<comments>http://mike.brevoort.com/2007/08/28/broken-part-iii/#comments</comments>
		<pubDate>Wed, 29 Aug 2007 03:30:02 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Cycling]]></category>

		<category><![CDATA[Personal]]></category>

		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2007/08/28/broken-part-iii/</guid>
		<description><![CDATA[Severe focused blunt force trauma.  That&#8217;s how my orthopedist described what caused my fracture.  Apparently I broke it in an unusual location where the clavicle is in fact much thicker and closer to the center of my chest.  He thinks I don&#8217;t need surgery, but I&#8217;ll find out for sure next week [...]]]></description>
			<content:encoded><![CDATA[<p>Severe focused blunt force trauma.  That&#8217;s how my <a href="http://www.panoramaortho.com/physicians/biographies/mcnair-patrick.html">orthopedist</a> described what caused my fracture.  Apparently I broke it in an unusual location where the clavicle is in fact much thicker and closer to the center of my chest.  He thinks I don&#8217;t need surgery, but I&#8217;ll find out for sure next week when he checks how the bones shift around as the swelling subsides.  I have a 1cm reduction in my clavicle and there are at least 4 bone chips floating around as well as a general sloping of my shoulder.</p>
<p>I&#8217;ve spent the last 3 nights and many waking hours in a borrowed recliner.  It has been the only place I am reasonably comfortable.  I&#8217;ll try to go into work tomorrow, but I doubt how useful I&#8217;ll be.  This weekend we planned to go camping at Grand Lake in a small cabin.  I&#8217;m threatening to stay home, but Jeanie&#8217;s threatening to bring the recliner <img src='http://mike.brevoort.com/wp-includes/images/smilies/icon_smile.gif' alt=':)' class='wp-smiley' /> </p>
<p>I found a really cool new feature on with <a href="http://maps.google.com">Google Maps</a> today when mapping out directions to the doctors office.  You can now modify the route by clicking and dragging points along the route.  Check it out <a href="http://maps.google.com/help/maps/directions/index.html#utm_campaign=en&#038;utm_source=en-mapshpp-na-us-google&#038;utm_medium=mapshpp">here</a>.</p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2007/08/28/broken-part-iii/feed/</wfw:commentRss>
		</item>
		<item>
		<title>Google on My Mind</title>
		<link>http://mike.brevoort.com/2007/08/27/google-on-my-mind/</link>
		<comments>http://mike.brevoort.com/2007/08/27/google-on-my-mind/#comments</comments>
		<pubDate>Mon, 27 Aug 2007 20:40:15 +0000</pubDate>
		<dc:creator>Mike</dc:creator>
		
		<category><![CDATA[Technology]]></category>

		<guid isPermaLink="false">http://mike.brevoort.com/2007/08/27/google-on-my-mind/</guid>
		<description><![CDATA[I&#8217;ve been thinking about Google a lot recently and how my personal online life is dominated by Google tools.  The list is impressive.  Here are the Google tools I use most:

Search - Google.com
Google Toolbar for IE and Firefox
GBookmarks for Firefox
Google Apps for your Domain for brevoort.com (GMail, Docs and Spreadsheets, Calender, GTalk)
Google Reader [...]]]></description>
			<content:encoded><![CDATA[<p>I&#8217;ve been thinking about Google a lot recently and how my personal online life is dominated by Google tools.  The list is impressive.  Here are the Google tools I use most:</p>
<ul>
<li><a href="http://google.com">Search - Google.com</a></li>
<li><a href="http://www.google.com">Google Toolbar for IE and Firefox</a></li>
<li><a href="http://www.rjonna.com/ext/gbookmarks.php">GBookmarks for Firefox</a></li>
<li><a href="https://www.google.com/a/">Google Apps for your Domain</a> for brevoort.com (GMail, Docs and Spreadsheets, Calender, GTalk)</li>
<li><a href="http://www.google.com/reader">Google Reader</a> (every day)</li>
<li><a href="http://www.feedburner.com">Feedburner</a></li>
<li><a href="http://picasa.google.com/">Picasa</a></li>
<li><a href="http://picasaweb.google.com/">Picasa Web Albums</a></li>
<li><a href="http://maps.google.com">Google Maps</a></li>
<li><a href="http://earth.google.com">Google Earth</a></li>
<li><a href="http://news.google.com">Google News</a></li>
<li><a href="http://desktop.google.com">Google Desktop</a> (ok, sometimes I use MSN desktop)</li>
<li><a href="http://google.com/analytics">Google Analytics</a></li>
</ul>
<p>Best of alll?  I don&#8217;t pay a dime out of pocket for any of it.  At work we purchased the <a href="http://google.com/enterprise">Google Search Appliance</a> at work for our internal sites and we use the Google Maps API internally for a location search.  I&#8217;ve been experimenting with <a href="http://googlemashups.com">Google Mashups</a> and I recently started looking at <a href="http://code.google.com/p/google-guice/">Guice</a> which is a new Inversion of Control (IoC) framework (think <a href="http://www.springframework.org/">Spring</a>).</p>
<p>So is <a href="http://www.google.com/url?sa=t&#038;ct=res&#038;cd=3&#038;url=http%3A%2F%2Fwww.motherjones.com%2Fnews%2Ffeature%2F2006%2F11%2Fgoogle.html&#038;ei=-TTTRoS3J5uSiwGr6LnNBg&#038;usg=AFQjCNHQyDTA1YQucrgP0RAxLfrGCY1tNg&#038;sig2=Kqj_sS22mdk07rDrQGdT9g">Google evil</a>?  Has my privacy been infringed?  I can&#8217;t say.  Maybe I have &#8220;sold out&#8221; a little, but at least I&#8217;m not paying for it!  </p>
]]></content:encoded>
			<wfw:commentRss>http://mike.brevoort.com/2007/08/27/google-on-my-mind/feed/</wfw:commentRss>
		</item>
	</channel>
</rss>
