<?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-831382000244017172</id><updated>2012-01-05T22:08:18.742-08:00</updated><category term='bpel'/><category term='cvs'/><category term='continuous integration'/><category term='soap'/><category term='security'/><category term='collaboration'/><category term='accountability'/><category term='karma'/><category term='glassfish'/><category term='jax-rs'/><category term='maven'/><category term='jersey'/><category term='jbi'/><category term='xslt'/><category term='hudson'/><category term='netbeans'/><category term='rest'/><category term='problem solving'/><category term='grails'/><category term='e2'/><category term='agile'/><category term='perfection'/><category term='jetty'/><category term='metric'/><category term='rss'/><category term='innovation'/><category term='cruisecontrol'/><category term='ci'/><category term='microformat'/><category term='testing'/><category term='learning'/><category term='reuse'/><category term='subversion'/><title type='text'>The CodeBeneath</title><subtitle type='html'>continuous integration in practice, software testing techniques and thoughts on getting things done in the workplace</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>38</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-5255429804322576702</id><published>2010-11-09T18:35:00.000-08:00</published><updated>2010-11-09T22:17:13.126-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='security'/><title type='text'>Mutual PKI Authentication With a Java-based Application</title><content type='html'>I recently added certificate based authentication to an application I have been working on for awhile.  It took longer to get done than I would have thought primarily because the number of moving pieces and most advice and guidance I found online was incomplete.  I hope that documenting the complete setup I used will save someone else some time and frustration.&lt;br /&gt;&lt;br /&gt;The requirements were:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Wiring together two Java applications via SOAP, using CXF framework&lt;/li&gt;&lt;li&gt;The producing application needed a valid server certificate&lt;/li&gt;&lt;li&gt;The consuming application needed to present a valid client certificate to the server&lt;/li&gt;&lt;li&gt;These  certificates needed to be signed by a trusted root and share the same certificate authority&lt;/li&gt;&lt;li&gt;For authentication purposes, no custom code within the application itself&lt;/li&gt;&lt;li&gt;Use the Java keytool program to create keypairs and generate signing requests&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;The broad strokes:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Create the necessary certificates&lt;/li&gt;&lt;li&gt;Setup tomcat on the producing side to accept only https connections and require client authentication&lt;/li&gt;&lt;li&gt;Setup the consuming application to present client certs that can satisfy the secure authentication handshake&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;span style="font-size:130%;"&gt;&lt;span style="font-weight: bold;"&gt;Creating and signing the certificates&lt;/span&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I have previously created a CA setup using strictly openssl, but for this project, I needed to lean more heavily on &lt;code&gt;keytool&lt;/code&gt; to originate the keypairs and openssl to do the certificate signing.  The &lt;code&gt;keytool&lt;/code&gt; program comes with the JDK.  I used a windows based openssl binaries from &lt;a href="http://www.slproweb.com/products/Win32OpenSSL.html"&gt;SLP&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Setup a directory structure as recommended by openssl installation and set an environment variable, &lt;code&gt;%SSLDIR%&lt;/code&gt;, to point to your CA directory.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Create CA and RA&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Create the CA's keypair and self-signed certificate&lt;br /&gt;#   -x509 means create self-sign cert&lt;br /&gt;#   -keyout means generate keypair&lt;br /&gt;#   -nodes means do not encrypt private key.&lt;br /&gt;#   -set_serial sets the serial number of the certificate&lt;br /&gt;openssl req -verbose -x509 -new -nodes -set_serial 1234 -subj "/CN=codebeneathCA/OU=project/O=company/ST=MO/C=US" -days 7300 -out %SSLDIR%/certs/cacert.pem -keyout %SSLDIR%/private/caprivkey.pem -config %SSLDIR%/openssl.config&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Create the Root Authority's keypair and Certificate Request&lt;br /&gt;#    without -x509, we generate an x509 cert request.&lt;br /&gt;#   -keyout means generate keypair&lt;br /&gt;#   -nodes means do not encrypt private key.&lt;br /&gt;openssl req -verbose -new -nodes -subj "/CN=codebeneathRoot/OU=project/O=company/ST=MO/C=US" -days 7300 -out %SSLDIR%/certs/csrra.pem -keyout %SSLDIR%/private/raprivkey.pem -config %SSLDIR%/openssl.config&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Have the CN=codebeneathCA issue a certificate for the CN=codebeneathRoot&lt;br /&gt;# We need -extfile exts -extenstions x509_extensions to make sure&lt;br /&gt;# CN=TheRA can be a Certificate Authority.&lt;br /&gt;openssl ca -batch -days 7300 -cert %SSLDIR%/certs/cacert.pem -keyfile %SSLDIR%/private/caprivkey.pem -in %SSLDIR%/certs/csrra.pem -out %SSLDIR%/certs/ra-ca-cert.pem -extfile exts -extensions x509_extensions -config %SSLDIR%/openssl.config&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Create Server Cert&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Create keypairs and Cert Request for a certificate for server&lt;br /&gt;# This procedure must be done in JKS, because we need to use a JKS keystore.&lt;br /&gt;# The current version of CXF using PCKS12 will not work for a number of&lt;br /&gt;# internal CXF reasons.&lt;br /&gt;keytool -genkey -alias codebeneath-server -dname "CN=10.9.5.210,OU=codebeneath-server,O=company,ST=MO,C=US" -keystore C:/certs/server-keystore.jks -storetype jks -storepass password -keypass password -validity 365&lt;br /&gt;keytool -certreq -alias codebeneath-server -keystore C:/certs/server-keystore.jks -storetype jks -storepass password -keypass password -file codebeneath-server-csr.pem&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Have the CN=codebeneathRoot issue a certificate for server via the Certificate Requests.&lt;br /&gt;openssl ca -batch -days 7300 -cert %SSLDIR%/certs/ra-ca-cert.pem -keyfile %SSLDIR%/private/raprivkey.pem -in %SSLDIR%/requests/codebeneath-server-csr.pem -out %SSLDIR%/requests/codebeneath-server-ra.pem -config %SSLDIR%/openssl.config&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Rewrite the certificates in PEM only format. This allows us to concatenate them into chains.&lt;br /&gt;openssl x509 -in %SSLDIR%/certs/cacert.pem -out %SSLDIR%/certs/cacert.pem -outform PEM&lt;br /&gt;openssl x509 -in %SSLDIR%/certs/ra-ca-cert.pem -out %SSLDIR%/certs/ra-ca-cert.pem -outform PEM&lt;br /&gt;openssl x509 -in %SSLDIR%/requests/codebeneath-server-ra.pem -out %SSLDIR%/requests/codebeneath-server-ra.pem -outform PEM&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Create a chain readable by CertificateFactory.getCertificates.&lt;br /&gt;type %SSLDIR%\requests\codebeneath-server-ra.pem %SSLDIR%\certs\ra-ca-cert.pem %SSLDIR%\certs\cacert.pem &gt; %SSLDIR%\requests\codebeneath-server.chain&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Replace the certificate in the server keystore with their respective full chains.&lt;br /&gt;keytool -import -file codebeneath-server.chain -alias codebeneath-server -keystore C:/certs/server-keystore.jks -storetype jks -storepass password -keypass password -noprompt&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Create the server Truststore file containing the CA cert.&lt;br /&gt;keytool -import -file cacert.pem -alias codebeneathCA -keystore C:/certs/server-truststore.jks -storepass password -noprompt&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Create Client Cert&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Create keypairs and Cert Request for a certificate for client (codebeneath)&lt;br /&gt;# This procedure must be done in JKS, because we need to use a JKS keystore.&lt;br /&gt;# The current version of CXF using PCKS12 will not work for a number of&lt;br /&gt;# internal CXF reasons.&lt;br /&gt;keytool -genkey -alias codebeneath -dname "CN=10.9.5.105,OU=codebeneath-client,O=company,ST=MO,C=US" -keystore C:/certs/client-keystore.jks -storetype jks -storepass password -keypass password -validity 365&lt;br /&gt;keytool -certreq -alias codebeneath -keystore C:/certs/client-keystore.jks -storetype jks -storepass password -keypass password -file codebeneath-csr.pem&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Have the CN=codebeneathRoot issue a certificate for client via the Certificate Requests.&lt;br /&gt;openssl ca -batch -days 7300 -cert %SSLDIR%/certs/ra-ca-cert.pem -keyfile %SSLDIR%/private/raprivkey.pem -in %SSLDIR%/requests/codebeneath-csr.pem -out %SSLDIR%/requests/codebeneath-ra.pem -config %SSLDIR%/openssl.config&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Rewrite the certificates in PEM only format. This allows us to concatenate them into chains.&lt;br /&gt;openssl x509 -in %SSLDIR%/requests/codebeneath-ra.pem -out %SSLDIR%/requests/codebeneath-ra.pem -outform PEM&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Create a chain readable by CertificateFactory.getCertificates.&lt;br /&gt;type %SSLDIR%\requests\codebeneath-ra.pem %SSLDIR%\certs\ra-ca-cert.pem %SSLDIR%\certs\cacert.pem &gt; %SSLDIR%\requests\codebeneath.chain&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Replace the certificate in the client keystore with their respective full chains.&lt;br /&gt;keytool -import -file codebeneath.chain -alias codebeneath -keystore C:/certs/client-keystore.jks -storetype jks -storepass password -keypass password -noprompt&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Create the client Truststore file containing the CA cert and server cert.&lt;br /&gt;keytool -import -file cacert.pem -alias codebeneathCA -keystore C:/certs/client-truststore.jks -storepass password -noprompt&lt;br /&gt;keytool -import -file codebeneath-server-ra.pem -alias codebeneath-server -keystore C:/certs/client-truststore.jks -storepass password -noprompt&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Create trust for the server to know the client&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Import the client cert into the server Truststore.&lt;br /&gt;keytool -import -file codebeneath-ra.pem -alias codebeneath -keystore C:/certs/client-truststore.jks -storepass password -noprompt&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Export client cert from keystore for browser import &lt;/span&gt;&lt;br /&gt;&lt;br /&gt;The &lt;code&gt;keytool&lt;/code&gt; program cannot export private keys in any format, so use &lt;a href="http://portecle.sourceforge.net/"&gt;portecle&lt;/a&gt; to do these steps so that we can verify the setup using a browser without involving the actual client application.&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Start portecle: &lt;code&gt;java -jar ./portecle.jar&lt;/code&gt;&lt;/li&gt;&lt;li&gt;File... Open keystore file... select the &lt;code&gt;./client-keystore.jks&lt;/code&gt; file. Type the password.&lt;/li&gt;&lt;li&gt;Right-client the "codebeneath" certificate name. Select Export...&lt;/li&gt;&lt;li&gt;Export Type: Private Key and Certificates. Export Format: PKCS #12. Click OK. Type the password.&lt;/li&gt;&lt;li&gt;For the PKCS #12 password, it must be non-blank value or else the browser will not import it correctly.&lt;/li&gt;&lt;li&gt;In the browser, import the certificate as a "Personal" certificate.&lt;/li&gt;&lt;li&gt;In the browser, import the CA certificate as a trusted authoritative certificate.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Double check the results so far&lt;/span&gt;...&lt;br /&gt;&lt;br /&gt;On the server machine:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;keytool -list -keystore ./server-keystore.jks&lt;/code&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;one signed certificate chain, PrivateKeyEntry, alias="codebeneath-server"&lt;/li&gt;&lt;/ul&gt;&lt;code&gt;keytool -list -keystore ./server-truststore.jks&lt;/code&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;CA certificate, trustedCertEntry, alias="codebeneathca",&lt;/li&gt;&lt;li&gt;client certificate,trustedCertEntry, alias="codebeneath"&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;On the client machine:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;keytool -list -keystore ./client-keystore.jks&lt;/code&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;one signed certificate chain, PrivateKeyEntry, alias="codebeneath"&lt;/li&gt;&lt;/ul&gt;&lt;code&gt;keytool -list -keystore ./client-truststore.jks&lt;/code&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;CA certificate, trustedCertEntry, alias="codebeneathca",&lt;/li&gt;&lt;li&gt;server certificate, trustedCertEntry, alias="codebeneath-server"&lt;/li&gt;&lt;/ul&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;Server setup using tomcat&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;To accomplish the required mutual authentication (server is trusted https:// and client presents a valid certificate) requires setup within tomcat and the server war web.xml file. There is no code involved, it is strictly the setup of the servlet container to handle the security handshaking.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Tomcat server.xml file&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Note that the &lt;code&gt;clientAuth&lt;/code&gt; attribute must be "true" for things to work properly, even though the specification says that each war may individually be setup to require/not require client auth.&lt;br /&gt;&lt;pre class="brush:xml"&gt;...&lt;br /&gt;&amp;lt;Connector port=\"8443\" protocol=\"HTTP/1.1\" SSLEnabled=\"true\"&lt;br /&gt;        maxThreads=\"150\" scheme=\"https\" secure=\"true\"&lt;br /&gt;        clientAuth=\"true\" sslProtocol=\"TLS\"&lt;br /&gt;        keystoreFile=\"C:/certs/server-keystore.jks\"&lt;br /&gt;        keystorePass=\"password\"&lt;br /&gt;        keyAlias=\"codebeneath-server\"&lt;br /&gt;        truststoreFile=\"C:/certs/server-truststore.jks\"&lt;br /&gt;        truststorePass=\"password\"&lt;br /&gt;        /&amp;gt;&lt;br /&gt;...&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Tomcat tomcat-users.xml file&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Each username must match a client certificate subject line. Use portecle to open the client cert and cut-and-paste the "Subject:" line to this file's username value.  So, even though the client certificate is valid given the truststore requirements, each client must also be specifically listed in this file to have access to the server application.&lt;br /&gt;&lt;pre class="brush:xml"&gt;...&lt;br /&gt;&amp;lt;role rolename=\"codebeneathRole\"&amp;gt;&lt;br /&gt;&amp;lt;user username=\"CN=10.9.5.43, OU=project, O=company, ST=MO, C=US\" password=\"password\" roles=\"codebeneathRole\"&amp;gt;&lt;br /&gt;...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Server war web.xml file&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;Both "role-name" elements must match the tomcat-users.xml defined rolename.  The "auth-method" value of "CLIENT-CERT" will require a client certificate for authentication.  The "transport-guarentee" value of "CONFIDENTIAL" will redirect all requests&lt;br /&gt;...&lt;br /&gt;&lt;pre class="brush:xml"&gt;&amp;lt;login-config&amp;gt;&lt;br /&gt;&amp;lt;auth-method&amp;gt;CLIENT-CERT&amp;lt;/auth-method&amp;gt;&lt;br /&gt;&amp;lt;/login-config&amp;gt;&lt;br /&gt;&amp;lt;security-role&amp;gt;&lt;br /&gt;&amp;lt;role-name&amp;gt;codebeneathRole&amp;lt;/role-name&amp;gt;&lt;br /&gt;&amp;lt;/security-role&amp;gt;&lt;br /&gt;&amp;lt;security-constraint&amp;gt;&lt;br /&gt;&amp;lt;display-name&amp;gt;CodeBeneath&amp;lt;/display-name&amp;gt;&lt;br /&gt;&amp;lt;web-resource-collection&amp;gt;&lt;br /&gt; &amp;lt;web-resource-name&amp;gt;CodeBeneath&amp;lt;/web-resource-name&amp;gt;&lt;br /&gt; &amp;lt;description&amp;gt;&amp;lt;/description&amp;gt;&lt;br /&gt; &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;&lt;br /&gt; &amp;lt;http-method&amp;gt;GET&amp;lt;/http-method&amp;gt;&lt;br /&gt; &amp;lt;http-method&amp;gt;POST&amp;lt;/http-method&amp;gt;&lt;br /&gt; &amp;lt;http-method&amp;gt;HEAD&amp;lt;/http-method&amp;gt;&lt;br /&gt; &amp;lt;http-method&amp;gt;PUT&amp;lt;/http-method&amp;gt;&lt;br /&gt; &amp;lt;http-method&amp;gt;OPTIONS&amp;lt;/http-method&amp;gt;&lt;br /&gt; &amp;lt;http-method&amp;gt;TRACE&amp;lt;/http-method&amp;gt;&lt;br /&gt; &amp;lt;http-method&amp;gt;DELETE&amp;lt;/http-method&amp;gt;&lt;br /&gt;&amp;lt;/web-resource-collection&amp;gt;&lt;br /&gt;&amp;lt;auth-constraint&amp;gt;&lt;br /&gt; &amp;lt;description&amp;gt;&lt;br /&gt; &amp;lt;role-name&amp;gt;codebeneathRole&amp;lt;/role-name&amp;gt;&lt;br /&gt;&amp;lt;/auth-constraint&amp;gt;&lt;br /&gt;&amp;lt;user-data-constraint&amp;gt;&lt;br /&gt; &amp;lt;description&amp;gt;&lt;br /&gt; &amp;lt;transport-guarantee&amp;gt;CONFIDENTIAL&amp;lt;/transport-guarantee&amp;gt;&lt;br /&gt;&amp;lt;/user-data-constraint&amp;gt;&lt;br /&gt;&amp;lt;/security-constraint&amp;gt;&lt;br /&gt;...&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Debug SSL&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In the event that the mutual authentication fails, we can enable specific debugging on the server side.  In the &lt;code&gt;catalina.bat&lt;/code&gt; file, add the switch &lt;code&gt;-Djavax.net.debug=ssl,handshake&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;Client Setup&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;Since the project used SOAP WS-*, including WS-Security, on the client side we used the CXF project to handle the wsdl-to-java code generation and to create the client port.  My project uses CXF version 2.2.11, with the additional "cxf-rt-ws-security" dependency jar.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Client java code&lt;/span&gt;&lt;br /&gt;&lt;pre class="brush:java"&gt;package com.codebeneath;&lt;br /&gt;&lt;br /&gt;import org.apache.cxf.endpoint.Client;&lt;br /&gt;import org.apache.cxf.frontend.ClientProxy;&lt;br /&gt;import org.apache.cxf.jaxws.JaxWsProxyFactoryBean;&lt;br /&gt;//import org.apache.cxf.interceptor.LoggingInInterceptor;&lt;br /&gt;import org.apache.cxf.interceptor.LoggingOutInterceptor;&lt;br /&gt;import org.apache.cxf.transport.http.HTTPConduit;&lt;br /&gt;import org.apache.cxf.configuration.jsse.TLSClientParameters;&lt;br /&gt;import org.apache.cxf.binding.soap.saaj.SAAJOutInterceptor;&lt;br /&gt;import org.apache.cxf.ws.security.wss4j.WSS4JOutInterceptor;&lt;br /&gt;import org.apache.ws.security.handler.WSHandlerConstants;&lt;br /&gt;&lt;br /&gt;import java.net.URL;&lt;br /&gt;import java.security.GeneralSecurityException;&lt;br /&gt;import java.security.KeyStore;&lt;br /&gt;import java.security.KeyStoreException;&lt;br /&gt;import java.security.NoSuchAlgorithmException;&lt;br /&gt;import java.security.UnrecoverableKeyException;&lt;br /&gt;import java.security.cert.CertificateException;&lt;br /&gt;import java.util.HashMap;&lt;br /&gt;import java.util.Map;&lt;br /&gt;import java.util.logging.Logger;&lt;br /&gt;import javax.net.ssl.KeyManager;&lt;br /&gt;import javax.net.ssl.KeyManagerFactory;&lt;br /&gt;import javax.net.ssl.TrustManager;&lt;br /&gt;import javax.net.ssl.TrustManagerFactory;&lt;br /&gt;import javax.xml.ws.BindingProvider;&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* Client a SOAP WS client for the service.  Handles logging support, WS-Security and PKI certificates&lt;br /&gt;*/&lt;br /&gt;public final class Client {&lt;br /&gt;&lt;br /&gt;private static final String DEFAULT_PASSWORD = "password";&lt;br /&gt;private static final String KEYSTORE_FILENAME = "/client-keystore.jks";&lt;br /&gt;private static final String TRUSTSTORE_FILENAME = "/client-truststore.jks";&lt;br /&gt;&lt;br /&gt;private Client() {&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt; * Create a new WS client&lt;br /&gt; * @param serviceUrl, the service URL endpoint&lt;br /&gt; * @param sigPropFile, the properties file for client PKI.  Must be on the classpath at runtime&lt;br /&gt; * @return client port&lt;br /&gt; */&lt;br /&gt;public static TroubleTicketServiceSoap createClient(URL serviceUrl, String sigPropFile, String keystoreDir) {&lt;br /&gt;&lt;br /&gt; JaxWsProxyFactoryBean factory = new JaxWsProxyFactoryBean();&lt;br /&gt; factory.setAddress(serviceUrl.toString());&lt;br /&gt; factory.setServiceClass(TroubleTicketServiceSoap.class);&lt;br /&gt;&lt;br /&gt; // request and response traces in the server.log&lt;br /&gt; addLogging(factory);&lt;br /&gt;&lt;br /&gt; // SOAP WS-Security header&lt;br /&gt; addSecurityHeaders(factory, sigPropFile);&lt;br /&gt;&lt;br /&gt; // create the client&lt;br /&gt; TroubleTicketServiceSoap port = (TroubleTicketServiceSoap) factory.create();&lt;br /&gt; ((BindingProvider) port).getRequestContext().put("schema-validation-enabled", Boolean.TRUE.toString());&lt;br /&gt;&lt;br /&gt; // handle trust of server certificates&lt;br /&gt; addCertificateHandling(port, serviceUrl, keystoreDir);&lt;br /&gt;&lt;br /&gt; return port;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private static void addLogging(JaxWsProxyFactoryBean factory) {&lt;br /&gt; factory.getOutInterceptors().add(new LoggingOutInterceptor());  // SOAP Request&lt;br /&gt;//  factory.getInInterceptors().add(new LoggingInInterceptor());    // SOAP Response&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private static void addSecurityHeaders(JaxWsProxyFactoryBean factory, String sigPropFile) {&lt;br /&gt;&lt;br /&gt; // setup the properties for WS-Security.  This is PKI signed.  The SIG_PROP_FILE must be on the classpath&lt;br /&gt; Map&lt;string, object=""&gt; wss4jOutProps = new HashMap&lt;string, object=""&gt;();&lt;br /&gt; wss4jOutProps.put(WSHandlerConstants.ACTION, WSHandlerConstants.SIGNATURE);&lt;br /&gt; wss4jOutProps.put(WSHandlerConstants.USER, "client");&lt;br /&gt; wss4jOutProps.put(WSHandlerConstants.PW_CALLBACK_CLASS, ClientPasswordCallback.class.getName());&lt;br /&gt; wss4jOutProps.put(WSHandlerConstants.SIG_PROP_FILE, sigPropFile);&lt;br /&gt; wss4jOutProps.put(WSHandlerConstants.SIG_KEY_ID, "DirectReference");&lt;br /&gt;&lt;br /&gt; // add required security interceptors&lt;br /&gt; factory.getOutInterceptors().add(new SAAJOutInterceptor());&lt;br /&gt; factory.getOutInterceptors().add(new WSS4JOutInterceptor(wss4jOutProps));&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private static void addCertificateHandling(TroubleTicketServiceSoap port, URL serviceUrl, String keystoreDir) {&lt;br /&gt; Client proxy = ClientProxy.getClient(port);&lt;br /&gt; HTTPConduit conduit = (HTTPConduit) proxy.getConduit();&lt;br /&gt; TLSClientParameters tcp = new TLSClientParameters();&lt;br /&gt; tcp.setDisableCNCheck(true);&lt;br /&gt;&lt;br /&gt; try {&lt;br /&gt;  KeyStore trustStore = KeyStore.getInstance("JKS");&lt;br /&gt;  KeyStore keyStore = KeyStore.getInstance("JKS");&lt;br /&gt;&lt;br /&gt;  // accept all self-signed server certs&lt;br /&gt;//   tcp.setTrustManagers(CertificateAcceptorCXF.acceptAllCerts());&lt;br /&gt;&lt;br /&gt;  File truststoreFile = new File(keystoreDir + TRUSTSTORE_FILENAME);&lt;br /&gt;  trustStore.load(new FileInputStream(truststoreFile), DEFAULT_PASSWORD.toCharArray());&lt;br /&gt;  TrustManagerFactory trustFactory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm());&lt;br /&gt;  trustFactory.init(trustStore);&lt;br /&gt;  TrustManager[] tm = trustFactory.getTrustManagers();&lt;br /&gt;  tcp.setTrustManagers(tm);&lt;br /&gt;&lt;br /&gt;  // enable client certs for authentication&lt;br /&gt;  File keystoreFile = new File(keystoreDir + KEYSTORE_FILENAME);&lt;br /&gt;  keyStore.load(new FileInputStream(keystoreFile), DEFAULT_PASSWORD.toCharArray());&lt;br /&gt;  KeyManagerFactory keyFactory = KeyManagerFactory.getInstance(KeyManagerFactory.getDefaultAlgorithm());&lt;br /&gt;  keyFactory.init(keyStore, DEFAULT_PASSWORD.toCharArray());&lt;br /&gt;  KeyManager[] km = keyFactory.getKeyManagers();&lt;br /&gt;  tcp.setKeyManagers(km);&lt;br /&gt;&lt;br /&gt; } catch (KeyStoreException kse) {&lt;br /&gt;  System.out.println("Security configuration failed with the following: " + kse.getCause());&lt;br /&gt; } catch (NoSuchAlgorithmException nsa) {&lt;br /&gt;  System.out.println("Security configuration failed with the following: " + nsa.getCause());&lt;br /&gt; } catch (FileNotFoundException fnfe) {&lt;br /&gt;  System.out.println("Security configuration failed with the following: " + fnfe.getCause());&lt;br /&gt; } catch (UnrecoverableKeyException uke) {&lt;br /&gt;  System.out.println("Security configuration failed with the following: " + uke.getCause());&lt;br /&gt; } catch (CertificateException ce) {&lt;br /&gt;  System.out.println("Security configuration failed with the following: " + ce.getCause());&lt;br /&gt; } catch (GeneralSecurityException gse) {&lt;br /&gt;  System.out.println("Security configuration failed with the following: " + gse.getCause());&lt;br /&gt; } catch (IOException ioe) {&lt;br /&gt;  System.out.println("Security configuration failed with the following: " + ioe.getCause());&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; if (serviceUrl.getProtocol().trim().equalsIgnoreCase("https")) {&lt;br /&gt;  conduit.setTlsClientParameters(tcp);&lt;br /&gt;  LOG.info("GSTv3 Web Service protocol is 'https' so " + "set the TLSClientParameters on the "&lt;br /&gt;    + "HTTPConduit. This should cause the" + " request to accept all certificates "&lt;br /&gt;    + "and disable the CN Check.");&lt;br /&gt; } else {&lt;br /&gt;  LOG.info("GSTv3 Web Service url did was not 'https' so did NOT " + "set the TLSClientParameters "&lt;br /&gt;    + "on the HTTPConduit.");&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;}&lt;/string,&gt;&lt;/string,&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;Client client-sign.properties&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre class="brush:js"&gt;# Do not change these two properties&lt;br /&gt;#&lt;br /&gt;org.apache.ws.security.crypto.provider=org.apache.ws.security.components.crypto.Merlin&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.type=jks&lt;br /&gt;# Set your keystore location and password here!&lt;br /&gt;#&lt;br /&gt;org.apache.ws.security.crypto.merlin.file=c:/certs/client-keystore.jks&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.password=password&lt;br /&gt;org.apache.ws.security.crypto.merlin.keystore.alias=codebebeath&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;font-size:130%;" &gt;&lt;br /&gt;Conclusion&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I hope that this complete picture of mutual PKI certificate authentication helps.  If anything is not clear, let me know and I will try to clarify.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-5255429804322576702?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/5255429804322576702/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=5255429804322576702' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5255429804322576702'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5255429804322576702'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2010/11/mutual-pki-authentication-with-java.html' title='Mutual PKI Authentication With a Java-based Application'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-9115155478474893031</id><published>2010-06-10T07:39:00.000-07:00</published><updated>2010-06-10T07:44:28.351-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perfection'/><title type='text'>"Perfectionism is a crime against humanity"</title><content type='html'>I wish I had written this...&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;You could say that perfectionism is a crime against humanity. Adaptability is the characteristic that enables the species to survive-and if there’s one thing perfectionism does, it rigidifies behavior. It constricts people just when the fast-moving world requires more flexibility and comfort with ambiguity than ever. It turns people into success slaves.&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;There are several people I work with or deal with in my life that don't realize that the return on investment for their efforts is detrimental to success of the project.  It is an ongoing battle and the battle-cry is "Moving on...".&lt;br /&gt;&lt;br /&gt;Read the rest of the source article here:  &lt;a href="http://blogs.techrepublic.com.com/career/?p=2087&amp;tag=results;CR1"&gt;Is perfectionism killing your career?&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-9115155478474893031?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/9115155478474893031/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=9115155478474893031' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/9115155478474893031'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/9115155478474893031'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2010/06/perfectionism-is-crime-against-humanity.html' title='&quot;Perfectionism is a crime against humanity&quot;'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-2449700955188805732</id><published>2009-12-28T07:42:00.000-08:00</published><updated>2009-12-28T07:48:01.973-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='collaboration'/><category scheme='http://www.blogger.com/atom/ns#' term='innovation'/><category scheme='http://www.blogger.com/atom/ns#' term='problem solving'/><title type='text'>"There’s no success quite like failure"</title><content type='html'>From &lt;a href="http://www.wired.com/magazine/2009/12/fail_accept_defeat/all/1"&gt;Accept Defeat: The Neuroscience of Screwing Up&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"This is why other people are so helpful: They shock us out of our cognitive box. “I saw this happen all the time,” Dunbar says. “A scientist would be trying to describe their approach, and they’d be getting a little defensive, and then they’d get this quizzical look on their face. It was like they’d finally understood what was important.”&lt;br /&gt;&lt;br /&gt;What turned out to be so important, of course, was the unexpected result, the experimental error that felt like a failure. The answer had been there all along — it was just obscured by the imperfect theory, rendered invisible by our small-minded brain. It’s not until we talk to a colleague or translate our idea into an analogy that we glimpse the meaning in our mistake. Bob Dylan, in other words, was right: There’s no success quite like failure."&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-2449700955188805732?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/2449700955188805732/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=2449700955188805732' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2449700955188805732'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2449700955188805732'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2009/12/theres-no-success-quite-like-failure.html' title='&quot;There’s no success quite like failure&quot;'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-1887408363746545898</id><published>2009-12-04T06:21:00.000-08:00</published><updated>2009-12-04T06:33:11.431-08:00</updated><title type='text'>Grail Projects in Hudson, Pt 2.</title><content type='html'>I just recently concluded a first sprints worth of work using Grails for the first time.   We &lt;a href="http://codebeneath.blogspot.com/2009/09/shipping-is-feature.html"&gt;delivered&lt;/a&gt;. &lt;br /&gt;&lt;br /&gt;While cutting the release, I found one additional gotcha in our CI environment.  The grails application.properties file holds several key pieces of information including the version number, grails plugin versions and the last build date.  This means the file must be checked into source control, but is also modified locally every build by CI (Hudson, nudge, nudge, wink wink).  This caused our build to fail while bumping our project version.&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;CI will fail a grails+maven project if you change the version number of the project in the application.properties file.&lt;ul&gt;&lt;li&gt;Workaround: 1) Have your CI project perform a clean checkout instead of an update for you build.  2) At the end of your build, add a custom hook to delete the locally modified application.properties file.&lt;/li&gt;&lt;li&gt;Fix:  I think have the build timestamp in this file may be a mistake as Grails grows more mature.  As an improvement, it should be removed.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;/li&gt;&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-1887408363746545898?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/1887408363746545898/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=1887408363746545898' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1887408363746545898'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1887408363746545898'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2009/12/grail-projects-in-hudson-pt-2.html' title='Grail Projects in Hudson, Pt 2.'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-3494388106859660311</id><published>2009-11-20T15:52:00.000-08:00</published><updated>2009-11-20T16:48:38.234-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ci'/><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='grails'/><title type='text'>Grail Projects in Hudson</title><content type='html'>Here is a quick reference for adding a Grails project to Hudson. I'm assuming the webtest plugin is installed becuase there is no reason not to use it.  It rocks.  Also note that for integration into my existing maven build, I'm using the built-in Grails v1.1.1 maven integration.  The project structure was created via the maven archetype:&lt;br /&gt;&lt;pre&gt;&lt;blockquote&gt;mvn org.apache.maven.plugins:maven-archetype-plugin:2.0-alpha-4:generate \&lt;br /&gt;-DarchetypeGroupId=org.grails \&lt;br /&gt;-DarchetypeArtifactId=grails-maven-archetype \&lt;br /&gt;-DarchetypeVersion=1.1 \&lt;br /&gt;-DgroupId=com.black -DartifactId=grails-project&lt;/blockquote&gt;&lt;/pre&gt;&lt;br /&gt;With that, here is what works so far:&lt;br /&gt;&lt;br /&gt;General sequence (these are also the steps for keeping a development team in synch):&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;pre&gt;svn up&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;pre&gt;grails upgrade --non-interactive&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;pre&gt;mvn clean install -DnonInteractive=true&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Notes:&lt;br /&gt;&lt;ul&gt;&lt;li&gt; Step 2 above: This upgrades the grails project to include all core grails plugins and also creates some important files under web-app/WEB-INF (applicationContext.xml, ...).  We would rather run &lt;code&gt;mvn grails:exec -Dcommand=upgrade -DnonInteractive&lt;/code&gt;, however, this fails with an error:  &lt;code&gt;Embedded error: java.lang.reflect.InvocationTargetException&lt;br /&gt;/home/black/dev/branches/parent/grails-project/null/src/war not found.&lt;/code&gt;  Anybody have an idea on this?&lt;/li&gt;&lt;li&gt;Step 3 above:  This step will upgrade all non-core plugins added/modified/removed since the last checkout.  Since grails will ask the "are you sure" question, make sure in CI to add the non-interactive switch as above, which will have the effect of cheerfully answering "Y" to all of grails questions.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;Problems you may run into:&lt;br /&gt;&lt;ol&gt;&lt;br /&gt;&lt;li&gt;As part of a big maven multiproject build, grails may consume enough memory to produce an out of memory error.&lt;ul&gt;&lt;li&gt; Fix: Set &lt;code&gt;MAVEN_OPTS=-Xmx512m -XX:MaxPermSize=150m&lt;/code&gt; &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Adding new grails plugins to the build will freeze the CI build due to the "are you sure" prompt.  The same will happen when  removing a grails plugin.&lt;ul&gt;&lt;li&gt; Fix: Add the non-interactive switch to the build (&lt;code&gt;mvn -DnonInteractive=true&lt;/code&gt; or &lt;code&gt;grails --non-interactive&lt;/code&gt;) &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Webtest launches a window during the test run that needs a display, so CI may need to run the webtests headless.&lt;ul&gt;&lt;li&gt; Fix: Add non-interactive switch to the build (&lt;code&gt;mvn -DnonInteractive=true&lt;/code&gt; or &lt;code&gt;grails --non-interactive&lt;/code&gt;)&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Webtest starts the jetty server on port 8080 by default which may clash with other services on your CI box using that port.&lt;ul&gt;&lt;li&gt; Fix: Use the switch &lt;code&gt;mvn -Dserver.port=8183&lt;/code&gt;.   Note: The webtest.properties file property &lt;code&gt;&lt;code&gt;wt.config.port&lt;/code&gt;&lt;/code&gt; seems to be used if the code under test were already hosted on another running server besides the provided jetty. &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A maven multiproject build that includes a grails project with webtests will look for &lt;code&gt;/test/webtest/conf/webtest.properties&lt;/code&gt; in the wrong directory.  That is, it will not look in ${basedir}, but always in the root project where you invoked maven to start with.&lt;br /&gt;&lt;ul&gt;&lt;li&gt; Workaround: Create a hudson job that only builds the grails war/webtest project.  Create a downstream dependency on your main CI hudson job to trigger this one to build. &lt;/li&gt;&lt;br /&gt;&lt;li&gt; Fix:  ?? Anyone have an answer for this one? &lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;I hope this helps.  For the few items above that have lingering questions, I would be interested in hearing if you have solved these.  Let me know.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-3494388106859660311?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/3494388106859660311/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=3494388106859660311' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/3494388106859660311'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/3494388106859660311'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2009/11/grail-projects-in-hudson.html' title='Grail Projects in Hudson'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-1360145009894670625</id><published>2009-09-24T07:48:00.000-07:00</published><updated>2009-09-24T07:53:01.402-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perfection'/><title type='text'>"Shipping is a feature."</title><content type='html'>Another good variation on &lt;a href="http://codebeneath.blogspot.com/2007/08/perfect-is-enemy-of-very-good.html"&gt;Perfect is the Enemy of Very Good&lt;/a&gt;:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"A 50%-good solution that people actually have solves more problems and survives longer than a 99% solution that nobody has because it’s in your lab where you’re endlessly polishing the damn thing. Shipping is a feature. A really important feature. Your product must have it."&lt;br /&gt;-- &lt;a href="http://www.joelonsoftware.com/items/2009/09/23.html"&gt;http://www.joelonsoftware.com/items/2009/09/23.html&lt;/a&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-1360145009894670625?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/1360145009894670625/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=1360145009894670625' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1360145009894670625'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1360145009894670625'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2009/09/shipping-is-feature.html' title='&quot;Shipping is a feature.&quot;'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-5005973494802325016</id><published>2009-06-24T21:27:00.000-07:00</published><updated>2009-06-24T21:39:36.175-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><title type='text'>Hudson Gadget For Google Desktop v1.0</title><content type='html'>For all you developers that rely on Hudson for continuous integration build status, I have released version 1.0 of the Hudson Status Gadget for Google Desktop.  The code is part of the Hudson project itself, so it is open source.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;More information can be found on its  &lt;a href="http://wiki.hudson-ci.org/display/HUDSON/Hudson+Google+Desktop+Gadget"&gt;home page&lt;/a&gt;.  &lt;/blockquote&gt;Release note highlights for version 1.0:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;In addition to monitoring the multiple main hudson dashboards, any tabbed view can be also be monitored&lt;/li&gt;&lt;li&gt;A scrollbar shows for a large number of jobs that would take more space then the gadget allows&lt;/li&gt;&lt;li&gt;Views can be collapsed to a single line with all jobs statuses of that view aggregating their status to the single view line&lt;/li&gt;&lt;li&gt;A refresh button has been added for manual status refreshes&lt;/li&gt;&lt;li&gt;Blinking build statuses are now animated&lt;/li&gt;&lt;/ul&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/SkL-gyJT2PI/AAAAAAAACow/NnlMHiDCWY4/s1600-h/screenshot_large.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer; width: 300px; height: 225px;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/SkL-gyJT2PI/AAAAAAAACow/NnlMHiDCWY4/s320/screenshot_large.png" alt="" id="BLOGGER_PHOTO_ID_5351119146439661810" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;Enjoy and let me know how it works out for you.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-5005973494802325016?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/5005973494802325016/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=5005973494802325016' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5005973494802325016'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5005973494802325016'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2009/06/hudson-gadget-for-google-desktop-v10.html' title='Hudson Gadget For Google Desktop v1.0'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_AEOHFrmyURo/SkL-gyJT2PI/AAAAAAAACow/NnlMHiDCWY4/s72-c/screenshot_large.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-8194624477131499759</id><published>2008-11-23T21:00:00.000-08:00</published><updated>2008-11-23T19:02:47.186-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>The Supply Chain of Continuous Integration</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/SFAyn5xZxGI/AAAAAAAABg0/LG0P2UjUpOQ/s1600-h/ci_logo.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/SFAyn5xZxGI/AAAAAAAABg0/LG0P2UjUpOQ/s400/ci_logo.png" alt="" id="BLOGGER_PHOTO_ID_5210720429971981410" border="0" /&gt;&lt;/a&gt;When I was first introduced to Continuous Integration, I viewed it as a black box with a well defined interface.  It was the same kind throw-it-over-the-wall mentality that some people have with testing: When the code is "done", give it to the testing shop and mark the checkbox complete.  It may or may not return with feedback attached.&lt;br /&gt;&lt;br /&gt;I got a different perspective of how CI should work, however, while working on a CI team.   More than a little of this new perspective is probably due to working in a company that believes in and practices agile development and scrum management.  With agile, we are much better at involving testers into day-to-day activities rather than the cold baton hand-off as sprint review time rolls near.&lt;br /&gt;&lt;br /&gt;What got me thinking about a CI supply chain concept was the story from The Earth is Flat about how UPS has evolved from a package delivery company to an integral part of many companies day-to-day business operations.  They started with a core business of picking up and shipping packages.  But it turns out, there are inefficiencies to this bolted on approach.  In order for the shipping to be efficient for both UPS and the contracting company, the entire supply chain had to be prepped in advance.  This includes both pre and post shipment.  The visibility into business processes had to be bidirectional.&lt;br /&gt;&lt;br /&gt;I think the same is true with continuous integration.  It is based on the old computer adage, GIGO, Garbage In Garbage Out.  So a team that writes plenty of code, but no tests.  What is the value of CI for them?  The feedback is minimal.  To get the most value out of an automated build system, there must be some forethought into what kind of quality feedback you want to see, and then what teams can do to define and integrate the right reporting tools into the build.&lt;br /&gt;&lt;br /&gt;For example, even after automated tests written and incorporated into the build, you may want to get feedback on some quality measures on your code.  In our case, we wanted Checkstyle and PMD reporting on our Java code.  We use maven as our build tool, so adding the reporting into our builds was simple.  But then the question becomes, what coding standards do we want to compare to?  What PMD rulesets represent a sane minimum that teams can deal with and learn from at the same time?&lt;br /&gt;&lt;br /&gt;So now, a team dedicated to providing CI services is recommending quality reports to both developers and stakeholders AND helping to define the rules that code should be compared against.  My first take on this approach, deviating from a traditional CI definition was a sense of invasiveness.  Upon further reflection, I have embraced the blurred lines between teams for several reasons.  Firstly, it breaks artificial team boundaries and keeps communication lines open.  CI is no longer a black box, it is a visible, value contributing component of system development.  Secondly, developers won't (at least consistently across an organization) take the time to inject QA into their processes.  A CI team, however, does have the time to take the first steps and get the ball rolling. The conversation is more constructive when you have something in place actually working then just a bunch of talking going on about what could be.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_AEOHFrmyURo/SFJ7Vcn66FI/AAAAAAAABg8/9GpnPcj1kWs/s1600-h/ci-venn.png"&gt;&lt;img style="margin: 0px auto 10px; display: block; text-align: center; cursor: pointer;" src="http://4.bp.blogspot.com/_AEOHFrmyURo/SFJ7Vcn66FI/AAAAAAAABg8/9GpnPcj1kWs/s320/ci-venn.png" alt="" id="BLOGGER_PHOTO_ID_5211363327212185682" border="0" /&gt;&lt;/a&gt;I think a simple diagram emphasizes the point.  If "A" is a development team, "B" is a CI team, and "C" represents the stakeholders of the solution being developed, then the shaded areas pinpoint a missed opportunity unless you adopt the supply chain analogy.  These are not hard control points, but juicy overlap, waiting to be optimized by as AB and BC working together.&lt;br /&gt;&lt;br /&gt;So far, I've talked about the pre-shipment benefit leading into CI.  I also now believe the supply chain post-CI is equally important.  That is, where does the feedback go and how do you make is so easy, there is no reason not to use it.  In our case, this encompassed two audiences with two different needs.&lt;br /&gt;&lt;br /&gt;1) Developers.  They need the low level test results, test coverage, Checkstyle and PMD reports.&lt;br /&gt;&lt;br /&gt;2) Stakeholders (Project Managers, Solution Owners, Scrum Masters).  They need to know that the developer teams are using the system.  What is the source control commit frequency? How long do CI builds stay broken?  How long are the automated builds taking?  In short, the interest is around are they getting their monies worth on CI investment and knowing if the teams "get it".  It provides them with just enough information to start asking the right questions to the right people.&lt;br /&gt;&lt;br /&gt;So the supply chain from CI into management visibility, in our case, ended up being a enterprise level portal aggregating project CI metrics together into an at-a-glance view of how well their agile teams are performing.  This could be stop light charts on current CI conditions or simple graphs of build times or number of tests as plotted over the last 30 days.&lt;br /&gt;&lt;br /&gt;As always seem to be the case, the benefit of this proposed supply chain approach is inceased communication.&lt;br /&gt;&lt;br /&gt;Again, this initially seemed to stray from traditional core CI functionality, but in reality it is simply providing the visibility into the process that scrum promises.  Warts and all.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-8194624477131499759?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/8194624477131499759/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=8194624477131499759' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8194624477131499759'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8194624477131499759'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2008/06/supply-chain-of-continuous-integration.html' title='The Supply Chain of Continuous Integration'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_AEOHFrmyURo/SFAyn5xZxGI/AAAAAAAABg0/LG0P2UjUpOQ/s72-c/ci_logo.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-2331322812974728325</id><published>2008-06-04T10:18:00.000-07:00</published><updated>2008-06-04T10:28:55.939-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><title type='text'>Hudson Status Google Gadget on Linux</title><content type='html'>&lt;a href="http://google-opensource.blogspot.com/2008/06/google-gadgets-for-linux.html"&gt;Google &lt;/a&gt; has just announced gadget support for the linux version of Google Desktop.  This is exciting news for me as that means the Hudson Status gadget is now cross-platform.  For all you developers out there that rely on Hudson, this means you don't have to visit your Hudson site or check your email or your feed reader to get the latest job statuses.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The dots are right there on your desktop!&lt;/blockquote&gt;&lt;br /&gt;For you existing users out there, make sure you upgrade to the newest gadget version, which has some important bug fixes.  More information about the latest release, v0.9.5, can be found on the &lt;a href="http://hudson.gotdns.com/wiki/display/HUDSON/Hudson+Google+Desktop+Gadget"&gt;Hudson wiki&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-2331322812974728325?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/2331322812974728325/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=2331322812974728325' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2331322812974728325'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2331322812974728325'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2008/06/hudson-status-google-desktop-gadget-on.html' title='Hudson Status Google Gadget on Linux'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-72926863674069453</id><published>2008-04-21T12:30:00.000-07:00</published><updated>2008-04-21T12:53:13.409-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Maven SNAPSHOT Traceability</title><content type='html'>So your scrum team is creating non-unique SNAPSHOT versioned artifacts throughout your sprint.  How do trace that SNAPSHOT version back to  a baseline that is of any QA relevance (build number, subversion revision,  datetime stamp)?&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&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-antrun-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;configuration&amp;gt;&lt;br /&gt;       &amp;lt;tasks&amp;gt;&lt;br /&gt;         &amp;lt;tstamp&amp;gt;&lt;br /&gt;           &amp;lt;format property="now" pattern="MM/dd/yyyy hh:mm" unit="hour" /&amp;gt;&lt;br /&gt;         &amp;lt;/tstamp&amp;gt;&lt;br /&gt;         &amp;lt;property name="build.version" value="${version} (private-${now}-${user.name})" /&amp;gt;&lt;br /&gt;         &amp;lt;property name="hudson.build" value="hudson-${BUILD_NUMBER}, subversion-${SVN_REVISION}" /&amp;gt;&lt;br /&gt;                         &lt;br /&gt;         &amp;lt;!-- put the version file --&amp;gt;&lt;br /&gt;         &amp;lt;echo message="The build id is: ${build.version}" /&amp;gt;&lt;br /&gt;         &amp;lt;mkdir dir="target/${project.build.finalName}/" /&amp;gt;&lt;br /&gt;         &amp;lt;echo file="target/${project.build.finalName}/version.properties"&amp;gt;version=${build.version} ${hudson.build}&lt;br /&gt;         &amp;lt;/echo&amp;gt;&lt;br /&gt;       &amp;lt;/tasks&amp;gt;&lt;br /&gt;     &amp;lt;/configuration&amp;gt;&lt;br /&gt;     &amp;lt;goals&amp;gt;&lt;br /&gt;       &amp;lt;goal&amp;gt;run&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;br /&gt;...&lt;br /&gt;&lt;br /&gt;&amp;lt;profiles&amp;gt;&lt;br /&gt; &amp;lt;profile&amp;gt;&lt;br /&gt;   &amp;lt;id&amp;gt;release&amp;lt;/id&amp;gt;&lt;br /&gt;   &amp;lt;properties&amp;gt;&lt;br /&gt;       &amp;lt;!-- for releases, just use the POM version. --&amp;gt;&lt;br /&gt;       &amp;lt;build.version&amp;gt;${version}&amp;lt;/build.version&amp;gt;&lt;br /&gt;       &amp;lt;hudson.build&amp;gt;&amp;lt;/hudson.build&amp;gt;&lt;br /&gt;   &amp;lt;/properties&amp;gt;&lt;br /&gt; &amp;lt;/profile&amp;gt;&lt;br /&gt;&amp;lt;/profiles&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This little piece of magic creates  a &lt;code&gt;version.properties&lt;/code&gt; file that contains some valuable information.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;version=1.1.0-SNAPSHOT (private-04/21/2008 02:23-jblack), hudson-453, subversion-1124 &lt;/code&gt;&lt;br /&gt;&lt;br /&gt;When you do a formal release, specify the &lt;code&gt;-Prelease&lt;/code&gt; profile to have this file simply hold the pom version.&lt;br /&gt;&lt;br /&gt;For the plugin configuration above, this file gets put in a war project root webapp directory, suitable for immediate viewing from your browser!&lt;br /&gt;&lt;br /&gt;The mad props for this idea go to &lt;span class="ltcaption1"&gt;&lt;b&gt;&lt;a href="http://weblogs.java.net/blog/kohsuke/"&gt;Kohsuke&lt;/a&gt;.  &lt;/b&gt;:)&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt; &lt;/span&gt;&lt;/code&gt;&lt;/code&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-72926863674069453?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/72926863674069453/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=72926863674069453' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/72926863674069453'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/72926863674069453'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2008/04/maven-snapshot-traceability.html' title='Maven SNAPSHOT Traceability'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-440303166936688991</id><published>2008-04-09T22:01:00.001-07:00</published><updated>2008-04-17T07:15:59.542-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><title type='text'>Hudson Gadget For Google Desktop</title><content type='html'>I took a first stab at creating a google desktop gadget that can display the job names and statuses of any Hudson instance.  Simply configure it to the Hudson server url and it will discover all the jobs and start polling the for blue dots.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/R_2fWFcvU3I/AAAAAAAABgQ/wNYMuFAlYUg/s1600-h/screenshot_large.png"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; float: left; cursor: pointer;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/R_2fWFcvU3I/AAAAAAAABgQ/wNYMuFAlYUg/s400/screenshot_large.png" alt="" id="BLOGGER_PHOTO_ID_5187477547569337202" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://codebeneath.googlepages.com/hudsongadget" target="_top" rel="nofollow"&gt;Hudson Gadget, Version 0.9.0.&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Let me know if you have any improvement ideas or other issues.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;[update] This gadget has been contributed to the open source &lt;/span&gt;&lt;a style="font-style: italic;" href="http://hudson.gotdns.com/wiki/display/HUDSON/Hudson+Google+Desktop+Gadget"&gt;Hudson &lt;/a&gt;&lt;span style="font-style: italic;"&gt;project.&lt;/span&gt;&lt;b&gt;&lt;br /&gt;&lt;br /&gt;&lt;/b&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-440303166936688991?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/440303166936688991/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=440303166936688991' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/440303166936688991'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/440303166936688991'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2008/04/hudson-gadget-for-google-desktop.html' title='Hudson Gadget For Google Desktop'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_AEOHFrmyURo/R_2fWFcvU3I/AAAAAAAABgQ/wNYMuFAlYUg/s72-c/screenshot_large.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-5954613755516831532</id><published>2008-03-31T20:44:00.000-07:00</published><updated>2008-03-31T21:05:52.699-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='reuse'/><title type='text'>Code Reuse Inside Your Own Company</title><content type='html'>A short read over on Dr. Dobbs, &lt;a href="http://www.ddj.com/architect/207000711"&gt;Where Are the Clients In a SOA?&lt;/a&gt; made some good observations about the mindset of creating reusable SOA components, especially when the reuse is likely to occur in your own back yard.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;"The best "enterprise architecture" plan will never be realized as long as developers assume the client of a piece of code is another piece of code."&lt;/blockquote&gt;&lt;blockquote&gt;"As software developers we are not very good customers of the work our colleagues do. We are quicker to search the Internet for a utility or example than we are to search our own internal repositories." &lt;/blockquote&gt;&lt;blockquote&gt;"More importantly, we aren't thinking about other developers when we create the code. We don't create interfaces that make it easy for people to use our code in ways we have not considered. &lt;span style="font-weight: bold; font-style: italic;"&gt;We don't provide design-level abstractions that help other programmers and architects fit our solution into theirs.&lt;/span&gt; We don't market the code to the rest of the team or to other teams within the organization. We don't do any market research to make sure the nontraditional customers of our code are going to make the most of it. We don't package it in ways that also make it easy to use outside our intended use."&lt;br /&gt;&lt;/blockquote&gt;The emphasis is mine, although it should be yours, too.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-5954613755516831532?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/5954613755516831532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=5954613755516831532' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5954613755516831532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5954613755516831532'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2008/03/code-reuse-inside-your-own-company.html' title='Code Reuse Inside Your Own Company'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-5951953543850844952</id><published>2008-03-07T08:23:00.000-08:00</published><updated>2008-03-07T12:21:22.648-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jetty'/><category scheme='http://www.blogger.com/atom/ns#' term='jax-rs'/><category scheme='http://www.blogger.com/atom/ns#' term='jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>Jersey... Jetty and Maven style!</title><content type='html'>I recently created a webapp that due to time constraints, I implemented with plain old servlets which I am comfortable with.   In reality, the project was a perfect candidate to implement in RESTful style.  I had been loosely following the &lt;a href="https://jersey.dev.java.net/"&gt;Jersey&lt;/a&gt; project, part of the larger java.net &lt;a href="https://metro.dev.java.net/"&gt;Metro&lt;/a&gt; project, so I decided to give Jersey a try.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_AEOHFrmyURo/R9F4h9ILI5I/AAAAAAAABfQ/8yZ-nhLY8Y4/s1600-h/Jersey_yellow.png"&gt;&lt;img style="float: left; cursor: pointer;" src="http://2.bp.blogspot.com/_AEOHFrmyURo/R9F4h9ILI5I/AAAAAAAABfQ/8yZ-nhLY8Y4/s400/Jersey_yellow.png" alt="" id="BLOGGER_PHOTO_ID_5175049971565274002" border="0" /&gt;&lt;/a&gt;"Jersey is the open source ... JAX-RS (JSR 311) Reference Implementation for building RESTful Web services. But, it is also more than the Reference Implementation. Jersey provides  additional APIs and extension points (SPIs) so that developers may extend Jersey to suite their needs."&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;My only requirements were really around a quick turn-around dev setup.  So I wanted to use maven and the jetty plugin to preview the results quickly.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;span style="font-weight:bold;"&gt;Jersey is still only available as an early access preview (0.5-ea) , so some of the examples I found were "out of date", as the team is still refactoring the implementation.&lt;/span&gt;&lt;/blockquote&gt;&lt;br /&gt;So, first step: create a maven war project structure and configure the web.xml to look this this:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;!DOCTYPE web-app PUBLIC "-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN" "http://java.sun.com/dtd/web-app_2_3.dtd"&amp;gt;&lt;br /&gt;&amp;lt;web-app&amp;gt;&lt;br /&gt;&amp;lt;servlet&amp;gt;&lt;br /&gt;  &amp;lt;servlet-name&amp;gt;Jersey&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;  &amp;lt;servlet-class&amp;gt;com.sun.ws.rest.spi.container.servlet.ServletContainer&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;  &amp;lt;init-param&amp;gt;&lt;br /&gt;      &amp;lt;param-name&amp;gt;com.sun.ws.rest.config.property.resourceConfigClass&amp;lt;/param-name&amp;gt;&lt;br /&gt;      &amp;lt;param-value&amp;gt;com.sun.ws.rest.api.core.PackagesResourceConfig&amp;lt;/param-value&amp;gt;&lt;br /&gt;  &amp;lt;/init-param&amp;gt;&lt;br /&gt;  &amp;lt;init-param&amp;gt;&lt;br /&gt;      &amp;lt;param-name&amp;gt;com.sun.ws.rest.config.property.packages&amp;lt;/param-name&amp;gt;&lt;br /&gt;      &amp;lt;param-value&amp;gt;org.naiade.tutorials.jersey;com.gestalt.gci.rest.resources&amp;lt;/param-value&amp;gt;&lt;br /&gt;  &amp;lt;/init-param&amp;gt;&lt;br /&gt;  &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;&lt;br /&gt;&amp;lt;/servlet&amp;gt;&lt;br /&gt;&amp;lt;servlet-mapping&amp;gt;&lt;br /&gt;  &amp;lt;servlet-name&amp;gt;Jersey&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;  &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&amp;lt;/servlet-mapping&amp;gt;&lt;br /&gt;&amp;lt;/web-app&amp;gt;&lt;br /&gt;&lt;/pre&gt;The two key things here are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;use the ServletContainer servlet (not ServletAdapter)&lt;/li&gt;&lt;li&gt;use the pluggable PackagesResourceConfig parameter value.  This allows Jersey to auto find all REST resources based on the package names.  This is configured in the additional parameter value.  Here we provide a couple of packages to look in, semicolon separated.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;For the code, I used the ShoeService example I found &lt;a href="http://objectif-naiade.blogspot.com/2007/11/java-rest-framework-jersey.html"&gt;here.&lt;/a&gt;&lt;br /&gt;As I mentioned before, Jersey is still evolving so I ended up making two code changes to still be able to compile under 0.5-ea:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;@HttpMethod()&lt;/code&gt; changed to &lt;code&gt;@GET&lt;/code&gt; or &lt;code&gt;@POST&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;@UriTemplate()&lt;/code&gt; changed to &lt;code&gt;@Path()&lt;/code&gt;&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;The maven project looks like:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&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 http://maven.apache.org/maven-v4_0_0.xsd"&amp;gt;&lt;br /&gt;&amp;lt;modelVersion&amp;gt;4.0.0&amp;lt;/modelVersion&amp;gt;&lt;br /&gt;&amp;lt;groupId&amp;gt;com.shoe&amp;lt;/groupId&amp;gt;&lt;br /&gt;&amp;lt;artifactId&amp;gt;shoeservice&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;name&amp;gt;Shoe Service REST&amp;lt;/name&amp;gt;&lt;br /&gt;&amp;lt;packaging&amp;gt;war&amp;lt;/packaging&amp;gt;&lt;br /&gt;&amp;lt;dependencies&amp;gt;&lt;br /&gt;   &amp;lt;!-- jersery.dev.java.net --&amp;gt;&lt;br /&gt;   &amp;lt;dependency&amp;gt;&lt;br /&gt;       &amp;lt;groupId&amp;gt;jersey&amp;lt;/groupId&amp;gt;&lt;br /&gt;       &amp;lt;artifactId&amp;gt;jersey&amp;lt;/artifactId&amp;gt;&lt;br /&gt;       &amp;lt;version&amp;gt;0.5-ea&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;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;org.apache.maven.plugins&amp;lt;/groupId&amp;gt;&lt;br /&gt;           &amp;lt;artifactId&amp;gt;maven-compiler-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;           &amp;lt;version&amp;gt;2.0.2&amp;lt;/version&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;/plugin&amp;gt;&lt;br /&gt;       &amp;lt;plugin&amp;gt;&lt;br /&gt;           &amp;lt;groupId&amp;gt;org.mortbay.jetty&amp;lt;/groupId&amp;gt;&lt;br /&gt;           &amp;lt;artifactId&amp;gt;maven-jetty-plugin&amp;lt;/artifactId&amp;gt;&lt;br /&gt;           &amp;lt;version&amp;gt;6.1.8&amp;lt;/version&amp;gt;&lt;br /&gt;           &amp;lt;configuration&amp;gt;&lt;br /&gt;               &amp;lt;scanIntervalSeconds&amp;gt;10&amp;lt;/scanIntervalSeconds&amp;gt;&lt;br /&gt;           &amp;lt;/configuration&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.apache.geronimo.specs&amp;lt;/groupId&amp;gt;&lt;br /&gt;                   &amp;lt;artifactId&amp;gt;geronimo-j2ee_1.4_spec&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;scope&amp;gt;provided&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;/plugin&amp;gt;&lt;br /&gt;   &amp;lt;/plugins&amp;gt;&lt;br /&gt;&amp;lt;/build&amp;gt;&lt;br /&gt;&amp;lt;/project&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;To bring it all together, it's as simple as&lt;br /&gt;&lt;blockquote&gt;&lt;code&gt;mvn jetty:run&lt;/code&gt;&lt;/blockquote&gt;Open you browser and hit &lt;code&gt;http://localhost:8080/shoeservice/shoe&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;Easy&lt;/span&gt;.&lt;br /&gt;&lt;br /&gt;I really see Jersey taking off as it is a good project and easy to work with.  If you are looking for a good Java solution to creating a RESTful accessible services, give it a try.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-5951953543850844952?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/5951953543850844952/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=5951953543850844952' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5951953543850844952'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5951953543850844952'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2008/03/jersey-jetty-and-maven-style.html' title='Jersey... Jetty and Maven style!'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_AEOHFrmyURo/R9F4h9ILI5I/AAAAAAAABfQ/8yZ-nhLY8Y4/s72-c/Jersey_yellow.png' height='72' width='72'/><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-2735967147937011665</id><published>2008-02-12T12:24:00.000-08:00</published><updated>2008-02-12T10:52:07.642-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='netbeans'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><title type='text'>NetBeans 6.0 Maven Integration</title><content type='html'>As a proponent of continuous integration, I naturally don't endorse one IDE over another.  The choice should be free and open for developers to choice what works best for them.  The important thing is that the project should not be reliant on an IDE to build.  That is, there must be an independent build tool that can function outside of any IDE, whether it be ant or maven or whatever.&lt;br /&gt;&lt;br /&gt;I have used several IDEs over the last couple of year to include Eclipse, IDEA and NetBeans .  They all have their strengths and weakness and for the projects I have been working on, I ended up switching around quite a bit.   With the recent announcement of the new &lt;a href="http://www.netbeans.org/"&gt;Netbeans 6.0&lt;/a&gt;, I wanted to revisit it's capabilities and see what was new, particularly with its maven integration.&lt;br /&gt;&lt;br /&gt;While the Netbeans installer was downloading, I started to search for what plugins I would need to do maven-&gt;Netbeans or Netbeans -&gt;maven project conversions.  Most of what I found looked outdated and didn't look easy to use.  With a little stumbling around I found out that the &lt;a href="http://mevenide.codehaus.org/mevenide-netbeans-project/index.html"&gt;Mevenide &lt;/a&gt;project &lt;span style="font-weight: bold;"&gt;does &lt;/span&gt;now support Netbeans 6.0 (this is not apparent on their Netbeans project page).  So I installed the Mevenide Maven2 plugin.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;And was blown away!&lt;/blockquote&gt;&lt;br /&gt;There is no conversion of project types from maven-&gt;NetBeans .  It just works natively.  File &gt; Open Project and browse for your existing maven project.  Bingo.&lt;br /&gt;&lt;br /&gt;I actually started by opening a parent project, &amp;lt;packaging&amp;gt;pom&amp;lt;/packaging&amp;gt;, thinking that the Mavenide wouldn't know what to do with a multi module project, but it just worked, too!  It showed me the project files, libraries, test libraries and the modules that this parent included.   Too cool.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/R7HkAQIm93I/AAAAAAAABfA/t9evydXDjp0/s1600-h/netbeans-maven-1.PNG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; cursor: pointer;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/R7HkAQIm93I/AAAAAAAABfA/t9evydXDjp0/s400/netbeans-maven-1.PNG" alt="" id="BLOGGER_PHOTO_ID_5166160940552877938" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So then I double clicked my war module name hoping for the best and was not disappointed.  It opened the war project in the project pane and I was off and running.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_AEOHFrmyURo/R7HkIgIm94I/AAAAAAAABfI/yypRQuaiC5w/s1600-h/netbeans-maven-2.PNG"&gt;&lt;img style="margin: 0pt 10px 10px 0pt; cursor: pointer;" src="http://4.bp.blogspot.com/_AEOHFrmyURo/R7HkIgIm94I/AAAAAAAABfI/yypRQuaiC5w/s400/netbeans-maven-2.PNG" alt="" id="BLOGGER_PHOTO_ID_5166161082286798722" border="0" /&gt;&lt;br /&gt;&lt;/a&gt;&lt;br /&gt;Right click on the project name to run the more common maven goals from right inside NetBeans.  To run custom goals, you can create a Custom &gt; Goals... goal that you can use to chain together whatever goals you want for a maven execution.  This is the only thing I did that actually created a custom file on my filesystem.  Everything else was native.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;How's that for a slice of fried gold?  &lt;/blockquote&gt;So, here's a big high five to to the Mevenide team on a job well done!  (And update your NetBeans project page to highlight that it works in Netbeans 6.0.  This is important news to advertise!).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-2735967147937011665?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/2735967147937011665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=2735967147937011665' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2735967147937011665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2735967147937011665'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2008/02/netbeans-60-maven-integration.html' title='NetBeans 6.0 Maven Integration'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_AEOHFrmyURo/R7HkAQIm93I/AAAAAAAABfA/t9evydXDjp0/s72-c/netbeans-maven-1.PNG' height='72' width='72'/><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-3174084057964895623</id><published>2008-01-15T13:21:00.000-08:00</published><updated>2008-02-07T22:11:12.388-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='collaboration'/><category scheme='http://www.blogger.com/atom/ns#' term='learning'/><title type='text'>Global Collaboraton In the Second Grade</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/R6vrXr-3_iI/AAAAAAAABew/LT6md3cLS5Q/s1600-h/world.gif"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 244px; height: 156px;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/R6vrXr-3_iI/AAAAAAAABew/LT6md3cLS5Q/s320/world.gif" alt="" id="BLOGGER_PHOTO_ID_5164480189886365218" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I just finished reading &lt;a href="http://en.wikipedia.org/wiki/The_World_is_Flat"&gt;The World is Flat&lt;/a&gt; by Thomas L. Friedman.  He had some good insights and specific example about how the world is increasingly connected and how it functions as a global marketplace.  The most interesting take away I had was that simply providing the cabling into countries that had not been previously connected was enough of a spark to allow individuals with curiosity and an innovative spirit to engage a global community.&lt;br /&gt;&lt;br /&gt;Those of us in the software business, take this interconnectedness for granted now.  I find it fascinating, being a father of two, that my children will know no different; the world was always connected.  In the span of a single generation the world got very small indeed.  I have friends who grew up their entire life in single small town.  Any road trip of more than 100 miles was a big deal.  But my kids won't have to "go" anywhere to instantly know what is going on on the other side of the planet or to actively collaborate with people on other continents.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_AEOHFrmyURo/R6vs8b-3_jI/AAAAAAAABe4/txtfG2BelyM/s1600-h/line_of_kids.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer; width: 238px; height: 178px;" src="http://2.bp.blogspot.com/_AEOHFrmyURo/R6vs8b-3_jI/AAAAAAAABe4/txtfG2BelyM/s320/line_of_kids.jpg" alt="" id="BLOGGER_PHOTO_ID_5164481920758185522" border="0" /&gt;&lt;/a&gt;So with this in mind, I had an idea for an education software project.  My second-grader already has computer lab class once a week where he and his classmates learn and solve problems on a standalone system.  What better time to learn about teamwork, on a global scale, then now, while he is in school.&lt;br /&gt;&lt;br /&gt;My idea is a collaborative system that lets kids work together to solve and reinforce basic school (and life) skills.  As math is a universal language, it would be a good first subject.&lt;br /&gt;&lt;br /&gt;Picture this:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The system is browser-based and has a easy to use, friendly RIA interface.&lt;/li&gt;&lt;li&gt;One student from the USA joins the "Collabmath" course; One student from India joins the same course and they are paired together to solve 5 math problems.&lt;/li&gt;&lt;li&gt;Each student will have a friendly avatar with their county's flag next to it.  Each student's name can be clicked on to hear it pronounced in the native language.  Google maps can show each student's school marked on a world map.&lt;/li&gt;&lt;li&gt;The math problem will be presented in a large left hand workspace, and a chat window could be in a right-hand pane.&lt;/li&gt;&lt;li&gt;The chat window can be used by the students to provide encouragement or ask for help.  The chat would be I18N capable and feature emoticons that would make the session more personable.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_AEOHFrmyURo/R6vk_7-3_hI/AAAAAAAABeo/sS9pCRT6W54/s1600-h/emots.jpg"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; cursor: pointer; width: 298px; height: 70px;" src="http://4.bp.blogspot.com/_AEOHFrmyURo/R6vk_7-3_hI/AAAAAAAABeo/sS9pCRT6W54/s400/emots.jpg" alt="" id="BLOGGER_PHOTO_ID_5164473184794705426" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Each student takes turns solving a problem a step at a time.  When each step is complete, control transfers to the other student, and so on.  So, for a simple example, if the problem is to draw an octagon, the first student will draw the first line segment, then the second student will draw a complementary line segment connected to the first.  When the octagon is complete, the students will have worked together to solve the problem.&lt;/li&gt;&lt;li&gt;At the conclusion of each problem, a practical real world example of the use of that problem in each culture could be presented.  In my example, the students would learn that in America, the octagon is the shape of a traffic stop sign; a related symbolic  example could be presented that would be relevant in the country of India.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;A wide range subjects and grade levels could be taught, of course, but for starters, this could get the ball rolling.  I envision my kids being excited one day, coming home from school, "Dad! Guess what?  I learned division and fractions with a kid from &lt;span style="font-weight: bold;"&gt;China &lt;/span&gt;today!  Her name was Ming Wu! It was &lt;span style="font-weight: bold;"&gt;so cool&lt;/span&gt;!"&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I can see it.  Can you?&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-3174084057964895623?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/3174084057964895623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=3174084057964895623' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/3174084057964895623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/3174084057964895623'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2008/01/global-collaboraton-in-second-grade.html' title='Global Collaboraton In the Second Grade'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_AEOHFrmyURo/R6vrXr-3_iI/AAAAAAAABew/LT6md3cLS5Q/s72-c/world.gif' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-8312119417126689320</id><published>2007-12-06T07:08:00.000-08:00</published><updated>2007-12-06T07:15:33.605-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Continuous Integration Strategies (Part IV)</title><content type='html'>Bonus edition.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Check out &lt;a href="http://www.ibm.com/developerworks/java/library/j-ap11297/index.html"&gt;Continuous Integration Anti-Patterns.&lt;br /&gt;&lt;/a&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-8312119417126689320?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/8312119417126689320/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=8312119417126689320' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8312119417126689320'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8312119417126689320'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/12/continuous-integration-strategies-part_06.html' title='Continuous Integration Strategies (Part IV)'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-4876042830832129956</id><published>2007-12-04T19:24:00.000-08:00</published><updated>2007-12-04T21:59:11.449-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Continuous Integration Strategies (Part III)</title><content type='html'>How do you get your code to talk to you?  Continuous integration is all about automated feedback.   Beyond test reports and self-describing-code there are many techniques and tools that you can use to find out if you are producing "quality" code.&lt;br /&gt;&lt;br /&gt;When you pick the right reports and integration them into the software build, the level of effort required to use these tools becomes very low (and we all know that developers are legendarily lazy).&lt;br /&gt;&lt;br /&gt;Ideally, you should not have to remember to ask, the code should tell.&lt;br /&gt;&lt;br /&gt;It's like being the parent of a teenager.  At the dinner table you ask,&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;"So, how was school today?"&lt;br /&gt;"I dunno."&lt;br /&gt;&lt;br /&gt;"What did you learn?"&lt;br /&gt;"Nothin'"&lt;br /&gt;&lt;br /&gt;"Did anything interesting happen?"&lt;br /&gt;"I dunno."&lt;br /&gt;&lt;br /&gt;"How did you do on your history test"&lt;br /&gt;(shrug)&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;You would rather he came home with the enthusiasm of a kindergartener eager to tell you about his day as soon as he gets home.  No effort on your part to ask, he just gushes unprompted,&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;"Dad!! Guess what happened today at school??  It was so cool, I got an A+ on my spelling test!!"&lt;br /&gt;&lt;/blockquote&gt;That's what continuous integration can do for you.&lt;br /&gt;&lt;br /&gt;So, recalling the computer adage, Garbage In, Garbage Out, we have carefully picked a select few reports that we feel give us a good measure of quality and integrated them into our build so that the feedback from CI is meaningful.  Although several CI engines (we currently use Hudson) do have plugins to generate quality reports such as unit test results and test coverage, we feel it is important that developers have full access to run all the same reports that CI will run. Since we run maven, it is easy to plugin the reports of interest into the &lt;code&gt;&amp;lt;reporting&amp;gt;&lt;/code&gt; section of the pom.  That keeps the report configurations in source control along with the code.&lt;br /&gt;&lt;br /&gt;So which reports do we run?  Here's the rundown:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Checkstyle&lt;/b&gt;.  Validates source code against coding standards and reports any violations.  We customized the ruleset, packaged it in a versioned jar and deployed it to our maven repository.  Additionally, we forced it to run as part of every build in the &lt;code&gt;validate&lt;/code&gt; phase, and configured it to fail the build if violations are found.  It's possible for an individual project to override the custom ruleset, but we don't encourage that.&lt;/li&gt;&lt;li&gt;&lt;b&gt;PMD&lt;/b&gt;.  Performs design time analysis of the source code against a standard ruleset.  We customized the ruleset, packaged it in a versioned jar and deployed it to our maven repository.  Additionally, we forced it to run as part of every build in the &lt;code&gt;validate&lt;/code&gt; phase, and configured it to fail the build if violations are found.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Test Results &lt;/b&gt;(surefire).  This is a no-brainer.  You want to see the results of your tests.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Code Coverage &lt;/b&gt;(cobertura).  Shows branch and line coverage for your source files.   I think the key to using this metric is to not to set a percentage that the project team must meet, but to be smart about interpreting the trends.  "Metrics are meant to help you think, not to do the thinking for you."&lt;/li&gt;&lt;li&gt;&lt;b&gt;Javadoc&lt;/b&gt;.   Creates the API documentation for your project.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Dashboard&lt;/b&gt;.   Aggregates maven multiproject build reports into a single report page.  This is critical so that developers and stakeholders don't have to hunt and drill down page after page to find the meaningful metrics you worked so hard to set up.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;In addition, make full use of Maven's pom to declare all the sections that feed the default generated site, including:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&amp;lt;scm&amp;gt;  Source control management.  New team members, for example will need to know the subversion URL for checkout.&lt;/li&gt;&lt;li&gt;&amp;lt;developers&amp;gt;  Identifies subject matter experts and feeds developer activity reports.&lt;/li&gt;&lt;li&gt;&amp;lt;ciManagement&amp;gt;  Identifies the CI engine being used and the URL to see the live status and force new builds.&lt;/li&gt;&lt;li&gt;&amp;lt;issueManagement&amp;gt;  Identifies the issue tracking system.  I think there are maven plugins that will map issues to source control commits, providing bi-traceability of code to requirements.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Also, don't forget for maven multiproject builds to review the Dependency Convergence report.  It will show all dependencies for all projects along with the versions of those dependencies.  This will help you find dependencies your using inadvertently using multiple versions of.&lt;br /&gt;&lt;br /&gt;Once you have these reports baked in, make the results easy to find.  Use your CI engine to generate the maven site on a nightly basis and publish the results to a web server where developers or project stakeholders can find them.&lt;br /&gt;&lt;br /&gt;After you spend the time and effort to identify which reports you want and get them configured and working correctly, make it repeatable for new projects by creating an archetype template project.  This sets up a model pom.xml and project directory structure right off the bat for new projects.  When it's there from the start, with no effort on the team's part, good things happen.&lt;br /&gt;&lt;br /&gt;With a little up front effort, your code (with a little help from continuous integration) can talk to you.  What are your strategies?  How do you use CI to reveal code quality?  I would be interested in hearing your strategies.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-4876042830832129956?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/4876042830832129956/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=4876042830832129956' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/4876042830832129956'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/4876042830832129956'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/12/continuous-integration-strategies-part.html' title='Continuous Integration Strategies (Part III)'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-7708029707186909683</id><published>2007-11-02T08:05:00.000-07:00</published><updated>2007-11-02T08:22:47.198-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='innovation'/><title type='text'>"The people are the network"</title><content type='html'>I found this fantastic read that expands globally off of my &lt;a href="http://codebeneath.blogspot.com/2007/09/there-goes-gravity.html"&gt;previous&lt;/a&gt; "organization-charts-get-in-the-way" post.  It is long, but &lt;span style="font-weight: bold; font-style: italic;"&gt;well &lt;/span&gt;worth the read.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://blog.futurestreetconsulting.com/?p=39"&gt;Mob Rules (The Law of Fives)&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is my favorite quote from the text:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;“The net regards hierarchy as a failure, and routes around it.”&lt;/blockquote&gt;&lt;br /&gt;The summary is equally short and powerful:&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;br /&gt;"Still, there is one thing I can recommend: &lt;strong&gt;have courage and &lt;em&gt;keep moving&lt;/em&gt;&lt;/strong&gt;&lt;span style="font-weight: normal; font-style: normal;"&gt;.&lt;/span&gt;&lt;span&gt;  &lt;/span&gt;Standing still is not an option."&lt;br /&gt;&lt;span&gt;&lt;/span&gt;&lt;br /&gt;&lt;/blockquote&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-7708029707186909683?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/7708029707186909683/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=7708029707186909683' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/7708029707186909683'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/7708029707186909683'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/11/people-are-network.html' title='&quot;The people are the network&quot;'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-2374060707121565079</id><published>2007-10-31T10:39:00.000-07:00</published><updated>2007-10-31T11:20:51.559-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Continuous Integration Strategies (Part II)</title><content type='html'>Your CI environment is reporting a broken build.  Now what?&lt;br /&gt;&lt;br /&gt;I would like to stress that the faster you jump on the problem, the easier it is to solve.  The changeset will be smaller and the person who most likely committed the offending code will have the changes he made freshly in mind.&lt;br /&gt;&lt;blockquote&gt;It is a good policy that your team does not commit any additional changes to source control until the build is fixed.&lt;/blockquote&gt;At times, it is too easy to ignore a broken build message, whether it is an email notification or a &lt;a href="http://cruisecontrol.sourceforge.net/main/configxml.html#x10"&gt;flashing red light&lt;/a&gt; or a &lt;a href="http://hudson.gotdns.com/wiki/display/HUDSON/Hudson+Build+Status+Lava+Lamps"&gt;lava lamp&lt;/a&gt;.  Sometimes, the team will assume someone else is working the issue.  I will always recommend having immediate and active communication that the problem is being worked so that positive control in maintained.&lt;br /&gt;&lt;br /&gt;There were some additional words of wisdom that recently circulated here that I would like to share with you.  &lt;span style="font-style: italic;"&gt;Props to &lt;a href="http://chadthedeveloper.blogspot.com/"&gt;Chad &lt;/a&gt;for nailing the importance of the entire team owning CI and having active communication about its status.  The emphasis is mine.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;Make sure you're at least running unit tests before you commit.  You can also have a buddy immediately update and build if you want feedback before waiting for Cruise Control to build.  Also, after you commit, watch your email for a notification that the build has broken.  If you're not going to be around, use a check-in buddy as mentioned previously.&lt;br /&gt;&lt;br /&gt;If you've just committed a change and receive a build failure notification email, look into what's causing the problem asap.  If it's a quick fix, just make the change and re-commit.  Optionally &lt;span style="font-weight: bold;"&gt;reply to the build failure notification so that the team is aware you're putting in the fix.  If it's not a quick fix, reply to the build failure notification stating that you're working the issue; again so the team is aware.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;...The build is everyone's responsibility.&lt;br /&gt;&lt;br /&gt;If you see that the build is remaining broken for a period of time, take it upon yourself to investigate.  Find out if anyone is working the issue.  If not, try to identify the problem and notify the party responsible so that the build can be fixed quickly.  Now if you can't figure out what's wrong and identify who is responsible, take it upon yourself to fix the issue.  If you are too busy, find someone that can.  If you can't figure it out, ask for help.  Use it as an opportunity to learn something.  When you do finally find the problem, let the responsible party know what happened.  Then they can learn something as well.&lt;br /&gt;&lt;br /&gt;To sum it up, there shouldn't be any duration of time where the build is broken but isn't being looked into.  While everyone should be watching after they commit to see if they've broken the build, it won't always get caught.  You shouldn't be saying to yourself "I didn't break it so it's not my job to fix it".  &lt;/blockquote&gt;And that's the word.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_AEOHFrmyURo/RyjFPVmUY_I/AAAAAAAABbE/iCjND-E0PS4/s1600-h/bullseye.jpg"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_AEOHFrmyURo/RyjFPVmUY_I/AAAAAAAABbE/iCjND-E0PS4/s400/bullseye.jpg" alt="" id="BLOGGER_PHOTO_ID_5127565043047883762" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-2374060707121565079?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/2374060707121565079/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=2374060707121565079' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2374060707121565079'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2374060707121565079'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/10/continuous-integration-strategies-part_31.html' title='Continuous Integration Strategies (Part II)'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_AEOHFrmyURo/RyjFPVmUY_I/AAAAAAAABbE/iCjND-E0PS4/s72-c/bullseye.jpg' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-2055662975125357466</id><published>2007-10-19T12:29:00.001-07:00</published><updated>2007-10-24T14:07:14.493-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cruisecontrol'/><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='cvs'/><category scheme='http://www.blogger.com/atom/ns#' term='maven'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Continuous Integration Strategies (Part I.I)</title><content type='html'>At the end of each successful continuous integration build and test suite, we label the workspace with a certified build tag within source control.  This allows for bi-traceability from build sequence number to the tag name for QA purposes.  Additionally, we can also do a simple lookup on the build number in Hudson to get a subversion revision number.&lt;br /&gt;&lt;br /&gt;Below are two examples of how we have accomplished this.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Maven 1 and cruisecontrol and cvs:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We wrote a custom jelly goal that called ant's cvs task.&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;goal name="nct:createcertifiedtag"&amp;gt;&lt;br /&gt; &amp;lt;ant:cvs command="tag certified-build-${label}" /&amp;gt;&lt;br /&gt;&amp;lt;/goal&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice the &lt;code&gt;"label"&lt;/code&gt; property.  Cruisecontrol provides maven that property to use at runtime with the value set to the build number. &lt;code&gt;&lt;/code&gt;  We use this custom goal at the end of the cruisecontrol project's maven goal element:&lt;br /&gt;&lt;code&gt;&lt;/code&gt;&lt;pre&gt;&lt;br /&gt;&amp;lt;maven projectfile="${PROJECT_ROOT}/project.xml" goal=clean install nct:certifiedtag" /&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Maven 2 and hudson and subversion:&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;In the maven pom.xml (or the parent pom.xml), specify the all the source control details so things become easier later.  For example, our build includes:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&lt;br /&gt;&amp;lt;scm&amp;gt;&lt;br /&gt; &amp;lt;connection&amp;gt;scm:svn:https://svnhost/svn/sto/trunk&amp;lt;/connection&amp;gt;    &lt;br /&gt; &amp;lt;developerConnection&amp;gt;scm:svn:https://svnhost/svn/sto/trunk&amp;lt;/developerConnection&amp;gt;&lt;br /&gt; &amp;lt;url&amp;gt;https://svnhost/svn/sto/trunk&amp;lt;/url&amp;gt;&lt;br /&gt;&amp;lt;/scm&amp;gt;&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Hudson provides maven a &lt;code&gt;"hudson.build.number"&lt;/code&gt; property to use at runtime populated with the build number.  We use it by referencing that on the Goals line in the Hudson job configuration.  Additionally, we made an improve over using an external process call to 'svn' by using the maven 2 &lt;a href="http://maven.apache.org/scm/plugins/index.html"&gt;SCM plugin&lt;/a&gt;.&lt;br /&gt;&lt;pre&gt;clean install scm:tag -Dtag=certified-build-${hudson.build.number} &lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;[update: &lt;code&gt;${hudson.build.number}&lt;/code&gt; seems to be buggy.  I have successfully used &lt;code&gt;${BUILD_NUMBER}&lt;/code&gt; in its place]&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-2055662975125357466?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/2055662975125357466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=2055662975125357466' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2055662975125357466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2055662975125357466'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/10/continuous-integration-strategies-part_19.html' title='Continuous Integration Strategies (Part I.I)'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-6100890923369262450</id><published>2007-10-18T19:11:00.000-07:00</published><updated>2007-10-18T21:32:43.391-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='cruisecontrol'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Continuous Integration Strategies (Part I)</title><content type='html'>&lt;a href="http://martinfowler.com/articles/continuousIntegration.html"&gt;Continuous integration &lt;/a&gt;is a powerful concept, usually associated with only compilation and unit testing.  However, there is additional  benefit to be had if you look beyond unit testing.  I would like to present some strategies that I have tried that allow full suites of tests to be ran in orderly stages, from unit tests, to integration tests to acceptance tests.  For this series unit tests are defined as single class tests with no external dependencies on network or container resources.  Integration tests are white box testing of class interactions and acceptance tests are black box system tests.&lt;br /&gt;&lt;br /&gt;This first post on the subject deals with strategies using &lt;a href="http://maven.apache.org/"&gt;maven &lt;/a&gt;and &lt;a href="http://cruisecontrol.sourceforge.net/"&gt;cruisecontrol&lt;/a&gt;.  Later posts will move on to maven 2 and &lt;a href="https://hudson.dev.java.net/"&gt;hudson&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;First off, a lesson learned.  When we first migrated from ant to maven, we were not sure how best to configure cruisecontrol to handle CI.  Our code base is a large selection of components that comprise a toolset of capabilities.  There are many small projects that build on each other, so there are many dependencies on our on artifacts.  In fact, from a maven point of view, we could build our toolset with a single, rather large, multiproject build.&lt;br /&gt;&lt;br /&gt;It seemed logical to map each component maven project (itself a multiproject consisting of api + implementations + tests) to a cruisecontrol project.  That presented a nice one-to-one view of the system on the build status page.  Each project was independently triggered via cvs commits.  This seemed to work for awhile, but it became clear that this is highly unstable because commits spanning multiple cruisecontrol projects would trigger the builds in an unpredictable order, causing the build to break or tests to fail.&lt;br /&gt;&lt;br /&gt;The lesson learned and correction we took was to not fight maven, but let it determine the build order from start to finish.  So we created a single cruisecontrol project, pointed it at the top-most maven project.xml with the goal &lt;code&gt;multiproject:install&lt;/code&gt; and the property &lt;code&gt;-Dmaven.test.failure.ignore=true&lt;/code&gt;.  Then for each component project we wanted test status granularity on, we created a cruisecontrol project that ran a custom maven plugin that scanned the &lt;code&gt;test-results&lt;/code&gt; directory and failed that project if test failures were found.  Additionally, that cruisecontrol project also used &lt;code&gt;&amp;lt;merge&amp;gt;&lt;/code&gt; to aggregate maven's &lt;code&gt;test-results&lt;/code&gt; files so our developers could drill down and see which test failed and the details why.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_AEOHFrmyURo/Rxgkh1fU-yI/AAAAAAAABXs/OR2KJo3km1s/s1600-h/ci-strategy.PNG"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_AEOHFrmyURo/Rxgkh1fU-yI/AAAAAAAABXs/OR2KJo3km1s/s800/ci-strategy.PNG" alt="" id="BLOGGER_PHOTO_ID_5122884739846699810" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;As a quick aside, Hudson has very nice maven integration that mimics (and improves on) this kind of setup automatically.&lt;/blockquote&gt;Our next step was to enable a controlled progression of testing, where all unit test would run first, followed by integration tests &lt;i&gt;only if all unit tests passed&lt;/i&gt;. This was accomplished in three steps: 1) one maven multiproject build responsible for compiling and unit testing, 2) a custom test failure check plugin (basically find + grep) serving as the go-no-go gate, then 3) another maven multiproject build running only integration tests.  This three step orchestration was handled by a custom maven plugin running a mix of jelly and shell scripting.&lt;br /&gt;&lt;br /&gt;The different types of tests were in different directories, as maven subprojects, under the component, so we were able to use &lt;code&gt;maven.multiproject.includes&lt;/code&gt; and &lt;code&gt;maven.multiproject.excludes&lt;/code&gt; on the directory names to achieve steps 1) and 3) above.&lt;br /&gt;&lt;br /&gt;To facilitate the the including and excluding for the unit test pass, the &lt;code&gt;~/build.properties&lt;/code&gt; included these properties:&lt;br /&gt;&lt;pre&gt;  maven.multiproject.includes=**/project.xml&lt;br /&gt; maven.multiproject.excludes=project.xml,*/project.xml,**/inttest/project.xml,**/tck/project.xml&lt;br /&gt;&lt;/pre&gt;For the integration test pass, the &lt;code&gt;~/build.properties&lt;/code&gt; included these properties:&lt;br /&gt;&lt;pre&gt;  gcp.integration.multiproject.includes=**/inttest/project.xml&lt;br /&gt; gcp.integration.multiproject.excludes=**/tck/project.xml&lt;br /&gt;&lt;/pre&gt;and the plugin goal to actually run the integration tests looked like this:&lt;br /&gt;&lt;pre&gt;  &amp;lt;goal name="gcp:integration-tests"&amp;gt;&lt;br /&gt;   &amp;lt;j:set var="usethese" scope="parent" value="${gcp.integration.multiproject.includes}"/&amp;gt;&lt;br /&gt;   &amp;lt;j:set var="notthese" scope="parent" value="${gcp.integration.multiproject.excludes}"/&amp;gt;&lt;br /&gt;   &amp;lt;j:set var="thisgoal" scope="parent" value="test:test"/&amp;gt;&lt;br /&gt;   ${systemScope.put('maven.multiproject.includes', usethese)}&lt;br /&gt;   ${systemScope.put('maven.multiproject.excludes', notthese)}&lt;br /&gt;   ${systemScope.put('goal', thisgoal)}&lt;br /&gt;   &amp;lt;maven:maven descriptor="${CC_HOME}/checkout/gcp/project.xml" goals="multiproject:goal"/&amp;gt;&lt;br /&gt; &amp;lt;/goal&amp;gt;&lt;br /&gt;&lt;/pre&gt;The crazy jelly maneuvering to get the includes and excludes properties to stick after the call to &lt;code&gt;maven:maven&lt;/code&gt; is a story for another day.  (&lt;span style="font-style: italic;"&gt;If you want a sneek peek, however, &lt;/span&gt;&lt;a href="http://weblog.jamisbuck.org/2004/6/10/jelly-scripting-for-the-soulless"&gt;&lt;span style="font-style: italic;"&gt;start here.&lt;/span&gt;&lt;/a&gt;)  The concept of dynamically using maven properties to properly setup the integration test run should still be clear.&lt;br /&gt;&lt;br /&gt;Hopefully, this first post in a series will help you think about ways to get more out of your CI environment.  I'm curious to know what you think about the strategy we took and I invite you to share how you accomplish CI for your projects.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-6100890923369262450?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/6100890923369262450/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=6100890923369262450' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6100890923369262450'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6100890923369262450'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/10/continuous-integration-strategies-part.html' title='Continuous Integration Strategies (Part I)'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_AEOHFrmyURo/Rxgkh1fU-yI/AAAAAAAABXs/OR2KJo3km1s/s72-c/ci-strategy.PNG' height='72' width='72'/><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-1678516703317943714</id><published>2007-10-10T13:35:00.000-07:00</published><updated>2007-10-10T14:29:41.422-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hudson'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Hudson, At Your Continuous Integration Service</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/Rw0-Q1fU-wI/AAAAAAAABWo/YmF_83TJYJM/s1600-h/hudson-250_268px.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/Rw0-Q1fU-wI/AAAAAAAABWo/YmF_83TJYJM/s320/hudson-250_268px.png" alt="" id="BLOGGER_PHOTO_ID_5119816810347428610" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I love &lt;a href="https://hudson.dev.java.net/"&gt;Hudson&lt;/a&gt;.  I have previously been a CruiseControl fan, but no longer.  Hudson just works and has some excellent integration features with Maven2 and Subversion.&lt;br /&gt;&lt;br /&gt;Generally, Hudson does this kind of stuff you would expect from a CI engine:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;&lt;b&gt;Easy installation&lt;/b&gt;: Just &lt;tt&gt;java -jar hudson.war&lt;/tt&gt;, or deploy it in a servlet container. No additional install, no database.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Easy configuration&lt;/b&gt;: Hudson can be configured entirely from its friendly web GUI with extensive on-the-fly error checks and inline help. There's no need to tweak XML manually anymore, although if you'd like to do so, you can do that, too.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Change set support&lt;/b&gt;: Hudson can generate a list of changes made into the build from CVS/Subversion. This is also done in a fairly efficient fashion, to reduce the load of the repository.&lt;b&gt;&lt;br /&gt;&lt;/b&gt;&lt;/li&gt;&lt;li&gt;&lt;b&gt;RSS/E-mail Integration&lt;/b&gt;: Monitor build results by RSS or e-mail to get real-time notifications on failures.&lt;/li&gt;&lt;li&gt;&lt;b&gt;JUnit/TestNG test reporting&lt;/b&gt;: JUnit test reports can be tabulated, summarized, and displayed with history information, such as when it started breaking, etc. History trend is plotted into a graph.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Distributed builds&lt;/b&gt;: Hudson can distribute build/test loads to multiple computers. This lets you get the most out of those idle workstations sitting beneath developers' desks.&lt;/li&gt;&lt;li&gt;&lt;b&gt;Plugin Support&lt;/b&gt;: Hudson can be extended via 3rd party plugins. You can write plugins to make Hudson support tools/processes that your team uses.&lt;/li&gt;&lt;/ol&gt;Then the cool stuff kicks in!&lt;br /&gt;&lt;br /&gt;Since each build has a persistent workspace, we can go back in time to see that workspace to trace what happened.  This means Hudson can do after-the-fact tagging and has permanent links to all builds, including "latest build"/"latest successful build", so that they can be easily linked from elsewhere.&lt;br /&gt;&lt;br /&gt;I really like the matrix style jobs that you can create.  For a matrix build, you can specify the JDK version as one axis and a slave Hudson (distributed builds) as another axis.  Add to that a third axis of arbitrary property/values pairs that your build understands and can act on (e.g. think, maven &lt;code&gt;-PmyProfile&lt;/code&gt; or &lt;code&gt;-Dapp.runSystemTests=true&lt;/code&gt; or &lt;code&gt;-Ddatabase.flavor=mysql&lt;/code&gt;).   This is powerful stuff right out of the box to support multiple compilers, OSes, etc., without having to create a new job for each configuration.&lt;br /&gt;&lt;br /&gt;For maven 2 projects, Hudson will autodiscover the &lt;code&gt;&amp;lt;modules&amp;gt;&lt;/code&gt; in a multiproject build and list them as sub-jobs in Hudson, complete with their own status and viewable workspaces.  Links to project artifacts are linked to from the build status pages.&lt;br /&gt;&lt;br /&gt;The plugins are also starting to become plentiful.  There are plugins for JIRA and Trac integration, code violations charting, publishing builds to Google calendar!, and &lt;a href="http://hudson.gotdns.com/wiki/display/HUDSON/Plugins"&gt;many more&lt;/a&gt;.   The one with the most potential I think is the Jabber plugin.  Not only can it send IM notifications to an individual or a group, but the newest (cvs head) version comes with a bot that you can interact with to schedule builds, get project statuses, and monitor the build queue.&lt;br /&gt;&lt;br /&gt;As always, sometimes small things mean a lot.  An example I appreciate in Hudson is seeing the build in progress scrolling by via the browser.  For CruiseControl, you would have to login to the build box and "tail -f" to get the same real-time information.&lt;br /&gt;&lt;br /&gt;I'm not a &lt;a href="http://groovy.codehaus.org/"&gt;groovy &lt;/a&gt;fan yet, but I was blown away when I found a built in groovy console right there in the web UI!  You can use for trouble-shooting and diagnostics of your builds or plugins.&lt;br /&gt;&lt;br /&gt;All in all, I am really, really impressed with Hudson as a product, and with the support and development going on around it.  There is a new version released literally every week.  It makes me, for the first time, want to contribute to an OSS project.  This is good stuff.  Check it out.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-1678516703317943714?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/1678516703317943714/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=1678516703317943714' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1678516703317943714'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1678516703317943714'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/10/hudson-at-your-continuous-integration.html' title='Hudson, At Your Continuous Integration Service'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_AEOHFrmyURo/Rw0-Q1fU-wI/AAAAAAAABWo/YmF_83TJYJM/s72-c/hudson-250_268px.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-5086193774529635352</id><published>2007-10-08T07:53:00.000-07:00</published><updated>2007-10-08T08:20:57.959-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='innovation'/><category scheme='http://www.blogger.com/atom/ns#' term='accountability'/><title type='text'>"Continuous Partial Attention"</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_AEOHFrmyURo/RwpHgVfU-vI/AAAAAAAABWg/MNy2Jbti_dI/s1600-h/partial-attention.png"&gt;&lt;img style="margin: 0pt 0pt 10px 10px; float: right; cursor: pointer;" src="http://2.bp.blogspot.com/_AEOHFrmyURo/RwpHgVfU-vI/AAAAAAAABWg/MNy2Jbti_dI/s320/partial-attention.png" alt="" id="BLOGGER_PHOTO_ID_5118982547309918962" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;I have blogged about being a "knowledge worker" before, but just found another good &lt;a href="http://www.slideshare.net/trib/i-am-knowledge-worker-20"&gt;slideshare &lt;/a&gt;resource about it thanks to a link by &lt;a href="http://limnthis.typepad.com/limn_this/2007/10/wouldnt-slidesh.html"&gt;Jim S&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The funniest thing was on slide 25 where a knowledge worker is described as having "Continuous Partial Attention." &lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I love that description.  &lt;/blockquote&gt;I work with several people who exhibit this trait and it is one the key characteristics, I think, that makes them successful in the workplace.  To be sure, they are also criticized because their team-level focus seems to be lacking, however, this is made up for in the bigger benefit to the company.  This is just another way to transcend team boundaries and have organization-wide impact.&lt;br /&gt;&lt;br /&gt;And the reason it's funny is because it's true.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-5086193774529635352?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/5086193774529635352/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=5086193774529635352' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5086193774529635352'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/5086193774529635352'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/10/continuous-partial-attention.html' title='&quot;Continuous Partial Attention&quot;'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_AEOHFrmyURo/RwpHgVfU-vI/AAAAAAAABWg/MNy2Jbti_dI/s72-c/partial-attention.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-2998798028620904746</id><published>2007-09-25T23:29:00.000-07:00</published><updated>2007-09-25T21:24:59.025-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='problem solving'/><title type='text'>How to Host a Web Server at Home With Dynamic DNS</title><content type='html'>There are a lot of moving parts to get right to do this, and not a lot of good advice about how to do it successfully.  I hope that my steps will help you get it right the first time.&lt;br /&gt;&lt;br /&gt;My need for an at home hosted web service came from trying to develop a simple FaceBook application.  I needed my application hosted somewhere quick and didn't want to pay for hosting.  I'm comfortable using &lt;a href="http://tomcat.apache.org/"&gt;Tomcat &lt;/a&gt;to host my java applications, so I decided to try to host my application at my computer at home for easier development and debugging.&lt;br /&gt;&lt;br /&gt;The trick for hosting a site that is publicly accessible is to be able to use a non-moving target for DNS servers to resolve.  Since most people don't get a static IP for home use without paying extra for it, they are stuck with a Dynamic IP address.  This means that behind the scenes, you are "leasing" the IP address for a period of time, after which it is revoked and another address is assigned to you.  For normal internet use, this is a seamless operation, but for web hosting we need a little help.  I suggest using the free service offered by &lt;a href="http://www.dyndns.com/"&gt;DynDNS &lt;/a&gt;to do hostname to Dynamic IP mapping.  With their service you can create a hostname (for example,  http://codebeneath.dyndns.org, a subdomain to dyndns.org)  and configure that to resolve to your current IP address, which they detect for you.  Or if you prefer, hit &lt;a href="http://www.ipchicken.com/"&gt;IP Chicken&lt;/a&gt; :)&lt;br /&gt;&lt;br /&gt;Now, you need a way to detect when your IP address changes and automatically update your DynDNS entry.  There are standalone clients that can do this, but I was lucky enough to have the capability built into my router, a LinkSys BEFSX41.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_AEOHFrmyURo/Rvna1FfU-fI/AAAAAAAABSY/-iGQv9dKar0/s1600-h/router-ddns-service.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_AEOHFrmyURo/Rvna1FfU-fI/AAAAAAAABSY/-iGQv9dKar0/s320/router-ddns-service.png" alt="" id="BLOGGER_PHOTO_ID_5114359457397406194" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I have heard that some internet providers block port 80, so I decided just to use the default Tomcat port, which is 8080.  If you stick with port 80 and run into problems, this may be the cause.&lt;br /&gt;&lt;br /&gt;Next, we need to configure the router to forward all incoming port 8080 traffic to Tomcat.  There are several ways to do this, depending on your router.  The solution I don't recommend is enabling the DMZ Host setting (this is overkill and a potential security problem if you don't know what you are doing).  The remaining alternatives are Port Range Forwarding and UPnP Forwarding.  Port Range Forwarding is geared more for specialized internet applications such as videoconferencing or online gaming and can be used to forward an entire block of address at once.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;For my case, I picked the simplest thing that worked: UPnP Forwarding.  When I started, this was not the clear choice of how to do this, and many differing opinions steered me to conflicting directions.  Compounding my confusion was an out-dated router firmware that provided administrative pages with different terminology and that did not even have UPnP Forwarding as an option.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;I do recommend updating your router's firmware to avoid the same confusion I had and to have the latest security patches in place.&lt;/blockquote&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;Login to your router by pointing your browser at the admin URL.  For my LinkSys, the admin URL is http://192.168.1.1/.  The default username is blank (none) and the password is "admin"&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;If you still have your default password, &lt;span style="font-weight: bold;"&gt;change it immediately before proceeding further!&lt;/span&gt;&lt;/blockquote&gt;&lt;span style="font-weight: bold;"&gt;&lt;/span&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/RvnWg1fU-cI/AAAAAAAABSA/dbkxxlmRYaQ/s1600-h/router-password.PNG"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/RvnWg1fU-cI/AAAAAAAABSA/dbkxxlmRYaQ/s320/router-password.PNG" alt="" id="BLOGGER_PHOTO_ID_5114354711458544066" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Click "Applications and Gaming" &gt; "UPnP Fowarding".&lt;br /&gt;&lt;br /&gt;On the first blank line enter "&lt;span style="font-family:courier new;"&gt;tomcat&lt;/span&gt;" as the application name, "&lt;span style="font-family:georgia;"&gt;8080&lt;/span&gt;" as the Ext. Port, "&lt;span style="font-family:courier new;"&gt;TCP&lt;/span&gt;" as the protocol to allow, "&lt;span style="font-family:courier new;"&gt;8080&lt;/span&gt;" as Int. Port, your internal network address (e.g. &lt;span style="font-family:courier new;"&gt;192.168.1.xxx&lt;/span&gt;) and mark the Enabled checkbox.  To obtain your internal network address, open a command shell and type &lt;span style="font-family:courier new;"&gt;ipconfig. &lt;/span&gt;A gotcha to watch out for is that your &lt;span style="font-weight: bold;"&gt;internal address&lt;/span&gt; may also be dynamic if your router assigns addresses via &lt;a href="http://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol"&gt;DHCP&lt;/a&gt;.  To avoid this, you could statically configure your network devices addresses, but for my purposes DHCP was OK.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_AEOHFrmyURo/RvnXBlfU-dI/AAAAAAAABSI/RllGbOfz1iQ/s1600-h/router-upnp-forwarding.png"&gt;&lt;img style="cursor: pointer;" src="http://2.bp.blogspot.com/_AEOHFrmyURo/RvnXBlfU-dI/AAAAAAAABSI/RllGbOfz1iQ/s320/router-upnp-forwarding.png" alt="" id="BLOGGER_PHOTO_ID_5114355274099259858" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Save the router settings and close your browser.&lt;br /&gt;&lt;br /&gt;Start Tomcat and make sure you can access &lt;span style="font-family:courier new;"&gt;http://localhost:8080&lt;/span&gt;.  If so, then try the public address you created at DynDNS (in my case &lt;span style="font-family:courier new;"&gt;http://codebeneath.dyndns.org:8080&lt;/span&gt;).  If all is well, you should be seeing the Tomcat welcome page!  Deploy your web application and happy web hosting.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_AEOHFrmyURo/RvnXjFfU-eI/AAAAAAAABSQ/_TAbf82gAgI/s1600-h/tomcat.png"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_AEOHFrmyURo/RvnXjFfU-eI/AAAAAAAABSQ/_TAbf82gAgI/s320/tomcat.png" alt="" id="BLOGGER_PHOTO_ID_5114355849624877538" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-2998798028620904746?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/2998798028620904746/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=2998798028620904746' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2998798028620904746'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/2998798028620904746'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/how-to-host-web-server-at-home-with.html' title='How to Host a Web Server at Home With Dynamic DNS'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_AEOHFrmyURo/Rvna1FfU-fI/AAAAAAAABSY/-iGQv9dKar0/s72-c/router-ddns-service.png' height='72' width='72'/><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-8509586126929625571</id><published>2007-09-18T06:42:00.000-07:00</published><updated>2007-09-18T06:47:05.867-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perfection'/><category scheme='http://www.blogger.com/atom/ns#' term='karma'/><title type='text'>Broncos, 2-0</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.denverbroncos.com/index.php"&gt;&lt;img style="cursor: pointer;" src="http://photo.stamps.com/web/images/catalog/nflimages/First_Release/Denver_Broncos/small_denver_logo_LE.jpg" alt="" border="0" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-8509586126929625571?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/8509586126929625571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=8509586126929625571' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8509586126929625571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8509586126929625571'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/09/broncos-2-0.html' title='Broncos, 2-0'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-8489974900612780274</id><published>2007-09-13T23:44:00.000-07:00</published><updated>2007-09-13T21:45:06.104-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='e2'/><category scheme='http://www.blogger.com/atom/ns#' term='accountability'/><title type='text'>tHERE gOES gRAVITY</title><content type='html'>What's another word for 'empowered'?  I don't like it because it is overused as a corporate buzzword and under appreciated as a game-changing concept. It's the concept that I like.   In your company, do you feel free to cross those invisible corporate boundaries to make things happen?  To discuss, collaborate, act?  I'm lucky enough to work for a company that makes me feel that way.  And it's not because they make a big announcement heralding the idea to the  masses: "You are empowered to make decisions".&lt;br /&gt;&lt;br /&gt;I've heard that before and it wasn't true.  There were still job titles, egos and chains of command to contended with.&lt;br /&gt;&lt;br /&gt;'Empowered' sounds like you're being &lt;span style="font-style: italic;"&gt;granted permission&lt;/span&gt; from those higher up the food chain to act.  I guess the people surrounding me today, the CEO, the CIO, and my co-workers make me want to be &lt;span style="font-weight: bold;"&gt;Accountable&lt;/span&gt;; a concept I believe is self-granted.  If I have an idea, I can own it, grow it and make it work.  And not as a solo effort, either.  We are encouraged to reach out and work directly with the people who can make ideas into reality.&lt;br /&gt;&lt;br /&gt;The actions I see on a daily basis speak louder than any words to confirm this.  After being hired, I met several of people in senior leadership positions in the company.  As an example of their commitment to keep the communication channels open, they were adamant that there should not be an company organization chart.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-style: italic;"&gt;Not this:&lt;/blockquote&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_AEOHFrmyURo/RuoJ2EgXCgI/AAAAAAAABRY/XadK8gZGRSY/s1600-h/OrgChartBig.gif"&gt;&lt;img style="cursor: pointer;" src="http://4.bp.blogspot.com/_AEOHFrmyURo/RuoJ2EgXCgI/AAAAAAAABRY/XadK8gZGRSY/s400/OrgChartBig.gif" alt="Not this" id="BLOGGER_PHOTO_ID_5109907551732304386" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-style: italic;"&gt;&lt;blockquote&gt;This:&lt;/blockquote&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/RuoMn0gXChI/AAAAAAAABRg/rYFG7KBewgE/s1600-h/direct-communication.png"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/RuoMn0gXChI/AAAAAAAABRg/rYFG7KBewgE/s400/direct-communication.png" alt="This" id="BLOGGER_PHOTO_ID_5109910605454051858" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;No artificial power structures in place or asking permission from the boss to talk to your peers who happen to work in a different department or on a different contract. As an organization, we are very flat, by design, and it Just Works.  It's like a big tree, whose branches are weighed down by an ice storm, all suddenly shake free their icy repression and spring up to the same height as the highest branch.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;oh, tHERE gOES gRAVITY!&lt;/blockquote&gt;Being in an environment like this has changed me.  I am no longer in a cubicle reporting to a single boss.  I am one of many influences in the company who increasingly don't see team boundaries, we see Singularity.  We see opportunities to improve our business and our culture and we're making it happen.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;&lt;/blockquote&gt;This is individual accountability to own and promote ideas and to collaborate as a next generation enterprise using lightweight, online tools (including the phone!) to make it easy.  This is collaborating on ideas on your corporate wiki.   This is sharing your bookmarked feeds collectively through Yahoo Pipes. This is about sharing your experiences and knowledge by blogging about it. This is keeping a pulse on new technologies, not for your team, but for other teams in the company.  This is a call.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;This is the &lt;a href="http://weblogs.java.net/blog/boneill42/archive/2007/08/an_open_technol.html"&gt;Manifesto &lt;/a&gt;that I believe in.&lt;br /&gt;&lt;/blockquote&gt;&lt;br /&gt;As&lt;span style="text-decoration: underline;"&gt; &lt;/span&gt;&lt;a href="http://scottgavin.info/?page_id=11"&gt;Charlie&lt;/a&gt; says, "Advertise yourself", your knowledge and what you are passionate about,  so when the time comes, people will also reach out to you to collaborate with.&lt;br /&gt;&lt;br /&gt;Even with obstacles removed from your path, it can still be tough to follow through. I am out of my personal comfort zone on a regular basis.  However, I know it's the way forward, for my company, but most importantly for me.  It's my way of providing leadership at work.   I can't do it with words.  I have to do it by leading by example.&lt;br /&gt;&lt;br /&gt;You can, too.  Make it an infectious attitude. &lt;a href="http://www.azlyrics.com/lyrics/eminem/loseyourself.html"&gt;Lose Yourself&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-8489974900612780274?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/8489974900612780274/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=8489974900612780274' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8489974900612780274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8489974900612780274'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/09/there-goes-gravity.html' title='tHERE gOES gRAVITY'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_AEOHFrmyURo/RuoJ2EgXCgI/AAAAAAAABRY/XadK8gZGRSY/s72-c/OrgChartBig.gif' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-6355955063276058371</id><published>2007-08-30T08:15:00.000-07:00</published><updated>2007-08-30T08:25:50.028-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='soap'/><title type='text'>Simple SOAP posting from the command line</title><content type='html'>From the linux command line, you can do use &lt;code&gt;curl&lt;/code&gt; to submit a soap file to a URL.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;curl -H "Content-Type: text/xml; charset=utf-8" \&lt;br /&gt;     -H "SOAPAction:" \&lt;br /&gt;     -d @soap.txt \&lt;br /&gt;     -X POST http://localhost:18181/httpWSDLService/httpWSDLPort&lt;br /&gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;where the &lt;code&gt;@&lt;/code&gt; specifies a file as input and &lt;code&gt;soap.txt&lt;/code&gt; is the file.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-6355955063276058371?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/6355955063276058371/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=6355955063276058371' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6355955063276058371'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6355955063276058371'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/simple-soap-posting-from-command-line.html' title='Simple SOAP posting from the command line'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-6538591103382257430</id><published>2007-08-28T15:32:00.000-07:00</published><updated>2007-08-28T15:08:40.934-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='microformat'/><category scheme='http://www.blogger.com/atom/ns#' term='problem solving'/><title type='text'>Microformats: A DoD Use Case</title><content type='html'>Microformats have been around awhile, but I have just recently took the time to find out what they are and what they can do.  A good starting place I can recommend is &lt;a href="http://microformats.org/wiki/Main_Page"&gt;microformats.org.&lt;/a&gt;  Basically, using microformats means adding attributes to existing html elements to enhance the meaning of the content.&lt;br /&gt;&lt;br /&gt;The physical address presented on this blog is marked up, for example, as a &lt;a href="http://microformats.org/wiki/hcard"&gt;hCard&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&amp;lt;div class="vcard"&amp;gt;&lt;br /&gt;  &amp;lt;div class="adr"&amp;gt;&lt;br /&gt;    &amp;lt;div class="org fn"&amp;gt;&lt;br /&gt;      &amp;lt;div class="organization-name"&gt;Gestalt-LLC&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;div class="street-address"&gt;320 East 4th Street&amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;span class="locality"&amp;gt;Joplin&amp;lt;/span&amp;gt;, &lt;br /&gt;    &amp;lt;abbr class="region" title="Missouri"&amp;gt;MO&amp;lt;/abbr&amp;gt;&lt;br /&gt;    &amp;lt;span class="postal-code"&amp;gt;65801&amp;lt;/span&amp;gt;&lt;br /&gt;  &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/div&amp;gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This could be thought of as a lightweight semantic web enabler.  And the beauty of course is the simplicity of it.  It is suitable for use in marking up HTML, RSS, Atom or XML.&lt;br /&gt;&lt;br /&gt;So now the question: what could be accomplished with microformats?  What might be a scenario in a military domain?&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A soldier is observing and recording enemy aircraft taking off from a hostile airfield.  He microblogs about it on a ruggedized PDA marking up the aircraft information in a military airframe microformat (date, time, aircraft type, payload configuration, number of aircraft in formation, observed tail numbers, for example).&lt;/li&gt;&lt;li&gt;This is an unanticipated observation, so the data may not be immediately useful or actionable.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;But, Google will find it (and yes, the DoD has Google appliances running on their classified networks).  Not necessarily based on the microformating, but based on its normal searchable content.  Technorati, however, &lt;span style="font-style: italic;"&gt;does &lt;/span&gt;have a &lt;a href="http://kitchen.technorati.com/contact/search/"&gt;microformat search engine&lt;/a&gt;.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;These search results are microformat parsed and fed into a Temporal Analysis System (TAS) whose job in life is to predict the future by comparing recent events with similar historical events chains and their outcomes.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;It turns out that what the soldier observed, perhaps 10 cargo aircraft, all with extra fuel pods, bearing NNW, from grid reference 18SUU8401, is a  90% predictor that the hostile battalion will be mobilizing and deploying in 72 hours.&lt;/li&gt;&lt;li&gt;Useful information to know derived from someone outside the normal chain of command and information flow!&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;Powerful and simple.&lt;br /&gt;&lt;p class="MsoNormal"&gt;&lt;span style=";font-family:Courier New;font-size:100%;color:navy;"   &gt;&lt;span style=";font-family:'Courier New';font-size:12;color:navy;"   &gt;&lt;/span&gt;&lt;/span&gt;&lt;o:p&gt;&lt;/o:p&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-6538591103382257430?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/6538591103382257430/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=6538591103382257430' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6538591103382257430'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6538591103382257430'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/microformats-dod-use-case.html' title='Microformats: A DoD Use Case'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-3492856216891998543</id><published>2007-08-27T07:08:00.000-07:00</published><updated>2007-08-27T07:25:50.136-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='netbeans'/><category scheme='http://www.blogger.com/atom/ns#' term='jbi'/><title type='text'>Source Control Tips for Netbeans Projects</title><content type='html'>This post is specifically for SOA projects centered around JBI composite applications and service units, but I think 90% would still apply to any project type.  If you create the projects inside of &lt;a href="http://www.netbeans.org/"&gt;NetBeans &lt;/a&gt;and use an external source control tool, like &lt;a href="http://www.syntevo.com/smartsvn/"&gt;SmartSVN&lt;/a&gt;, these are the directories you'll want to exclude and ignore.&lt;br /&gt;&lt;br /&gt;For a service unit project, like a BPEL module, exclude&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;build&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;nbproject/private&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;For a composite application, exclude&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;build&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;dist&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;nbproject/private&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;src/jbiasa&lt;/code&gt;&lt;/li&gt;&lt;li&gt;&lt;code&gt;src/jbiServiceUnits&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;After you successfully commit the projects, make sure the above directories stay out of subversion by setting the ignore property.  In SmartSVN, do this by clicking on the the project name and using the menu bar to select &lt;code&gt;Properties &gt; Edit Ignore Patterns...&lt;/code&gt;.  Enter the directory names, save and don't forget to commit!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-3492856216891998543?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/3492856216891998543/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=3492856216891998543' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/3492856216891998543'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/3492856216891998543'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/source-control-tips-for-netbeans.html' title='Source Control Tips for Netbeans Projects'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-6239533879035570111</id><published>2007-08-21T13:06:00.000-07:00</published><updated>2007-08-21T13:24:03.419-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perfection'/><category scheme='http://www.blogger.com/atom/ns#' term='problem solving'/><title type='text'>Procrastination</title><content type='html'>&lt;a href="http://www.despair.com/proc24x30pri.html"&gt;Hard work often pays off after time, but laziness always pays off now&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-6239533879035570111?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/6239533879035570111/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=6239533879035570111' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6239533879035570111'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6239533879035570111'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/procrastination.html' title='Procrastination'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-1598306042326950509</id><published>2007-08-21T12:04:00.001-07:00</published><updated>2007-08-21T12:53:13.706-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='netbeans'/><category scheme='http://www.blogger.com/atom/ns#' term='jbi'/><category scheme='http://www.blogger.com/atom/ns#' term='rss'/><category scheme='http://www.blogger.com/atom/ns#' term='xslt'/><title type='text'>Using BPELs doXslTransform() function</title><content type='html'>As a improvement to my previous design of aggregating RSS feeds from various continuous integration systems, I wanted to do a simple XSL transform to make my post-processing easier. If I could get each &lt;entry&gt; element to be on it's own line, then things become much easier for the later parsing that I have to do.&lt;br /&gt;&lt;br /&gt;Given that I am using OpenESB and JBI to accomplish my goals, I have two choices of how to perform the transform.  1) Use the &lt;a href="http://wiki.open-esb.java.net/Wiki.jsp?page=XSLTSE"&gt;XSLTSE &lt;/a&gt;or 2) Use the &lt;a href="http://docs.oasis-open.org/wsbpel/2.0/CS01/wsbpel-v2.0-CS01.html#_Toc151784222"&gt;BPEL function doXslTransform()&lt;/a&gt;. I choose the latter option because it was the simplest thing that worked. (I will have another post about using the XSLTSE though. Stay tuned!)&lt;br /&gt;&lt;br /&gt;To start, I created the XSL stylesheet that takes input and produces output that validated to the same schema (rssbcext.xsd in this case). Then in my Netbeans BPEL editor, I clicked on the Assign1 operation.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_AEOHFrmyURo/Rss8B4B6cAI/AAAAAAAABP8/T8d6UxVlZZ8/s1600-h/bpel-flow.png"&gt;&lt;img style="cursor: pointer;" src="http://3.bp.blogspot.com/_AEOHFrmyURo/Rss8B4B6cAI/AAAAAAAABP8/T8d6UxVlZZ8/s400/bpel-flow.png" alt="" id="BLOGGER_PHOTO_ID_5101237005845164034" border="0" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This brings up the BPEL Mapper.  Add the doXslTranform functoid (&lt;span style="font-style: italic;"&gt;and no, I did not come up with that term&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_AEOHFrmyURo/RstClYB6cDI/AAAAAAAABQU/km_BhFlcOHQ/s1600-h/bpel-mapper-2.png"&gt;&lt;img style="cursor:pointer; cursor:hand;" src="http://1.bp.blogspot.com/_AEOHFrmyURo/RstClYB6cDI/AAAAAAAABQU/km_BhFlcOHQ/s400/bpel-mapper-2.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5101244212800286770" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The Netbeans tooling is not yet complete for this function, so we have to add the stylesheet &lt;code&gt;file-name&lt;/code&gt; as a URN string literal.  In my case it was &lt;code&gt;'urn:stylesheet:citransform.xsl'&lt;/code&gt;.  Drag the RSSConsumerWSDLOperationIn.part1 to the &lt;code&gt;node-set&lt;/code&gt; and drag the &lt;code&gt;return-node&lt;/code&gt; to the FileWSDLOperationIn.part1.&lt;br /&gt;&lt;br /&gt;Clean and Build, then deploy to Glassfish. That's all there is to it.  My RSS feeds are now in a compact format, making my processing easier and saving valuable disk space.  :)&lt;/entry&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-1598306042326950509?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/1598306042326950509/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=1598306042326950509' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1598306042326950509'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1598306042326950509'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/using-bpels-doxsltransform-function.html' title='Using BPELs doXslTransform() function'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_AEOHFrmyURo/Rss8B4B6cAI/AAAAAAAABP8/T8d6UxVlZZ8/s72-c/bpel-flow.png' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-6836176804416671116</id><published>2007-08-17T13:00:00.000-07:00</published><updated>2007-08-31T06:38:54.440-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='netbeans'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='bpel'/><category scheme='http://www.blogger.com/atom/ns#' term='jbi'/><category scheme='http://www.blogger.com/atom/ns#' term='rss'/><category scheme='http://www.blogger.com/atom/ns#' term='continuous integration'/><title type='text'>Using JBI To Keep An Eye on Continuous Integration</title><content type='html'>I'm a big fan of Continuous Integration.  We thrive on it at work to get feedback for our code integration on a constant basis.  As part of a bigger company effort, we wanted to be able to create team dashboards showing CI health (server up, building, not broken too long, etc).  The teams here mostly use &lt;a href="http://cruisecontrol.sourceforge.net/"&gt;CruiseControl&lt;/a&gt;, but we also a few teams utilizing &lt;a href="https://hudson.dev.java.net/"&gt;Hudson &lt;/a&gt;and &lt;a href="http://luntbuild.javaforge.com/"&gt;Luntbuild&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;So what's an easy way to keep tabs on 3 different build systems?  RSS of course!&lt;br /&gt;&lt;br /&gt;CruiseControl publishes out an RSS feed, Luntbuild publishes an ATOM feed and Luntbuild recently added RSS and ATOM feeds (committed, but not distributed yet, as of 1 Aug 2007).&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-weight: bold;"&gt;And I don't want to write any code to aggregate these feeds together.&lt;/blockquote&gt;&lt;br /&gt;Enter JBI, Open-ESB, and the &lt;a href="https://rss-bc.dev.java.net/"&gt;RSS Binding Component&lt;/a&gt; (BC).&lt;br /&gt;&lt;br /&gt;Start by downloading the latest &lt;a href="https://open-esb.dev.java.net/Downloads_OpenESB_Addons_NB6.html"&gt;Open-ESB/Glassfish&lt;/a&gt; bundle.  Start up Netbeans.  To subscribe to multiple RSS feeds via the RSS BC, we need an RSS provider and an RSS consumer composite application.&lt;br /&gt;&lt;br /&gt;Create the provider BPEL module by creating a new Netbeans project (New Project &gt; Service Oriented Architecture &gt; BPEL Module).  Name it CIProviderBpelModule.  Now we need to import two xml schemas into our project (rssbcext.xsd and wsaext.xsd). Follow the steps &lt;a href="https://rss-bc.dev.java.net/extensions/import_schema.htm"&gt;outlined here&lt;/a&gt; to do the imports.&lt;br /&gt;&lt;br /&gt;&lt;blockquote&gt;The WS-Addressing extension schema is used to have access to the element EndpointReferenceList, which we'll use to feed the RSS feed URLs into the system via a SOAP request.&lt;/blockquote&gt;&lt;br /&gt;Create two WSDLs, one for http and the other for rss, with the New WSDL Document wizard.&lt;br /&gt;&lt;br /&gt;On the Name and Location step, name the rss WSDL "rssciprovider" and import the rssbcext.xsd schema, By File, with a prefix of "rssbcext".  On the Concrete Configuration section, make sure RSS is selected as the Binding Type.&lt;br /&gt;&lt;br /&gt;On the Name and Location step, name the http WSDL "httpci" and import the wsaext.xsd schema, By File, with a prefix of "wsaext".  On the Concrete Configuration section, make sure SOAP is selected as the Binding Type.&lt;br /&gt;&lt;br /&gt;At this point both WSDLs should validate (Alt+Shift+F9) correctly.  This will make sure all schemas are imported correctly.&lt;br /&gt;&lt;br /&gt;Open the httpci.wsdl and navigate to the request message part1.  Click the element and then in the Properties pane of Netbeans, change the element attribute from &lt;code&gt;type="xsd:string"&lt;/code&gt; to &lt;code&gt;element="wsa:EndpointReferenceList"&lt;/code&gt;  Do the same for the reply message part1.  (Make sure you pick &lt;a href="http://jlorenzen.blogspot.com/2007/08/most-common-wsdl-stumbling-block.html"&gt;&lt;span style="font-weight: bold;"&gt;element&lt;/span&gt; and not &lt;span style="font-weight: bold;"&gt;type&lt;/span&gt;&lt;/a&gt;.  Thanks James!).&lt;br /&gt;&lt;br /&gt;Open the rssciprovider.wsdl and navigate to the request message part1.  Change the type to an wsaext element EndpointReferenceList as above.  You can also remove the reply message and the output from the operation and binding as this will be an In-Only message.  For the binding operation, change the input to &lt;code&gt;&lt;rss:input feedlist="part1"&gt;&lt;/rss:input&gt;&lt;/code&gt;.  For the service port change the rss:input to &lt;code&gt;&lt;rss:address correlationid="1"&gt;&lt;/rss:address&gt;&lt;/code&gt;.  This correlationId is important and will match up to a correlationId in our rssciconsumer.wsdl.&lt;br /&gt;&lt;br /&gt;Validate the WSDLs and then Process Files &gt; New &gt; BPEL Process.  Give it a name "rssProviderBpelProcess".  Drag and drop the two WSDLs into the process flow diagram; this will create partner links.  Name them "httpPartnerLink" and "rssProviderPartnerLink".  Swap Roles for the rssPartnerLink to "Partner Role".&lt;br /&gt;&lt;br /&gt;From the Palette pane, drag and drop Receive, Assign, Invoke, Assign and Reply operations onto the BPEL flow.  Edit Receive1 to point to the httpPartnerLink and create an input variable.  Edit Invoke1 to point to rssProviderPartnerLink and create an input variable.  Edit Reply1 to point to httpPartnerLink and create an output variable.  Click Assign1 and using the BPEL Mapper pane at the bottom of Netbeans drag a line from HttpciOperationIn.part1 to RSSciOperationIn.part1 (ignore data types don't match warning).&lt;br /&gt;&lt;br /&gt;For the SOAP response, we will just hardcode something to acknowledge the RSS provider is subscribed.   Click Assign2 and using the BPEL Mapper create a String Literal with a value of "Done.".  Drag a line from the String Literal to HttpciOperationOut.part1.&lt;br /&gt;&lt;br /&gt;Validate the BPEL file.&lt;br /&gt;&lt;br /&gt;Create a new Composite Application project "CIProviderCA" and add the JBI Module project CIProviderBpelModule to it.  Clean and build.&lt;br /&gt;&lt;br /&gt;&lt;blockquote style="font-weight: bold;"&gt;Halfway there!&lt;/blockquote&gt;&lt;br /&gt;Create the consumer BPEL module by creating a new Netbeans project named CIConsumerBpelModule.  Import the rssbcext.xsd schema into the project.&lt;br /&gt;&lt;br /&gt;Create a rssciconsumer.wsdl with the rssbcext.xsd schema imported as before.  Make sure "RSS" is selected as the Binding Type.  Edit the wsdl and change the message part element to &lt;code&gt;element="rssbcext:EntryList"&lt;/code&gt;.  Change the operation input to &lt;code&gt;&lt;rss:input pollinginterval="3600" filterbytype="none"&gt;&lt;/rss:input&gt;&lt;/code&gt;.  Change the service port address to &lt;code&gt;&lt;rss:address correlationid="1"&gt;&lt;/rss:address&gt;&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Create a fileci.wsdl with the rssbcext.xsd schema imported as before.  Make sure "File" is selected as the Binding Type. Edit the wsdl and change the message part element to &lt;code&gt;element="rssbcext:EntryList"&lt;/code&gt;.  Remove all output references; In-Only again.  Change the operation input to &lt;code&gt;&lt;file:message use="literal" filename="ci-feeds.xml" pollinginterval="1000" multiplerecordsperfile="true" recorddelimiter="\r\n"&gt;&lt;/file:message&gt;&lt;/code&gt;.&lt;br /&gt;&lt;br /&gt;Validate the WSDLs and then create a new BPEL Process named "rssConsumerBpelProcess".  Drag and drop the two WSDLs into the process flow diagram; this will create partner links.  Name them "filePartnerLink" and "rssConsumerPartnerLink".  Swap Roles for the filePartnerLink to "Partner Role".&lt;br /&gt;&lt;br /&gt;From the Palette pane, drag and drop Receive, Assign, Invoke operations onto the BPEL flow.  Edit Receive1 to point to the rssConsumerPartnerLink and create an input variable.  Edit Invoke1 to point to filePartnerLink and create an input variable.  Click Assign1 and using the BPEL Mapper drag a line from RssciconsumerOperationIn.part1 to FileciOperationIn.part1 (ignore data types don't match warning).&lt;br /&gt;&lt;br /&gt;Validate the BPEL file.&lt;br /&gt;&lt;br /&gt;Create a new Composite Application project "CIConsumerCA" and add the JBI Module project CIConsumerBpelModule to it.  Clean and build.&lt;br /&gt;&lt;br /&gt;Start glassfish and deploy both JBI Composite Applications to it.&lt;br /&gt;&lt;br /&gt;In the provider CA project, create a test a new test case, pointing it to the httpci.wsdl and the httpciOperation. &lt;span style="font-style: italic;"&gt;Sweeeeeeeeet.&lt;/span&gt;  Edit the test case input.  Each Endpoint reference only needs the Address element to be valid.  Add as many as EndpointRerefences as you need to the EndpointRerefenceList.  Run the test.&lt;br /&gt;&lt;br /&gt;Look in &lt;code&gt;C:\Temp&lt;/code&gt; (or whatever directory the file service port referenced) and you should see a &lt;code&gt;ci-feeds.xml&lt;/code&gt; file with an aggregation of all the continuous integration RSS/ATOM feeds in it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-6836176804416671116?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/6836176804416671116/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=6836176804416671116' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6836176804416671116'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6836176804416671116'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/using-jbi-to-keep-eye-on-continuous.html' title='Using JBI To Keep An Eye on Continuous Integration'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-1025240872801262316</id><published>2007-08-13T10:00:00.001-07:00</published><updated>2007-09-16T11:37:27.944-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='agile'/><category scheme='http://www.blogger.com/atom/ns#' term='karma'/><category scheme='http://www.blogger.com/atom/ns#' term='problem solving'/><category scheme='http://www.blogger.com/atom/ns#' term='accountability'/><title type='text'>Accountability and the Knowledge Worker</title><content type='html'>Are you a &lt;a href="http://home.att.net/%7Enickols/credo.htm"&gt;Knowledge Worker&lt;/a&gt;?  &lt;a href="http://www.skullworks.com/"&gt;Fred Nickols&lt;/a&gt; really sets the framework for how companies can really make a difference when they are drive by a new breed of accountable individual (any he did this back in the '80s)&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I seek out and I set direction.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I resist supervision; I welcome support.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I work at my own pace within the constraints posed by the situation.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I put my name on what I do -- and the names of those who contribute in any way to any         endeavor for which I am accountable.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I promote, praise, recognize and reward accomplishment and service.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I contribute more than I consume.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I seek control over myself and influence over events about me.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I care about my work -- it is a reflection of me.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;     &lt;ul&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I care about the enterprise of which I am a part and I will do my best to protect it         against &lt;em&gt;all&lt;/em&gt; threats -- whether they are external or internal.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I care about the people with whom I work; they are not just co-workers or colleagues --         they are also friends, allies and comrades.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I refuse to simply "obey orders" and "go along with the program."          &lt;/span&gt; &lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I will hold my superiors as accountable for their actions as they hold me accountable         for mine.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I will not turn a "blind eye" or a "deaf ear" to wrongdoing at any         level of any organization of which I am a part.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I will do my best to share what I know with those who are interested in learning from me         and I will do my best to learn from those who are skilled, knowledgeable or competent in         areas I am not.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style=";font-family:Arial;font-size:85%;"  &gt;I will at all times strive to advance and improve the knowledge base that undergirds my         skills, abilities and accomplishments.&lt;/span&gt;&lt;/li&gt;&lt;/ul&gt;This ties in nicely with the theme of &lt;a href="http://www.meridianmagazine.com/leadership/050317results.html"&gt;accountability&lt;/a&gt;.  What it's not: imposed on you by others.  What it is: self-directed behavior.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.meridianmagazine.com/leadership/050317results.html"&gt;&lt;img src="http://www.duncanworldwide.com/images/LadderofAccountabilitysmall.jpg" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-1025240872801262316?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/1025240872801262316/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=1025240872801262316' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1025240872801262316'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1025240872801262316'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/accountability-and-knowledge-worker.html' title='Accountability and the Knowledge Worker'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-6778336434886288370</id><published>2007-08-01T07:59:00.000-07:00</published><updated>2007-08-01T08:02:57.789-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='karma'/><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Testing Karma</title><content type='html'>&lt;p&gt;I think &lt;a href="http://www.nbc.com/My_Name_Is_Earl/about.shtml"&gt;Earl&lt;/a&gt; said it best when he said "Do good things, and good things happen."  What kind  of good things?  How about good software engineering practices?  Use Case  modeling, sequence diagramming, continuous integration and automated testing are  all good starts.&lt;br /&gt;&lt;br /&gt;Diagrams and models are good because they clearly  communicate intent.  The picture is worth more than a thousand words if a  stakeholder (or peer) can talk to the diagram and say something as simple as  "delete that line and add this one", or "change the arrow to point the other way  on the sequence flow".  Then everyone goes, "ah... now I get it".  Practices  like these definitely pay dividends over waiting to fix any architectural flaw  later in the lifecycle of the system.&lt;br /&gt;&lt;br /&gt;Creating automated tests, too, are  equally valuable.  And not hard.  And can help you develop better code.   Sometimes writing unit tests after the fact can be a real head-scratcher.   Thinking about the code and the test as a singular unit can help structure the  code in better ways.  "When you think of code and test as one, testing is easy  and code is beautiful."&lt;br /&gt;&lt;br /&gt;Also from &lt;a href="http://www.artima.com/weblogs/viewpost.jsp?thread=203994"&gt;artima&lt;/a&gt;  :&lt;br /&gt;&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The perfect is the enemy of the good. An imperfect test today is better  than a perfect test someday.&lt;/li&gt;&lt;li&gt;Rejoice when they pass; Rejoice when they fail.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;&lt;br /&gt;Be creative and  flexible when writing your tests.  Write that ugly test for the "untestable"  code.  Take that first step and good things start to happen.  And when you're  walking, you won't notice the steps because they will have become  habit.&lt;br /&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-6778336434886288370?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/6778336434886288370/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=6778336434886288370' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6778336434886288370'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6778336434886288370'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/testing-karma.html' title='Testing Karma'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-8336569898414740694</id><published>2007-08-01T07:46:00.002-07:00</published><updated>2008-11-10T13:50:12.962-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='testing'/><title type='text'>Unit testing advice</title><content type='html'>&lt;div&gt; &lt;/div&gt; &lt;div&gt;Good stuff from the guys over at the &lt;a href="http://googletesting.blogspot.com/2007/02/tott-naming-unit-tests-responsibly.html"&gt;Google Testing Blog&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I had never really given much thought to naming the methods in a unit test  class.  But if you give it a little though, the method names by themselves can  read like a list of low-level functional requirements.&lt;br /&gt;&lt;br /&gt;Why write test methods like&lt;br /&gt;&lt;ul&gt;&lt;li&gt; &lt;code&gt;testClient()&lt;/code&gt; or&lt;/li&gt;&lt;li&gt; &lt;code&gt;testURI()&lt;/code&gt; or&lt;/li&gt;&lt;li&gt; &lt;code&gt;testRun()&lt;/code&gt;&lt;/li&gt;&lt;/ul&gt;when you can name the methods&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;testTryToInvokeServiceBehindFirewallDirect()&lt;/code&gt; or&lt;/li&gt;&lt;li&gt;&lt;code&gt;testCompiledInputCommandIsConvertedAndDispatched() &lt;/code&gt; or &lt;/li&gt;&lt;li&gt;&lt;code&gt;testWriteDataToFile()&lt;/code&gt;?&lt;/li&gt;&lt;/ul&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-8336569898414740694?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/8336569898414740694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=8336569898414740694' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8336569898414740694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/8336569898414740694'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/unit-testing-advice.html' title='Unit testing advice'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-6606521339691540648</id><published>2007-08-01T07:46:00.001-07:00</published><updated>2007-08-01T07:46:42.704-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='metric'/><title type='text'>The rest of the world is so smart</title><content type='html'>&lt;div&gt;North America really needs to get with the world program.  Witness:&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;Paper Sizes:&lt;/div&gt; &lt;div&gt;&lt;a href="http://www.cl.cam.ac.uk/%7Emgk25/iso-paper.html"&gt;http://www.cl.cam.ac.uk/~mgk25/iso-paper.html&lt;/a&gt;&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;Date and Time:&lt;/div&gt; &lt;div&gt;&lt;a href="http://www.cl.cam.ac.uk/%7Emgk25/iso-time.html"&gt;http://www.cl.cam.ac.uk/~mgk25/iso-time.html&lt;/a&gt;&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;and of course the Metric System:&lt;/div&gt; &lt;div&gt;&lt;a href="http://www.cl.cam.ac.uk/%7Emgk25/metric-system-faq.txt"&gt;http://www.cl.cam.ac.uk/~mgk25/metric-system-faq.txt&lt;/a&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-6606521339691540648?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/6606521339691540648/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=6606521339691540648' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6606521339691540648'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/6606521339691540648'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/rest-of-world-is-so-smart.html' title='The rest of the world is so smart'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-3175504186878340009</id><published>2007-08-01T07:44:00.000-07:00</published><updated>2007-08-21T13:24:28.745-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rest'/><category scheme='http://www.blogger.com/atom/ns#' term='perfection'/><category scheme='http://www.blogger.com/atom/ns#' term='soap'/><title type='text'>Words from the guy who was responsible for the Google SOAP API</title><content type='html'>&lt;div&gt;Nelson Minar doesn't work for Google anymore and bears no ill will that  Google yanked the SOAP API that he was responsible for.  If fact, he has a  pretty negative view of SOAP, its interoperability problems and  brittleness.&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;But I particularly liked the last sentence of his post, "Just do  something that works".  It ties in nicely with my last post, Solving The Problem  in a Very Good (Not Perfect) Way.&lt;/div&gt; &lt;div&gt; &lt;/div&gt;&lt;a href="http://www.somebits.com/weblog/tech/bad/whySoapSucks.html"&gt;http://www.somebits.com/weblog/tech/bad/whySoapSucks.html&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-3175504186878340009?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/3175504186878340009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=3175504186878340009' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/3175504186878340009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/3175504186878340009'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/words-from-guy-who-was-responsible-for.html' title='Words from the guy who was responsible for the Google SOAP API'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-831382000244017172.post-1411295661275294718</id><published>2007-08-01T07:27:00.000-07:00</published><updated>2007-08-21T13:32:45.301-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='perfection'/><category scheme='http://www.blogger.com/atom/ns#' term='problem solving'/><title type='text'>Perfect is the enemy of Very Good</title><content type='html'>&lt;div&gt;I found this a good, quick read and helpful as a reminder on how to tackle hard  problems.  My takeaway was to take action and not wait until "all the data is in" before making a decision.  In short, deliver the Very  Good as opposed to Perfection.  As the CEO of the company I work for  said recently, working in a vaguely correct direction is better than standing still.  [&lt;span style="font-style: italic;"&gt;Update: In the software business, the remark about "working in a vaguely correct direction is better than standing still" works because of &lt;/span&gt;&lt;a style="font-style: italic;" href="http://en.wikipedia.org/wiki/Scrum_%28development%29" rel="nofollow"&gt;Scrum&lt;/a&gt;&lt;span style="font-style: italic;"&gt;. If you are talking to your client and demonstrating your solution every 30 days, your can have that meaningful conversation where both parties refine the problem and the solution together.&lt;/span&gt;]&lt;br /&gt;&lt;br /&gt;&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;The full discussion for the quotes below is here: &lt;a href="http://pliantalliance.org/?p=34"&gt;http://pliantalliance.org/?p=34&lt;/a&gt;&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;blockquote dir="ltr" style="margin-right: 0px;"&gt;&lt;em&gt;Never let perfect stand in the way of very good.&lt;br /&gt;&lt;br /&gt;&lt;/em&gt; &lt;div&gt;&lt;em&gt;It may be tempting to wait until you have the time, the knowledge, or  the inclination to design the “perfect” solution, especially if you aim to solve  the real problem. However, you must temper this instinct with practicality.  While it may look like you are holding out for “the perfect solution” (that  solves the whole, real problem), what you are really doing is preventing “the  very good solution” that can be applied in the interim. We must recognize that  perfection can only be approached asymptotically through evolution of design and  implementation: in essence by refining our deployed very good solutions to make  them more perfect. Solving the real problem should be applied where scope allows  and it should guide our path to tell us where perfect is, but we are allowed to  get there in more than one step. Very good solutions have value, and value  delayed is value lost.&lt;/em&gt;&lt;/div&gt;&lt;/blockquote&gt; &lt;div&gt;The same site had a good collection of quotes regarding change and  flexibility:&lt;/div&gt; &lt;div&gt;&lt;a href="http://pliantalliance.org/?page_id=5"&gt;http://pliantalliance.org/?page_id=5&lt;/a&gt;&lt;/div&gt; &lt;div&gt; &lt;/div&gt; &lt;div&gt;"He that will not apply new remedies must expect new evils; for time is the  greatest innovator.."&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/831382000244017172-1411295661275294718?l=codebeneath.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://codebeneath.blogspot.com/feeds/1411295661275294718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=831382000244017172&amp;postID=1411295661275294718' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1411295661275294718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/831382000244017172/posts/default/1411295661275294718'/><link rel='alternate' type='text/html' href='http://codebeneath.blogspot.com/2007/08/perfect-is-enemy-of-very-good.html' title='Perfect is the enemy of Very Good'/><author><name>Jeff Black</name><uri>http://www.blogger.com/profile/17053090306859709462</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='21' src='http://lh4.google.com/codebeneath/RdPDVYNvneI/AAAAAAAAA3M/cc2qi8Jgic0/s144/025_0.JPG'/></author><thr:total>1</thr:total></entry></feed>
