<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-6091379212671975314</id><updated>2011-11-27T20:37:18.905-05:00</updated><category term='christianity'/><category term='mlc'/><category term='maven'/><category term='methodology'/><category term='confession'/><category term='productivity'/><category term='code'/><category term='blogs'/><category term='behavior'/><category term='busyness'/><category term='system administration'/><title type='text'>Pteropus</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>19</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-3786082710729701580</id><published>2008-04-24T21:50:00.002-04:00</published><updated>2008-04-24T21:55:54.678-04:00</updated><title type='text'>Securing Subversion via LDAP -- A Followup</title><content type='html'>After some discussion of &lt;a href="http://pteropus.blogspot.com/2008/04/securing-subversion-via-ldap.html"&gt;this&lt;/a&gt; on the Subversion mailing list we discovered that checking out the parent of a protected child will pull that child's contents to the local workspace. That probably isn't what you want so...&lt;br /&gt;&lt;br /&gt;Let's say we have the repository paths .../parent and .../child and we want only a certain group of people to be able to checkout or update the child. In my initial pass at this I did the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/parent&amp;gt;&lt;br /&gt;  Require valid-user&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/parent/child&amp;gt;&lt;br /&gt;  Require group ...&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This will protect against the following:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  svn ls http://myserver/svn/myRepo/parent/child&lt;br /&gt;  svn co http://myserver/svn/myRepo/parent/child&lt;/pre&gt;&lt;br /&gt;and commits against the child.&lt;br /&gt;&lt;br /&gt;It will *not* protect against:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  svn co http://myserver/svn/myRepo/parent&lt;/pre&gt;&lt;br /&gt;which will check out the child as it processes parent.&lt;br /&gt;&lt;br /&gt;When we look in the apache logs we see a PROPFIND on the parent and some internal things but no mention of the child.&lt;br /&gt;&lt;br /&gt;While digging through the mod_dav_svn source I discovered that, as it traverses the directory tree, it does an internal GET on each path to determine if the path is accessible. However, these GETs use the internal subversion URIs, not the ones that we naturally think of. So, to protect our child path, we can add another &amp;lt;Location/&amp;gt; tag thusly:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Location ~ /svn/myRepo/!svn/ver/[0-9]+/parent/child&amp;gt;&lt;br /&gt;  Require group ...&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that this needs to come after:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;  &amp;lt;Location /svn/myRepo/!svn&amp;gt;&lt;br /&gt;    Require valid-user&lt;br /&gt;  &amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;I wasted a couple of hours because I had that nugget tucked away in an included conf file following the one I was tweaking.&lt;br /&gt;&lt;br /&gt;So, in order to protect the child the way we wanted to in the first place, our configuration would be:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/parent&amp;gt;&lt;br /&gt;  Require valid-user&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/parent/child&amp;gt;&lt;br /&gt;  Require group ...&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;Location ~ /svn/myRepo/!svn/ver/[0-9]+/parent/child&amp;gt;&lt;br /&gt;  Require group ...&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What's really nice about this is that a checkout of the parent silently ignores the now-protected child and populates the local workspace with everything that is unprotected.&lt;br /&gt;&lt;br /&gt;I haven't tested it (yet) but it is reasonable to assume that the same technique will work with the more complicated &amp;lt;Limit/&amp;gt; and &amp;lt;LimitExcept/&amp;gt; tags.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-3786082710729701580?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/3786082710729701580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=3786082710729701580' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3786082710729701580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3786082710729701580'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2008/04/securing-subversion-via-ldap-followup.html' title='Securing Subversion via LDAP -- A Followup'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-4383585655807967878</id><published>2008-04-18T09:50:00.005-04:00</published><updated>2008-04-18T10:09:44.828-04:00</updated><title type='text'>Securing Subversion via LDAP</title><content type='html'>This started out as an email reply to the Subversion users mailing list but I decided to post it here instead since it got a little wordy. I can't believe it's been a year since I posted anything, time flies when you're falling off of a mountain bike...&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;So, in our environment, we're using the LDAP interface to Active Directory. All of our users are known there and we have defined groups to represent various bits of the repository. I'm using mod_auth_ldap in front of SVN to do the integration.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;$ grep svn.conf httpd.conf&lt;br /&gt; include conf/svn.conf&lt;br /&gt;&lt;br /&gt;$ cat svn.conf&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;IfModule mod_dav_svn.c&amp;gt;&lt;br /&gt;  &amp;lt;IfModule util_ldap.c&amp;gt;&lt;br /&gt;    &amp;lt;IfModule mod_auth_ldap.c&amp;gt;&lt;br /&gt;&lt;br /&gt;      LDAPSharedCacheSize 200000&lt;br /&gt;      LDAPCacheEntries    1024&lt;br /&gt;      LDAPCacheTTL  600&lt;br /&gt;      LDAPOpCacheEntries  1024&lt;br /&gt;      LDAPOpCacheTTL      600&lt;br /&gt;&lt;br /&gt;      &amp;lt;Location /ldap-status&amp;gt;&lt;br /&gt;        SetHandler ldap-status&lt;br /&gt;        Order deny,allow&lt;br /&gt;        Deny from all&lt;br /&gt;        Allow from .myco.com 172.18.&lt;br /&gt;      &amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;br /&gt;      &amp;lt;Location /svn&amp;gt;&lt;br /&gt;  DAV svn&lt;br /&gt;  SVNParentPath /usr/local/svn/repositories&lt;br /&gt;  SVNListParentPath on&lt;br /&gt;  SVNAutoVersioning on&lt;br /&gt;  AuthName "Enterprise Shared SCM Repository"&lt;br /&gt;  AuthType Basic&lt;br /&gt;  Require valid-user&lt;br /&gt;  AuthLDAPUrl ldap://myLdapHost/DC=foo,DC=myco, \&lt;br /&gt;    DC=com?SAMAccountName?sub?(objectCategory=person)&lt;br /&gt;  AuthLDAPBindDN "CN=foobar,DC=foo,DC=myco,DC=com"&lt;br /&gt;  AuthLDAPBindPassword ...&lt;br /&gt;&lt;br /&gt;      &amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;br /&gt;      include conf/svn/Authorization.conf&lt;br /&gt;&lt;br /&gt;    &amp;lt;/IfModule&amp;gt;&lt;br /&gt;  &amp;lt;/IfModule&amp;gt;&lt;br /&gt;&amp;lt;/IfModule&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So that sets up the basic wiring and in Authorization.conf I break it down into Subversion specifics:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cat svn/Authorization.conf&lt;br /&gt;&lt;br /&gt;include conf/svn/root.conf&lt;br /&gt;include conf/svn/sandbox.conf&lt;br /&gt;include conf/svn/admin.conf&lt;br /&gt;include conf/svn/BusinessUnit1.conf&lt;br /&gt;include conf/svn/BusinessUnit2.conf&lt;br /&gt;...&lt;br /&gt;include conf/svn/BusinessUnit3.conf&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We've taken the route of one large repository shared by our multiple business units. There are pros and cons of this approach (as there are with the repository-per-project approach). For various reasons we felt that a single repository would be most appropriate.&lt;br /&gt;&lt;br /&gt;Defining the per-whatever rules below was a bit of trial and error. I put together a set of typical use cases for various paths and who should and should not access them. By running through those cases and watching the logs I was able to sort out the Limit tags necessary to secure things the way I needed.&lt;br /&gt;&lt;br /&gt;Since the repository is shared across business units, the first thing is to make sure that only the admin can create top-level directories. this is done in the root.conf file included above:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cat svn/root.conf&lt;br /&gt;&lt;br /&gt;# Any attempt to modify the myRepo repository requires&lt;br /&gt;# membership in the admin group.&lt;br /&gt;&amp;lt;Location /svn/myRepo&amp;gt;&lt;br /&gt;  &amp;lt;Limit MERGE MKCOL POST PUT DELETE PATCH PROPPATCH&amp;gt;&lt;br /&gt;    Require group CN=ACL-SVN-ADMIN,OU=SVN,DC=foo,DC=myco,DC=com&lt;br /&gt;  &amp;lt;/Limit&amp;gt;&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;br /&gt;# On many operations, Subversion does some behind the&lt;br /&gt;# scenes work at .../!svn&lt;br /&gt;&amp;lt;Location /svn/myRepo/!svn&amp;gt;&lt;br /&gt;  Require valid-user&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;br /&gt;# Any paths below the root that are not otherwise secured&lt;br /&gt;# only require a valid user for read/write access&lt;br /&gt;&amp;lt;LocationMatch /svn/myRepo/.*&amp;gt;&lt;br /&gt;    Require valid-user&lt;br /&gt;&amp;lt;/LocationMatch&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In order to encourage people to use Subversion, I have setup a special "sandbox" are of the repository that is more or less a free-for-all. Given the above configuration, this config isn't entirely necessary but if we change the default policy (via the LocationMatch tag) from all-access to no-access our sandbox would break.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cat svn/sandbox.conf&lt;br /&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/sandbox&amp;gt;&lt;br /&gt;    Require valid-user&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Now I define business unit specific access. For each path we want to secure, the business unit tells me if they want the path to be read/write for all members of the group or if they want to have some people with read-only and others with read-write.&lt;br /&gt;&lt;br /&gt;For instance, I keep the actual Subversion configuration in the repository. Because some of that may be "sensitive" (e.g. -- the LDAP bind password) I need to protect it.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cat svn/admin.conf&lt;br /&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/Admin/SVN&amp;gt;&lt;br /&gt;    Require group CN=ACL-SVN-ADMIN,OU=SVN,DC=foo,DC=myco,DC=com&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Thus, any access (read/write/merge/...) to /svn/myRepo/Admin/SVN requires membership in the administration group.&lt;br /&gt;&lt;br /&gt;In some cases, we may have a project where it is acceptable for anybody with a valid username/password to view the files but only members of a particular group can edit them:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cat svn/BusinessUnit1/someProject1.conf&lt;br /&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/BusinessUnit1/SharedSource&amp;gt;&lt;br /&gt;  &amp;lt;Limit MERGE MKCOL POST PUT DELETE PATCH PROPPATCH&amp;gt;&lt;br /&gt;    Require group CN=ACL-SVN-ADMIN,OU=SVN,DC=foo,DC=myco,DC=com&lt;br /&gt;    Require group CN=ACL-SVN-BusinessUnit1-SharedSource-RW, \&lt;br /&gt;      OU=ACLs,DC=foo,DC=myco,DC=com&lt;br /&gt;  &amp;lt;/Limit&amp;gt;&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The group that protects this branch is "CN=ACL-SVN-BusinessUnit1-SharedSource-RW,...". I've included the administration group also, however, so that the repository admins can help out if people get in a bind. This may or may not be something you want to do or you may have a different group (e.g. -- a help desk or support org) that would do this kind of thing:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/BusinessUnit1/SharedSource&amp;gt;&lt;br /&gt;  &amp;lt;Limit MERGE MKCOL POST PUT DELETE PATCH PROPPATCH&amp;gt;&lt;br /&gt;    Require group CN=ACL-SVN-Support,OU=SVN,DC=foo,DC=myco,DC=com&lt;br /&gt;    Require group CN=ACL-SVN-BusinessUnit1-SharedSource-RW, \&lt;br /&gt;      OU=ACLs,DC=foo,DC=myco,DC=com&lt;br /&gt;  &amp;lt;/Limit&amp;gt;&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;In any case, you can see how you can list multiple 'Require group' directives for the path. These are OR'd together BTW.&lt;br /&gt;&lt;br /&gt;There may be other paths that I do not want to be world-readable. In the simple case this is identical to my /svn/myRepo/Admin/SVN example. In the more complex case, however, you may have one group of people who need read-only and another who need read-write access.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;$ cat svn/BusinessUnit2/someProject1.conf&lt;br /&gt;&lt;br /&gt;&amp;lt;Location /svn/myRepo/BusinessUnit2/someProject1&amp;gt;&lt;br /&gt;  &amp;lt;LimitExcept MERGE MKCOL POST PUT DELETE PATCH PROPPATCH&amp;gt;&lt;br /&gt;    Require group CN=ACL-SVN-ADMIN,OU=SVN,DC=foo,DC=myco,DC=com&lt;br /&gt;    Require group CN=ACL-SVN-BusinessUnit2-someProject1-RO, \&lt;br /&gt;      OU=ACLs,DC=foo,DC=myco,DC=com&lt;br /&gt;    Require group CN=ACL-SVN-BusinessUnit2-someProject1-RW, \&lt;br /&gt;      OU=ACLs,DC=foo,DC=myco,DC=com&lt;br /&gt;  &amp;lt;/LimitExcept &amp;gt;&lt;br /&gt;  &amp;lt;Limit MERGE MKCOL POST PUT DELETE PATCH PROPPATCH&amp;gt;&lt;br /&gt;    Require group CN=ACL-SVN-ADMIN,OU=SVN,DC=foo,DC=myco,DC=com&lt;br /&gt;    Require group CN=ACL-SVN-BusinessUnit2-someProject1-RW, \&lt;br /&gt;      OU=ACLs,DC=foo,DC=myco,DC=com&lt;br /&gt;  &amp;lt;/Limit &amp;gt;&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;By pairing the Limit and LimitExcept tags I can control who gets to make changes separately from who gets to view files.&lt;br /&gt;&lt;br /&gt;One last thing I'll cover is protecting sub-paths within a parent path. Assuming that below /svn/myRepo/BusinessUnit2/someProject1 we have the traditional Subversion directories of "trunk", "tags" and "branches", it is very likely that your enterprise will want to secure the branches area to a specific group. In our case, we want to be sure that our branched code is only modifiable by a few experienced people who are allowed to make changes to the production candidate.&lt;br /&gt;&lt;br /&gt;So, building on the /svn/myRepo/BusinessUnit2/someProject1 example, we further restrict the branches to this smaller group of people:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;Location ~ /svn/myRepo/BusinessUnit2/someProject1/[^/]+/branches&amp;gt;&lt;br /&gt;  &amp;lt;Limit MERGE MKCOL POST PUT DELETE PATCH PROPPATCH&amp;gt;&lt;br /&gt;    Require group CN=ACL-SVN-ADMIN,OU=SVN,DC=foo,DC=myco,DC=com&lt;br /&gt;    Require group CN=ACL-SVN-BusinessUnit2-someProject1-branches-RW, \&lt;br /&gt;      OU=ACLs,DC=foo,DC=myco,DC=com&lt;br /&gt;  &amp;lt;/Limit &amp;gt;&lt;br /&gt;&amp;lt;/Location&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's about all of the highlights I can think of to share. The most critical thing is to setup a test LDAP (I used OpenLDAP) and repository that is similar to your production environment. Then drill through as many usecases as you can think of to ensure that you're getting the behavior you expect. Once you're done, leave the test environment in place so that when somebody thinks something is broken you can go there and prove that it is user error :-)&lt;br /&gt;&lt;br /&gt;My future goal is to define a simple XML format such that business unit (or project) owners can define their authorization requirements and submit them to an agreed-upon repository path. I would then have code to transform that XML into the Apache rules and drop them into place. In other words, I really don't intend to create and maintain all of the Apache configs by hand as we roll this out to dozens of projects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-4383585655807967878?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/4383585655807967878/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=4383585655807967878' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/4383585655807967878'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/4383585655807967878'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2008/04/securing-subversion-via-ldap.html' title='Securing Subversion via LDAP'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-8931925855743767795</id><published>2007-04-12T19:20:00.000-04:00</published><updated>2007-04-12T19:48:37.638-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>From maven to mvn : Part 7 -- A Groovier Timestamp</title><content type='html'>With a bit of help from the maven users list I've sorted out the details of creating a maven plugin written in Groovy. It isn't as clean or elegant as I had hoped but things are still brewing so I suspect they will only get better. I'm told that there is a discussion about this on the Groovy mailing list as well.&lt;br /&gt;&lt;br /&gt;Before I continue I would like to thank Dennis Lundberg and Martin Gilday on the maven users list. You can see our conversation &lt;a href="http://www.mail-archive.com/users@maven.apache.org/msg64425.html"&gt;here&lt;/a&gt;. Dennis turned me on to the groovy compiler and Martin's &lt;a href="http://www.martingilday.org/articles/Groovy+Maven+Mojos"&gt;blog entry&lt;/a&gt; got me hooked up with javalike-maven-plugin-tools for generating my project descriptor.&lt;br /&gt;&lt;br /&gt;So... Let's get down to it and figure out how to write a simple plugin in Groovy. My goal will be to replace my &lt;a href="http://pteropus.blogspot.com/2007/03/from-maven-to-mvn-part-5.html"&gt;earlier attempt&lt;/a&gt; with a compiled Groovy class.&lt;br /&gt;&lt;br /&gt;To begin with, let's take a look at TimestampMojo.groovy:&lt;br /&gt;&lt;pre&gt;/**&lt;br /&gt; * Set the current time into a system property for use by resource&lt;br /&gt; * filtering.&lt;br /&gt; *&lt;br /&gt; * @goal execute&lt;br /&gt; * @phase generate-resources&lt;br /&gt; * @requiresDependencyResolution runtime&lt;br /&gt; */&lt;br /&gt;class TimestampMojo extends AbstractMojo&lt;br /&gt;{&lt;br /&gt;   /**&lt;br /&gt;    * The System property which will hold the current time.&lt;br /&gt;    *&lt;br /&gt;    * @parameter default-value="timestamp"&lt;br /&gt;    */&lt;br /&gt;  String propertyName;&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * The format string given to MessageFormat.format()&lt;br /&gt;   *&lt;br /&gt;   * @parameter default-value="{0,date,yyyy-MM-dd HH:mm:ss}"&lt;br /&gt;   */&lt;br /&gt;  String formatString;&lt;br /&gt;&lt;br /&gt;  public void execute()&lt;br /&gt;      throws MojoExecutionException&lt;br /&gt;  {&lt;br /&gt;      String timestamp = MessageFormat.format(formatString, new Date())&lt;br /&gt;      System.setProperty(propertyName, timestamp)&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;Well, that's simple enough. Note that there appear to be quite a few extra semi-colons for a Groovy script. It turns out that they're necessary because of the javalike plugin we will use to build the plugin descriptor. Martin's blog entry goes into that in detail so I won't repeat it here.&lt;br /&gt;&lt;br /&gt;Next we need a pom to build the plugin. It's a bit large so I'll break it down into bite-sized pieces beginning with the typical header parts:&lt;br /&gt;&lt;pre&gt;&amp;lt;project xmlns="http://maven.apache.org/POM/4.0.0"&lt;br /&gt;xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;xsi:schemaLocation="http://maven.apache.org/POM/4.0.0&lt;br /&gt;http://maven.apache.org/maven-v4_0_0.xsd"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;groupId&amp;gt;myco.util.mvn.timestamp-plugin&amp;lt;/groupId&amp;gt;&lt;br /&gt;  &amp;lt;artifactId&amp;gt;myco-timestamp-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;  &amp;lt;packaging&amp;gt;maven-plugin&amp;lt;/packaging&amp;gt;&lt;br /&gt;  &amp;lt;version&amp;gt;1.1-SNAPSHOT&amp;lt;/version&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;name&amp;gt;myco-timestamp-plugin Maven Mojo&amp;lt;/name&amp;gt;&lt;br /&gt;&lt;/pre&gt;No surprises there so let's move on to specify some extra repositories. We do this because some of the things we need are hosted at Codehaus rather than the default repository.&lt;br /&gt;&lt;pre&gt;  &amp;lt;repositories&amp;gt;&lt;br /&gt;    &amp;lt;repository&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;Codehaus&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;url&amp;gt;http://repository.codehaus.org/org/codehaus/mojo/&amp;lt;/url&amp;gt;&lt;br /&gt;    &amp;lt;/repository&amp;gt;&lt;br /&gt;  &amp;lt;/repositories&amp;gt;&lt;br /&gt;  &amp;lt;pluginRepositories&amp;gt;&lt;br /&gt;    &amp;lt;pluginRepository&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;Snapshots&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;url&amp;gt;http://repository.codehaus.org/org/codehaus/mojo/&amp;lt;/url&amp;gt;&lt;br /&gt;    &amp;lt;/pluginRepository&amp;gt;&lt;br /&gt;  &amp;lt;/pluginRepositories&amp;gt;&lt;br /&gt;&lt;/pre&gt;And now we come to the build section where we define the plugins we need in order to build our plugin. However, we first need to tell the groovy plugin where it can find it's sources:&lt;br /&gt;&lt;pre&gt;  &amp;lt;build&amp;gt;&lt;br /&gt;    &amp;lt;sourceDirectory&amp;gt;${basedir}/src/main/groovy&amp;lt;/sourceDirectory&amp;gt;&lt;br /&gt;&lt;/pre&gt;Did someone say groovy plugin? Yup. If we're going to write the source as groovy then we need some way to compile that into a .class...&lt;br /&gt;&lt;pre&gt;    &amp;lt;plugins&amp;gt;&lt;br /&gt;      &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;groovy-maven-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;version&amp;gt;1.0-alpha-2&amp;lt;/version&amp;gt;&lt;br /&gt;        &amp;lt;executions&amp;gt;&lt;br /&gt;          &amp;lt;execution&amp;gt;&lt;br /&gt;            &amp;lt;goals&amp;gt;&lt;br /&gt;              &amp;lt;goal&amp;gt;compile&amp;lt;/goal&amp;gt;&lt;br /&gt;            &amp;lt;/goals&amp;gt;&lt;br /&gt;          &amp;lt;/execution&amp;gt;&lt;br /&gt;        &amp;lt;/executions&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;Remember the annotations in my *.groovy file above? Well, we're going to use the plugin plugin to turn those into a plugin descriptor. Out of the box that plugin doesn't know anything about groovy, though, so we use javalike-maven-plugin-tools to enhance it a bit. As of this writting (April 12, 2007) the javalike plugin hasn't escaped from the Codehaus mojo sandbox so you'll have to check it out of their svn repo and build it yourself. Martin's blog covers that but I'll repeat it here for convenience:&lt;br /&gt;&lt;pre style="font-style: italic;"&gt;svn co http://svn.codehaus.org/mojo/trunk/mojo \&lt;br /&gt;    /mojo-sandbox/groovy-maven-tools/javalike-maven-plugin-tools/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;pre&gt;      &amp;lt;plugin&amp;gt;&lt;br /&gt;        &amp;lt;artifactId&amp;gt;maven-plugin-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;        &amp;lt;dependencies&amp;gt;&lt;br /&gt;          &amp;lt;dependency&amp;gt;&lt;br /&gt;            &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;&lt;br /&gt;            &amp;lt;artifactId&amp;gt;&lt;br /&gt;              javalike-maven-plugin-tools&lt;br /&gt;            &amp;lt;/artifactId&amp;gt;&lt;br /&gt;            &amp;lt;version&amp;gt;2.0-SNAPSHOT&amp;lt;/version&amp;gt;&lt;br /&gt;          &amp;lt;/dependency&amp;gt;&lt;br /&gt;        &amp;lt;/dependencies&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;    &amp;lt;/plugins&amp;gt;&lt;br /&gt;  &amp;lt;/build&amp;gt;&lt;br /&gt;&lt;/pre&gt;Now that we have our plugins defined we need to define our dependencies. Of course, the plugins will pull in whatever they need but because somebody is going to use us as a plugin (we hope) we need to include groovy in our dependencies so that that somebody will get it (transitively) when they build.&lt;br /&gt;&lt;pre&gt;  &amp;lt;dependencies&amp;gt;&lt;br /&gt;    &amp;lt;dependency&amp;gt;&lt;br /&gt;      &amp;lt;groupId&amp;gt;groovy&amp;lt;/groupId&amp;gt;&lt;br /&gt;      &amp;lt;artifactId&amp;gt;groovy-all&amp;lt;/artifactId&amp;gt;&lt;br /&gt;      &amp;lt;version&amp;gt;1.0&amp;lt;/version&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;    &amp;lt;dependency&amp;gt;&lt;br /&gt;      &amp;lt;groupId&amp;gt;org.apache.maven&amp;lt;/groupId&amp;gt;&lt;br /&gt;      &amp;lt;artifactId&amp;gt;maven-plugin-api&amp;lt;/artifactId&amp;gt;&lt;br /&gt;      &amp;lt;version&amp;gt;2.0&amp;lt;/version&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;    &amp;lt;dependency&amp;gt;&lt;br /&gt;      &amp;lt;groupId&amp;gt;junit&amp;lt;/groupId&amp;gt;&lt;br /&gt;      &amp;lt;artifactId&amp;gt;junit&amp;lt;/artifactId&amp;gt;&lt;br /&gt;      &amp;lt;version&amp;gt;3.8.1&amp;lt;/version&amp;gt;&lt;br /&gt;      &amp;lt;scope&amp;gt;test&amp;lt;/scope&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;  &amp;lt;/dependencies&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;And that's it! Drop the above into a normal maven project structure (remember, TimestampMojo.groovy goes into src/main/groovy). Compile and install and you're ready to go.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Using the plugin is as simple as using any other. For this kind of thing I would recommend putting it into your ancestral pom.xml that all of your other projects extend but that's a personal choice. In any case, usage looks like this:&lt;br /&gt;&lt;pre&gt;    &amp;lt;plugin&amp;gt;&lt;br /&gt;      &amp;lt;groupId&amp;gt;myco.util.mvn.timestamp-plugin&amp;lt;/groupId&amp;gt;&lt;br /&gt;      &amp;lt;artifactId&amp;gt;myco-timestamp-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;      &amp;lt;executions&amp;gt;&lt;br /&gt;        &amp;lt;execution&amp;gt;&lt;br /&gt;          &amp;lt;goals&amp;gt;&lt;br /&gt;            &amp;lt;goal&amp;gt;execute&amp;lt;/goal&amp;gt;&lt;br /&gt;          &amp;lt;/goals&amp;gt;&lt;br /&gt;        &amp;lt;/execution&amp;gt;&lt;br /&gt;      &amp;lt;/executions&amp;gt;&lt;br /&gt;    &amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;The &lt;span style="font-style: italic;"&gt;execute &lt;/span&gt;goal is tied to the &lt;span style="font-style: italic;"&gt;generate-resources&lt;/span&gt; lifecycle phase so that the property will be available to you prior to copying of resources. This means that any ${timestamp} in any filtered resource will be replaced by the current timestamp. If you don't remember how to setup filtered resources, well, neither do I so here it is again:&lt;br /&gt;&lt;pre&gt;  &amp;lt;resources&amp;gt;&lt;br /&gt;    &amp;lt;resource&amp;gt;&lt;br /&gt;      &amp;lt;directory&amp;gt;${basedir}/src/main/java&amp;lt;/directory&amp;gt;&lt;br /&gt;      &amp;lt;filtering&amp;gt;true&amp;lt;/filtering&amp;gt;&lt;br /&gt;      &amp;lt;includes&amp;gt;&lt;br /&gt;        &amp;lt;include&amp;gt;**/*.xml&amp;lt;/include&amp;gt;&lt;br /&gt;      &amp;lt;/includes&amp;gt;&lt;br /&gt;    &amp;lt;/resource&amp;gt;&lt;br /&gt;  &amp;lt;/resources&amp;gt;&lt;br /&gt;&lt;/pre&gt;And if you want to filter your jsp's before creating the war file (I like to have a ${timestamp} in my footer.jsp...) then you want something like this:&lt;br /&gt;&lt;pre&gt;    &amp;lt;plugin&amp;gt;&lt;br /&gt;      &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;      &amp;lt;artifactId&amp;gt;maven-war-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;      &amp;lt;configuration&amp;gt;&lt;br /&gt;        &amp;lt;webResources&amp;gt;&lt;br /&gt;          &amp;lt;resource&amp;gt;&lt;br /&gt;            &amp;lt;directory&amp;gt;&lt;br /&gt;              ${basedir}/src/main/webapp&lt;br /&gt;            &amp;lt;/directory&amp;gt;&lt;br /&gt;            &amp;lt;filtering&amp;gt;true&amp;lt;/filtering&amp;gt;&lt;br /&gt;            &amp;lt;includes&amp;gt;&lt;br /&gt;              &amp;lt;include&amp;gt;**/*.xml&amp;lt;/include&amp;gt;&lt;br /&gt;              &amp;lt;include&amp;gt;**/footer.jsp&amp;lt;/include&amp;gt;&lt;br /&gt;            &amp;lt;/includes&amp;gt;&lt;br /&gt;          &amp;lt;/resource&amp;gt;&lt;br /&gt;        &amp;lt;/webResources&amp;gt;&lt;br /&gt;      &amp;lt;/configuration&amp;gt;&lt;br /&gt;    &amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;Remember these bits are in the pom of the application using the timestamp plugin, not in the pom of the plugin itself.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-8931925855743767795?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/8931925855743767795/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=8931925855743767795' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/8931925855743767795'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/8931925855743767795'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/04/from-maven-to-mvn-part-7-groovier.html' title='From maven to mvn : Part 7 -- A Groovier Timestamp'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-3098619099756362370</id><published>2007-04-10T22:22:00.000-04:00</published><updated>2007-04-12T18:22:54.338-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>From maven to mvn : Part 6 -- Classloader Maddness &amp; A Custom Plugin</title><content type='html'>Wow. I had no idea it has been a month since my last post. I would plead business but that's cheating. After all, we're all busy...&lt;br /&gt;&lt;br /&gt;Tonight's post is going to be a bit eclectic &amp; is the result of considerable (mythical) man-hours of work. Much of that time was spent figuring out why things were broken rather than actually fixing them. The fixing part was really quite short though it was interrupted from time to time by more figuring parts.&lt;br /&gt;&lt;br /&gt;The Setup:&lt;br /&gt;&lt;br /&gt;I have a utility that I wrote quite some time ago that implements the notion of &lt;a href="http://www.martinfowler.com/articles/evodb.html"&gt;Evolutionary Database Design&lt;/a&gt;. It's an internal tool that I may or may not be able to publish someday but that's not really relevant right now. What is relevant is that it is simply a jar that relies on a number of dependencies, most notably Spring. It runs happily from the command line, maven 1 and Eclipse.&lt;br /&gt;&lt;br /&gt;The Plan:&lt;br /&gt;&lt;br /&gt;Create a maven 2 plugin around my utility by firing it's Main class with the appropriate parameters.&lt;br /&gt;&lt;br /&gt;The Pain:&lt;br /&gt;&lt;br /&gt;Classloaders. Man I &lt;span style="font-weight: bold;"&gt;hate &lt;/span&gt;classloaders.&lt;br /&gt;&lt;br /&gt;It's a long and unpleasant tale and I won't bore you with the details. You can read some about it &lt;a href="http://www.mail-archive.com/users@maven.apache.org/msg64037.html"&gt;here&lt;/a&gt; though the rafb posts are long gone.&lt;br /&gt;&lt;br /&gt;The short version is that maven creates a custom classloader for itself (a reasonable thing to do) and puts a pom's dependencies into it. In my case, one of those is Spring and when the context begins loading classes it gets tripped up in classloader evilness and begins to belive &lt;span style="font-style: italic;"&gt;org.springframework.beans.factory.xml. SimplePropertyNamespaceHandler] does&lt;/span&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;not implement the NamespaceHandler interface&lt;/span&gt; which is patently not true.&lt;br /&gt;&lt;br /&gt;The Solution:&lt;br /&gt;&lt;br /&gt;I can't explain exactly why but it took me *hours* to figure out that the problem was classloader related and *more* hours to work out the solution. I can only say that it was most likely a Monday (and possibly a Tuesday) and hope that you will forgive me for being somewhat dense.&lt;br /&gt;&lt;br /&gt;What you really want to know, though, is how I solved the problem. It is my expectation that someone else may very well run into the same thing so here we go...&lt;br /&gt;&lt;br /&gt;First we have my mojo:&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;/**&lt;br /&gt; * Invoke EDD to generate SQL from XML.&lt;br /&gt; *&lt;br /&gt; * @goal generateSql&lt;br /&gt; * @phase compile&lt;br /&gt; * @requiresDependencyResolution runtime&lt;br /&gt; */&lt;br /&gt;public class GenerateSqlMojo extends AbstractMojo&lt;br /&gt;{&lt;br /&gt;  /**&lt;br /&gt;   * Where to get the *.xml/*.sql files to be processed.&lt;br /&gt;   *&lt;br /&gt;   *&lt;br /&gt;   * @parameter expression="${project.build.directory}/classes"&lt;br /&gt;   * @required&lt;br /&gt;   */&lt;br /&gt;  private String inputDirectory;&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * Prefix for the files created by EDD.&lt;br /&gt;   *&lt;br /&gt;   * @parameter expression="${project.build.directory}/classes/${project.artifactId}"&lt;br /&gt;   * @required&lt;br /&gt;   */&lt;br /&gt;  private String outputPrefix;&lt;br /&gt;&lt;br /&gt;  /**&lt;br /&gt;   * The classpath elements of the project. We need this so that we can&lt;br /&gt;   * get the JDBC driver for the project.&lt;br /&gt;   *&lt;br /&gt;   * @parameter expression="${project.runtimeClasspathElements}"&lt;br /&gt;   * @required&lt;br /&gt;   * @readonly&lt;br /&gt;   */&lt;br /&gt;  private List classpathElements;&lt;br /&gt;&lt;br /&gt;  public void execute() throws MojoExecutionException&lt;br /&gt;  {&lt;br /&gt;    try&lt;br /&gt;    {&lt;br /&gt;      new File(outputPrefix).getParentFile().mkdirs();&lt;br /&gt;      new UtilityExecutor().execute(inputDirectory,&lt;br /&gt;                                    outputPrefix, classpathElements);&lt;br /&gt;    }&lt;br /&gt;    catch (final Exception cause)&lt;br /&gt;    {&lt;br /&gt;      throw new MojoExecutionException("Failed to invoke EDD", cause);&lt;br /&gt;    }&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;OK... Not very exciting... In particular we see nothing about classpaths or classloaders other than the &lt;span style="font-style: italic;"&gt;classpathElements &lt;/span&gt;parameter/attribute. All of that evilness is hidden in my UtilityExecutor. I'll tell you now: It could be done better! A more robust / reusable solution would extract the classloader rot out of UtilityExecutor and into a more generic helper class. That's a great idea... I'll do it later.&lt;br /&gt;&lt;br /&gt;So, let's see UtilityExecutor:&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;public class UtilityExecutor&lt;br /&gt;{&lt;br /&gt;  public UtilityExecutor()&lt;br /&gt;  {&lt;br /&gt;    super();&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public void execute(final String inputDirectory, final String outputPrefix,&lt;br /&gt;                      final Boolean saveOnlyMode,&lt;br /&gt;                      final List&amp;lt;String&amp;gt; classpathElements)&lt;br /&gt;                      throws ...&lt;br /&gt;  {&lt;br /&gt;    final URL[] urls = buildUrls(classpathElements);&lt;br /&gt;    final URLClassLoader cl =&lt;br /&gt;      new URLClassLoader(urls, ClassLoader.getSystemClassLoader());&lt;br /&gt;    Thread.currentThread().setContextClassLoader(cl);&lt;br /&gt;&lt;br /&gt;    final Class clazz = cl.loadClass(getClass().getName());&lt;br /&gt;    final Constructor ctor =&lt;br /&gt;          clazz.getConstructor(&lt;br /&gt;              new Class[] { String.class, String.class, Boolean.class });&lt;br /&gt;    ctor.newInstance(new Object[] { inputDirectory, outputPrefix, saveOnlyMode });&lt;br /&gt;  }&lt;br /&gt;&lt;br /&gt;  public UtilityExecutor(final String inputDirectory,&lt;br /&gt;                         final String outputDirectory,&lt;br /&gt;                         final Boolean saveOnlyMode)&lt;br /&gt;  {&lt;br /&gt;    final Main main = new Main();&lt;br /&gt;    main.setSaveOnlyMode(saveOnlyMode.booleanValue());&lt;br /&gt;    main.setSaveTarget(outputDirectory);&lt;br /&gt;    main.execute(new String[] { inputDirectory });&lt;br /&gt;  }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So let's break this down...&lt;br /&gt;&lt;br /&gt;1) GenerateSqlMojo fires &lt;span style="font-style: italic;"&gt;new UtilityExecutor().execute(...)&lt;/span&gt; (a convenience method which simply delegates to the &lt;span style="font-style: italic;"&gt;execute(...)&lt;/span&gt; method shown).&lt;br /&gt;&lt;br /&gt;2) &lt;span style="font-style: italic;"&gt;execute(...)&lt;/span&gt; builds a &lt;span style="font-style: italic;"&gt;URL[]&lt;/span&gt; of for our custom classloader (more on that in a minute).&lt;br /&gt;&lt;br /&gt;3) The custom classloader is created.&lt;br /&gt;&lt;br /&gt;Now it gets a bit weird...&lt;br /&gt;&lt;br /&gt;4) We ask the custom classloader to load the Class for ourselves. This causes the class to be loaded from the custom classloader against whatever classpath we built in #2 then&lt;br /&gt;&lt;br /&gt;5) fetch the "do some work" constructor for ourself and&lt;br /&gt;&lt;br /&gt;6) finally invoke the constructor to launch the utility. (I chose to do the "work" in the constructor to reduce the need for reflection BTW.)&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Now a note about the &lt;span style="font-style: italic;"&gt;buildUrls(...)&lt;/span&gt; method. There isn't anything really magic about what we're doing here. Simply take the URLs from our current classloader (the classloader that loaded the mojo) and combine them with the classpath elements provided from our execution environment. These classpath elements are the dependencies of the pom in which the plugin is being invoked. In my case I need this because the JDBC driver to be used is specific to the client of the plugin rather than to the plugin itself. I also need these because the utility expects to find some of it's runtime configuration in a properties file in &lt;span style="font-style: italic;"&gt;${basedir}/target/classes&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;Pay particular attention to the code in bold... If your classpath element is a directory you need to append the trailing slash or your resources there (e.g. -- ${basedir}/target/classes) will not be found. This was something of a painful and frustrating lesson...&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;  private URL[] buildUrls(final List&lt;string&gt; classpathElements) throws MalformedURLException&lt;br /&gt;  {&lt;br /&gt;    final URL[] mojoUrls = ((URLClassLoader) getClass().getClassLoader()).getURLs();&lt;br /&gt;    final URL[] urls = new URL[mojoUrls.length + classpathElements.size()];&lt;br /&gt;    int ndx = 0;&lt;br /&gt;    for (final URL url : mojoUrls) urls[ndx++] = url;&lt;br /&gt;    for (String cpe : classpathElements)&lt;br /&gt;    {&lt;br /&gt;      final File file = new File(cpe);&lt;br /&gt;      &lt;span style="color:red; font-weight:bold;"&gt;if (file.isDirectory())cpe += "/";&lt;/span&gt;&lt;br /&gt;      urls[ndx++] = new URL("file:/" + cpe);&lt;br /&gt;    }&lt;br /&gt;    return urls;&lt;br /&gt;  }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;BTW, I also have an UpdateSchemaMojo that is basically the same as GenerateSqlMojo but invokes a different convenience method on UtilityExecutor.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;A Custom Lifecycle:&lt;br /&gt;&lt;br /&gt;Now the point of my plugin is to create SQL from XML and optionally apply it to your schema. Great. That doesn't really fit with the typical maven build lifecycle. Never one to try the shallow end of the pool first I decided to jump in the deep end (with lead weights) and create a custom package type for my plugin. It turns out not to be too difficult.&lt;br /&gt;&lt;br /&gt;To create a custom lifecycle you only need to create a plexus components.xml file. In my case it looks like this:&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;&amp;lt;component-set&amp;gt;&lt;br /&gt;  &amp;lt;components&amp;gt;&lt;br /&gt;    &amp;lt;component&amp;gt;&lt;br /&gt;      &amp;lt;role&amp;gt;&lt;br /&gt;        org.apache.maven.lifecycle.mapping.LifecycleMapping&lt;br /&gt;      &amp;lt;/role&amp;gt;&lt;br /&gt;      &amp;lt;role-hint&amp;gt;sa&amp;lt;/role-hint&amp;gt;&lt;br /&gt;      &amp;lt;implementation&amp;gt;&lt;br /&gt;        org.apache.maven.lifecycle.mapping.DefaultLifecycleMapping&lt;br /&gt;      &amp;lt;/implementation&amp;gt;&lt;br /&gt;      &amp;lt;configuration&amp;gt;&lt;br /&gt;        &amp;lt;phases&amp;gt;&lt;br /&gt;          &amp;lt;process-resources&amp;gt;&lt;br /&gt;            org.apache.maven.plugins:maven-resources-plugin:resources&lt;br /&gt;          &amp;lt;/process-resources&amp;gt;&lt;br /&gt;          &amp;lt;compile&amp;gt;&lt;br /&gt;            myco.util:myco-util-edd-plugin:generateSql&lt;br /&gt;          &amp;lt;/compile&amp;gt;&lt;br /&gt;          &amp;lt;package&amp;gt;&lt;br /&gt;            org.apache.maven.plugins:maven-jar-plugin:jar&lt;br /&gt;          &amp;lt;/package&amp;gt;&lt;br /&gt;          &amp;lt;install&amp;gt;&lt;br /&gt;            org.apache.maven.plugins:maven-install-plugin:install,&lt;br /&gt;            myco.util:myco-util-edd-plugin:updateSchema&lt;br /&gt;          &amp;lt;/install&amp;gt;&lt;br /&gt;          &amp;lt;deploy&amp;gt;&lt;br /&gt;            org.apache.maven.plugins:maven-deploy-plugin:deploy&lt;br /&gt;          &amp;lt;/deploy&amp;gt;&lt;br /&gt;        &amp;lt;/phases&amp;gt;&lt;br /&gt;      &amp;lt;/configuration&amp;gt;&lt;br /&gt;    &amp;lt;/component&amp;gt;&lt;br /&gt;    &amp;lt;component&amp;gt;&lt;br /&gt;      &amp;lt;role&amp;gt;&lt;br /&gt;        org.apache.maven.artifact.handler.ArtifactHandler&lt;br /&gt;      &amp;lt;/role&amp;gt;&lt;br /&gt;      &amp;lt;role-hint&amp;gt;sa&amp;lt;/role-hint&amp;gt;&lt;br /&gt;      &amp;lt;implementation&amp;gt;&lt;br /&gt;        org.apache.maven.artifact.handler.DefaultArtifactHandler&lt;br /&gt;      &amp;lt;/implementation&amp;gt;&lt;br /&gt;      &amp;lt;configuration&amp;gt;&lt;br /&gt;        &amp;lt;type&amp;gt;sa&amp;lt;/type&amp;gt;&lt;br /&gt;        &amp;lt;extension&amp;gt;zip&amp;lt;/extension&amp;gt;&lt;br /&gt;        &amp;lt;packaging&amp;gt;sa&amp;lt;/packaging&amp;gt;&lt;br /&gt;      &amp;lt;/configuration&amp;gt;&lt;br /&gt;    &amp;lt;/component&amp;gt;&lt;br /&gt;  &amp;lt;/components&amp;gt;&lt;br /&gt;&amp;lt;/component-set&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It isn't really that bad... All I'm doing here is defining the stages of the lifecycle I'm interested in and the plugins to fire at each one. Notice that in install I specify both the standard install plugin as well as my own updateSchema plugin. This means that after my sa artifact is published to the local repo updateSchema will be fired to (optionally) update my database for me. (In case you're wondering, UpdateSchemaMojo has a Boolean parameter that will enable/disable the database update.)&lt;br /&gt;&lt;br /&gt;The &lt;span style="font-style: italic;"&gt;type, extension &lt;/span&gt;and &lt;span style="font-style: italic;"&gt;packaging tags&lt;/span&gt; are there to tell maven about my custom packaging type and what to call it when it is installed. We will see what this means in just a bit.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;The only thing left is the pom.xml for my plugin. There is nothing magic here either:&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;&amp;lt;project xmlns="http://maven.apache.org/POM/4.0.0"&lt;br /&gt;            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0&lt;br /&gt;            http://maven.apache.org/maven-v4_0_0.xsd"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;version&amp;gt;1.4-SNAPSHOT&amp;lt;/version&amp;gt;&lt;br /&gt;  &amp;lt;groupId&amp;gt;myco.util&amp;lt;/groupId&amp;gt;&lt;br /&gt;  &amp;lt;artifactId&amp;gt;myco-util-edd-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;  &amp;lt;packaging&amp;gt;maven-plugin&amp;lt;/packaging&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;name&amp;gt;EDD Maven Mojo&amp;lt;/name&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;dependencies&amp;gt;&lt;br /&gt;    ... As necessary&lt;br /&gt;  &amp;lt;/dependencies&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Using The Plugin:&lt;br /&gt;&lt;br /&gt;OK, so now we know everything there is to know about the plugin. All that's left is to use it. Since we're introducing a new packaging type there is a detail we need to be aware of.&lt;br /&gt;&lt;br /&gt;The pom starts out as usual:&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;&amp;lt;project xmlns="http://maven.apache.org/POM/4.0.0"&lt;br /&gt;            xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;            xsi:schemaLocation="http://maven.apache.org/POM/4.0.0&lt;br /&gt;            http://maven.apache.org/maven-v4_0_0.xsd"&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;parent&amp;gt;&lt;br /&gt;    ... as necessary&lt;br /&gt;  &amp;lt;/parent&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;name&amp;gt;contacts database&amp;lt;/name&amp;gt;&lt;br /&gt;  &amp;lt;artifactId&amp;gt;contacts-database&amp;lt;/artifactId&amp;gt;&lt;/code&gt;&lt;/span&gt;&lt;br /&gt;&lt;/pre&gt;then we specify our custom packaging type:&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;  &amp;lt;packaging&amp;gt;sa&amp;lt;/packaging&amp;gt;&lt;br /&gt;&lt;/pre&gt;and then define the plugin. There are two things to note here: (a) the extensions tag tells maven that the plugin extends the standard behavior and (b) the enableEDD property that enables/disables the UpdateSchemaMojo.&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;  &amp;lt;build&amp;gt;&lt;br /&gt;    &amp;lt;plugins&amp;gt;&lt;br /&gt;     &amp;lt;plugin&amp;gt;&lt;br /&gt;         &amp;lt;groupId&amp;gt;myco.util&amp;lt;/groupId&amp;gt;&lt;br /&gt;         &amp;lt;artifactId&amp;gt;myco-util-edd-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;         &amp;lt;version&amp;gt;1.4-SNAPSHOT&amp;lt;/version&amp;gt;&lt;br /&gt;         &lt;span style="font-weight: bold;"&gt;&amp;lt;extensions&amp;gt;true&amp;lt;/extensions&amp;gt;&lt;/span&gt;&lt;br /&gt;         &amp;lt;configuration&amp;gt;&lt;br /&gt;             &amp;lt;enableEDD&amp;gt;${enableEDD}&amp;lt;/enableEDD&amp;gt;&lt;br /&gt;         &amp;lt;/configuration&amp;gt;&lt;br /&gt;      &amp;lt;/plugin&amp;gt;&lt;br /&gt;    &amp;lt;/plugins&amp;gt&lt;br /&gt;&lt;/pre&gt;In my case the plugin needs some runtime configuration from a property file so I use a resource tag to be sure that gets copied into ${basedir}/target/classes. I also need the Oracle JDBC driver so that the utility can connect to the database to update the schema.&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;    &amp;lt;resources&amp;gt;&lt;br /&gt;      ... As necessary&lt;br /&gt;    &amp;lt;/resources&amp;gt;&lt;br /&gt;  &amp;lt;/build&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;dependencies&amp;gt;&lt;br /&gt;    &amp;lt;dependency&amp;gt;&lt;br /&gt;      &amp;lt;!--&lt;br /&gt;        We have an Oracle database so we need to have&lt;br /&gt;        this in our classpath to pickup the JDBC driver.&lt;br /&gt;        --&amp;gt;&lt;br /&gt;      &amp;lt;groupId&amp;gt;oracle&amp;lt;/groupId&amp;gt;&lt;br /&gt;      &amp;lt;artifactId&amp;gt;ojdbc14&amp;lt;/artifactId&amp;gt;&lt;br /&gt;      &amp;lt;version&amp;gt;10.2.0.2&amp;lt;/version&amp;gt;&lt;br /&gt;    &amp;lt;/dependency&amp;gt;&lt;br /&gt;  &amp;lt;/dependencies&amp;gt;&lt;br /&gt;&lt;/pre&gt;Modifying the schema with every build is probably not a good idea so we disable the update by default and invoke '&lt;span style="font-style: italic;"&gt;mvn -DenableDD=true install&lt;/span&gt;' on demand. You could also control this with your ~/.m2/settings.xml and profiles.&lt;br /&gt;&lt;pre style="font-size:85%;"&gt;  &amp;lt;properties&amp;gt;&lt;br /&gt;    &amp;lt;enableEDD&amp;gt;false&amp;lt;/enableEDD&amp;gt;&lt;br /&gt;  &amp;lt;/properties&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Postfix:&lt;br /&gt;&lt;br /&gt;That's it my friend. It is a bit of a long post (hopefully making up for the previous one's shortness) but there was rather a lot to cover. Let's review what I've got here:&lt;br /&gt;&lt;br /&gt;A) How to create a Java Mojo implementing your custom Maven 2 plugin functionality.&lt;br /&gt;&lt;br /&gt;B) How to create a custom classloader to deal with oddball classloader issues.&lt;br /&gt;&lt;br /&gt;C) A clever, IMO, way to isolate the classloader issues and minimize reflection. (Though, honestly, UtilityExecutor could be a bit more clever and less hackish.)&lt;br /&gt;&lt;br /&gt;D) How to specify a custom package type with it's own custom lifecycle.&lt;br /&gt;&lt;br /&gt;E) How to use your shiny new plugin and it's custom packaging type.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Yes, it took me a bit of time to get everything working. That's one of the problems with jumping into the deep end of the pool when you're first learning something new. On the other hand, I'm reasonably confident that I've faced many (hopefully most) of the odd edge-cases when creating plugins. With any luck this will be a true statement and I can lean on these experiences when I create my next, and hopefully simpler, plugin.&lt;br /&gt;&lt;br /&gt;As always, thanks for reading. Feel free to post feedback &amp;amp; questions. Peace ya'll.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-3098619099756362370?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/3098619099756362370/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=3098619099756362370' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3098619099756362370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3098619099756362370'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/04/from-maven-to-mvn-part-5.html' title='From maven to mvn : Part 6 -- Classloader Maddness &amp; A Custom Plugin'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-8696570167549620306</id><published>2007-03-05T15:20:00.000-05:00</published><updated>2007-04-12T17:43:09.872-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>From maven to mvn : Part 5 -- A Groovy Timestamp</title><content type='html'>Good afternoon and happy Monday! I hope you had a great weekend, I know I did. I have to apologize for today's entry up front. It isn't about writing a plugin as I had hoped it would be. It's also somewhat brief. Well, it's Monday and there have been more than the usual number of distractions...&lt;br /&gt;&lt;br /&gt;I've been bothered about abusing the &lt;a href="http://commons.ucalgary.ca/projects/maven-buildnumber-plugin/index.html"&gt;maven-buildnumber-plugin&lt;/a&gt; to set a timestamp property used in ${foo} replacement. I happened to be doing some reading over the weekend and came across the &lt;a href="http://mojo.codehaus.org/groovy-maven-plugin/index.html"&gt;groovy-maven-plugin&lt;/a&gt;. Which lets you run a random bit of &lt;a href="http://groovy.codehaus.org/"&gt;groovy&lt;/a&gt; script during your build process. Now I still intend to explore the exciting world of creating my own maven plugins but I think this is a good place to start. In fact, when I *do* write my own plugins in the future they will very likely be written in groovy using this technique.&lt;br /&gt;&lt;br /&gt;So, today's brief entry will (a) drop the buildnumber plugin and (b) use the groovy-maven plugin to replace its functionality.&lt;br /&gt;&lt;br /&gt;Begin by adding repository entries to your pom:&lt;br /&gt;&lt;pre&gt;&amp;lt;repositories&amp;gt;&lt;br /&gt;  &amp;lt;repository&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;Codehaus Snapshots&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;url&amp;gt;http://snapshots.repository.codehaus.org/&amp;lt;/url&amp;gt;&lt;br /&gt;  &amp;lt;/repository&amp;gt;&lt;br /&gt;&amp;lt;/repositories&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;pluginRepositories&amp;gt;&lt;br /&gt;  &amp;lt;pluginRepository&amp;gt;&lt;br /&gt;      &amp;lt;id&amp;gt;Codehaus Snapshots&amp;lt;/id&amp;gt;&lt;br /&gt;      &amp;lt;url&amp;gt;http://snapshots.repository.codehaus.org/&amp;lt;/url&amp;gt;&lt;br /&gt;  &amp;lt;/pluginRepository&amp;gt;&lt;br /&gt;&amp;lt;/pluginRepositories&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note: If you already have repositories declared just add these new entries, don't blow away what you've got.&lt;br /&gt;&lt;br /&gt;Next, declare the plugin and some script for it to execute:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;plugin&amp;gt;&lt;br /&gt;  &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;&lt;br /&gt;  &amp;lt;artifactId&amp;gt;groovy-maven-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;  &amp;lt;executions&amp;gt;&lt;br /&gt;      &amp;lt;execution&amp;gt;&lt;br /&gt;          &amp;lt;phase&amp;gt;generate-resources&amp;lt;/phase&amp;gt;&lt;br /&gt;          &amp;lt;goals&amp;gt;&lt;br /&gt;              &amp;lt;goal&amp;gt;execute&amp;lt;/goal&amp;gt;&lt;br /&gt;          &amp;lt;/goals&amp;gt;&lt;br /&gt;          &amp;lt;configuration&amp;gt;&lt;br /&gt;              &amp;lt;source&amp;gt;&lt;br /&gt;                  &amp;lt;body&amp;gt;&lt;br /&gt;                      import java.util.Date&lt;br /&gt;                      import java.text.MessageFormat&lt;br /&gt;                      def timestamp = MessageFormat.format("{0,date,yyyy-MM-dd HH:mm:ss}", new Date())&lt;br /&gt;                      System.setProperty("timestamp", timestamp)&lt;br /&gt;                  &amp;lt;/body&amp;gt;&lt;br /&gt;              &amp;lt;/source&amp;gt;&lt;br /&gt;          &amp;lt;/configuration&amp;gt;&lt;br /&gt;      &amp;lt;/execution&amp;gt;&lt;br /&gt;  &amp;lt;/executions&amp;gt;&lt;br /&gt;&amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it! Now, during the generate-resources phase of the &lt;a href="http://maven.apache.org/guides/introduction/introduction-to-the-lifecycle.html"&gt;maven build lifecycle&lt;/a&gt; groovy will execute the script to set the system property &lt;span style="font-style: italic;"&gt;timestamp&lt;/span&gt; to the current time.&lt;br /&gt;&lt;br /&gt;That's it for today. I have to run off to take care of some other things. I promise you a better Part 6.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-8696570167549620306?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/8696570167549620306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=8696570167549620306' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/8696570167549620306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/8696570167549620306'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/03/from-maven-to-mvn-part-5.html' title='From maven to mvn : Part 5 -- A Groovy Timestamp'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-6712783801156615302</id><published>2007-03-01T14:14:00.000-05:00</published><updated>2007-04-12T17:42:14.986-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>From maven to mvn : Part 4 -- Transitive Dependencies</title><content type='html'>So I took a break from maven yesterday to do some performance investigation using &lt;a href="http://jamonapi.sourceforge.net/"&gt;JAMon&lt;/a&gt; and &lt;a href="http://eclipse.org/aspectj"&gt;AspectJ&lt;/a&gt;. We don't need no fancy-pants monitoring tools. Just a bit of advice, a Monitor or two and an editor that can handle &lt;span style="font-style: italic;"&gt;huge&lt;/span&gt; log files.&lt;br /&gt;&lt;br /&gt;But that's not what I'm here to talk about today.&lt;br /&gt;&lt;br /&gt;Today I'm continuing the m1-to-m2 migration and turning my attention to my nasty pile of dependencies. With m2's way cool transitive dependency management I hope to drop my 300 lines of dependencies in pom.xml to something a bit more manageable.&lt;br /&gt;&lt;br /&gt;Now you probably don't want me to dump the original 300 lines here so I can't really show you what I'm starting with. However, the high level framework bits include: spring, hibernate, acegi, aspectj and misc Jakarta commons. If you've worked with any of these you know that each one introduces still other dependencies you have to deal with.&lt;br /&gt;&lt;br /&gt;To get started I simply commented out all of the dependencies we didn't produce in-house and started compiling. AspectJ was the first thing mvn complained about so now I have to go figure out how to add it correctly... I mentioned this &lt;a href="http://maven.apache.org/guides/getting-started/index.html#How%20do%20I%20use%20external%20dependencies?"&gt;link&lt;/a&gt; earlier (Part 2 I believe). I'm not a big fan of repeating someone else's words so please take a moment to read the link. I'll wait.&lt;br /&gt;&lt;br /&gt;Did you see this bit: "&lt;span style="font-style: italic;"&gt;site:www.ibiblio.org maven2 log4j&lt;/span&gt;" If you didn't, go back and look for it. That is the secret to finding your m2 dependencies. I will caution you, however, that maven-metadata.xml may not always list the most recent available versions. It's worth doing a directory listing to see what is there.&lt;br /&gt;&lt;br /&gt;For instance, "&lt;span style="font-style: italic;"&gt;site:www.ibiblio.org maven2 aspectj&lt;/span&gt;" gets me to a &lt;a href="http://mirrors.ibiblio.org/pub/mirrors/maven2/aspectj/aspectjrt/maven-metadata.xml"&gt;maven-metadata.xml&lt;/a&gt; that does not list version 1.5.2a which, as it turns out, is the one I want. The &lt;a href="http://mirrors.ibiblio.org/pub/mirrors/maven2/aspectj/aspectjrt/"&gt;directory listing&lt;/a&gt;, however, shows me a &lt;a href="http://mirrors.ibiblio.org/pub/mirrors/maven2/aspectj/aspectjrt/1.5.2a/"&gt;1.5.2a subdirectory&lt;/a&gt; which has exactly what I need.&lt;br /&gt;&lt;br /&gt;Also in maven-metadata.xml you will find the appropriate &lt;span style="font-style: italic;"&gt;groupId&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;artifactId&lt;/span&gt; values you need for your dependency. Armed with all of this information I can now add the appropriate pom.xml entry:&lt;br /&gt;&lt;pre&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt; &amp;lt;groupId&amp;gt;aspectj&amp;lt;/groupId&amp;gt;&lt;br /&gt; &amp;lt;artifactId&amp;gt;aspectjrt&amp;lt;/artifactId&amp;gt;&lt;br /&gt; &amp;lt;version&amp;gt;1.5.2a&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Good. That was easy. Now rinse &amp; repeat for my other dependencies and lets see where we end up...&lt;br /&gt;&lt;br /&gt;Well, that didn't last very long. My very next dependency is hibernate. My google search showed up beanlib (which is really cool BTW) and the old net.sf.hibernate stuff but not the 3.2.1.ga version I need. Fortunately, I know how the m1 repository is layed out and a bit about how the m2 repository is layed out. On a hunch I try a &lt;a href="http://mirrors.ibiblio.org/pub/mirrors/maven2/org/hibernate/hibernate/"&gt;reasonable URL&lt;/a&gt; and find &lt;a href="http://mirrors.ibiblio.org/pub/mirrors/maven2/org/hibernate/hibernate/3.2.1.ga/maven-metadata.xml"&gt;something useful&lt;/a&gt;. (I also noticed that 3.2.2.ga is available so I may consider an upgrade soon. But I digress...) Now I know the &lt;span style="font-style: italic;"&gt;groupId&lt;span style="font-style: italic;"&gt; &lt;/span&gt;&lt;/span&gt;and &lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;&lt;span style="font-style: italic;"&gt;artifactId&lt;/span&gt;&lt;/span&gt;&lt;/span&gt; values for my hibernate dependency so that gets added to my pom.xml&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Now&lt;/span&gt; rinse &amp; repeat. Occasionally a dependency shows up with a &lt;span style="font-style: italic;"&gt;groupId&lt;/span&gt; or &lt;span style="font-style: italic;"&gt;artifactId&lt;/span&gt; that I wasn't quite expecting from my m1 days but those things are easy to work with.&lt;br /&gt;&lt;br /&gt;javax.transaction introduced a new wrinkle. Apparently you have to go get that from Sun yourself. Thanks guys. &amp;lt;sigh/&amp;gt; Fortunately m2 tells us exactly what to do:&lt;br /&gt;&lt;pre&gt;Missing:&lt;br /&gt;----------&lt;br /&gt;1) javax.transaction:jta:jar:1.0.1B&lt;br /&gt;&lt;br /&gt;Try downloading the file manually from:&lt;br /&gt;http://java.sun.com/products/jta&lt;br /&gt;&lt;br /&gt;Then, install it using the command:&lt;br /&gt;mvn install:install-file -DgroupId=javax.transaction -DartifactId=jta \&lt;br /&gt;    -Dversion=1.0.1B -Dpackaging=jar -Dfile=/path/to/file&lt;br /&gt;&lt;br /&gt;Path to dependency:&lt;br /&gt;  1) com.myCompany.personal.jcej:contacts:war:1.0-SNAPSHOT&lt;br /&gt;  2) org.hibernate:hibernate:jar:3.2.1.ga&lt;br /&gt;  3) javax.transaction:jta:jar:1.0.1B&lt;br /&gt;&lt;/pre&gt;Oh what fun... However, if you run across other dependencies that are not available at the venerable ibiblio you can use this same technique to install them to your local repository. Certainly not ideal but tolerable until they do become available.&lt;br /&gt;&lt;br /&gt;It is worth pointing out that as you make your way through your dependencies you may be able to remove some you added previously. For instance, I added org.hibernate/hibernate because my build broke without it. Later I added org.hibernate/hibernate-annotations because the build broke on that. The later requires the former so I can remove my explicit org.hibernate/hibernate dependency and rely on the transitive dependency from org.hibernate/hibernate-annotations. It is a bit more work to backtrack this way but in the long run it will make for a much smaller and easier to maintain pom.xml. For simplicity it is worth &lt;span style="font-style: italic;"&gt;my&lt;/span&gt; time to inspect my dependencies' pom.&lt;br /&gt;&lt;br /&gt;While exploring that, because I know acegisecurity depends on spring, I discovered that some plugins specify their dependency versions via properties. Now that's a great idea! Because it centralizes dependency version management &lt;span style="font-style: italic;"&gt;and&lt;/span&gt; lets me override their required version by overriding the property.&lt;br /&gt;&lt;br /&gt;Thus I now have a properties section in my pom.xml something like this:&lt;br /&gt;&lt;pre&gt;&amp;lt;properties&amp;gt;&lt;br /&gt; &amp;lt;spring.version&amp;gt;2.0.1&amp;lt;/spring.version&amp;gt;&lt;br /&gt; ...&lt;br /&gt;&lt;/pre&gt;I just have to pay attention to my dependencies dependencies. But I was going to do that anyway. Using the properties is easy:&lt;br /&gt;&lt;pre&gt;&amp;lt;dependency&amp;gt;&lt;br /&gt; &amp;lt;groupId&amp;gt;org.springframework&amp;lt;/groupId&amp;gt;&lt;br /&gt; &amp;lt;artifactId&amp;gt;spring&amp;lt;/artifactId&amp;gt;&lt;br /&gt; &amp;lt;version&amp;gt;${spring.version}&amp;lt;/version&amp;gt;&lt;br /&gt;&amp;lt;/dependency&amp;gt;&lt;br /&gt;&lt;/pre&gt;[ time passes ]&lt;br /&gt;&lt;br /&gt;So after about an hour (which isn't bad considering the normal afternoon interruptions) I get to the point where things are successfully compiling. But my unit tests are failing!? Looking at the surefire reports clues me in to the thought that maybe some of my dependencies aren't consistent:&lt;br /&gt;&lt;pre&gt;    java.lang.NoSuchMethodError: org.springframework.util.ObjectUtils.nullSafeToString(Ljava/lang/Object;)Ljava/lang/String;&lt;br /&gt;&lt;/pre&gt;That don't look good...&lt;br /&gt;&lt;br /&gt;Build again with the tests disabled (&lt;span style="font-style: italic;"&gt;mvn -Dmaven.test.skip=true install&lt;/span&gt;) and see what we have in our war's lib directory. Yea... Well... Who put spring 1.2.8 jars in there? This is a case where transitive dependencies hurt rather than help.&lt;br /&gt;&lt;br /&gt;OK, roll the sleeves up and crank up the tunes. Lets go figure this one out. First, we need to tell maven to tell us more: &lt;span style="font-style: italic;"&gt;mvn -X -Dmaven.test.skip=true install&lt;/span&gt;. It's probably wise to capture that via nohup or script since it will be a tad verbose. I know that acegisecurity depends on spring and after a few minutes I realize that the &lt;span style="font-style: italic;"&gt;spring.version&lt;/span&gt; property I was so excited about didn't really help me. Sure, I set it to 2.0.1 in my own pom.xml but that didn't override the value in the acegisecurity dependency's pom. Drat. After a bit of &lt;a href="http://maven.apache.org/ref/2.0.4/maven-model/maven.html#class_dependency"&gt;reading&lt;/a&gt; I come to the conclusion that I need to exclude spring from acegisecurity's dependencies. Once I do this I can specify the spring versions I want in my own pom.xml and we should be in good shape.&lt;br /&gt;&lt;br /&gt;Better but still not good. We have the correct version of spring and that's good. But my hsqldb-based unit tests are failing. They're complaining about "&lt;span style="font-style: italic;"&gt;The database is already in use by another process&lt;/span&gt;" and I wasn't getting that before I started mucking about with transitive dependencies. This turned out to be similar to the spring problem in that struts-menu was pulling an old version of hsqldb.&lt;br /&gt;&lt;br /&gt;There seems to be some difference in the way transitive dependencies are excluded. In the acegisecurity-uses-spring case spring is specified in the &lt;span style="font-style: italic;"&gt;dependencyManagement&lt;/span&gt; section. When I add an &lt;span style="font-style: italic;"&gt;exclusion&lt;/span&gt; to my acegisecurity dependency declaration all works as expected. Namely, my spring dependency is used instead of acegi's. However, struts-menu declares its dependency on hsqldb in its &lt;span style="font-style: italic;"&gt;dependencies&lt;/span&gt;, not under &lt;span style="font-style: italic;"&gt;dependencyManagement&lt;/span&gt;. When I add an &lt;span style="font-style: italic;"&gt;exclusion&lt;/span&gt; to struts-menu not only does mvn not include the version of hsqldb that struts-menu wants but it does not include *any* version of hsqldb though it is clearly listed in my own &lt;span style="font-style: italic;"&gt;dependencies&lt;/span&gt;. Could be operator error... Just be cautious. (If you have any word on this please drop me a comment so I can quit pulling my hair out over it.)&lt;br /&gt;&lt;br /&gt;That's it for today. My m1-built war still has different dependencies than my m2-built war but I'm confident that those remaining dependencies can be solved by applying the steps above. It's raining and 5:00 and I would like to get home before dinner gets cold. I'll wrap this up in the morning and if I make any amazing discoveries I'll document 'em here for you. Otherwise you can safely assume that things went well.&lt;br /&gt;&lt;br /&gt;Part 5 coming soon. I'm not quite sure what that'll be but it might have something to do with creating my own plugin. Check back soon...&lt;br /&gt;&lt;br /&gt;(Oh, and if you're keeping score, my 300 lines of dependencies are now about 120 lines. Maybe less if I drop the odd comment here and there. I call that an improvement!)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-6712783801156615302?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/6712783801156615302/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=6712783801156615302' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/6712783801156615302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/6712783801156615302'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/03/from-maven-to-mvn-part-4.html' title='From maven to mvn : Part 4 -- Transitive Dependencies'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-7899175479567017054</id><published>2007-02-27T10:21:00.000-05:00</published><updated>2007-04-12T17:43:41.491-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>From maven to mvn : Part 3 -- Filtering Resources &amp; Timestamps</title><content type='html'>Good morning all!&lt;br /&gt;&lt;br /&gt;We left off yesterday with an application that will build and tests that will pass. We even have a war file sitting in our target directory. Before I try to deploy that, however, there are some loose ends I want to clean up.&lt;br /&gt;&lt;br /&gt;Our goals for Part 3 are:&lt;br /&gt;- @foo@ (or ${foo}) replacement&lt;br /&gt;- attempt to deploy the war&lt;br /&gt;&lt;br /&gt;M1 grew out of a set of ant build scripts. As such, filter-when-copying worked pretty much like you would expect from ant. In other words, any @foo@ text in a filter-when-copying resource would be replaced by the &lt;span style="font-style: italic;"&gt;foo&lt;/span&gt; property value. M2 uses the newer ${foo} syntax and that's probably a good thing.&lt;br /&gt;&lt;br /&gt;The first thing I want to do is identify the files I need to change:&lt;br /&gt;&lt;pre&gt;  grep -r '@.*@' src/ | grep -v /.svn | grep -v /images/&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It turns out that I'm only using a few properties. One no-brainer is the application version that comes from the pom. The others are application specific properties I need to deal with.&lt;br /&gt;&lt;br /&gt;So, @pom.artifactId@ should simply become ${pom.artifactId}. &lt;a href="http://maven.apache.org/guides/getting-started/index.html#How%20do%20I%20filter%20resource%20files?"&gt;Enabling filters&lt;/a&gt; is easy enough to do by adding &lt;span style="font-style: italic;"&gt;&amp;lt;filtering&amp;gt;true&amp;lt;/filtering&amp;gt;&lt;/span&gt; to the appropriate &lt;span style="font-style: italic;"&gt;resource&lt;/span&gt; tag(s).&lt;br /&gt;&lt;br /&gt;That gets me very close but not quite done... You see, I'm filtering /WEB-INF/geronimo-web.xml. The one that gets copied into target/test-classes (because of my pom's &lt;span style="font-style: italic;"&gt;testResources&lt;/span&gt; as discussed in Part 2) is fine. However, the one in target/contacts-1.0-SNAPSHOT (which is managed by the war plugin) is not filtered. Hrm...&lt;br /&gt;&lt;br /&gt;It turns out that the war plugin is responsible for managing that bit of the project. Well, that makes sense. So, to change it's behavior we need to configure the plugin:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;build&amp;gt;&lt;br /&gt;  ...&lt;br /&gt;  &amp;lt;plugin&amp;gt;&lt;br /&gt;    &amp;lt;groupId&amp;gt;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;    &amp;lt;artifactId&amp;gt;maven-war-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;    &amp;lt;configuration&amp;gt;&lt;br /&gt;        &amp;lt;webResources&amp;gt;&lt;br /&gt;          &amp;lt;resource&amp;gt;&lt;br /&gt;            &amp;lt;directory&amp;gt;${basedir}/src/main/webapp&amp;lt;/directory&amp;gt;&lt;br /&gt;            &amp;lt;filtering&amp;gt;true&amp;lt;/filtering&amp;gt;&lt;br /&gt;            &amp;lt;includes&amp;gt;&lt;br /&gt;              &amp;lt;include&amp;gt;**/*.xml&amp;lt;/include&amp;gt;&lt;br /&gt;            &amp;lt;/includes&amp;gt;&lt;br /&gt;          &amp;lt;/resource&amp;gt;&lt;br /&gt;        &amp;lt;/webResources&amp;gt;&lt;br /&gt;      &amp;lt;/configuration&amp;gt;&lt;br /&gt;  &amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;Now if you read the &lt;a href="http://maven.apache.org/plugins/maven-war-plugin/"&gt;plugin documentation&lt;/a&gt; about &lt;a href="http://maven.apache.org/plugins/maven-war-plugin/examples/adding-filtering-webresources.html"&gt;this topic&lt;/a&gt; you will probably reach the conclusion that I'm doing it wrong. That is probably the correct conclusion. What I &lt;span style="font-style: italic;"&gt;should&lt;/span&gt; do is create a &lt;span style="font-style: italic;"&gt;${basedir}/otherResources&lt;/span&gt; directory and put the to-be-filtered things there. Well, as with my dependency ick, I'm simply going to defer that to later. I still haven't quite decided for myself whether I like the idea of separating the resources from everything else anyway...&lt;br /&gt;&lt;br /&gt;So, now I have my ${pom.*} properties filtered but I still have my application-specific properties to contend with. In particular, I have a &lt;span style="font-style: italic;"&gt;myApp.timestamp&lt;/span&gt; property supplied by the venerable ant &lt;span style="font-style: italic;"&gt;&amp;lt;tstamp/&amp;gt;&lt;/span&gt; tag that I slam into footer.jsp to display the build time (very handy for our QA folks.) It turns out that getting the timestamp (oh so easy with m1) is a bit of a trick with m2. Thanks to some good folks on the &lt;a href="http://www.mail-archive.com/users@maven.apache.org/msg60432.html"&gt;forum&lt;/a&gt; for pointing out a clever use of the &lt;a href="http://commons.ucalgary.ca/projects/maven-buildnumber-plugin/howto.html"&gt;build number plugin&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;First -- tell maven where to find the plugin:&lt;br /&gt;&lt;pre&gt;&amp;lt;pluginRepositories&amp;gt;&lt;br /&gt;  &amp;lt;pluginRepository&amp;gt;&lt;br /&gt;    &amp;lt;id&amp;gt;tlc&amp;lt;/id&amp;gt;&lt;br /&gt;    &amp;lt;name&amp;gt;TLC Repository&amp;lt;/name&amp;gt;&lt;br /&gt;    &amp;lt;url&amp;gt;http://commons.ucalgary.ca/pub/m2&amp;lt;/url&amp;gt;&lt;br /&gt;  &amp;lt;/pluginRepository&amp;gt;&lt;br /&gt;&amp;lt;/pluginRepositories&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Second -- configure the plugin to provide a timestamp:&lt;br /&gt;&lt;pre&gt;&amp;lt;plugin&amp;gt;&lt;br /&gt;  &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;&lt;br /&gt;  &amp;lt;artifactId&amp;gt;maven-buildnumber-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;  &amp;lt;version&amp;gt;0.9.4&amp;lt;/version&amp;gt;&lt;br /&gt;  &amp;lt;configuration&amp;gt;&lt;br /&gt;    &amp;lt;format&amp;gt;{0,date,yyyy-MM-dd HH:mm:ss}&amp;lt;/format&amp;gt;&lt;br /&gt;    &amp;lt;items&amp;gt;&lt;br /&gt;      &amp;lt;item&amp;gt;timestamp&amp;lt;/item&amp;gt;&lt;br /&gt;    &amp;lt;/items&amp;gt;&lt;br /&gt;    &amp;lt;doCheck&amp;gt;false&amp;lt;/doCheck&amp;gt;&lt;br /&gt;    &amp;lt;doUpdate&amp;gt;false&amp;lt;/doUpdate&amp;gt;&lt;br /&gt;  &amp;lt;/configuration&amp;gt;&lt;br /&gt;  &amp;lt;executions&amp;gt;&lt;br /&gt;    &amp;lt;execution&amp;gt;&lt;br /&gt;      &amp;lt;phase&amp;gt;validate&amp;lt;/phase&amp;gt;&lt;br /&gt;      &amp;lt;goals&amp;gt;&lt;br /&gt;        &amp;lt;goal&amp;gt;create&amp;lt;/goal&amp;gt;&lt;br /&gt;      &amp;lt;/goals&amp;gt;&lt;br /&gt;    &amp;lt;/execution&amp;gt;&lt;br /&gt;  &amp;lt;/executions&amp;gt;&lt;br /&gt;&amp;lt;/plugin&amp;gt;&lt;br /&gt;&lt;/pre&gt;In my case I also have to add an &lt;span style="font-style: italic;"&gt;&amp;lt;include...&amp;gt;&lt;/span&gt; to my filtering since I want my timestamp in &lt;span style="font-style: italic;"&gt;footer.jsp. &lt;/span&gt;With all of that in place I now get the date/time in my footer.jsp via ${buildNumber}. It isn't nearly so easy or flexible as with m1 but right now I'm interested in results, not cleverness. Perhaps I'll find something better in the future. (Or maybe write my own timestamp plugin!)&lt;br /&gt;&lt;br /&gt;(BTW: If you have simple, static properties they are very easy to define in an external file or directly in pom.xml as described by the &lt;a href="http://maven.apache.org/guides/getting-started/index.html#How%20do%20I%20filter%20resource%20files?"&gt;link above&lt;/a&gt;.)&lt;br /&gt;&lt;br /&gt;OK, so now I'm filtering and building and testing and packaging so I &lt;span style="font-style: italic;"&gt;should&lt;/span&gt; have a deployable war. I'm not brave enough to try to deploy via m2 (perhaps another day) so I'll fire up my application server of choice and deploy the war manually. Nothing really to write about here. You probably don't care. I just thought you would like to know that it actually deploys correctly.&lt;br /&gt;&lt;br /&gt;You will have to excuse me now; there are some other, non-maven things I need to address this afternoon. (Yes, it is afternoon now. What began in the morning has bled over the international lunch line due to the usual distractions.) If my meeting schedule allows I'll pickup Part 4 tomorrow and try to do a bit of cleanup on my dependencies.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-7899175479567017054?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/7899175479567017054/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=7899175479567017054' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/7899175479567017054'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/7899175479567017054'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/02/from-maven-to-mvn-part-3.html' title='From maven to mvn : Part 3 -- Filtering Resources &amp; Timestamps'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-6218787200044538977</id><published>2007-02-26T13:56:00.000-05:00</published><updated>2007-04-12T17:37:42.941-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>From maven to mvn : Part 2 -- Java 5 &amp; AspectJ</title><content type='html'>In this morning's &lt;span style="font-style: italic;"&gt;Part 1&lt;/span&gt; I walked through last Thursday morning's activities that began my process of converting a simple project from maven 1.0.4 to 2.0.4. This afternoon (actually, after I come back from the meeting I'm about to go off to) I'll run through last Thursday afternoon's activities where I dealt with dependencies, AspectJ and other details to get my application's war file built.&lt;br /&gt;&lt;br /&gt;[ time passes ]&lt;br /&gt;&lt;br /&gt;OK, I'm back from my meeting and ready to dig into Part 2. My goals for Part 2 are carried over from Part 1:&lt;br /&gt;- compile the classes and aspects&lt;br /&gt;- build a war (not necessarily deployable)&lt;br /&gt;- test cases should execute and pass&lt;br /&gt;- @foo@ replacement is not necessary&lt;br /&gt;&lt;br /&gt;As you recall, Part 1 ended with build failures. I was intentionally vague about the details since your details will differ from mine. However, we have to solve the specific case so we will tackle my specific issues. Hopefully this will give you some insight into your own solution.&lt;br /&gt;&lt;br /&gt;We start with this noise:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;path/to/UserDelete.java:[26,5] annotations are not supported in -source 1.3&lt;br /&gt;(try -source 1.5 to enable annotations)&lt;br /&gt;@Override&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Great! I'm using the shiny new Java 5 stuff and maven thinks I'm stuck in dinosaur land. Well, clearly, a lot of people are using m2 to build Java 5 applications so this must be an easy thing to solve.&lt;br /&gt;&lt;br /&gt;As it turns out, the solution is &lt;a href="http://maven.apache.org/plugins/maven-compiler-plugin/examples/set-compiler-source-and-target.html"&gt;documented &lt;/a&gt;on the maven site quite clearly. Rather than paste all of that here I'll just point you there. Be sure you specify 1.5 (or whatever comes after that) and you'll be good to go.&lt;br /&gt;&lt;br /&gt;Now my compile is marginally better. No complaints about @Foo but lots of complaints because of missing dependencies.  Time to address that...&lt;br /&gt;&lt;br /&gt;Recall that I'm starting with a project that currently builds under m1. This means that (a) I already know what my dependencies are and (b) I have  a copy of them in ~/.maven/repository. By inspecting my previous project.xml (or the /WEB-INF/lib directory of the m1-built jar) I can quickly identify my dependencies.&lt;br /&gt;&lt;br /&gt;Again, the maven &lt;a href="http://maven.apache.org/guides/getting-started/index.html#How%20do%20I%20use%20external%20dependencies?"&gt;documentation&lt;/a&gt; comes to the rescue. Dependency management is at the heart of what maven does for you. Follow the link to get the word on defining &lt;span style="font-style: italic;"&gt;your&lt;/span&gt; dependencies.&lt;br /&gt;&lt;br /&gt;M2 does this really cool thing called &lt;span style="font-style: italic;"&gt;transitive dependency management&lt;/span&gt;. What that means is that if you depend on foo and foo depends on bar then you only need to define foo in your pom.xml. This is vastly different from (and much easier than) m1 where you had to define not only your own dependencies but your dependencies' dependencies as well.&lt;br /&gt;&lt;br /&gt;At this point in our series I am not going to be that clever. The project.xml from my m1-buildable project lists my entire dependency tree so I'm going to simply replicate that in my pom.xml. The result will be hideous but I'll get smarter later (possibly in Part 3 if I'm lucky).&lt;br /&gt;&lt;br /&gt;One thing you will notice is that you no longer need to worry about the &amp;lt;property.../&amp;gt; tags to indicate what is or is not in the final war. (You will notice this because if you copy the entire dependencies section from your project.xml to pom.xml you will get xml violations when you try to compile your project.) M2 is smart enough to figure that out based on the dependency's &lt;span style="font-style: italic;"&gt;scope&lt;/span&gt; but we'll worry about that later as well.&lt;br /&gt;&lt;br /&gt;So, after adding my dependencies (300 lines worth) I can try again to build my application. Unfortunately many of my dependencies are not available in the m2 repository. Truthfully, I probably specified them incorrectly. What I &lt;span style="font-style: italic;"&gt;should&lt;/span&gt; do is &lt;a href="http://maven.apache.org/guides/getting-started/index.html#How%20do%20I%20use%20external%20dependencies?"&gt;find them in the m2 repository&lt;/a&gt; and respecify them with the correct group and application id values. Well, I'm in a hurry and I know I have them in my local m1 repository so I'm not going to do the right thing. Instead, m2 suggests I can do this:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;10) ojdbc:ojdbc:jar:1.4&lt;br /&gt;&lt;br /&gt;Try downloading the file manually from the project website.&lt;br /&gt;&lt;br /&gt;Then, install it using the command:&lt;br /&gt;mvn install:install-file -DgroupId=ojdbc -DartifactId=ojdbc \&lt;br /&gt;  -Dversion=1.4 -Dpackaging=jar -Dfile=/path/to/file&lt;br /&gt;&lt;br /&gt;Path to dependency:&lt;br /&gt;1) foo.bar.baz:contacts:war:1.0-SNAPSHOT&lt;br /&gt;2) ojdbc:ojdbc:jar:1.4&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;And that is exactly what I intend to do... For every missing dependency I will locate it in my ~/.maven/repository directory and invoke the magic charm above to put it into my ~/.m2/repository directory. As I stated above, this is &lt;span style="font-style: italic;"&gt;not&lt;/span&gt; the Right Way to do it but it &lt;span style="font-style: italic;"&gt;is &lt;/span&gt;expedient. Once I get smarter (by reading more at the maven site) I will apply what I've learned.&lt;br /&gt;&lt;br /&gt;Now my dependencies are in place and I can actually build the code! Sadly, however, my unit tests are failing. &amp;lt;sigh/&amp;gt; Well, this is a Spring-based application and that means a pile of configuration xml files. Unsurprisingly, they're not in target/test-classes and that's why our tests are failing.&lt;br /&gt;&lt;br /&gt;You can find the answer to resources in the &lt;a href="http://maven.apache.org/guides/getting-started/index.html"&gt;getting started guide&lt;/a&gt;. Since I'm coming from an m1 project mine were originally in src/java (now at src/main/java) as well as src/test (src/test/java). M2 seems to want them at src/*/resources but I'm in no mood at this time of day to extrude them from their current location. Therefore... I will use what the GSG has told me and blaze my own path:&lt;br /&gt;&lt;pre&gt;&amp;lt;build&amp;gt;&lt;br /&gt;...&lt;br /&gt;&amp;lt;resources&amp;gt;&lt;br /&gt;  &amp;lt;resource&amp;gt;&lt;br /&gt;    &amp;lt;directory&amp;gt;${basedir}/src/main/java&amp;lt;/directory&amp;gt;&lt;br /&gt;    &amp;lt;includes&amp;gt;&lt;br /&gt;      &amp;lt;include&amp;gt;**/*.xml&amp;lt;/include&amp;gt;&lt;br /&gt;    &amp;lt;/includes&amp;gt;&lt;br /&gt;  &amp;lt;/resource&amp;gt;&lt;br /&gt;&amp;lt;/resources&amp;gt;&lt;br /&gt;&amp;lt;testResources&amp;gt;&lt;br /&gt;  &amp;lt;testResource&amp;gt;&lt;br /&gt;    &amp;lt;directory&amp;gt;${basedir}/src/test/java&amp;lt;/directory&amp;gt;&lt;br /&gt;    &amp;lt;includes&amp;gt;&lt;br /&gt;      &amp;lt;include&amp;gt;**/*.xml&amp;lt;/include&amp;gt;&lt;br /&gt;    &amp;lt;/includes&amp;gt;&lt;br /&gt;  &amp;lt;/testResource&amp;gt;&lt;br /&gt;  &amp;lt;testResource&amp;gt;&lt;br /&gt;    &amp;lt;directory&amp;gt;${basedir}/src/main/webapp&amp;lt;/directory&amp;gt;&lt;br /&gt;    &amp;lt;includes&amp;gt;&lt;br /&gt;      &amp;lt;include&amp;gt;**/*.xml&amp;lt;/include&amp;gt;&lt;br /&gt;    &amp;lt;/includes&amp;gt;&lt;br /&gt;  &amp;lt;/testResource&amp;gt;&lt;br /&gt;&amp;lt;/testResources&amp;gt;&lt;br /&gt;&lt;/pre&gt;Are you still with me? We're getting closer. Really. I promise. If we compile now we will find our Spring resources in target/test-classes where they're needed. Our tests are still going to fail, however, because... we are not compiling my AspectJ aspects. Oops...&lt;br /&gt;&lt;br /&gt;Maven (both m1 and m2 but more so with m2) is built around plug-ins. It turns out that there is one just for AspectJ. You can read all about it at &lt;a href="http://mojo.codehaus.org/aspectj-maven-plugin/"&gt;codehaus&lt;/a&gt;. Configuring pom.xml to include it is quite simple:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;build&amp;gt;&lt;br /&gt;&amp;lt;plugins&amp;gt;&lt;br /&gt;  ...&lt;br /&gt;  &amp;lt;plugin&amp;gt;&lt;br /&gt;    &amp;lt;groupId&amp;gt;org.codehaus.mojo&amp;lt;/groupId&amp;gt;&lt;br /&gt;    &amp;lt;artifactId&amp;gt;aspectj-maven-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;    &amp;lt;configuration&amp;gt;&lt;br /&gt;      &amp;lt;source&amp;gt;1.5&amp;lt;/source&amp;gt;&lt;br /&gt;      &amp;lt;target&amp;gt;1.5&amp;lt;/target&amp;gt;&lt;br /&gt;    &amp;lt;/configuration&amp;gt;&lt;br /&gt;    &amp;lt;executions&amp;gt;&lt;br /&gt;      &amp;lt;execution&amp;gt;&lt;br /&gt;        &amp;lt;goals&amp;gt;&lt;br /&gt;          &amp;lt;goal&amp;gt;compile&amp;lt;/goal&amp;gt;       &amp;lt;!-- use this goal to weave all your main classes --&amp;gt;&lt;br /&gt;          &amp;lt;goal&amp;gt;test-compile&amp;lt;/goal&amp;gt;  &amp;lt;!-- use this goal to weave all your test classes --&amp;gt;&lt;br /&gt;        &amp;lt;/goals&amp;gt;&lt;br /&gt;      &amp;lt;/execution&amp;gt;&lt;br /&gt;    &amp;lt;/executions&amp;gt;&lt;br /&gt;  &amp;lt;/plugin&amp;gt;&lt;br /&gt;&amp;lt;/plugins&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;(I would like to say that's the last time you will see a pom.xml snippet in this series but that would probably be a lie.)&lt;br /&gt;&lt;br /&gt;So, now we have all of &lt;span style="font-style: italic;"&gt;that&lt;/span&gt; in our pom.xml, the compiler set to 1.5 compliance and our 300 lines of dependency declaration... &lt;span style="font-style: italic;"&gt;mvn install&lt;/span&gt; reveals... A successful compile, test, package cycle! We now have a war! I doubt very much that it will deploy but, my friends, it is past time for me to go home and that is exactly what I intend to do.&lt;br /&gt;&lt;br /&gt;Tomorrow I will begin exploring Part 3. If that goes well and I don't have too many meetings I will endeavor to post that here shortly thereafter. Please be patient, you never quite know what a Tuesday may bring...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-6218787200044538977?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/6218787200044538977/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=6218787200044538977' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/6218787200044538977'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/6218787200044538977'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/02/from-maven-to-mvn-part-2.html' title='From maven to mvn : Part 2 -- Java 5 &amp; AspectJ'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-5282034471330242732</id><published>2007-02-26T10:58:00.000-05:00</published><updated>2007-04-12T17:36:00.942-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>From maven to mvn : Part 1 -- Building a war</title><content type='html'>I've been a huge fan of maven since the 0.4 days when it was a pile of reusable ant scripts. I encourage any and all Java developers I come across to convert their projects and use it. However, I'm not going to try to convince you here that you need to use maven. If you do then you're already on the path. If you don't then, well, there are other sites that will attempt to convince you. What I intend to do here is describe my journey from maven 1.0.4 to maven 2.0.4.&lt;br /&gt;&lt;br /&gt;For a long time now (and I say "long time" because I really have no sense of time) I've been using a modified version of maven 1.0.4. Modified in the sense that I've tweaked a few of the built-in plug-ins, added my own plug-ins and also written a fair amount of value-add in maven.xml. I also want to point out that we use this as the core build framework where I work. Prior to 1.0.4 we used, of course, 1.0.3 and prior to that was some other 1.x. In other words, we've been using some version of maven for a Very Long Time.&lt;br /&gt;&lt;br /&gt;I've been following the maven 2 (henceforth simply "m2") effort since its inception. They fine folks there have made some huge improvements. IMO m2 is so very different from m1 (maven 1.x that is) that it is very nearly should have been called something other than "maven". That may be a bit extreme but I simply want to point out that migrating from maven 1.x to 2.x is not a drop-in scenario.&lt;br /&gt;&lt;br /&gt;For quite some time now (where "quite some time" is something less than "long time") I've been pondering conversion to m2. I've had a few false starts where a plug-in I needed wasn't there. I've also had a pretty busy schedule such that I just couldn't dedicate the time to learn m2 well enough to give it a fair shot. Fortunately, some other folks I work with have had a bit of time and have begun their own effort to move to m2.&lt;br /&gt;&lt;br /&gt;So, with that being said, this series will track my efforts to convert a very simple web application ("Contacts") from m1 to m2. While simple, the project does have some requirements that do not fit m2's out of the box behavior. My theory is that if I can build this application I can probably then convert our core m1-based build framework to m2.&lt;br /&gt;&lt;br /&gt;In this episode my goals are:&lt;br /&gt;- compile the classes and aspects&lt;br /&gt;- build a war (not necessarily deployable)&lt;br /&gt;- test cases should execute and pass&lt;br /&gt;- @foo@ replacement is not necessary&lt;br /&gt;&lt;br /&gt;Step 1&lt;br /&gt;&lt;br /&gt;Build, test and deploy Contacts using the current m1-based build system. I want to be sure that what I think is working is actually working.&lt;br /&gt;&lt;br /&gt;Step 2&lt;br /&gt;&lt;br /&gt;The application has several components (sub-projects in maven-speak). For this bit I will focus only on the war. Therefore, copy Contacts/view to Contacts-m2/view. Subsequently clean up Contacts-m2/view to get rid of m1 artifacts (maven.xml, etc.)&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mkdir -p Contacts-m2/view/&lt;br /&gt;cp --recursive Contacts/view/* Contacts-m2/view/&lt;br /&gt;cd Contacts-m2 ; rm -f maven.* project.*&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Step 3&lt;br /&gt;&lt;br /&gt;The project layout has changed. Therefore we have to do some simple relocation:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mkdir src/main&lt;br /&gt;mv src/java src/main/java&lt;br /&gt;mv src/aj src/main/aspect&lt;br /&gt;mv src/webapp src/main/webapp&lt;br /&gt;mv src/test src/test-tmp&lt;br /&gt;mkdir src/test&lt;br /&gt;mv src/test-tmp/src/test/java&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Step 4&lt;br /&gt;&lt;br /&gt;Now we need to have some m2 artifacts so that m2 knows what to do with all of these things. Specifically, we need a pom.xml that describes our project and how to build it.&lt;br /&gt;&lt;br /&gt;The easiest way to bootstrap this is to use m2's archetype system:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mvn archetype:create -DgroupId=com.mycompany.app -DartifactId=my-app&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Step 5&lt;br /&gt;&lt;br /&gt;Well, &lt;span style="font-style: italic;"&gt;Step 4&lt;/span&gt; didn't go so well because we're behind a corporate firewall. To deal with this we need to tell m2 how to deal with the &lt;a href="http://maven.apache.org/guides/mini/guide-proxies.html"&gt;proxy&lt;/a&gt;. So now we have a ~/.m2/settings.xml file. We may have to come back to this later but we're good for now.&lt;br /&gt;&lt;br /&gt;Try the archetype again... all good. It will take a while since m2 needs to pull dependencies for its plug-ins.&lt;br /&gt;&lt;br /&gt;Step 6&lt;br /&gt;&lt;br /&gt;Now that we have the archetype we will take its pom.xml and throw everything else away.&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;mv my-app/pom.xml .&lt;br /&gt;rm-rf my-app&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;The changes to pom.xml will eventually be significant. For now, we just need to change the &lt;span style="font-style: italic;"&gt;groupId&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;artifactId&lt;/span&gt;, &lt;span style="font-style: italic;"&gt;name&lt;/span&gt; and &lt;span style="font-style: italic;"&gt;packaging&lt;/span&gt; tags. The first three are whatever you need for your application. &lt;span style="font-style: italic;"&gt;packaging&lt;/span&gt; will be &lt;span style="font-style: italic;"&gt;war&lt;/span&gt; since we're building one of those.&lt;br /&gt;&lt;br /&gt;Note that we could have used the webapp archetype. I really wanted to start with the basics, though, so I went with jar.&lt;br /&gt;&lt;br /&gt;Step 6&lt;br /&gt;&lt;br /&gt;Now lets build it and see what happens! It's magic, right? Everything should Just Work?&lt;br /&gt;&lt;br /&gt;Start with &lt;span style="font-style: italic;"&gt;mvn clean&lt;/span&gt; just to make sure we didn't flub the pom.xml. There's nothing to clean, of course, but it gives us a good feeling because it cannot possibly fail!&lt;br /&gt;&lt;br /&gt;Next try &lt;span style="font-style: italic;"&gt;mvn install&lt;/span&gt; to build, test and package the application. Ah well... success was short-lived. My application uses Java 5 stuff, AspectJ and a pile of dependencies. With the default pom.xml this build isn't going to complete.&lt;br /&gt;&lt;br /&gt;Conclusion&lt;br /&gt;&lt;br /&gt;Thus concludes Part 1. I didn't complete all of my goals but i did make some significant progress.&lt;br /&gt;&lt;br /&gt;Actual execution took place over a four-hour period during which I was also dealing with my inbox and the inevitable hourly "emergencies" of software development. During the that window of time I probably spent between 90 and 120 minutes doing the things documented above. (The actual documentation -- four days later -- took about 45 minutes.)&lt;br /&gt;&lt;br /&gt;I'm off to lunch now. When I get back I'll proof-read this post and document Part 2 where I sort out the build failures in Step 6.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-5282034471330242732?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/5282034471330242732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=5282034471330242732' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/5282034471330242732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/5282034471330242732'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/02/from-maven-to-mvn-part-1.html' title='From maven to mvn : Part 1 -- Building a war'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-8934199516828623937</id><published>2007-02-25T00:18:00.000-05:00</published><updated>2007-02-25T00:38:00.603-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogs'/><title type='text'>Specialization</title><content type='html'>pteropus is something of a stream of consciousness for me. Ideas come to me and some of them come out here. My ideas tend to fall into four broad categories: Christianity, Java geek stuff, other geek stuff, everything else.&lt;br /&gt;&lt;br /&gt;For a while now I've been focusing some of my thoughts on my faith into a &lt;a href="http://jcej.tragus.org/blog/john-14-6.html"&gt;dedicated blog&lt;/a&gt;. I mention it now because I haven't mentioned it before. I struggled a bit with the decision to create the dedicated blog because I'm proud of my faith and want to put it "out there" for all to see. As such, you'll still see some of those thoughts here; thoughts much along the lines of what I've already posted. If you're one of my geek readers I hope you find something of interest in these posts. If so I invite you to visit &lt;a href="http://jcej.tragus.org/blog/john-14-6.html"&gt;John-14-6&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;On the theory that I may someday feel the need to fire up other specialized blogs I've setup a &lt;a href="http://jcej.tragus.org/blog"&gt;focal point&lt;/a&gt; for my blog-related interests.&lt;br /&gt;&lt;br /&gt;Thanks for visiting, come again soon and invite your friends.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-8934199516828623937?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/8934199516828623937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=8934199516828623937' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/8934199516828623937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/8934199516828623937'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/02/specialization.html' title='Specialization'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-568607947968129758</id><published>2007-02-22T23:01:00.000-05:00</published><updated>2007-02-27T11:25:13.494-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='productivity'/><category scheme='http://www.blogger.com/atom/ns#' term='busyness'/><title type='text'>Information Overload</title><content type='html'>I'm not the first one to notice this and I won't be the last but here it is: We are overloaded with the amount of information we attempt to process each and every day. I'll even narrow it down and say we're overloaded with just the number of websites we attempt to consume every day. Never mind the shows stacking up on TiVo or the Cd's we got at Christmas and haven't listened to yet. And you can simply forget the dead-tree-ware (books, that is) that were given to us on our birthdays months and months ago! At latest count I have over 50 blogs I monitor &lt;span style="font-style: italic;"&gt;every day&lt;/span&gt;. It's no wonder I rarely have time to update this one!&lt;br /&gt;&lt;br /&gt;So what are we going to do about it?&lt;br /&gt;&lt;br /&gt;Well, the naive would say that we simply cut back. Drop my 50 to 5 and be done with it. OK, fine, but the problem with that is I've spent a lot of time and energy to be one of best Java programmer (and C++ programmer before that) I can possibly be. I have a passion for what I do and if I'm going to bother to do it then I want to be good at it. To do that means that I need to keep up with the technology and all that's new. After all, you don't get better by doing the same thing the same way all the time. You get better by learning from folks who know things you don't know. To do &lt;span style="font-style: italic;"&gt;that&lt;/span&gt; means reading lots and lots of stuff put out by people who are a lot smarter than I am. And &lt;span style="font-style: italic;"&gt;that&lt;/span&gt; sets me up for the information overload.&lt;br /&gt;&lt;br /&gt;So, again, what are we going to do about it?&lt;br /&gt;&lt;br /&gt;Well, these days any website (be it blog or other) worth visiting has some kind of feed available. Even my own poor blog here has an atom feed and I'm certainly not in the top set of sites to visit! (Actually, I'm not sure that anyone visits at all. Maybe I'm just typing in a vacuum...)&lt;br /&gt;&lt;br /&gt;Armed with some sort of site feed we can begin to leverage some pretty cool tools called aggregators. If you don't know what I'm talking about go ask &lt;a href="http://en.wikipedia.org/wiki/News_aggregator"&gt;Wikipedia&lt;/a&gt;. From my limited investigation they fall into two broad categories: local installables and web-based. (Of course I guess you could say that most applications these days fall into those categories...) My daily digital life involves three to six computers so a local install is not really useful thus I've begun to look into the online variety.&lt;br /&gt;&lt;br /&gt;My first attempt to fence in my mass of feeds was Google's customized home page. If you're not doing this then you're missing the boat. And not just any boat. I mean &lt;span style="font-weight: bold;"&gt;the&lt;/span&gt; boat. Google's customized home page is what every ISP or portal-thing vendor has been trying to do since the www got started. There are hundreds, nay, thousands of widgets you can drop on there to customize your web experience. Beyond that, anything with a feed can be dropped on just as easily. Soon, however, I reached a critical mass where I had a half-dozen customized tabs with ten or twenty widgets each and things were beginning to be ignored.&lt;br /&gt;&lt;br /&gt;My new favorite toy is &lt;a href="http://www.google.com/reader/view/"&gt;Google Reader&lt;/a&gt;. I played with it a year or more ago when, I think, it went by the name Google Lens. I'm probably wrong about that name because every time I say it nobody knows what I'm talking about. Whatever you name it, it's cool.&lt;br /&gt;&lt;br /&gt;Now I'm not going to turn this into a Reader tutorial. You're probably a pretty smart person and can figure it out for yourself. I just wanted to take a moment to make you aware of it in case you're not already. However... here are some of the cool things I can do: tag sets of related feeds, mark come-back-to-read-later articles with a big yellow star and share my favorite articles in an &lt;a href="http://www.google.com/reader/shared/05169570292844996758"&gt;automatically maintained meta-blog thing&lt;/a&gt;. That last one is wicked cool because it actually has its own RSS feed so its like a feed of feeds kind of thing and as a coder I really dig the recursion of it.&lt;br /&gt;&lt;br /&gt;Along the same lines as Reader is &lt;a href="http://www.bloglines.com/"&gt;Bloglines&lt;/a&gt;. I tried it for a while before I settled on Reader. It has an equally cool set of features. There is obviously overlap as well as some interesting features that set it apart. (FWIW I settled on Reader because of some odd usability issues I had with Bloglines. YMMV)&lt;br /&gt;&lt;br /&gt;I encourage you to try 'em both out and any others you run across. It isn't really about which one you settle on as long as you settle on &lt;span style="font-style: italic;"&gt;something&lt;/span&gt;. In my opinion (of course it's mine since it's my blog) there is just no way to keep up with the torrent of information without some sort of tool that will help us organize it all.&lt;br /&gt;&lt;br /&gt;My other favorite toy is &lt;a href="http://del.icio.us/"&gt;del.icio.us&lt;/a&gt;. del.icio.us does for bookmarks what Reader does for feeds. It lets me aggregate all of my bookmarks into one web-accessible location and organize them by any relevant set of tags I want. What's more, my friends with a del.icio.us account can drop things onto my pile and vice-versa. They even have these cool &lt;a href="http://del.icio.us/help/faq#How_do_I_install_the_buttons_aga"&gt;browser buttons&lt;/a&gt; that let me tag a link without visiting the page. So, now, instead of maintaining an html page of my bookmarks or trying to sync six to twelve browser instance's bookmarks I just install those cool buttons everywhere I go and link 'em online.&lt;br /&gt;&lt;br /&gt;Clearly these are not the only tools available to help manage the information overload. Even if they were the only tools for their niche we're still faced with all of our other input sources. Rather than simply sigh and succumb to the avalanche take a moment to find things to help get things under control. There are a lot of smart folks out there with the same problem and some of them are giving us some powerful tools to take back control. If I wore a hat I'd definitely take it off to 'em!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-568607947968129758?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/568607947968129758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=568607947968129758' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/568607947968129758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/568607947968129758'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/02/information-overload.html' title='Information Overload'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-1717045695270259381</id><published>2007-01-24T20:20:00.000-05:00</published><updated>2007-01-24T20:24:13.545-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='behavior'/><category scheme='http://www.blogger.com/atom/ns#' term='confession'/><title type='text'>Convergence</title><content type='html'>&lt;a href="http://www.fnokd.com/"&gt;Bob&lt;/a&gt; just wrote an &lt;a href="http://www.fnokd.com/2007/01/23/book-review-leadership-self-deception/"&gt;interesting entry&lt;/a&gt; that dovetails nicely with some &lt;a href="http://john-14-6.blogspot.com/2007/01/consistency.html"&gt;things on my mind&lt;/a&gt; that fit with the &lt;a href="http://shawnlovejoy.typepad.com/shawn_lovejoy/2007/01/confession_day.html"&gt;current message series&lt;/a&gt; at &lt;a href="http://www.mountainlakechurch.org/"&gt;church&lt;/a&gt;. Weird.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-1717045695270259381?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/1717045695270259381/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=1717045695270259381' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/1717045695270259381'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/1717045695270259381'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/01/convergence.html' title='Convergence'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-2150716058999383569</id><published>2007-01-10T21:05:00.000-05:00</published><updated>2007-01-19T23:31:10.994-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mlc'/><category scheme='http://www.blogger.com/atom/ns#' term='christianity'/><title type='text'>WOW</title><content type='html'>Let me repeat that:  &lt;span style="font-weight: bold;"&gt;WOW&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I just got back home from what &lt;a href="http://www.mountainlakechurch.org"&gt;our church&lt;/a&gt; calls Wednesday Night Live. It  is a periodic meeting for those of us who call Mountain Lake home. &lt;a href="http://shawnlovejoy.com/"&gt;Our pastor&lt;/a&gt; generally lets us in on some of the things that are coming up over the next few months. Tonight was no exception to that and, in fact, tonight was a totally pumped-up version of a normal WNL (if ever any of our WNLs could be considered "normal" by anyone).&lt;br /&gt;&lt;br /&gt;Mountain Lake will be seven years old this coming weekend. We have been attending for the last three of those. God has done some amazing things in all of that time. He has changed the life of everyone who has walked through those doors. Ask anyone who has been there, we've all seen and experienced it personally.&lt;br /&gt;&lt;br /&gt;The new chapter introduced tonight by Shawn is going to be an order of magnitude beyond the amazing things we've already seen. Fasten your seat belts and hold on tight. It's going to be an awesome journey!&lt;br /&gt;&lt;br /&gt;&lt;span class="" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;&lt;/span&gt; &lt;span class="down" style="display: block;" id="formatbar_CreateLink" title="Link" onmouseover="ButtonHoverOn(this);" onmouseout="ButtonHoverOff(this);" onmouseup="" onmousedown="CheckFormatting(event);FormatbarButton('richeditorframe', this, 8);ButtonMouseDown(this);"&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-2150716058999383569?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/2150716058999383569/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=2150716058999383569' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/2150716058999383569'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/2150716058999383569'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/01/wow.html' title='WOW'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-3705918645168135216</id><published>2007-01-09T19:23:00.000-05:00</published><updated>2007-01-19T23:30:23.165-05:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='busyness'/><title type='text'>You need a blog...</title><content type='html'>This is what a good freind of mine told me today at lunch.&lt;br /&gt;&lt;br /&gt;I promptly replied, "I &lt;span style="font-weight: bold;"&gt;have&lt;/span&gt; a blog. I just never have time to update it."&lt;br /&gt;&lt;br /&gt;And that really brings us to the crux of the matter doesn't it? I mean, I'm just so busy that I never seem to have any "me" time for things like blogging.&lt;br /&gt;&lt;br /&gt;I'm not busy in the single parent of six working two jobs to make ends meet while going to night school to get a PhD in quantum physics kinda busy. I'm about as busy as you are. Maybe a little more or a little less but mostly about as busy as your average person.&lt;br /&gt;&lt;br /&gt;Now don't misunderstand me. I'm grateful that I have a full schedule. I'm active in my church, I have an awsome family, a career I actually enjoy and friends I can hang out with from time to time. I'm grateful for all of these things &lt;span style="font-weight: bold;"&gt;and&lt;/span&gt; the time to enjoy them. If I didn't have a full schedule it would be because some of these, and other important, things were missing. That would be a Bad Thing.&lt;br /&gt;&lt;br /&gt;When I say I'm too busy to blog I guess I mean that after all of the high-priority life things are taken care of and I find myself with an hour or two of responsibility-less time on my hands I have trouble choosing the thing to do next. Do I catch up on that book I've been putting off? (I have about a dozen in the queue.) Or maybe the latest episode of whatever that TiVo has been holding for a couple of months now? Then there's that DVD still in the shrink-wrap. Or maybe &lt;strike&gt;that&lt;/strike&gt; those pet Java projects that I've been tapping away at for a year or more. And, oh yea, the blog.&lt;br /&gt;&lt;br /&gt;My friend who recommended that I get a blog knows me well. I have many opinions, insights and so forth that I would like to share. Some are truly useful while others are probably useless.&lt;br /&gt;&lt;br /&gt;So, here is my first entry of '07. With luck it will be the first of many. Let's just hope I don't get distracted by... Oh! Shiny thing. Gotta go!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-3705918645168135216?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/3705918645168135216/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=3705918645168135216' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3705918645168135216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3705918645168135216'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2007/01/you-need-blog.html' title='You need a blog...'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-3724151758633952542</id><published>2006-10-02T09:36:00.001-04:00</published><updated>2006-10-20T14:23:10.159-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='methodology'/><category scheme='http://www.blogger.com/atom/ns#' term='code'/><title type='text'>Healthy Organic Code</title><content type='html'>&lt;a href="http://en.wikipedia.org/wiki/Healthy"&gt;Healthy&lt;/a&gt;: ... health is the ability to efficiently respond to challenges&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Physical exercise is the performance of some activity in order to develop or maintain physical fitness and overall health.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;For our code to stay healthy we need to exercise it through tests.&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Hygiene is the maintenance of healthy practices. In modern terminology, this is usually regarded as a particular reference to cleanliness.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;For our code to stay healthy we need to avoid bad things (e.g. -- anti-patterns) and embrace good things (e.g. -- best practices).&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Mental health is a concept that refers to a human individual's emotional and psychological well-being. ... Feeling capable and competent; being able to handle normal levels of stress, maintain satisfying relationships, and lead an independent life; and being able to "bounce back," or recover from difficult situations, are all signs of mental health.&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Healthy code is capable of performing its function well; can handle anticipated load; works well with other code and can be tested independently; recovers gracefully from (or reports appropriately) the unexpected.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;a href="http://en.wikipedia.org/wiki/Organic_%28model%29"&gt;Organic&lt;/a&gt;: ...the ability to adapt, learn, and evolve...&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;organic models include the ability to adapt, learn, and evolve&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Our code must not become stagnant. It must be possible to refactor our code to adapt to new situations. We must learn from our successes and mistakes. Our skills must evolve along with technology.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;organic models include emergent behaviour or emergent properties&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Write the code you need to write to solve the problem. Let the reusability bubble up to the surface as it will. In other words, don't write a generic, reusable, pluggable component based framework unless that's what you really need.&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;organic models [are] composed of heterogeneous (diverse) parts&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Find the right tool for the job. Put them all into your toolbox. Choose the right language, framework and methodology for the problem at hand.&lt;/span&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Note: The definitions are from Wikipedia. The correlations are my own.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-3724151758633952542?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/3724151758633952542/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=3724151758633952542' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3724151758633952542'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3724151758633952542'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2006/10/healthy-organic-code.html' title='Healthy Organic Code'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-3677383019035534315</id><published>2006-09-25T08:58:00.000-04:00</published><updated>2006-10-20T14:22:04.275-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='system administration'/><title type='text'>Stability Can Be Stressful</title><content type='html'>Last week I got the bright idea to add RAID-1 (mirroring) to the pile of moving parts on my intranet / backup server in the home network.&lt;br /&gt;&lt;br /&gt;Adding RAID to the kernel and configuring root-on-raid went quite smoothly. It was probably because of this that I got the bright idea to do LVM-on-raid on a Thursday evening instead of over a long weekend.&lt;br /&gt;&lt;br /&gt;Migrating LVM started by creating md1 on hdc4 via mkraid. This was then added to LVM through vgextend. (hdd is not yet in the case and that does complicate things a bit.) Now I needed to move the data from hda4 onto md1. This is where things began to go very badly...&lt;br /&gt;&lt;br /&gt;For reference there appears to be a bug in pvmove 2.02.05. pvmove will move data from one physical volume in a volume group onto another physical volume. In my case, I wanted to move from hda4 to md1 so that I could then add hda4 to md1 thus mirroring the data across both hda4 and hdc4.&lt;br /&gt;&lt;br /&gt;Keep in mind that the whole point of the operation is to add stability and reliability to the system...&lt;br /&gt;&lt;br /&gt;Due to the (alleged) bug I got kernel panics and a solid HDD activity light. No chance to reboot, complete system hang on any attempt. The only solution was hardware reset.&lt;br /&gt;&lt;br /&gt;Now you have to realize that this box is the backup- and file-sever for my home network. It has backups of all of the other hosts and some data that isn't found anywhere else in the network. As it turns out the most critical (and irreplacable) logical volumes live(d) physically on hda4.&lt;br /&gt;&lt;br /&gt;After the hard reboot I decided that even though I had cross-host backups it might be wise to do another of these critical bits. Unfortunately, pvmove was still trying to do its job even after the reboot and any attempt to read the in-move filesystems resulted in yet another kernel Oops and solid HDD activity.&lt;br /&gt;&lt;br /&gt;Hours transpire during which I try any number of things to cancel the pvmove (including &lt;span style="font-style: italic;"&gt;pvmove --abort&lt;/span&gt;) to no avail. I finally discovered that there is a version 2.02.06 of pvmove and immediately upgraded. To my horror I could no longer even identify the in-move logical volumes. Evil ioctl error messages accompanied any attempt. Fortunately, at this point an &lt;span style="font-style: italic;"&gt;--abort&lt;/span&gt; did work and I was finally able to see everything.&lt;br /&gt;&lt;br /&gt;I decided then and there that pvmove is not my friend and I don't want to associate with it any longer. I did what any sensible person would do... I created parallel filesystems for each one I wanted to move and used my old, reliable friend &lt;span style="font-style: italic;"&gt;rsync&lt;/span&gt; followed by &lt;span style="font-style: italic;"&gt;lvremove&lt;/span&gt; of the old volumes and &lt;span style="font-style: italic;"&gt;lvrename&lt;/span&gt; of the parallels. And you know what? It all worked perfectly.&lt;br /&gt;&lt;br /&gt;So this is a quick post covering about three and a half hours of sheer terror. Now that its all done (and hdd to be added very soon) I'm quite happy with the results. I feel much more confident that I can loose any one of the four drives and not loose a bit of data. I'm glad to be where I am. I just wish the road had been a bit smoother...&lt;br /&gt;&lt;br /&gt;(And, oh yea, I'm rsync'ing the latest backups to my dev box every night and have no plans to stop...)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-3677383019035534315?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/3677383019035534315/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=3677383019035534315' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3677383019035534315'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3677383019035534315'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2006/09/stability-can-be-stressful.html' title='Stability Can Be Stressful'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-4005309422671399374</id><published>2006-09-11T19:26:00.000-04:00</published><updated>2006-10-20T14:21:42.753-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='christianity'/><title type='text'>Wired to Win</title><content type='html'>"Wired to Win"... These were some of the last words spoken by our worship pastor &lt;a href="http://rickpearson.org/"&gt;Rick Pearson&lt;/a&gt; before he went home to Christ last summer. Rick was only 23 when he was hit suddenly and fataly with leukemia. Through it all he never lost faith and he never questioned God's will.&lt;br /&gt;&lt;br /&gt;Rick's diagnosis and treatment wasn't cheap. His family racked up a lot of bills along the way. In September of last year his &lt;a href="http://profile.myspace.com/index.cfm?fuseaction=user.viewprofile&amp;amp;friendid=79978245"&gt;sister&lt;/a&gt; and some friends setup a benefit concert. Through that they raised not only enough money to pay the debt but also to continue to contribute to several awsome organizations Rick supported: &lt;a href="http://www.compassion.com/"&gt;Compassion International&lt;/a&gt;, &lt;a href="http://www.268generation.com/"&gt;Passion&lt;/a&gt;, &lt;a href="http://www.bigstuf.com/"&gt;Big Stuff&lt;/a&gt; and &lt;a href="http://mountainlakechurch.org/"&gt;Mountain Lake Church&lt;/a&gt; as well as the &lt;a href="http://www.leukemia-lymphoma.org/"&gt;Leukemia and Lymphoma Society&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;At Mountain Lake Church Rick touched hundreds of people, maybe even a few thousand, in our community. Rick's Legacy is now touching thousands around the world. We may wonder why such a young, talented man of God would be called home at a seemingly untimely hour. We just attended the Second Annual &lt;a href="http://myspace.com/rickpearsonmemorial"&gt;Rick Pearson Memorial Benefit Concert&lt;/a&gt; and heard there about the amazing things done with the funds raised by last year's event. Perhaps Rick's task was just too big to be done here on Earth.&lt;br /&gt;&lt;br /&gt;Like so many others influenced by the events in Rick's life I wasn't there when the words were spoken. In my mind I imagine the conversation went something like this: "If I get well and am with you all for many more years, I win. If I don't and go to Heaven, I win. No matter what happens, I'm wired to win."&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-4005309422671399374?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/4005309422671399374/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=4005309422671399374' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/4005309422671399374'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/4005309422671399374'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2006/09/wired-to-win.html' title='Wired to Win'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-7757232023907647882</id><published>2006-09-06T17:21:00.000-04:00</published><updated>2006-09-06T17:23:45.070-04:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='methodology'/><title type='text'>M^3</title><content type='html'>For a long time now I've been following a software development methodology with no name. A few months ago a colleague and I were discussing such things and he challenged me to formalize my thoughts. I present it to you, today for comment and consideration.&lt;br /&gt;&lt;br /&gt;The Three M's (or M-cubed or M^3 or, if you like, The M-Factor) are (or is) quite simple:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Make it Work&lt;/li&gt;&lt;li&gt;Make it Better&lt;/li&gt;&lt;li&gt;Make it Right&lt;/li&gt;&lt;/ul&gt;There is nothing intricate, mysterious or complex here. It is just as you read it. In fact, most of you reading are probably responding with "well, duh" or something similar. And so you should. Any develompent methodology should be simple and non-intrusive and I believe that M^3 captures that sentiment.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;Make it Work&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Many teams fall into an analysis paralysis. You've heard the term. We've all been guilty of it at some point. The spirit of &lt;span style="font-style: italic;"&gt;Make it Work&lt;/span&gt; is to put all of that aside and just get something done. Do whatever it takes to have something you can touch and see. At this point I don't care if you have scriptlets in your JSPs or hard-coded magic values in your Java classes. Just get something (anything) done so that what you have in your head can be shared with others. In some cases you may find that what the feature you're working on isn't needed at all or may be needed in a different way than you imagined. Best to find that out early, after a quick hack than later after a lengthy, yet perfect, implementation.&lt;br /&gt;&lt;br /&gt;The challenge, of course, is to not stop at &lt;span style="font-style: italic;"&gt;Make it Work&lt;/span&gt;. Too many times we see junior (and not so junior) developers quite satisfied with solutions at this phase. Resist the urge to move on to the next thing before ensuring that the current thing is done correctly.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;Make it Better&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;This is where you'll spend most of your time in the process. Revise your &lt;span style="font-style: italic;"&gt;Make it Work&lt;/span&gt; results to get rid of the truly embarrasing things first (e.g. -- scriptlets, hard-coded values and whatever else).  If you're not following Test Driven Design then now is the time to start writting those unit tests. (If you are doing TDD then you already have at least a scaffold of tests as a result of &lt;span style="font-style: italic;"&gt;Make it Work&lt;/span&gt; and I applaud you.) Next look for opportunities for pluggable strategies. Then consider repackaging for reuse. Apply design patterns. Etc... In other words, iterate through &lt;span style="font-style: italic;"&gt;Make it Better&lt;/span&gt; as many times as necessary to reach a point of "good enough". The challenge, of course, is knowing when enough is enough.&lt;br /&gt;&lt;br /&gt;Many people will get hung up in &lt;span style="font-style: italic;"&gt;Make it Better.&lt;/span&gt; How many times have we seen the tinkerer refactor the same bit of code so many times that the end result looks nearly exactly like the original? Coding in circles isn't very productive... Refactoring is a good thing. A &lt;span style="font-style: italic;"&gt;very&lt;/span&gt; good thing. Just don't get caught up in the excitement of it all and loose track of the fact that you have to deliver something eventually.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Make it Better&lt;/span&gt; is also the place where you will respond to changes in your requirements. Not wholesale changes along the lines of "you're building a house but we want a boat" but those "oh, it works *that* way..." kinds of changes. In &lt;span style="font-style: italic;"&gt;Make it Better&lt;/span&gt; your code is fluid and can easily respond to small to middlin' changes that are, themselves, responses to the evolution of the solution.&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: center;"&gt;Make it Right&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;Once you have finally satisfied yourself that the result is good enough (or when someone tells you to quit refactoring for refactoring's sake) you have reached &lt;span style="font-style: italic;"&gt;Make it Right.&lt;/span&gt; This is the exact opposite of our initial &lt;span style="font-style: italic;"&gt;Make it Work&lt;/span&gt;. Now we want to do one last pass over the many iterations of our second phase to make sure that we have taken every reasonable (though not every &lt;span style="font-style: italic;"&gt;possible&lt;/span&gt;) opportunity to make our solution "right". Is it as extensible as it should be? Is it pluggable where appropriate? Etc...&lt;br /&gt;&lt;br /&gt;It is important to realize that &lt;span style="font-style: italic;"&gt;Make it Right&lt;/span&gt; is not the same as &lt;span style="font-style: italic;"&gt;Make it Perfect&lt;/span&gt;. There is no such thing as perfect software. Your &lt;span style="font-style: italic;"&gt;Make it Better&lt;/span&gt; iterations will eventually reach a point where further refinement adds no benefit. At that point you're ready to enter &lt;span style="font-style: italic;"&gt;Make it Right&lt;/span&gt; where you can "dot the I's and cross the T's".&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Does M^3 fit with other methodologies? Of course it does. At its heart M^3 is about agility. It is about getting something done as quickly as possible, improving and adapting and putting a nice shine on the final solution.&lt;br /&gt;&lt;br /&gt;So, there you have it; the methodology I've been following without realizing it for probably twenty years or so. In a lot of ways M^3 mirrors our natural learning process. Since much of development is the process of learning what the customer &lt;span style="font-style: italic;"&gt;really&lt;/span&gt; wants it only makes sense that our development process follow a similar approach.&lt;br /&gt;&lt;br /&gt;(Thanks goes to Scott, my friend and co-worker, who challenged me to actually think this through and formalize my thoughts.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-7757232023907647882?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://pteropus.blogspot.com/feeds/7757232023907647882/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=6091379212671975314&amp;postID=7757232023907647882' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/7757232023907647882'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/7757232023907647882'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2006/09/m3.html' title='M^3'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-6091379212671975314.post-3331197610471609940</id><published>2006-08-16T09:59:00.000-04:00</published><updated>2006-08-16T10:11:37.409-04:00</updated><title type='text'>Hello World</title><content type='html'>Sorry if that is cliche but a long time ago I learned C from "the white book" and it just makes sense for every new endeavor to start with a hearty &lt;span style="font-style: italic;"&gt;Hello World&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;So... I'm James and here you will find any number of somehow-related things. My day job is to be a Senior J2EE Architect so you can expect I'll monologue on any number of things that are Java in nature. I'm a Linux enthusiast from the 0.99 days and a big fan of open source in general so I may wander off in that direction from time to time as well.&lt;br /&gt;&lt;br /&gt;Thanks for dropping by. Come again soon and maybe I'll have something insightful to offer.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/6091379212671975314-3331197610471609940?l=pteropus.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3331197610471609940'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/6091379212671975314/posts/default/3331197610471609940'/><link rel='alternate' type='text/html' href='http://pteropus.blogspot.com/2006/08/hello-world.html' title='Hello World'/><author><name>James CE Johnson</name><uri>http://www.blogger.com/profile/13757035004484512465</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author></entry></feed>
