<?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-2771755619149574510</id><updated>2012-01-21T05:29:20.293-08:00</updated><category term='Threading'/><category term='Tomcat'/><category term='Websphere'/><category term='JSP/Servlet'/><category term='JNDI'/><category term='Agile Methodologies'/><category term='Java Security'/><category term='Web Services'/><category term='Deployment'/><category term='Performance Tuning'/><category term='Tools'/><category term='SQL Server 2005'/><category term='Grails'/><category term='Spring'/><category term='JDBC'/><category term='DataSource'/><title type='text'>DenisTek</title><subtitle type='html'></subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>20</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-1223343530907152408</id><published>2010-05-07T12:20:00.000-07:00</published><updated>2010-05-12T11:15:02.943-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java Security'/><title type='text'>Mutual Authentication with Client Certificate over HTTPS/SSL using Java</title><content type='html'>This blog is about SSL/TLS mutual authentication using Java. I am on the client side with a client certificate signed by an intermediate issuer and finally by Verisign. (aha, a certificate chain is here to make the situation not vanilla already.) The server requests a client certificate and recognizes Verisign as a Certification Authority (CA). You can safely ignore this blog if you have a self-certified certificate.&lt;br /&gt;&lt;br /&gt;During the implementation, I got tripped more than a couple of times over error messages like:&lt;br /&gt;&lt;pre class="mycode"&gt;403.7 Forbidden: Client Certificate Required.&lt;br /&gt;javax.net.ssl.SSLException: HelloRequest followed by an unexpected  handshake message&lt;br /&gt;&lt;/pre&gt;To save somebody some time in the future, a step by step instruction is provided below:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I assume you have a valid certificate or a chain of certificates, whose root is acceptable by the server. The valid certificate contains its private key. Run the following command to verify:&lt;br /&gt;&lt;pre class="mycode"&gt;keytool -list -v -keystore "your certificate file"&lt;br /&gt;...&lt;br /&gt;Entry type: &lt;span style="font-weight: bold;"&gt;PrivateKeyEntry&lt;/span&gt;&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:Georgia,serif;"&gt;Import your certificate and intermediate certificates into a browser like IE or Firefox and test out the https URL. This step will validate the certificates and save you a lot of troubles down the road. Java version of the SSL implementation is not as simple/mature as the browsers'. Please make sure all the certificates have not expired.&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:Georgia,serif;"&gt;Backup your keystore located at /your_home_directory/.keystore by default and the truststore located at somewhere similar to \Java\jre6\lib\security\cacerts&lt;br /&gt;&lt;/span&gt;&lt;/li&gt;&lt;li&gt;&lt;span style="font-family:Georgia,serif;"&gt;Use not-yet-commons-ssl utility to import your certificates into the Java keystore format. Sample command is:&lt;/span&gt;&lt;br /&gt;&lt;pre class="mycode"&gt;java -cp not-yet-commons-ssl-0.3.9.jar org.apache.commons.ssl.KeyStoreBuilder &lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Customize the following java code, replace the static final Strings to fit in your needs. Note that this implementation forcefully use a specific alias to present the corresponding certificate/certificate chain to the server. Somehow the default KeyManager simply disqualifies my certificate to be presented to the server.&lt;br /&gt;&lt;pre class="mycode"&gt;public class Main {&lt;br /&gt;&lt;br /&gt;private static final Logger logger = Logger.getLogger(Main.class.getName());&lt;br /&gt;private static final String LINE_BREAKER = System.getProperty("line.separator");&lt;br /&gt;&lt;br /&gt;private static final String CERTIFACATE_FILE = "your keystore location";&lt;br /&gt;private static final String CERTIFACATE_PASS = "changeit";&lt;br /&gt;private static final String CERTIFACATE_ALIAS = "your alias";&lt;br /&gt;private static final String TARGET_URL = "https://xyz.com";&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;public static void main(String[] args) {&lt;br /&gt;   String targetURL = TARGET_URL;&lt;br /&gt;   URL url;&lt;br /&gt;   HttpsURLConnection connection = null;&lt;br /&gt;   BufferedReader bufferedReader = null;&lt;br /&gt;   InputStream is = null;&lt;br /&gt;&lt;br /&gt;   try {&lt;br /&gt;       //Create connection&lt;br /&gt;       url = new URL(targetURL);&lt;br /&gt;       //Uncomment this in case server demands some unsafe operations&lt;br /&gt;       //System.setProperty("sun.security.ssl.allowUnsafeRenegotiation", "true");&lt;br /&gt;       connection = (HttpsURLConnection) url.openConnection();&lt;br /&gt;&lt;br /&gt;       connection.setRequestMethod("POST");&lt;br /&gt;       connection.setRequestProperty("Content-Type", "application/x-www-form-urlencoded");&lt;br /&gt;       connection.setRequestProperty("Content-Language", "en-US");&lt;br /&gt;&lt;br /&gt;       SSLSocketFactory sslSocketFactory = getFactory(new File(CERTIFACATE_FILE), CERTIFACATE_PASS, CERTIFACATE_ALIAS);&lt;br /&gt;       connection.setSSLSocketFactory(sslSocketFactory);&lt;br /&gt;&lt;br /&gt;       //Process response&lt;br /&gt;       is = connection.getInputStream();&lt;br /&gt;&lt;br /&gt;       bufferedReader = new BufferedReader(new InputStreamReader(is));&lt;br /&gt;       String line;&lt;br /&gt;       StringBuffer lines = new StringBuffer();&lt;br /&gt;       while ((line = bufferedReader.readLine()) != null) {&lt;br /&gt;           lines.append(line).append(LINE_BREAKER);&lt;br /&gt;       }&lt;br /&gt;       logger.info("response from " + targetURL + ":" + LINE_BREAKER + lines);&lt;br /&gt;&lt;br /&gt;   } catch (Exception e) {&lt;br /&gt;     ...&lt;br /&gt;   }&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt;    private static SSLSocketFactory getFactory(File pKeyFile, String pKeyPassword, String certAlias) throws Exception {&lt;br /&gt;        KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509");&lt;br /&gt;        KeyStore keyStore = KeyStore.getInstance("JKS");&lt;br /&gt;&lt;br /&gt;        InputStream keyInput = new FileInputStream(pKeyFile);&lt;br /&gt;        keyStore.load(keyInput, pKeyPassword.toCharArray());&lt;br /&gt;        keyInput.close();&lt;br /&gt;        keyManagerFactory.init(keyStore, pKeyPassword.toCharArray());&lt;br /&gt;&lt;br /&gt;        //Replace the original KeyManagers with the AliasForcingKeyManager&lt;br /&gt;        KeyManager[] kms = keyManagerFactory.getKeyManagers();&lt;br /&gt;        for (int i = 0; i &lt; kms.length; i++) {&lt;br /&gt;            if (kms[i] instanceof X509KeyManager) {&lt;br /&gt;                kms[i] = new AliasForcingKeyManager((X509KeyManager) kms[i], certAlias);&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        SSLContext context = SSLContext.getInstance("TLS");&lt;br /&gt;        context.init(kms, null, null);&lt;br /&gt;        return context.getSocketFactory();&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    /*&lt;br /&gt;     * This wrapper class overwrites the default behavior of a X509KeyManager and&lt;br /&gt;     * always render a specific certificate whose alias matches that provided in the constructor&lt;br /&gt;     */&lt;br /&gt;    private static class AliasForcingKeyManager implements X509KeyManager {&lt;br /&gt;&lt;br /&gt;        X509KeyManager baseKM = null;&lt;br /&gt;        String alias = null;&lt;br /&gt;&lt;br /&gt;        public AliasForcingKeyManager(X509KeyManager keyManager, String alias) {&lt;br /&gt;            baseKM = keyManager;&lt;br /&gt;            this.alias = alias;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        /*&lt;br /&gt;         * Always render the specific alias provided in the constructor&lt;br /&gt;         */&lt;br /&gt;        public String chooseClientAlias(String[] keyType, Principal[] issuers, Socket socket) {&lt;br /&gt;            return alias;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public String chooseServerAlias(String keyType, Principal[] issuers, Socket socket) {&lt;br /&gt;            return baseKM.chooseServerAlias(keyType, issuers, socket);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public X509Certificate[] getCertificateChain(String alias) {&lt;br /&gt;            return baseKM.getCertificateChain(alias);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public String[] getClientAliases(String keyType, Principal[] issuers) {&lt;br /&gt;            return baseKM.getClientAliases(keyType, issuers);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public PrivateKey getPrivateKey(String alias) {&lt;br /&gt;            return baseKM.getPrivateKey(alias);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public String[] getServerAliases(String keyType, Principal[] issuers) {&lt;br /&gt;            return baseKM.getServerAliases(keyType, issuers);&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Try to set&lt;br /&gt;&lt;pre class="mycode"&gt;-Dsun.security.ssl.allowUnsafeRenegotiation=true&lt;/pre&gt;if you get the error message like:&lt;br /&gt;&lt;pre class="mycode"&gt;javax.net.ssl.SSLException: HelloRequest followed by an unexpected  handshake message&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;If anything goes wrong, turn on -Djavax.net.debug=all to debug. Verify the keystore and truststore locations. Verify the presence of the certificates. Here is a sample log file with successful connection: http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/ReadDebug.html&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/2771755619149574510-1223343530907152408?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/1223343530907152408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2010/05/mutual-authentication-with-client.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/1223343530907152408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/1223343530907152408'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2010/05/mutual-authentication-with-client.html' title='Mutual Authentication with Client Certificate over HTTPS/SSL using Java'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-7696837221911120157</id><published>2010-02-25T12:35:00.000-08:00</published><updated>2010-03-05T07:25:16.907-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>Grails Environment Specific Logging to a File</title><content type='html'>I need to log my Grails application across different environments. For example,  a Grails embeded Jetty server in DEV environment and a Tomcat server in TEST environment. Different servers have different log directory locations. How to dynamically pick up the right location? The trick is to define the file location (a relative path in my case) as a global variable inside Config.groovy, customized it in the environment blocks, and use the variable location inside log4j closure.&lt;br /&gt;&lt;br /&gt;The following is the Config.groovy:&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;def logDirectory = '.'&lt;br /&gt;environments {&lt;br /&gt; development {&lt;br /&gt; }&lt;br /&gt; test {&lt;br /&gt;     logDirectory = "../logs"    &lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// log4j configuration&lt;br /&gt;log4j = {&lt;br /&gt; appenders {&lt;br /&gt;     console name:'stdout'&lt;br /&gt;     rollingFile  name:'file', file: logDirectory + '/app.log', threshold: org.apache.log4j.Level.INFO, maxFileSize:"1MB", maxBackupIndex: 10, 'append':true    &lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; error  'org.codehaus.groovy.grails.web.servlet',  //  controllers&lt;br /&gt;        'org.codehaus.groovy.grails.web.pages', //  GSP&lt;br /&gt;        'org.codehaus.groovy.grails.web.sitemesh', //  layouts&lt;br /&gt;        'org.codehaus.groovy.grails.web.mapping.filter', // URL mapping&lt;br /&gt;        'org.codehaus.groovy.grails.web.mapping', // URL mapping&lt;br /&gt;        'org.codehaus.groovy.grails.commons', // core / classloading&lt;br /&gt;        'org.codehaus.groovy.grails.plugins', // plugins&lt;br /&gt;        'org.codehaus.groovy.grails.orm.hibernate', // hibernate integration&lt;br /&gt;        'org.springframework',&lt;br /&gt;        'org.hibernate',&lt;br /&gt;        'org.codehaus.groovy.grails.scaffolding.view.ScaffoldingViewResolver'&lt;br /&gt;&lt;br /&gt; warn   'org.mortbay.log'&lt;br /&gt;&lt;br /&gt; root {&lt;br /&gt;     info 'stdout', 'file'&lt;br /&gt;     additivity = true&lt;br /&gt; }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It has been tested to work on Grails version is 1.1.1 with Tomcat 6.0.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-7696837221911120157?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/7696837221911120157/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2010/02/grails-environment-specific-logging-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/7696837221911120157'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/7696837221911120157'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2010/02/grails-environment-specific-logging-to.html' title='Grails Environment Specific Logging to a File'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-2631483492466638934</id><published>2009-12-30T06:51:00.000-08:00</published><updated>2009-12-30T07:11:31.750-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>Grails and Query Into Database View and Multiple Datasources</title><content type='html'>I want to bypass the Grails stack to query a database directly. It sounds silly. Anyway the very first reason why I chose Grails is to avoid that hassle. However I have a special case that it demands read-only queries into:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;multiple databases&lt;/li&gt;&lt;li&gt;a database view&lt;/li&gt;&lt;li&gt;Netezza, a database with no hibernate dialect&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;In my opinion, the difficulties of 2) and 3) keep any Object Relational Mapping (ORM) solutions out of the game. Otherwise, the Grails plugin, datasources, will be a good candidate to resolve issue 1).&lt;br /&gt;&lt;br /&gt;The following solution is a Groovy-style DAO query:&lt;br /&gt;&lt;ol style="text-align: left;"&gt;&lt;li&gt;define the second datasource in grails-app/conf/spring/resources.groovy:&lt;pre class="mycode"&gt;&lt;br /&gt;import org.apache.commons.dbcp.BasicDataSource&lt;br /&gt;beans = {&lt;br /&gt;dataSourceNetezza(BasicDataSource) {&lt;br /&gt; driverClassName = "org.netezza.Driver"&lt;br /&gt; url = "jdbc:netezza://server:5480/database_name"&lt;br /&gt; username = "xyz"&lt;br /&gt; password = "abc"&lt;br /&gt;}&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Use the dataSourceNetezza defined above in a controller where the Spring application context is available. Note that the dataSourceNetezza is auto-wired by Spring and the names have to match.&lt;/li&gt;&lt;/ol&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;import groovy.sql.Sql&lt;br /&gt;&lt;br /&gt;class TablesController {&lt;br /&gt;&lt;br /&gt;def dataSourceNetezza&lt;br /&gt;&lt;br /&gt;def list = {&lt;br /&gt; if (servletContext.dbList == null)&lt;br /&gt;  servletContext.dbList = new Sql(dataSourceNetezza).rows("SELECT distinct DATABASE_NAME FROM REPOSITORY")&lt;br /&gt; print servletContext.dbList&lt;br /&gt;}&lt;br /&gt; ...&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This approach looks extremely simple with less than 15 lines of code. Keep in mind that, there is no ORM.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-2631483492466638934?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/2631483492466638934/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2009/12/grails-and-query-into-database-view-or.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/2631483492466638934'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/2631483492466638934'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2009/12/grails-and-query-into-database-view-or.html' title='Grails and Query Into Database View and Multiple Datasources'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-7993380688161014788</id><published>2009-12-14T10:22:00.001-08:00</published><updated>2010-01-14T09:30:39.331-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Grails'/><title type='text'>Grails and Legacy Database</title><content type='html'>Recently I played around Grails with a legacy database. Here are some tips I gathered around the journey:&lt;br /&gt;1. I chose to use SpringSource Tool Suite, an IDE based on Eclipse. I love its feature to bring up Grails command prompt with a convenient short cut of 'ctrl-alt-g'. &lt;br /&gt;2. Google 'GRAG' and use its latest version to reverse engineer your legacy database. GRAG certainly has room for improvement but it does a decent job to create the domain objects and save you a lot of typing. BTW, it's totally fine if you don't want to use GRAG and prefer to manually generate the domain classes.&lt;br /&gt;3. Refine the domain definition. Note that Grails/Hibernate uses 'id' as the default field name for primary keys. You may not wish to change this convention or define another field as existingId. Unfortunately that's just the default behavior of GRAG 1.1.&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;static mapping = {&lt;br /&gt; table 'Legacy_Table'&lt;br /&gt; version false&lt;br /&gt; id column:'Existing_ID'&lt;br /&gt;} ...&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;4. If the existing primary key is in the type of a String the domain definition needs additional tweak:&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;static mapping = {&lt;br /&gt; table 'Legacy_Table'&lt;br /&gt; version false&lt;br /&gt; id column:'String_ID', generator:'assigned'&lt;br /&gt;}&lt;br /&gt;String id&lt;br /&gt;&lt;/pre&gt;An exception of 'Fail to convert to internal representation' will be thrown if you fail to declare the id as a String&lt;br /&gt;&lt;br /&gt;5. If you are careful enough you may find that the generator is defined as 'assigned' instead of the default value. This means the application will generate the primary key. Accordingly the save() operation has to explicitly set the String 'id'. Controller's save operation will look like:&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;   def save = {&lt;br /&gt;       def legacyTable = new legacyTable(params)&lt;br /&gt;legacyTable.id = params.id&lt;br /&gt;       if(!legacyTable.hasErrors() &amp;amp;&amp;amp; legacyTable.save())&lt;br /&gt;       ...&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Please let me know if it works. I will be surprised if not.&lt;br /&gt;&lt;br /&gt;One of the limitations Grails have is that, the 'version false' statement basically turns off the versioning from Gorm and its underline Hibernate implementation. If two users update the same database entry simultaneously, the system's behavior may not be predictable. In another word, we are counting on the optimistic lock on the data integrity.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-7993380688161014788?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/7993380688161014788/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2009/12/grails-and-legacy-database.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/7993380688161014788'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/7993380688161014788'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2009/12/grails-and-legacy-database.html' title='Grails and Legacy Database'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-778158112084731655</id><published>2009-08-05T09:06:00.000-07:00</published><updated>2009-08-05T09:16:27.451-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Websphere'/><title type='text'>Multiple Installation Instances of IBM Websphere Commerce 6.0 Developer</title><content type='html'>Websphere Commerce Developer installation is really intensive. It's perfectly fine to have multiple Toolkit setup on the same machine for multiple profiles, for example, two different fix pack versions.&lt;br /&gt;&lt;br /&gt;Steps:&lt;br /&gt;1. Install or reuse existing installation of RAD/WAS&lt;br /&gt;2. Install Commerce to a new location&lt;br /&gt;3. Run setup.bat if you don't have a setup.log in ${WCToolkitEE60_ROOT}\logs&lt;br /&gt;4. Run setdbtype to point the Commerce to the right database&lt;br /&gt;&lt;br /&gt;That's it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-778158112084731655?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/778158112084731655/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2009/08/multiple-installation-instances-of-ibm.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/778158112084731655'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/778158112084731655'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2009/08/multiple-installation-instances-of-ibm.html' title='Multiple Installation Instances of IBM Websphere Commerce 6.0 Developer'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-8422970587489545598</id><published>2008-10-01T14:07:00.000-07:00</published><updated>2008-10-02T11:42:32.055-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Threading'/><category scheme='http://www.blogger.com/atom/ns#' term='JSP/Servlet'/><title type='text'>Spawn a Thread in a Servlet</title><content type='html'>When I search this topic on Google, the first impression is that, 'Stop! Just Don't Do It'. Threading code is error prone, not mentioning in a managed container. Additionally there are alternatives like Message Driven Beans or Java Message System, which are specially designed for this purpose. However, you may have a specific situation where threading in Servlet seems to be the only viable solution, for example the JSP page kicks off a long running process at the backend. What can you do? Fortunately it's totally doable if you are careful enough.&lt;br /&gt;&lt;br /&gt;First, let's evaluate the risks. Obviously if your servlet is going to spawn multiple threads, this servlet cannot serve many concurrent users at the same time. Otherwise the thread pool will easily dry up and brings the server down to its knees. Concurrent access may cause race conditions and subtle synchronization problems too, especially in a load-balanced or clustered environment. Thread leak may be another risk. If not cleaned up appropriately, each leaked thread will have a reference to its classloader and prevent this classloader from being garbage collected. After enough reload of the application, you may run into out of PermGen space memory errors.&lt;br /&gt;&lt;br /&gt;If the risks mentioned above do not apply to your particular situation or didn't scare you off, let's continue. The solution is as the following:&lt;br /&gt;1. Instantiate your thread object as a daemon in your Servlet or ServletContextListener and call yourThread.start();&lt;br /&gt;2. Store it as an attribute in your application context;&lt;br /&gt;3. Return the HttpServletResponse;&lt;br /&gt;4. Clean up the thread when the servlet or the application context is destroyed&lt;br /&gt;5. Don't pass HttpservletRequest/Response/Session objects to the thread&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-8422970587489545598?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/8422970587489545598/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/10/spawn-thread-in-servlet.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/8422970587489545598'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/8422970587489545598'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/10/spawn-thread-in-servlet.html' title='Spawn a Thread in a Servlet'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-4446006449860964640</id><published>2008-08-18T13:10:00.000-07:00</published><updated>2008-08-22T06:21:30.742-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JNDI'/><title type='text'>List JNDI Names</title><content type='html'>Sometimes we need to list all the JNDI names in an application server for debugging purpose. Other times, we may wish to list available datasources in a dropdown list for end-users to choose from. How to do this? In a nutshell, the JNDI bindings are organized in a tree strucuture. The following code is a simple example to recursively exhaust this binding tree:&lt;br /&gt;&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;Context ctx = (Context)new InitialContext().lookup("java:comp/env");&lt;br /&gt;listContext(ctx, "");&lt;br /&gt;&lt;br /&gt;/**&lt;br /&gt;* Recursively exhaust the JNDI tree&lt;br /&gt;*/&lt;br /&gt;private static final void listContext(Context ctx, String indent) {&lt;br /&gt;try {&lt;br /&gt;   NamingEnumeration list = ctx.listBindings("");&lt;br /&gt;   while (list.hasMore()) {&lt;br /&gt;       Binding item = (Binding) list.next();&lt;br /&gt;       String className = item.getClassName();&lt;br /&gt;       String name = item.getName();&lt;br /&gt;       logger.log(Level.INFO, indent + className + " " + name);&lt;br /&gt;       Object o = item.getObject();&lt;br /&gt;       if (o instanceof javax.naming.Context) {&lt;br /&gt;    listContext((Context) o, indent + " ");&lt;br /&gt;       }&lt;br /&gt;   }&lt;br /&gt;} catch (NamingException ex) {&lt;br /&gt;   logger.log(Level.WARNING, "JNDI failure: ", ex);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-4446006449860964640?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/4446006449860964640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/08/list-jndi-names.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/4446006449860964640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/4446006449860964640'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/08/list-jndi-names.html' title='List JNDI Names'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-5797471601117469307</id><published>2008-08-12T05:09:00.000-07:00</published><updated>2008-09-09T06:49:21.626-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JSP/Servlet'/><title type='text'>Tag Libraries: JSTL, NetUI, Spring MVC, or Struts Tag Library</title><content type='html'>Not occasionally JSP developers need to iterate through collections and evaluate conditions before presenting the data. Tag libraries come in handy to provide a solution. However, there are quite a few Tag Libraries available, for example, the JSTL, NetUI, Spring MVC, and Struts tag libraries. Which one to choose? The quick answer is to use JSTL whenever possible. Use other tag libraries only when absolutely necessary.&lt;br /&gt;&lt;br /&gt;Tag Libraries are not much different than any other utility libraries. Different vendors provide different features. JavaServer Pages Standard Tag Library (JSTL) is the most-accepted one and still powerful enough for common usages. If a non-standard tag library is chosen for additional features and convenience, other developers may have to climb a learning curve before being productive. &lt;br /&gt;&lt;br /&gt;Additionally JSTL is designed to complement the HTML tags with 'logic control'&lt;br /&gt;capabilities. This is in contrast to NetUI tag libraries, who replaces the HTML tags. Another example is Spring MVC's tag library. It is tightly integrated ONLY with the Spring MVC framework for convenient data binding. Tag libraries competing with HTML tags may create further compatibility issues. Sometimes JSP pages are touched by both developers and UI design artists. The UI design artists do not want to see any specialized tags by all means. Struts Tag Library used to gain a lot of popularity due to the success of the legend Struts framework. Now its document website has comments like,  'Some of the features in this taglib are also available in the JavaServer Pages Standard Tag Library (JSTL). The Struts team encourages the use of the standard tags over the Struts specific tags when possible.'&lt;br /&gt;&lt;br /&gt;In a conclusion, JSTL tag library is the standard way to provides common logic processing features, such as to iterate through collections and to evaluate conditions. It complements the HTML tags. It will be the best choice for most common situations. Alternative ones, like NetUI, Spring MVC, or Struts Tag Library, may be chosen for additional functionalities.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-5797471601117469307?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/5797471601117469307/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/08/tag-libraries-jstl-or-netui.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/5797471601117469307'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/5797471601117469307'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/08/tag-libraries-jstl-or-netui.html' title='Tag Libraries: JSTL, NetUI, Spring MVC, or Struts Tag Library'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-7157810455785247059</id><published>2008-08-06T06:23:00.000-07:00</published><updated>2008-08-22T06:35:14.519-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JSP/Servlet'/><title type='text'>Alternative Ways to Define JSP/Servlet Attribute Scopes</title><content type='html'>As we know, there are four types of scopes that a JSP/Servlet attribute can use:&lt;br /&gt;Application: The attribute lasts the lifetime of an application instance.&lt;br /&gt;Session: The attribute survives throughout the conversation session.&lt;br /&gt;Request: The attribute will be discarded after the request is serviced.&lt;br /&gt;Page: The attribute is only visible within a particular JSP page.&lt;br /&gt;&lt;br /&gt;To keep data integrity, it's important to put attributes into the right scope. Most of the time developers will retrieve attributes from requests and put them into appropriate scopes thereafter. There are alternative ways.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;1. Set application scope attributes in a ServletContextListener.&lt;/span&gt;&lt;br /&gt;Sometimes "static" information should be loaded once and visible throughout the lifetime of an application. For example,&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;web.xml&lt;br /&gt;    &amp;lt;listener&amp;gt;&lt;br /&gt;        &amp;lt;listener-class&amp;gt;&lt;br /&gt;            com.denistek.SampleListener&lt;br /&gt;        &amp;lt;/listener-class&amp;gt;&lt;br /&gt;    &amp;lt;/listener&amp;gt;&lt;br /&gt;&lt;br /&gt;Java&lt;br /&gt;public class SampleListener implements ServletContextListener {&lt;br /&gt;    @Override&lt;br /&gt;    public void contextInitialized(ServletContextEvent event) {&lt;br /&gt;     //... get your attributes&lt;br /&gt;        event.getServletContext().setAttribute("attributes", attributes);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void contextDestroyed(ServletContextEvent event) {&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;2. Set application scope attributes in a Servlet with &amp;lt;load-on-startup&amp;gt; features.&lt;/span&gt;&lt;br /&gt;This is very similar to the above approach. For example, &lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;web.xml&lt;br /&gt;    &amp;lt;servlet&amp;gt;&lt;br /&gt;        &amp;lt;servlet-name&amp;gt;SampleServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;        &amp;lt;servlet-class&amp;gt;com.denistek.servlet.SampleServlet&amp;lt;/servlet-class&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;&lt;br /&gt;Java&lt;br /&gt;    @Override&lt;br /&gt;    public void init() throws ServletException {&lt;br /&gt;        super.init();&lt;br /&gt;        //... get your attributes&lt;br /&gt;        getServletContext().setAttribute("attributes", attributes);&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;3. Use filters to define session attributes. &lt;/span&gt;&lt;br /&gt;Sometimes there are multiple entry points where a session attribute can be updated. In this case we'd better use a filter to watch for this kind of update instead of scattering the logic across various JSP's/Servlets. For example,&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;web.xml&lt;br /&gt;    &amp;lt;filter&amp;gt;&lt;br /&gt;        &amp;lt;filter-name&amp;gt;SampleFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;        &amp;lt;filter-class&amp;gt;com.denistek.SampleFilter&amp;lt;/filter-class&amp;gt;&lt;br /&gt;    &amp;lt;/filter&amp;gt;&lt;br /&gt;    &amp;lt;filter-mapping&amp;gt;&lt;br /&gt;        &amp;lt;filter-name&amp;gt;SampleFilter&amp;lt;/filter-name&amp;gt;&lt;br /&gt;        &amp;lt;url-pattern&amp;gt;/*&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;    &amp;lt;/filter-mapping&amp;gt;&lt;br /&gt;&lt;br /&gt;Java&lt;br /&gt;public class SampleFilter implements Filter {&lt;br /&gt;&lt;br /&gt;    private SampleAttribute[] attributes = null;&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void doFilter(ServletRequest request, ServletResponse response, FilterChain chain) &lt;br /&gt;            throws IOException, ServletException{&lt;br /&gt;        HttpServletRequest rq = (HttpServletRequest) request;&lt;br /&gt;        //... populate attributes from request here&lt;br /&gt;        rq.getSession().setAttribute("sampleAttributes", attributes);&lt;br /&gt;        chain.doFilter(request, response);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void init(FilterConfig config) {&lt;br /&gt;        //... populate attributes from context here&lt;br /&gt;        attributes = config.getServletContext().getAttribute("sampleAttributes");&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @Override&lt;br /&gt;    public void destroy() {&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-7157810455785247059?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/7157810455785247059/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/08/alternative-ways-to-define-jspservlet.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/7157810455785247059'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/7157810455785247059'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/08/alternative-ways-to-define-jspservlet.html' title='Alternative Ways to Define JSP/Servlet Attribute Scopes'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-5253585590932767298</id><published>2008-07-28T12:11:00.000-07:00</published><updated>2008-08-22T06:38:27.162-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='DataSource'/><category scheme='http://www.blogger.com/atom/ns#' term='JNDI'/><category scheme='http://www.blogger.com/atom/ns#' term='JDBC'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL Server 2005'/><category scheme='http://www.blogger.com/atom/ns#' term='Tomcat'/><title type='text'>Configure Tomcat 6 DataSource using Sql Server 2005</title><content type='html'>This is a step-by-step instructions on how to configure Tomcat 6 DataSource using Sql Server 2005. The installation of Tomcat and SqlServer is not covered.&lt;br /&gt;1. Verify that you can login to the SQL Server using 'SQL Server Authentication'. You may wish to change the 'Server Authentication mode' to 'SQL Server and Windows Authentication mode'. You may also wish to check that the particular user's status of Login is 'Enabled'.&lt;br /&gt;2. Verify that 'Local and remote connections' is enabled.&lt;br /&gt;Go to Microsoft SQL Server 2005&amp;gt;Configuration Tools&amp;gt;SQL Server  Surface Area Configuration&amp;gt;Remote Connections: Enable TCP/IP&lt;br /&gt;3. Restart the database&lt;br /&gt;4. Download and drop the JDBC driver to tomcat_home/lib. You may find tomcat-dbcp.jar in the directory. This will make the driver available to both the Tomcat internal classes and the web application.&lt;br /&gt;5. Change the context.xml, for example:&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;context path="/BFS2"&amp;gt;&lt;br /&gt;    &amp;lt;Resource name="jdbc/TestDB" auth="Container" type="javax.sql.DataSource"&lt;br /&gt;          maxActive="100" maxIdle="30" maxWait="10000"&lt;br /&gt;          username="sa" password="passw0rd" driverClassName="com.microsoft.sqlserver.jdbc.SQLServerDriver"&lt;br /&gt;          url="jdbc:sqlserver://localhost:1433;databaseName=TestOne"  /&amp;gt;&lt;br /&gt;&amp;lt;/context&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;6. Change the web.xml, for example:&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;&amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;&amp;lt;web-app version="2.5" xmlns="http://java.sun.com/xml/ns/javaee" xsi="http://www.w3.org/2001/XMLSchema-instance" schemalocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd"&amp;gt;&lt;br /&gt;    &amp;lt;resource-ref&amp;gt;&lt;br /&gt;        &amp;lt;description&amp;gt;DB  Connection&amp;lt;/description&amp;gt;&lt;br /&gt;        &amp;lt;res-ref-name&amp;gt;jdbc/TestDB&amp;lt;/res-ref-name&amp;gt;&lt;br /&gt;        &amp;lt;res-type&amp;gt;javax.sql.DataSource&amp;lt;/res-type&amp;gt;&lt;br /&gt;        &amp;lt;res-auth&amp;gt;Container&amp;lt;/res-auth&amp;gt;&lt;br /&gt;    &amp;lt;/resource-ref&amp;gt;&lt;br /&gt;&amp;lt;/web-app&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;7. Write a testing JSP page like this:&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;&amp;lt;%@   page   contentType="text/html;charset=UTF-8" %&amp;gt;   &lt;br /&gt;&amp;lt;%@   page   import="java.sql.*" %&amp;gt; &lt;br /&gt;&amp;lt;%@   page   import="javax.sql.*" %&amp;gt; &lt;br /&gt;&amp;lt;%@   page   import="javax.naming.*" %&amp;gt; &lt;br /&gt;&amp;lt;HTML&amp;gt;&lt;br /&gt;&amp;lt;HEAD&amp;gt;&lt;br /&gt;&amp;lt;TITLE&amp;gt;JSP example&amp;lt;/TITLE&amp;gt;&lt;br /&gt;&amp;lt;/HEAD&amp;gt;&lt;br /&gt;&amp;lt;BODY&amp;gt;&lt;br /&gt;  &amp;lt;h1&amp;gt;Hello,test JNDI !  &amp;lt;/h1&amp;gt&lt;br /&gt;  &amp;lt;%&lt;br /&gt;    Context ctx = new InitialContext();  &lt;br /&gt;    Context envctx =  (Context) ctx.lookup("java:comp/env"); &lt;br /&gt;    DataSource ds =  (DataSource) envctx.lookup("jdbc/TestDB");  &lt;br /&gt;    Connection  conn=ds.getConnection();   &lt;br /&gt;    Statement  st=conn.createStatement();&lt;br /&gt;    String    sql="select * from status";  &lt;br /&gt;    ResultSet    rs=st.executeQuery(sql); &lt;br /&gt;    while(rs.next())   {&lt;br /&gt;  %&amp;gt;   &lt;br /&gt;  ID:&amp;lt;%=rs.getInt(1) %&amp;gt;  &lt;br /&gt;       Value:&amp;lt;%=rs.getString(2) %&amp;gt; &lt;br /&gt;       &amp;lt;br&amp;gt;&lt;br /&gt;  &amp;lt;%&lt;br /&gt;   }&lt;br /&gt;  %&amp;gt;   &lt;br /&gt;  Here is just JNDI datasource SQL Server 2005 + tomcat example &lt;br /&gt;  &amp;lt;%&lt;br /&gt;   rs.close(); &lt;br /&gt;   st.close();  &lt;br /&gt;   conn.close();   &lt;br /&gt;  %&amp;gt; &lt;br /&gt;&amp;lt;/BODY&amp;gt;&lt;br /&gt;&amp;lt;/HTML&amp;gt; &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;That's it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-5253585590932767298?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/5253585590932767298/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/07/configure-tomcat-6-datasource-using-sql.html#comment-form' title='10 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/5253585590932767298'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/5253585590932767298'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/07/configure-tomcat-6-datasource-using-sql.html' title='Configure Tomcat 6 DataSource using Sql Server 2005'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>10</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-1649798106138599791</id><published>2008-06-19T07:56:00.000-07:00</published><updated>2008-08-22T06:39:42.724-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Spring'/><title type='text'>5 Minute Introduction to Dependence Injection (DI, IoC)</title><content type='html'>Newbies are confused by the words, Dependency Injection (DI) and Inversion of Control (IoC). I was confused. This article is for those who dare to add these hot and fancy words onto their resume but have only 5 minutes bandwidth to learn the details. Without any further delay, this is the explanation.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Minute One: Concept Background&lt;/span&gt;&lt;br /&gt;Generally in a Object Oriented Language (OOL) like Java, there are no more than three relationships between two classes: inheritance, reference, and aggregation. Inheritance and polymorphisms are powerful features already built-in in the Java language. However, as to the the 'reference' relationship, it was awkward in Java until the appearance of DI.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Minute Two: Traditional Coding Style&lt;/span&gt;&lt;br /&gt;Traditionally, we write code like this when ClassB has a reference relationship with ClassA:&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;public ClassA implements InterfaceA { ...}&lt;br /&gt;public ClassB {&lt;br /&gt; InterfaceA a = new ClassA();&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;If we change our mind to plug in another implementation, say ClassAA, then we have to go back to the ClassB's source code to change at least one line of code:&lt;br /&gt;InterfaceA a = new ClassAA();&lt;br /&gt;&lt;br /&gt;In another word, ClassB is still strongly coupled with ClassA.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Minute Three: DI Coding Style&lt;/span&gt;&lt;br /&gt;DI code looks like this:&lt;br /&gt;&lt;pre class="mycode"&gt;&lt;br /&gt;public ClassA implements InterfaceA { ...}&lt;br /&gt;public ClassB {&lt;br /&gt;   InterfaceA a;&lt;br /&gt;   public void setA(InterfaceA input) { a = input; }&lt;br /&gt;    ....&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Note that ClassB does not prescribe whether ClassA or ClassAA  will be used prematurely. Only at the run time, the container will render either ClassA or ClassAA to ClassB according to the configuration. Details of the container and the configuration file are intentionally ignored here because they are trivial to the concept understanding. By the way, the new setA() method is needed by the container.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Minute Four: So What?&lt;/span&gt;&lt;br /&gt;What is the big deal to change one line of code into another line of configuration with more lines of code and a totally new container?&lt;br /&gt;&lt;br /&gt;The gain is that, now the hard-coded relationships are minimized. Only ClassB-InterfaceA and ClassA-InterfaceA relationships are maintained. The ClassB-ClassA relationship disappeared! Finally ClassA and ClassB are totally independent to each other. Each one can have it's own changes without affecting the other party and finally we can easily swap ClassA out without touching ClassB.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;Minute Five: Spring Framework&lt;/span&gt;&lt;br /&gt;DI and AoP are the two fundamental ideas behind the Spring Framework, with which our Plain Old Java Objects (POJO) can be easily enriched by a complete pack of enterprise level features, such as transaction control, persistence, security and etc. See http://www.springframework.org/ for more details.&lt;br /&gt;&lt;br /&gt;In my humble opinion, DI is simply the most important concept after Object Oriented concepts in Java.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-1649798106138599791?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/1649798106138599791/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/06/5-minute-introduction-to-dependence.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/1649798106138599791'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/1649798106138599791'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/06/5-minute-introduction-to-dependence.html' title='5 Minute Introduction to Dependence Injection (DI, IoC)'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-3167458946792547032</id><published>2008-05-27T11:49:00.000-07:00</published><updated>2008-05-27T12:35:13.197-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Deployment'/><title type='text'>Glassfish and Windows Service</title><content type='html'>If you want to automatically restart a Glassfish domain on Windows platform, you need to pay attention to some dirty details:&lt;br /&gt;1) Try not to install Glassfish under a directory with space. It will save you some troubles down the road. If you happen not to do so the first hand, be prepared to customize the following script to set up the Windows Service:&lt;br /&gt;&lt;br /&gt;C:\windows\system32\sc.exe create domain1 binPath= "C:\Program Files\glassfish-v2ur1\lib\appservService.exe \"\\\"C:\Program Files\glassfish-v2ur1\bin\asadmin.bat\\\" start-domain --user admin domain1\" \"\\\"C:\Program Files\glassfish-v2ur1\bin\asadmin.bat\\\" stop-domain domain1\\\"" start= auto type= own type= interact DisplayName= "Glassfish"&lt;br /&gt;&lt;br /&gt;Please pay special attention to those ugly escapes characters.&lt;br /&gt;&lt;br /&gt;2) When you log out of Windows, the java.exe will stop while the Windows Service is still running. The JVM shuts itself down upon the user logout event. This "feature" can be disabled by modifying the Glassfish's domains\domain1\config\domain.xml.  Open the file and search for jvm-options; add a new line at the bottom of the options for the new &lt;span style="font-weight: bold;"&gt;-Xrs&lt;/span&gt; JVM option following the format in the file; save the file and finally restart the Glassfish Windows Service to activate the change.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-3167458946792547032?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/3167458946792547032/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/05/glassfish-and-windows-service.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/3167458946792547032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/3167458946792547032'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/05/glassfish-and-windows-service.html' title='Glassfish and Windows Service'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-6046551390281486694</id><published>2008-05-09T11:28:00.000-07:00</published><updated>2008-05-09T14:08:35.663-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JDBC'/><category scheme='http://www.blogger.com/atom/ns#' term='Performance Tuning'/><title type='text'>JDBC Performance Tuning</title><content type='html'>When a JDBC call is slow, you can do a performance tuning on both the Java code side and the database server side.  The following is a list of tips.&lt;br /&gt;&lt;br /&gt;Java side performance tuning:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Time your database access to have an idea whether the Java code or the database is the culprit.&lt;/li&gt;&lt;li&gt;Make sure you are using the right JDBC driver. There are four types of JDBC drivers. Some types, like JDBC-ODBC bridge, are doomed to be slow.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Be sure to use database connection pool unless your application is entirely single threaded.&lt;/li&gt;&lt;li&gt;Be sure to close database resources, such as Connection, Statement, ResultSet even in situations when Exception's are thrown.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Use PreparedStatement correctly.&lt;/li&gt;&lt;/ol&gt;Database side performance tuning:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Determine whether indexes are necessary to create.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Update database statistics.&lt;/li&gt;&lt;li&gt;Run Explain Plan or similar tools on expensive queries to avoid full table scan.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;These tips sound like vanilla. However they are proven effective ways to kill 80% of your JDBC performance issues.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-6046551390281486694?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/6046551390281486694/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/05/jdbc-performance-tuning.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/6046551390281486694'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/6046551390281486694'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/05/jdbc-performance-tuning.html' title='JDBC Performance Tuning'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-4966766340101752350</id><published>2008-05-08T08:28:00.000-07:00</published><updated>2008-05-08T10:33:53.825-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JDBC'/><title type='text'>How to log JDBC Calls to a Stored Procedure</title><content type='html'>When debugging nasty problems, JDBC developers want to see logs. Of course we can log on the Java code side. A lot of times it is still not good enough. What if the problem is from the JDBC driver? We want to see what is really going on inside a stored procedure and what exactly do the input parameters or the complicated query statement look like.&lt;br /&gt;&lt;br /&gt;One simple approach is to insert the statement as a string into a log table. The following is an example in the syntax of SQL Server:&lt;br /&gt;declare @stmt1 nvarchar(2000)&lt;br /&gt;declare @stmt2 nvarchar(2000)&lt;br /&gt;&lt;br /&gt;select @stmt1 = '...'&lt;br /&gt;select @stmt2 = '...'&lt;br /&gt;&lt;br /&gt;if ( @input is not null ) begin&lt;br /&gt; select @stmt1 = @stmt1 + '...'&lt;br /&gt; select @stmt2 = @stmt2 + '...';&lt;br /&gt;end&lt;br /&gt;&lt;br /&gt;select @stmt1 = @stmt1 + @stmt2&lt;br /&gt;exec sp_executesql @stmt1&lt;br /&gt;&lt;span style="font-weight: bold;"&gt; insert into LogTable values (@stmt1, &lt;/span&gt;&lt;strong&gt;CURRENT_TIMESTAMP&lt;/strong&gt;&lt;span style="font-weight: bold;"&gt;)&lt;br /&gt;&lt;br /&gt;&lt;/span&gt;&lt;span&gt;In a conclusion, the trick is to write the statement as a string, insert it into a table before execution.&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-4966766340101752350?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/4966766340101752350/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/05/how-to-log-in-stored-procedure.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/4966766340101752350'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/4966766340101752350'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/05/how-to-log-in-stored-procedure.html' title='How to log JDBC Calls to a Stored Procedure'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-7541254635282798477</id><published>2008-05-06T09:07:00.000-07:00</published><updated>2008-05-06T09:38:01.897-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Tools'/><category scheme='http://www.blogger.com/atom/ns#' term='Web Services'/><title type='text'>How to Test a Web Service</title><content type='html'>When you develop a Web Service application, either on the client side or on the server side, you need a tester tool. If you are on the client side, before spending too much time on your code, it's good to test whether the Web Services server is stable and have some general idea of the sample requests and responses. If you are on the server side, you definitely need a tester to prove your deliveries. Sometimes developers come up with an in-house GUI or web application tester and end up in a situation where there are more bugs in the tester than the application itself. Fortunately there are free, open-source and stable tools available nowadays. We don't need to reinvent the wheel.&lt;br /&gt;&lt;br /&gt;Most of the time, Web Services are nothing more than SOAP/XML over HTTP.  In the simplest cases, any tool that can send text over HTTP to an URL can be used to test Web Services. But you will find it a must-have to generate sample SOAP requests from any given WSDL. The famous XMLSpy provides this kind of feature, unfortunately with a hefty fee. The free version of  &lt;a href="http://www.soapui.org/"&gt;soapUI&lt;/a&gt; by Eviware is my favorite.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-7541254635282798477?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/7541254635282798477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/05/how-to-test-web-service.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/7541254635282798477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/7541254635282798477'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/05/how-to-test-web-service.html' title='How to Test a Web Service'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-248262218109121636</id><published>2008-04-25T08:24:00.001-07:00</published><updated>2008-05-23T11:55:24.549-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web Services'/><title type='text'>Log Web Services Incoming Requests in JAX-WS</title><content type='html'>For any server side Web Services development, logging the incoming requests is crucial. The following code snippet gives an example. Even though this particular example uses SOAPMessageContext from JAX-WS's SOAPHandler, the approach can be easily modified to fit into other Web Services solutions.&lt;br /&gt;&lt;br /&gt;              ByteArrayOutputStream buffer = new ByteArrayOutputStream();&lt;br /&gt;              soapMessageContext.getMessage().writeTo(buffer);&lt;br /&gt;              log.info("SOAP Request:\n" + new String(buffer.toByteArray()));&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;Note that a lot of times logging comes hand in hand with incoming requests validation. Read this blog for more details.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-248262218109121636?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/248262218109121636/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/04/log-web-services-incoming-requests-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/248262218109121636'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/248262218109121636'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/04/log-web-services-incoming-requests-in.html' title='Log Web Services Incoming Requests in JAX-WS'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-1183720261490503587</id><published>2008-04-25T08:20:00.000-07:00</published><updated>2010-04-12T11:35:04.672-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile Methodologies'/><title type='text'>Continuous Integration</title><content type='html'>Agile Methodologies demand frequent deliveries in short cycles. It's desirable to fully automate the mundane process to checkout latest code, build, deploy, test, and send out notification on errors. Continuous Integration (CI) come into the play. There are some other significant  benefits to adopt a CI tool:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;A fully automated process will improve the quality standard for build processes. You don't need to worry about scenarios when the code works on programerA's environment, but not programerB's.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Code quality will be improved. For a large project with many programmers, it is inevitable to accidentally check in uncompilable code. You can count on the CI tool as a safety net  to catch the errors before they pollute the whole team's working environments. To be specifically, the CI tool can be configured to check out code every 15 or 30 minutes, catch the error, and send out notifications accordingly.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;You may have vivid memory how frustrated you were when hours of debugging work only pointed to non-reliable build process or non-stable code repository. So you cannot wait to try out a CI tool. Amazingly the efforts needed  are trivial. You may only need a hardware, an open source CI tool, and some configuration work. For the hardware, you need a dedicated box to do the CI work exclusively. For the software, there are several major players on the CI arena, for example, Hudson, Cruise Control and Continuum. Java World has a good article to compare them: http://www.javaworld.com/javaworld/jw-11-2006/jw-1101-ci.html&lt;br /&gt;&lt;br /&gt;Most configuration work is easy if you are familiar with build scripts like Maven or Ant. It may be a little bit challenging to configure those scripts to be environment-agnostic. You may wish to put all environment-specific variables into a configuration file, which includes, directory locations, database connection strings, external system URL's and etc.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-1183720261490503587?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/1183720261490503587/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/04/continuous-integration.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/1183720261490503587'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/1183720261490503587'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/04/continuous-integration.html' title='Continuous Integration'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-4103974422113172532</id><published>2008-04-25T07:29:00.000-07:00</published><updated>2008-05-31T08:09:46.966-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile Methodologies'/><title type='text'>Limitations of Agile Methodologies</title><content type='html'>Agile Methodologies are adaptive, iterative and incremental. Their effectiveness is well recognized in the industry. However, there is no silver bullet. The Agile Methodologies have their limitations and demand a close interaction among end users, business analysts, developers and testers in short cycles. It implies a break down of walls among divisions and the formation of a coherent and collaborative team from different groups. The Agile Methodologies work best for co-located teams. In certain circumstances this is simply too expensive or politically impossible. Another consideration point is the team size. When the size of the development team increases, it's agility decreases.&lt;br /&gt;&lt;br /&gt;In a conclusion, think twice before adopting Agile Methodologies for large scale projects or distributed development teams.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-4103974422113172532?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/4103974422113172532/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/04/limitations-of-agile-methodologies.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/4103974422113172532'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/4103974422113172532'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/04/limitations-of-agile-methodologies.html' title='Limitations of Agile Methodologies'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-6759168054199437237</id><published>2008-04-22T13:06:00.000-07:00</published><updated>2008-05-31T09:05:36.678-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Agile Methodologies'/><title type='text'>Agile Methodologies vs Waterfall</title><content type='html'>Let's consider the simplified version of a complicated question, how to develop a system composed of  ComponentA and ComponentB? ComponentA can be a software sub-system. ComponentA can also be deliverable artifacts like documentation, design, or code. The assumptions are: ComponentB cannot be started until ComponentA is finished; both ComponentA and ComponentB take significant efforts to finish.&lt;br /&gt;&lt;br /&gt;The straightforward answer is:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Step One: Finish ComponentA;&lt;/li&gt;&lt;li&gt;Step Two: Finish ComponentB;&lt;/li&gt;&lt;li&gt;Optional Step Three: Assemble ComponentA and ComponentB together.&lt;/li&gt;&lt;/ul&gt;This is exact the sequential waterfall approach. It mimics what an assembly line has been doing in the mature manufacturing industry over decades of years. However, software is quite different from hardware. By its very nature of flexibility, software development process is much less predictable.  Big surprises (or risks) may arise:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;The ultimate goal of the project is a moving target;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;By any chance, if ComponentA is delayed, all components down the chain will be delayed;&lt;/li&gt;&lt;li&gt;If a working project cannot be delivered finally, ComponentA may be totally useless.&lt;br /&gt;&lt;/li&gt;&lt;/ul&gt;To control the risks, Agile Methodologies, the iterative, adaptive and incremental approach, are preferred. The process looks like:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Step One: Finish (ComponentA-- and ComponentB--);  "--" means a simplified version of the system;&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Step Two: Finish (ComponentA- and ComponentB-);&lt;/li&gt;&lt;li&gt;Step Three: Finish (ComponentA and ComponentB).&lt;/li&gt;&lt;/ul&gt;The benefits are listed below:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Be adaptive to changing requirements. End user's feed back can be heard early. To do the right project is more important than to do the project right.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Progress and deliveries are very visible. We don't have to wait until the Step Three to see some workable results.&lt;/li&gt;&lt;li&gt;If by any chance, there are limited budget, time, or resources to finish the Step Three, the results of Step One and Step Two are still acceptable working systems.&lt;/li&gt;&lt;/ul&gt;For any approaches adaptive in their nature, people will raise the question, how to make the short term targets in line with the long term goals?  This question has an assumption that the 'long term goals' are well known. Agile Methodologies help most when the 'long term goal' is unclear or when the process how to achieve the 'long term goal' is unclear. The short term cycles help to clarify things during the iterative process. If both the 'long term goal' and the process how to achieve it are crystal clean and carved into a stone, go ahead using Waterfall with no hesitation.&lt;br /&gt;&lt;br /&gt;It's my motto that - Release early, release often, and release working products. However there is no panacea to resolve all software quality and delivery challenges. In another &lt;a href="http://denistek.blogspot.com/2008/04/limitations-of-agile-methodologies.html"&gt;post here&lt;/a&gt;, I discussed limitations of Agile Methodologies.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-6759168054199437237?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/6759168054199437237/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/04/agile-methodologies-vs-waterfall.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/6759168054199437237'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/6759168054199437237'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/04/agile-methodologies-vs-waterfall.html' title='Agile Methodologies vs Waterfall'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2771755619149574510.post-3620277336911737981</id><published>2008-04-21T13:22:00.000-07:00</published><updated>2008-04-22T12:48:22.746-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JDBC'/><title type='text'>java.lang.NullPointerException when accessing ResultSet from SQL Server</title><content type='html'>I used Java JDBC to call a SQL Server Stored Procedure and got a NullPointerException from the  last line of the following innocent code below:&lt;br /&gt;stmt.execute();&lt;br /&gt;            rs = stmt.getResultSet();&lt;br /&gt;            while (rs.next()) ...&lt;br /&gt;&lt;br /&gt;The stored procedure looks like:&lt;br /&gt; operation 1&lt;br /&gt; operation 2&lt;br /&gt; select ... from ...&lt;br /&gt;&lt;br /&gt;It turns out that 'SET NOCOUNT ON' should be added into the stored procedure before the 'operation 1'.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2771755619149574510-3620277336911737981?l=denistek.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://denistek.blogspot.com/feeds/3620277336911737981/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://denistek.blogspot.com/2008/04/get-javalangnullpointerexception-when.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/3620277336911737981'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2771755619149574510/posts/default/3620277336911737981'/><link rel='alternate' type='text/html' href='http://denistek.blogspot.com/2008/04/get-javalangnullpointerexception-when.html' title='java.lang.NullPointerException when accessing ResultSet from SQL Server'/><author><name>Denis Wang</name><uri>http://www.blogger.com/profile/15636962763176684837</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>0</thr:total></entry></feed>
