<?xml version='1.0' encoding='UTF-8'?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/'><id>tag:blogger.com,1999:blog-1017709164693452092</id><updated>2008-09-29T14:55:04.796+01:00</updated><title type='text'>Andy Grove's Blog</title><subtitle type='html'>Andy Grove's thoughts on software development and technology in general.</subtitle><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/'/><link rel='next' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default?start-index=26&amp;max-results=25'/><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://www.codefutures.com/weblog/andygrove/atom.xml'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>35</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>25</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-50174367541722065</id><published>2008-09-29T14:07:00.009+01:00</published><updated>2008-09-29T14:54:15.639+01:00</updated><title type='text'>MySQL Replication Lag</title><content type='html'>MySQL replication offers a quick and easy solution for having data replicated from a master database to one or more slave databases for failover and/or load balancing of read queries for scalability and is often the first step in a company’s backup and failover strategy.&lt;br /&gt; &lt;br /&gt;MySQL replication is completely asynchronous, meaning that the front-end application (often a web site) does not have to wait for replication to happen on each transaction and this keeps the master database operating efficiently.&lt;br /&gt; &lt;br /&gt;However, having a completely asynchronous replication solution means that the slave database is never 100% up to date. There is an even more serious issue with MySQL replication because it is single-threaded in nature to ensure transactions are applied to the slave database in the same order that they were applied to the master. This means that if the master database is under load with 100 concurrent transactions for a period of time and the slave is only able to process 1 concurrent transaction due to the single-threaded implementation then the slave database will very quickly fall behind the master database and this could mean thousands or tens of thousands of rows that are not replicated and if the master database fails then these transactions could be lost. I'm sure most companies would find this unacceptable.&lt;br /&gt;&lt;br /&gt;I'm curious how developers and DBAs view this issue. It seems unlikely that anyone is entirely comfortable with an unreliable "best effort" replication system for failover although it might be acceptable for scalability if the application is intelligent enough to work around any lag issues optimistically by querying a slave first and then going to the master if the slave is not up to date.&lt;br /&gt;&lt;br /&gt;Google have contributed open source code for &lt;a href="http://code.google.com/p/google-mysql-tools/wiki/SemiSyncReplicationDesign" target="new"&gt;SemiSyncReplication&lt;/a&gt; which provides a partial solution to the problem. Their approach is to have the master sychronously replicate the transaction to the slave mysql instance when the transaction is committed on the master but does not wait for the slave to actually write the transaction to disk, therefore maintaining a sensible balance between performance and reliability.&lt;br /&gt;&lt;br /&gt;How do you handle the issue of MySQL replication lag in your organization? Do you use Google SemiSyncReplication or have you found other products/tools to mitigate against replication lag?</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/50174367541722065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=50174367541722065' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/50174367541722065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/50174367541722065'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/09/mysql-replication-lag.html' title='MySQL Replication Lag'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-6811612333694497830</id><published>2008-07-20T15:11:00.006+01:00</published><updated>2008-07-20T16:58:28.462+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='spring dao'/><title type='text'>Spring DAO</title><content type='html'>Spring DAO support has been improved in the latest FireStorm 4.0 beta, which is now available for &lt;a href="http://www.codefutures.com/products/firestorm/download/"&gt;download&lt;/a&gt;. Each Spring DAO method is now annotated as @Transactional and the spring configuration file specifies a DataSourceTransactionManager. By default the DAO finder methods now return typed lists e.g. List&amp;lt;Customer&amp;gt; rather than arrays.&lt;br /&gt;&lt;br /&gt;There are some improvements to Spring MVC support as well. FireStorm now generates a single controller per table which extends MultiActionController.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/6811612333694497830/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=6811612333694497830' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/6811612333694497830'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/6811612333694497830'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/07/spring-dao.html' title='Spring DAO'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-1245862988113876673</id><published>2008-06-29T12:08:00.012+01:00</published><updated>2008-06-29T12:34:50.568+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java linux'/><title type='text'>The generated script approach to running shell commands from Java</title><content type='html'>Running external process from Java is simple enough using Runtime.exec() but there are some well documented limitations which are covered in detail in an old but still relevant JavaWorld article entitled &lt;a href="http://www.javaworld.com/javaworld/jw-12-2000/jw-1229-traps.html?page=1" target="new"&gt;When Runtime.exec() won't&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;I recently needed to launch a MySQL backup from Java but needed to redirect stdout and stderr to file. Unfortunately, Runtime.exec() can only be used to run an executable file and pass parameters in. It does not support more complex operations such as piping the output of one process into another process or even redirecting stdout or stderr. In other words, Runtime.exec() can’t be used as a linux command line replacement.&lt;br /&gt;&lt;br /&gt;For example, this works correctly:&lt;br /&gt;&lt;pre&gt;&lt;span class="code"&gt;Runtime.getRuntime().exec( “/usr/bin/mysqldump mydb --result-file=mydb.dump” );&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;But this will not work:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="code"&gt;Runtime.getRuntime().exec( “/usr/bin/mysqldump mydb --result-file=mydb.dump &gt;stdout.txt 2&gt;stderr.txt” );&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This last code fragment runs without error but the stdout.txt and stderr.txt files are not created.&lt;br /&gt;&lt;br /&gt;The standard solution to this problem is to write Java code to launch two threads, one to read the output stream from the process and one to read the error stream and then write the output from those streams to disk from within Java, but this seems like a heavyweight solution in this instance. There is also a risk of the subprocess hanging if the Java code does not read the output from the process quickly enough, as outlined in the javadocs:&lt;br /&gt;&lt;br /&gt;&lt;div style="text-align: left;"&gt;&lt;span style="font-style: italic;"&gt;“Because some native platforms only provide limited buffer size for standard input and output streams, failure to promptly write the input stream or read the output stream of the subprocess may cause the subprocess to block, and even deadlock.”&lt;/span&gt;&lt;br /&gt;&lt;/div&gt;&lt;br /&gt;A simpler solution to allow arbitrary linux command lines to be run from Java is to write the command to a shell script and then execute the shell script. For example:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;br /&gt;&lt;span class="code"&gt;private static void &lt;/span&gt;&lt;span class="code"&gt;runCommand(String cmd) &lt;/span&gt;&lt;span class="code"&gt;throws &lt;/span&gt;&lt;span class="code"&gt;IOException, InterruptedException {&lt;br /&gt; &lt;br /&gt;        &lt;/span&gt;&lt;span class="comment"&gt;// generate a script file containg the command to run&lt;/span&gt;&lt;span class="code"&gt;&lt;br /&gt;        &lt;/span&gt;&lt;span class="code"&gt;final &lt;/span&gt;&lt;span class="code"&gt;File scriptFile = &lt;/span&gt;&lt;span class="code"&gt;new &lt;/span&gt;&lt;span class="code"&gt;File(&lt;/span&gt;&lt;span class="literal"&gt;&amp;quot;/tmp/runcommand.sh&amp;quot;&lt;/span&gt;&lt;span class="code"&gt;);&lt;br /&gt;        PrintWriter w = &lt;/span&gt;&lt;span class="code"&gt;new &lt;/span&gt;&lt;span class="code"&gt;PrintWriter(scriptFile);&lt;br /&gt;        w.println( &lt;/span&gt;&lt;span class="literal"&gt;&amp;quot;#!/bin/sh&amp;quot; &lt;/span&gt;&lt;span class="code"&gt;);&lt;br /&gt;        w.println( cmd  ); &lt;br /&gt;        w.close(); &lt;br /&gt; &lt;br /&gt;        &lt;/span&gt;&lt;span class="comment"&gt;// make the script executable&lt;/span&gt;&lt;span class="code"&gt;&lt;br /&gt;        Process p = Runtime.getRuntime().exec( &lt;/span&gt;&lt;span class="literal"&gt;&amp;quot;chmod +x &amp;quot; &lt;/span&gt;&lt;span class="code"&gt;+ scriptFile.getAbsolutePath() );&lt;br /&gt;        p.waitFor(); &lt;br /&gt; &lt;br /&gt;        &lt;/span&gt;&lt;span class="comment"&gt;// execute the script&lt;/span&gt;&lt;span class="code"&gt;&lt;br /&gt;        p = Runtime.getRuntime().exec( scriptFile.getAbsolutePath() ); &lt;br /&gt;        p.waitFor(); &lt;br /&gt;    }&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Using this approach, I can now simply run:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="code"&gt;runCommand(“/usr/bin/mysqldump mydb --result-file=mydb.dump &gt;stdout.txt 2&gt;stderr.txt”);&lt;/span&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This approach works fine for my requirement, without the overhead of creating additional Java threads. However, this approach is not suitable if the Java application needs to read the output of the process before the process has completed, in which case the standard approach of launching threads to read the output streams should be used.&lt;br /&gt;&lt;br /&gt;The generated script approach is very convenient for enabling general purpose linux command line usage from Java.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/1245862988113876673/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=1245862988113876673' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1245862988113876673'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1245862988113876673'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/06/generated-script-approach-to-running.html' title='The generated script approach to running shell commands from Java'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-4188291178181130362</id><published>2008-06-18T22:23:00.002+01:00</published><updated>2008-06-18T22:26:33.349+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='internet'/><title type='text'>Skype 4.0 promises high definition full screen video</title><content type='html'>I'm looking forward to getting my hands on &lt;a href="http://news.bbc.co.uk/newsbeat/hi/technology/newsid_7461000/7461018.stm" target="new"&gt;Skype 4.0&lt;/a&gt;. I wonder how well full screen video will really work on standard broadband connections. Sounds a little too good to be true. You can do full screen video with the current Skype version but the picture quality is pretty poor.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/4188291178181130362/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=4188291178181130362' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/4188291178181130362'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/4188291178181130362'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/06/skype-40-promises-high-definition-full.html' title='Skype 4.0 promises high definition full screen video'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-1606883749214372583</id><published>2008-06-12T09:15:00.001+01:00</published><updated>2008-06-12T09:25:09.274+01:00</updated><title type='text'>Database sharding with Python</title><content type='html'>I just read an interesting post over on &lt;a href="http://highscalability.com" target="new"&gt;highscalability.com&lt;/a&gt;. There is an early stage open source project called &lt;a href="http://highscalability.com/pyshards-aspires-build-sharding-toolkit-python" target="new"&gt;Pyshards&lt;/a&gt; that provides database sharding for Python developers. It's interesting to see sharding toolkits emerge for languages other than Java.&lt;br /&gt;&lt;br /&gt;I've been working extensively with database sharding for around 9 months now and it's an exciting area of technology that offers a very cost-effective way to implement near-linear database scalability using commodity hardware.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/1606883749214372583/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=1606883749214372583' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1606883749214372583'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1606883749214372583'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/06/database-sharding-with-python.html' title='Database sharding with Python'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-2631772507385290520</id><published>2008-06-04T11:39:00.003+01:00</published><updated>2008-06-04T12:53:30.218+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>Is Maven the right choice for your project?</title><content type='html'>Over the past 6 months or so I have been working with some projects that have a Maven2 build system and other projects that use plain Ant build systems. I love the concepts behind Maven and see tremendous value in standardizing the build process across projects as well as having a better way of managing dependencies on third party jars without storing them in each project's source repository.&lt;br /&gt;&lt;br /&gt;However, there are some pitfalls to be aware of before adopting Maven which may or may not be issues depending on your requirements.&lt;br /&gt;&lt;br /&gt;First of all, adopting Maven will typically require a Maven server (repository) to be set up and maintained. For some smaller development departments this creates an extra IT support burden that can be avoided with an Ant build system.&lt;br /&gt;&lt;br /&gt;Another potential issue is that third party projects might not have good support for Maven. For instance, another Apache project, Axis2, recently released version 1.4 but there is an open support ticket &lt;a href="http://issues.apache.org/jira/browse/AXIS2-3069"&gt;AXIS2-3069&lt;/a&gt; regarding the Maven2 java2wsdl plugin which does not seem to be in a working state yet. The ticket has not been updated since 2007. Sure, the problem can be worked around by calling Ant from within the Maven2 project, but that's adding another layer of complexity compared to a standard Ant project.&lt;br /&gt;&lt;br /&gt;Another issue that I have hit is that Maven2 has poor error reporting when it is unable to resolve a dependency. I eventually tracked my issue down to a java keystore / server certificate error but Maven didn't provide any hints that this was the problem, even with debug flags set.&lt;br /&gt;&lt;br /&gt;Overall, I'm still a fan of Maven2 and I think the combination of Maven and Ant is extremely powerful but in my recent experience it does add extra cost to the development process.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/2631772507385290520/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=2631772507385290520' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/2631772507385290520'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/2631772507385290520'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/06/is-maven-right-choice-for-your-project.html' title='Is Maven the right choice for your project?'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-5830375383374887864</id><published>2008-06-02T11:04:00.002+01:00</published><updated>2008-06-02T11:09:57.034+01:00</updated><title type='text'>High Performance MySQL</title><content type='html'>I spent some time this morning reading High Performance MySQL by Jeremy Zawodny &amp;amp; Derek Balling (O'Reilly). I've had this book on my shelf for a year or two now and have found it an excellent reference whenever architecting solutions that use MySQL for persistence. The sections on server performance tuning, replication, and backup and recovery are especially useful. This book really is a must read for any developers, DBAs or sysadmins working with MySQL.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/5830375383374887864/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=5830375383374887864' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/5830375383374887864'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/5830375383374887864'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/06/high-performance-mysql.html' title='High Performance MySQL'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-4820581550467943203</id><published>2008-05-13T10:52:00.000+01:00</published><updated>2008-05-19T10:53:09.408+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dao'/><title type='text'>FireStorm/DAO on Mac OS X</title><content type='html'>The latest &lt;a href="http://www.codefutures.com/firestormdao/download/"&gt;FireStorm&lt;/a&gt;/&lt;a href="http://www.codefutures.com/firestormdao/download/"&gt;DAO&lt;/a&gt; 4.0 beta download for Mac OS X now uses a single menu bar at the top of the screen in true Mac style and the toolbar buttons no longer have ugly borders. Sorry we didn't do this sooner but we just started using Apple hardware.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/4820581550467943203/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=4820581550467943203' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/4820581550467943203'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/4820581550467943203'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/05/firestormdao-on-mac-os-x.html' title='FireStorm/DAO on Mac OS X'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-2285077143593976514</id><published>2008-03-27T08:38:00.003Z</published><updated>2008-03-27T09:08:07.508Z</updated><title type='text'>MacBook Pro - Day 1</title><content type='html'>As I mentioned a few &lt;a href="http://www.codesuccess.com/blog/2008/03/sony-vaio-or-macbook-pro.html"&gt;days ago&lt;/a&gt;, I was in the market for a new laptop and was mostly looking at Windows machines until I realised that there was no good reason why I shouldn't buy a MacBook Pro instead. I decided to take the plunge and I ordered the 15" model, which arrived yesterday.&lt;br /&gt;&lt;br /&gt;I've been developing on Intel machines since 1989, starting out with DOS and then working my way through Windows 3.1, 2000, NT, XP and Vista so to switch to a different OS is a bit of a leap of faith. From time to time I had considered installing linux on a laptop but frankly it is too time consuming to get things working and I have precious little time as it is. &lt;br /&gt;&lt;br /&gt;The main attraction of moving to Mac OS X is that it is linux based and I can now use many of the same commands on my development machine as I use on the environments I deploy to.  I also like the fact that the hardware and software have been produced by the same company and should therefore provide a stable and optimized platform.&lt;br /&gt;&lt;br /&gt;The install experience was easily the best I have experienced on any computer. I was able to check out a couple of Java project from subversion and get them building with minimal fuss. The MacBook Pro built one of the projects about 25% faster than my Windows Vista desktop machine which on paper has a better spec, so Mac OS X does appear to be more efficient.&lt;br /&gt;&lt;br /&gt;I love the fact that there is a DVI port built in and this gives a crisp image when connected to my 24" Samsung monitor. The MagSafe power adaptor was a bit of an experience. I'm not ashamed to admit that I had to search on Google to find out how I was supposed to disconnect it.&lt;br /&gt;&lt;br /&gt;I've installed vmware fusion (an impressive piece of software) and I have a Windows XP and a Fedora 8 virtual machine, primarily for running unit tests on those platforms.&lt;br /&gt;&lt;br /&gt;It will take me a couple of days to get used to the keyboard but as this will be my only machine on a trip to Colorado next week I'll be forced to get up to speed quickly.&lt;br /&gt;&lt;br /&gt;I'm extremely impressed so far!</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/2285077143593976514/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=2285077143593976514' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/2285077143593976514'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/2285077143593976514'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/03/macbook-pro-day-1.html' title='MacBook Pro - Day 1'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-4332128795324565253</id><published>2008-03-20T09:21:00.003Z</published><updated>2008-03-20T17:34:03.772Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>Sony Vaio or MacBook Pro?</title><content type='html'>I have the opportunity to buy a new laptop and I have a budget of £1,500 (plus VAT). My main requirements are decent processing power (2.4GHz Core 2 Duo + 4GB RAM) and a good quality high resolution display (ideally WUXGA 1920x1200) and a keyboard that it suited to development work (so functions like 'home' and 'end' can't involve complex multi-key actions involving several keys at the same time).&lt;br /&gt;&lt;br /&gt;My initial shortlist included the Lenovo Thinkpad T61p, the Dell Precision M6300, and the Sony Vaio AR61ZU. I was pretty much settled on the Sony although I was concerned that this might be a bit larger than I really wanted due to the 17" display. I've now realised that the MacBook Pro is also a contender so I now have to choose between that and the Sony Vaio (which is pretty much the Apple of the PC world).&lt;br /&gt;&lt;br /&gt;Apart the basics of email and web, I'll be using the machine for Java development work 90% of the time and the tools I use are available on all platforms. The only Microsoft specific tools I need to use are Word and Excel. They are both available on the Mac, at a price. I guess there's always the option of using OpenOffice but I don't know how well that really works, especially with Office 2007 files.&lt;br /&gt;&lt;br /&gt;The MacBook Pro is probably the better choice for development work given that the OS is linux based (most of the development work I do is targeted at unix or linux production environments).&lt;br /&gt;&lt;br /&gt;It's a tough call. Suggestions anyone?</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/4332128795324565253/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=4332128795324565253' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/4332128795324565253'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/4332128795324565253'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/03/sony-vaio-or-macbook-pro.html' title='Sony Vaio or MacBook Pro?'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-7406673496320095883</id><published>2008-01-27T10:22:00.000Z</published><updated>2008-01-27T10:36:20.779Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='scalability'/><title type='text'>More on Hibernate</title><content type='html'>I got some flack a few months back when I posted an article about &lt;a target="new" href="http://www.codesuccess.com/blog/2007/10/hibernate-conclusions.html"&gt;poor scalability when using Hibernate&lt;/a&gt; for a proof of concept I was working on at the time. For that particular project it seems that Hibernate probably wasn't the best fit. However, I've now had the opportunity to use Hibernate on another project and my experience has been much better this time. First of all, the productivity benefits of using Hibernate rather than writing Spring JDBC code are quite impressive. I was able to get a prototype web application up and running in just a couple of hours with Hibernate because I really did only have to focus on the business logic rather than writing low level code. Scalability isn't at all critical for this application since it will not have a huge number of users but I ran some tests anyway to compare one of the transactions with a hand-coded JDBC equivalent. Here are the results:&lt;br /&gt;&lt;br /&gt;&lt;img src="http://www.codesuccess.com/img/blog/jdbc_hibernate.jpg" border="0" alt="JDBC versus Hibernate"&gt;&lt;br /&gt;&lt;br /&gt;It's true - Hibernate does add some overhead to the transaction but it's a price I'm happy to pay for the time saved in development. I expect there are other transactions in the system where Hibernate will be faster than JDBC due to caching.&lt;br /&gt;&lt;br /&gt;I still stand by my &lt;a target="new" href="http://www.codesuccess.com/blog/2007/10/hibernate-conclusions.html"&gt;previous conclusion&lt;/a&gt;, which is that it is always worth benchmarking products/technologies against real use cases to make sure they are suitable choices. One size does not fit all.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/7406673496320095883/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=7406673496320095883' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/7406673496320095883'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/7406673496320095883'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/01/more-on-hibernate.html' title='More on Hibernate'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-1659767344103133229</id><published>2008-01-16T23:27:00.000Z</published><updated>2008-01-16T23:33:54.366Z</updated><category scheme='http://www.blogger.com/atom/ns#' term='industry'/><title type='text'>Sun to buy MySQL for $1 billion</title><content type='html'>I just heard that Sun are acquiring MySQL for around $1 billion. The press release is &lt;a target="new"  href="http://www.mysql.com/news-and-events/sun-to-acquire-mysql.html"&gt;here&lt;/a&gt;.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/1659767344103133229/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=1659767344103133229' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1659767344103133229'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1659767344103133229'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/01/sun-to-buy-mysql-for-1-billion.html' title='Sun to buy MySQL for $1 billion'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-3421966794706016665</id><published>2008-01-04T13:35:00.000Z</published><updated>2008-01-04T14:21:17.993Z</updated><title type='text'>Sending extended character sets with OpenSMPP</title><content type='html'>There's astonishingly little documentation on the web about sending international characters in SMS messages using &lt;a href="http://smstools.sourceforge.net/" target="new"&gt;OpenSMPP&lt;/a&gt; (which is based on the &lt;a href="http://opensmpp.logica.com/" target="new"&gt;Logica SMPP API&lt;/a&gt;). After a bit of research and some experimentation I've established that the following steps are required:&lt;br /&gt;&lt;br /&gt;1. data_coding must be set to 0x08 (instructs the SMSC to use UCS-2 encoding - see section 5.2.19 in the &lt;a href="http://www.hslsms.com/documents/SMPPV3.4.pdf" target="new"&gt;SMPP protocol specification &lt;/a&gt; for other data codings)&lt;br /&gt;2. When setting the message content, it must be encoded with UTF16&lt;br /&gt;&lt;br /&gt;Here's some partial source code to demonstrate the point:&lt;br /&gt;&lt;pre&gt;&lt;br /&gt; SubmitSM request = new SubmitSM();&lt;br /&gt; request.setDataCoding( 0x08 ); // UCS-2 &lt;br /&gt; request.setShortMessage( messageText, Data.ENC_UTF16 );&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;It's worth noting that the maximum message length is reduced from 160 characters to 70 characters when using UCS-2.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/3421966794706016665/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=3421966794706016665' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/3421966794706016665'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/3421966794706016665'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2008/01/sending-extended-character-sets-with.html' title='Sending extended character sets with OpenSMPP'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-8583415818075674541</id><published>2007-12-31T09:36:00.001Z</published><updated>2007-12-31T11:43:41.462Z</updated><title type='text'>MySQL SemiSyncReplication</title><content type='html'>One of the projects I'm involved in at the moment has required some research into strategies for clustering MySQL for HA. One of the interesting ideas that we came across is some code from Google that adds semi-synchronous replication to MySQL's built in replication feature. Standard MySQL replication is asynchronous, meaning that transactions are committed on the master database and at some point after that (could be milliseconds, could be minutes or longer) the slave databases will receive the transactions and replicate them. This is great for performance but if the master database dies then transactions are likely to be lost due to the lag between the master and slaves. One of Google's contribution is to make the master database wait until the transaction has been received by one or more slave databases before acknowledging to the client the transaction has been committed. This doesn't 100% guarantee that transactions won't be lost (the slave could die before the transaction is actually written to disk) but it dramatically reduces the likelihood of transactions being lost. Of course, the price to pay is that writes to the master will be slower but thats pretty much always the trade-off between performance and reliability.&lt;br /&gt;&lt;br /&gt;Here's a link to the Google MySQL Tools project.&lt;br /&gt;&lt;br /&gt;&lt;a href="http://code.google.com/p/google-mysql-tools/w/list"&gt;http://code.google.com/p/google-mysql-tools/w/list&lt;/a&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/8583415818075674541/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=8583415818075674541' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/8583415818075674541'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/8583415818075674541'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/12/mysql-semisyncreplication.html' title='MySQL SemiSyncReplication'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-6837672525107472436</id><published>2007-10-28T14:22:00.000Z</published><updated>2007-10-28T14:32:06.136Z</updated><title type='text'>Making the move to Google Apps</title><content type='html'>For some time now I’ve set up my various email addresses to forward to a Gmail account so that I have a very fast searchable archive of emails that is backed up and  I also like the convenience of being able to use Outlook when I’m at my desk and webmail when I’m not. The only problem with this setup is that whenever I reply to emails they are sent from my personal Gmail address “on behalf of” my work email address. That just doesn’t look professional so this morning I decided to sign up with Google Apps. Not a big decision really since the free Google Apps account provides everything I need, including 4GB of email storage.  &lt;p class="MsoNormal"&gt;If you’re not familiar with Google Apps, it lets you use Gmail and other Google services such as Calendar, Docs and Chat with a custom domain name. It was incredibly easy to set up. I had to upload an HTML page to my server containing a Google generated identifier so that I could prove to Google that I own the domain. Next, I had to update my DNS settings with MX records pointing to Google’s mail servers. Google then automatically verified that I had set this up correctly. After that there was a delay of around an hour before the new Gmail account was activated.&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;So I now have a fully managed email service with plenty of storage and arguably the world's best webmail experience and it hasn't cost a penny. Gmail also works very well with my Blackberry phone. Google have very recently added IMAP support to Gmail as well, which now gives me a choice of using Google's spam filtering (which is actually pretty good) or downloading spam emails to my client for filtering there with Cloudmark (the ultimate spam solution in my opinion).&lt;br /&gt;&lt;/p&gt;&lt;p class="MsoNormal"&gt;I'm pretty pleased with this setup so far.&lt;/p&gt;&lt;p class="MsoNormal"&gt;&lt;/p&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/6837672525107472436/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=6837672525107472436' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/6837672525107472436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/6837672525107472436'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/10/making-move-to-google-apps.html' title='Making the move to Google Apps'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-5170899422966145361</id><published>2007-10-24T06:54:00.000+01:00</published><updated>2007-10-24T07:13:19.599+01:00</updated><title type='text'>IntelliJ IDEA &amp; Maven</title><content type='html'>I started using the new 7.0 release of IntelliJ IDEA a couple of days ago and I'm loving the support for creating projects based on Eclipse or Maven project files.&lt;br /&gt;&lt;br /&gt;I've had some exposure to Maven in the past but have only recently started using it myself. Maven doesn't really do anything that I couldn't already do using Ant but it does mean I no longer have to spend time setting up large Ant build files each time I start a new project and it also reduces the size of my subversion repositories now that third party dependencies are stored in my Maven repository.&lt;br /&gt;&lt;br /&gt;One of the most compelling reasons for moving to Maven though is the release management process. Again, it doesn't do anything that I wasn't already doing manually but it really simplifies the process of building releases and tagging them in source control and also enforces it to a degree i.e. unit tests must pass before it will let you create a release.&lt;br /&gt;&lt;br /&gt;I'm certainly going to use Maven for all future simple jar/war projects but I haven't yet tried using Maven on more complex projects that have lots of ant tasks. However, Maven can be invoked from Ant so I'm sure it won't be an issue.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/5170899422966145361/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=5170899422966145361' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/5170899422966145361'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/5170899422966145361'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/10/intellij-idea-maven.html' title='IntelliJ IDEA &amp; Maven'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-2586813382553709565</id><published>2007-10-10T15:41:00.000+01:00</published><updated>2007-10-18T15:56:42.430+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>SimpleDateFormat and Thread Safety</title><content type='html'>It never fails to surprise me that so many developers are unaware that SimpleDateFormat is not thread-safe. It seems like almost all J2EE projects I work on have code that uses instance variables or static instance variables to store a SimpleDateFormat that is then used throughout the code base without any concurrency control.  Here’s a classic example:&lt;br /&gt;&lt;pre&gt;&lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaKeyword"&gt;class&lt;/span&gt; &lt;span class="javaIdentifier"&gt;MyService&lt;/span&gt; &lt;span class="javaComment"&gt;// could be a web service or a session bean&lt;br /&gt;&lt;/span&gt;{&lt;br /&gt; &lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaKeyword"&gt;void&lt;/span&gt; &lt;span class="javaIdentifier"&gt;someBusinessMethod&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;String&lt;/span&gt; &lt;span class="javaIdentifier"&gt;datestring&lt;/span&gt;, ...) {&lt;br /&gt;     &lt;span class="javaIdentifier"&gt;Date&lt;/span&gt; &lt;span class="javaIdentifier"&gt;date&lt;/span&gt; = &lt;span class="javaIdentifier"&gt;GlobalConst&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;DATE_FMT&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;parse&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;datestring&lt;/span&gt;);&lt;br /&gt;     &lt;span class="javaComment"&gt;// rest of code omitted&lt;br /&gt;&lt;/span&gt;    }&lt;br /&gt;}&lt;/pre&gt;This is particularly nasty because the code may well run without producing any exceptions but could easily parse the date incorrectly. If you don't believe how likely this is, here's some code to prove the point:&lt;br /&gt;&lt;pre&gt;&lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaKeyword"&gt;class&lt;/span&gt; &lt;span class="javaIdentifier"&gt;ProveNotSafe&lt;/span&gt; {&lt;br /&gt; &lt;span class="javaKeyword"&gt;static&lt;/span&gt; &lt;span class="javaIdentifier"&gt;SimpleDateFormat&lt;/span&gt; &lt;span class="javaIdentifier"&gt;df&lt;/span&gt;&lt;br /&gt;            = &lt;span class="javaKeyword"&gt;new&lt;/span&gt; &lt;span class="javaIdentifier"&gt;SimpleDateFormat&lt;/span&gt;( &lt;span class="javaLiteral"&gt;"dd-MMM-yyyy"&lt;/span&gt; );&lt;br /&gt; &lt;span class="javaKeyword"&gt;static&lt;/span&gt; &lt;span class="javaIdentifier"&gt;String&lt;/span&gt; &lt;span class="javaIdentifier"&gt;testdata&lt;/span&gt;[] = {&lt;br /&gt;     &lt;span class="javaLiteral"&gt;"01-Jan-1999"&lt;/span&gt;, &lt;span class="javaLiteral"&gt;"14-Feb-2001"&lt;/span&gt;, &lt;span class="javaLiteral"&gt;"31-Dec-2007"&lt;/span&gt;&lt;br /&gt; };&lt;br /&gt; &lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaKeyword"&gt;static&lt;/span&gt; &lt;span class="javaKeyword"&gt;void&lt;/span&gt; &lt;span class="javaIdentifier"&gt;main&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;String&lt;/span&gt;[] &lt;span class="javaIdentifier"&gt;args&lt;/span&gt;) {&lt;br /&gt;     &lt;span class="javaIdentifier"&gt;Runnable&lt;/span&gt; &lt;span class="javaIdentifier"&gt;r&lt;/span&gt;[] = &lt;span class="javaKeyword"&gt;new&lt;/span&gt; &lt;span class="javaIdentifier"&gt;Runnable&lt;/span&gt;[ &lt;span class="javaIdentifier"&gt;testdata&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;length&lt;/span&gt; ];&lt;br /&gt;     &lt;span class="javaKeyword"&gt;for&lt;/span&gt; (&lt;span class="javaKeyword"&gt;int&lt;/span&gt; &lt;span class="javaIdentifier"&gt;i&lt;/span&gt; = &lt;span class="javaLiteral"&gt;0&lt;/span&gt;; &lt;span class="javaIdentifier"&gt;i&lt;/span&gt; &amp;lt; &lt;span class="javaIdentifier"&gt;r&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;length&lt;/span&gt;; &lt;span class="javaIdentifier"&gt;i&lt;/span&gt;++) {&lt;br /&gt;         &lt;span class="javaKeyword"&gt;final&lt;/span&gt; &lt;span class="javaKeyword"&gt;int&lt;/span&gt; &lt;span class="javaIdentifier"&gt;i2&lt;/span&gt; = &lt;span class="javaIdentifier"&gt;i&lt;/span&gt;;&lt;br /&gt;         &lt;span class="javaIdentifier"&gt;r&lt;/span&gt;[&lt;span class="javaIdentifier"&gt;i&lt;/span&gt;] = &lt;span class="javaKeyword"&gt;new&lt;/span&gt; &lt;span class="javaIdentifier"&gt;Runnable&lt;/span&gt;() {&lt;br /&gt;             &lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaKeyword"&gt;void&lt;/span&gt; &lt;span class="javaIdentifier"&gt;run&lt;/span&gt;() {&lt;br /&gt;                 &lt;span class="javaKeyword"&gt;try&lt;/span&gt; {&lt;br /&gt;                     &lt;span class="javaKeyword"&gt;for&lt;/span&gt; (&lt;span class="javaKeyword"&gt;int&lt;/span&gt; &lt;span class="javaIdentifier"&gt;j&lt;/span&gt;=&lt;span class="javaLiteral"&gt;0&lt;/span&gt;; &lt;span class="javaIdentifier"&gt;j&lt;/span&gt;&amp;lt;&lt;span class="javaLiteral"&gt;1000&lt;/span&gt;; &lt;span class="javaIdentifier"&gt;j&lt;/span&gt;++) {&lt;br /&gt;                         &lt;span class="javaIdentifier"&gt;String&lt;/span&gt; &lt;span class="javaIdentifier"&gt;str&lt;/span&gt; = &lt;span class="javaIdentifier"&gt;testdata&lt;/span&gt;[&lt;span class="javaIdentifier"&gt;i2&lt;/span&gt;];&lt;br /&gt;                         &lt;span class="javaIdentifier"&gt;String&lt;/span&gt; &lt;span class="javaIdentifier"&gt;str2&lt;/span&gt; = &lt;span class="javaLiteral"&gt;null&lt;/span&gt;;&lt;br /&gt;                         &lt;span class="javaComment"&gt;/*synchronized(df)*/&lt;/span&gt; {&lt;br /&gt;                             &lt;span class="javaIdentifier"&gt;Date&lt;/span&gt; &lt;span class="javaIdentifier"&gt;d&lt;/span&gt; = &lt;span class="javaIdentifier"&gt;df&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;parse&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;str&lt;/span&gt;);&lt;br /&gt;                             &lt;span class="javaIdentifier"&gt;str2&lt;/span&gt; = &lt;span class="javaIdentifier"&gt;df&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;format&lt;/span&gt;( &lt;span class="javaIdentifier"&gt;d&lt;/span&gt; );&lt;br /&gt;                         }&lt;br /&gt;                         &lt;span class="javaKeyword"&gt;if&lt;/span&gt; (!&lt;span class="javaIdentifier"&gt;str&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;equals&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;str2&lt;/span&gt;)) {&lt;br /&gt;                             &lt;span class="javaKeyword"&gt;throw&lt;/span&gt; &lt;span class="javaKeyword"&gt;new&lt;/span&gt; &lt;span class="javaIdentifier"&gt;RuntimeException&lt;/span&gt;(&lt;br /&gt;&lt;span class="javaLiteral"&gt;                                 "date conversion failed after "&lt;/span&gt;&lt;br /&gt;                               + &lt;span class="javaIdentifier"&gt;j&lt;/span&gt; + &lt;span class="javaLiteral"&gt;" iterations. Expected "&lt;/span&gt;&lt;br /&gt;                               + &lt;span class="javaIdentifier"&gt;str&lt;/span&gt; + &lt;span class="javaLiteral"&gt;" but got "&lt;/span&gt; + &lt;span class="javaIdentifier"&gt;str2&lt;/span&gt; );&lt;br /&gt;                         }&lt;br /&gt;                     }&lt;br /&gt;                 } &lt;span class="javaKeyword"&gt;catch&lt;/span&gt; (&lt;span class="javaIdentifier"&gt;ParseException&lt;/span&gt; &lt;span class="javaIdentifier"&gt;e&lt;/span&gt;) {&lt;br /&gt;                     &lt;span class="javaKeyword"&gt;throw&lt;/span&gt; &lt;span class="javaKeyword"&gt;new&lt;/span&gt; &lt;span class="javaIdentifier"&gt;RuntimeException&lt;/span&gt;( &lt;span class="javaLiteral"&gt;"parse failed"&lt;/span&gt; );&lt;br /&gt;                 }&lt;br /&gt;             }&lt;br /&gt;         };&lt;br /&gt;         &lt;span class="javaKeyword"&gt;new&lt;/span&gt; &lt;span class="javaIdentifier"&gt;Thread&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;r&lt;/span&gt;[&lt;span class="javaIdentifier"&gt;i&lt;/span&gt;]).&lt;span class="javaIdentifier"&gt;start&lt;/span&gt;();&lt;br /&gt;     }&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;&lt;br /&gt;When I run this code, I get the following output:&lt;br /&gt;&lt;pre&gt;java.lang.RuntimeException: date conversion failed after 3 iterations.&lt;br /&gt;Expected 14-Feb-2001 but got 01-Dec-2007&lt;/pre&gt;Note that "01-Dec-2007" isn't even one of the strings in the test data. It is actually a combination of the dates being processed by the other two threads!&lt;br /&gt;&lt;br /&gt;If I uncomment the synchronized keyword then of course it runs without exception.&lt;br /&gt;&lt;br /&gt;If I want to quickly remove these issues from a code base I usually create a simple replacement class like this that wraps SimpleDateFormat.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaKeyword"&gt;class&lt;/span&gt; &lt;span class="javaIdentifier"&gt;ThreadSafeSimpleDateFormat&lt;/span&gt; {&lt;br /&gt;&lt;br /&gt; &lt;span class="javaKeyword"&gt;private&lt;/span&gt; &lt;span class="javaIdentifier"&gt;DateFormat&lt;/span&gt; &lt;span class="javaIdentifier"&gt;df&lt;/span&gt;;&lt;br /&gt;&lt;br /&gt; &lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaIdentifier"&gt;ThreadSafeSimpleDateFormat&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;String&lt;/span&gt; &lt;span class="javaIdentifier"&gt;format&lt;/span&gt;) {&lt;br /&gt;     &lt;span class="javaKeyword"&gt;this&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;df&lt;/span&gt; = &lt;span class="javaKeyword"&gt;new&lt;/span&gt; &lt;span class="javaIdentifier"&gt;SimpleDateFormat&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;format&lt;/span&gt;);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; &lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaKeyword"&gt;synchronized&lt;/span&gt; &lt;span class="javaIdentifier"&gt;String&lt;/span&gt; &lt;span class="javaIdentifier"&gt;format&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;Date&lt;/span&gt; &lt;span class="javaIdentifier"&gt;date&lt;/span&gt;) {&lt;br /&gt;     &lt;span class="javaKeyword"&gt;return&lt;/span&gt; &lt;span class="javaIdentifier"&gt;df&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;format&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;date&lt;/span&gt;);&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; &lt;span class="javaKeyword"&gt;public&lt;/span&gt; &lt;span class="javaKeyword"&gt;synchronized&lt;/span&gt; &lt;span class="javaIdentifier"&gt;Date&lt;/span&gt; &lt;span class="javaIdentifier"&gt;parse&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;String&lt;/span&gt; &lt;span class="javaIdentifier"&gt;string&lt;/span&gt;) &lt;span class="javaKeyword"&gt;throws&lt;/span&gt; &lt;span class="javaIdentifier"&gt;ParseException&lt;/span&gt; {&lt;br /&gt;     &lt;span class="javaKeyword"&gt;return&lt;/span&gt; &lt;span class="javaIdentifier"&gt;df&lt;/span&gt;.&lt;span class="javaIdentifier"&gt;parse&lt;/span&gt;(&lt;span class="javaIdentifier"&gt;string&lt;/span&gt;);&lt;br /&gt; }&lt;br /&gt;}&lt;/pre&gt;</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/2586813382553709565/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=2586813382553709565' title='12 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/2586813382553709565'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/2586813382553709565'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/10/simpledateformat-and-thread-safety.html' title='SimpleDateFormat and Thread Safety'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-1796303646250371085</id><published>2007-10-01T14:01:00.000+01:00</published><updated>2007-10-14T13:37:04.108+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='persistence'/><category scheme='http://www.blogger.com/atom/ns#' term='spring'/><title type='text'>Hibernate Conclusions</title><content type='html'>A while ago, I posted a couple of blog entries about the fact that I was going through the &lt;a href="http://www.codesuccess.com/blog/2007/08/waking-up-to-hibernate.html"&gt;learning curve&lt;/a&gt; with &lt;a href="http://www.codesuccess.com/blog/2007/08/waking-up-to-hibernate-part-2.html"&gt;Hibernate&lt;/a&gt;. Since then I have talked to some friends who have used Hibernate on large projects and I have also been prototyping an application using both Hibernate and Spring JDBC to understand the pros and cons of each approach.&lt;br /&gt;&lt;br /&gt;My initial reason for learning Hibernate was to make sure I knew enough in an interview situation if it came up. I also needed to recommend a persistence technology for a couple of projects that I am involved in and having heard so much about Hibernate I assumed that it would be a good fit.&lt;br /&gt;&lt;br /&gt;The biggest advantage that I can see with Hibernate is that it allows for rapid prototyping and development of new applications. That alone might be sufficient reason to use it on some projects. However, once you get past the simple initial requirements of persisting objects and get to the point where you need to run complex queries across a number of tables and perhaps use aggregate functions and other SQL capabilities, things get much more complex because Hibernate uses its own HQL query language that the runtime translates into SQL and this forces the development team through yet another learning curve.  Another issue with this approach is that you can’t simply develop and test SQL and then paste the working SQL into your source code or configuration files but have to then re-code them in HQL.&lt;br /&gt;&lt;br /&gt;Spring JDBC isn’t directly comparable to Hibernate because it doesn’t have the same goals (it doesn’t provide caching or object relational mapping for instance). However, it is certainly an alternative choice for implementing persistence. Spring JDBC provides a very small abstraction layer over raw JDBC and does this in an elegant way.  Setting up a new project does require more thought and effort than using Hibernate but that time is paid back once more complex application functionality is developed.&lt;br /&gt;&lt;br /&gt;However, the real surprise for me was how the performance and scalability compared between the two approaches. I spent a couple of days building a realistic subset of the application I was recommending the technology for and then ran benchmarks against a large database on dedicated test hardware.  The results showed that Hibernate didn’t scale at all well compared to Spring JDBC for this application.&lt;br /&gt;&lt;br /&gt;I posted some questions to the Hibernate forum to validate the approach I had taken and I received the rather terse and unhelpful reply &lt;span style="color: rgb(204, 102, 0);"&gt;“This is called 'A useless microbenchmark' and has been addressed a thousand times.”&lt;/span&gt; from a member of the Hibernate team.  I did some reading of the &lt;a target="new" href="http://www.hibernate.org/15.html#A8"&gt;Hibernate Performance FAQ&lt;/a&gt; and found some interesting statements in there, such as &lt;span style="color: rgb(204, 102, 0);"&gt;"We claim that Hibernate performs well"&lt;/span&gt; and &lt;span style="color: rgb(204, 102, 0);"&gt;"Many people try to benchmark Hibernate. All public benchmarks we have seen so far had (and most still have) serious flaws."&lt;/span&gt;. It almost seems like it is taboo to question Hibernate performance. I don't understand this defensive stance since most developers would recognize that there is a trade off between ease of use and performance.&lt;br /&gt;&lt;br /&gt;The bottom line for me is that for my application, Spring JDBC scales much, much better, which will save significant hardware dollars (or pounds). Spring JDBC also doesn't require much investment in terms of learning curves and I can have full control over the SQL I use which in turn will help me squeeze maximum throughput out of the hardware that is available to me.&lt;br /&gt;&lt;br /&gt;If you're considering using Hibernate on your projects I highly recommend running some performance tests based on your application requirements before getting in too deep.&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight: bold;"&gt;[14th Oct 2007]&lt;/span&gt; Wow! This article has certainly attracted more attention than I expected. I would like to draw attention to that fact that I state in the above article that Hibernate does not scale well for *this* *particular* *application*. Many people seem to think I'm saying that Hibernate doesn't scale well, period. That's not what I'm saying so please read again before posting comments :-)</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/1796303646250371085/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=1796303646250371085' title='21 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1796303646250371085'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1796303646250371085'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/10/hibernate-conclusions.html' title='Hibernate Conclusions'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-3507319020931391302</id><published>2007-09-28T08:02:00.000+01:00</published><updated>2007-09-28T10:14:18.123+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogging'/><title type='text'>Twittering, tumbling, and blogging</title><content type='html'>I've recently been experimenting with some different forms of blogging, mostly following the lead of a couple of friends who have invited me to use some of the new blogging web sites out there. Twitter is quite cool for seeing what friends are up to but for me at least doesn't seem like an appealing medium for communicating with a wider audience in the way that a blog does. I guess that's not the point of twitter anyway. Tumblr on the other hand does look interesting and seems to fill the middle ground between blogging and twittering. Tumblr doesn't really seem to be any different from a regular blog technically but it somehow seems more acceptable to use tumblr for short messages or sharing links. Basically, tumblr is a much less formal manner of blogging and suits me well for &lt;a target="new" href="http://andygrove.tumblr.com"&gt;my personal blog&lt;/a&gt; as it's somewhere I can post frequently if I have information I don't want to forget but doesn't require me to switch context from whatever else I'm doing in order to write a more formal blog entry with vaguely acceptable grammar.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/3507319020931391302/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=3507319020931391302' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/3507319020931391302'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/3507319020931391302'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/09/twittering-tumbling-and-blogging.html' title='Twittering, tumbling, and blogging'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-1421144069693403221</id><published>2007-09-23T10:21:00.000+01:00</published><updated>2007-09-23T10:32:58.988+01:00</updated><title type='text'>Way back when</title><content type='html'>I just rediscovered a web site I set up for a SQL tool that I wrote a long time ago (1997) called &lt;a target="new" href="http://web.archive.org/web/19970715014441/www.sgcltd.demon.co.uk/page2.html"&gt;HandySQL&lt;/a&gt; . Now &lt;a target="new" href="http://web.archive.org/web/19970715014441/www.sgcltd.demon.co.uk/page2.html"&gt;that's&lt;/a&gt; what I call web design!&lt;br /&gt;&lt;br /&gt;Luckily, my web design skills had improved a bit by 1999 when I set up the &lt;a target="new" href="http://web.archive.org/web/20000816160046/http://www.orbware.com/"&gt;Orbware&lt;/a&gt; web site.&lt;br /&gt;&lt;br /&gt;You just can't hide your past on internet!</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/1421144069693403221/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=1421144069693403221' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1421144069693403221'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1421144069693403221'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/09/what-was-i-thinking.html' title='Way back when'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-7704481530004921571</id><published>2007-09-22T13:35:00.000+01:00</published><updated>2007-09-22T13:57:24.073+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>Fedora 8 Test 2 supports Asus P5K mobo :-)</title><content type='html'>I've been trying on and off for a while, and without luck, to install Linux on one of my "test lab" machines. These machines have quad-core Intel processors on Asus P5K SE motherboards, which feature a Marvell PATA IDE controller. I'd tried a good number of distributions including the latest RHEL, Fedora, Ubuntu, and Mandriva releases but none of them would recognize the CD/DVD drive during installation. I did eventually manage to install Fedora 7 over the network but applications kept crashing due to kernel panics. After some research it seems that the Linux Kernel needed updating to support this motherboard and its chipset.&lt;br /&gt;&lt;br /&gt;This morning I downloaded Fedora 8 Test 2, which was released about a week ago and the install ran absolutely smoothly and I saw it specifically load a "pata_marvell" driver, which was reassuring.&lt;br /&gt;&lt;br /&gt;The only problem now is that the final release of FC8 is still about six weeks away so I just hope this test release is stable enough for my needs.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/7704481530004921571/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=7704481530004921571' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/7704481530004921571'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/7704481530004921571'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/09/fedora-8-test-2-supports-asus-p5k-mobo.html' title='Fedora 8 Test 2 supports Asus P5K mobo :-)'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-1894566170674577184</id><published>2007-09-19T20:12:00.000+01:00</published><updated>2007-09-19T20:17:55.194+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tools'/><title type='text'>Two Useful Tools</title><content type='html'>I've discovered two very useful tools this week that both have free and commercial versions available. The first, &lt;a href="http://www.soapui.com/"&gt;soapUI&lt;/a&gt;, is ideal for quickly testing WSDL-based SOAP services without the need to write code. The second, is &lt;a href="http://www.dbvisualizer.com/products/dbvis/"&gt;DBVisualizer&lt;/a&gt;, a general-purpose SQL client.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/1894566170674577184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=1894566170674577184' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1894566170674577184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/1894566170674577184'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/09/two-useful-tools.html' title='Two Useful Tools'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-2115173071621032306</id><published>2007-09-17T08:39:00.000+01:00</published><updated>2007-09-19T20:18:03.652+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mobile'/><title type='text'>iPhone to be exclusive to O2 in the UK?</title><content type='html'>Hmmm. Looks like the iPhone will be &lt;a href="http://www.engadget.com/2007/09/17/o2-apparently-locks-up-uk-iphone-deal-pays-through-nose/"&gt;exclusive to O2&lt;/a&gt; when it launches in the UK. O2 certainly seem to have a strategy of being the exclusive operator for some of the more advanced handsets and I've been suckered into becoming an O2 customer twice before for that very reason and ended up very disappointed with service both times. Also, I can barely pick up an O2 signal from my house so I guess I'll be sticking with my trusty BlackBerry on Vodafone.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/2115173071621032306/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=2115173071621032306' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/2115173071621032306'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/2115173071621032306'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/09/iphone-to-be-exclusive-to-o2-in-uk.html' title='iPhone to be exclusive to O2 in the UK?'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-8838857587562464624</id><published>2007-09-03T22:13:00.000+01:00</published><updated>2007-09-04T12:41:53.926+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><title type='text'>JPerf - Performance Testing for Java</title><content type='html'>I've been researching some ideas in my spare time over the past couple of weeks where performance is critical so I decided to put together a framework for easily running performance and scalability tests. The idea is to provide a framework that is as easy to use as JUnit and that encourages performance testing as an integral part of the development process, rather than as an afterthought before a product release or deployment.&lt;br /&gt;&lt;br /&gt;I've called the framework &lt;a target="new" href="http://sourceforge.net/projects/jperf"&gt;JPerf &lt;/a&gt;and I have made the code available on &lt;a target="new" href="http://sourceforge.net/projects/jperf"&gt;sourceforge&lt;/a&gt; - it's just a few classes at the moment but I will be evolving this as and when I need the functionality myself.&lt;br /&gt;&lt;br /&gt;The key interface is PerfTest which provides setUp(), test() and tearDown() methods. The test runners call setUp() once and then repeatedly call test() for the specified period of time and then call tearDown() at the end to free up resources, such as database connections.&lt;br /&gt;&lt;br /&gt;There are currently two test runners - PerfTestRunner and ScaleTestRunner, which measure performance and scalability, respectively. Here's some sample output from the example that ships with JPerf.&lt;br /&gt;&lt;br /&gt;PerfTestRunner:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;  [java] org.jperf.example.SimpleDateFormatTest: 243,971 iterations in 500ms (486,968 iterations per second)&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] org.jperf.example.MyDateFormatTest: 371,968 iterations in 500ms (742,451 iterations per second)&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;ScaleTestRunner:&lt;br /&gt;&lt;br /&gt;&lt;span style="font-size:85%;"&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 1 client threads, throughput is 100.0 tps and memory usage is 5137656&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 2 client threads, throughput is 202.0 tps and memory usage is 5175144&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 3 client threads, throughput is 300.0 tps and memory usage is 5223616&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 4 client threads, throughput is 402.0 tps and memory usage is 5271592&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 5 client threads, throughput is 504.0 tps and memory usage is 5330056&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 6 client threads, throughput is 602.0 tps and memory usage is 4888336&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 7 client threads, throughput is 702.0 tps and memory usage is 4979288&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 8 client threads, throughput is 798.0 tps and memory usage is 5057864&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 9 client threads, throughput is 900.0 tps and memory usage is 5137568&lt;/span&gt;&lt;br /&gt;&lt;span style="font-family: courier new;"&gt;  [java] With 10 client threads, throughput is 1000.0 tps and memory usage is 5215200&lt;/span&gt;&lt;br /&gt;&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;I'd be interested to hear ideas on what features people would like to see in this framework to make it useful for their projects. If you have any requirements please leave a comment here or post a feature request on sourceforge.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/8838857587562464624/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=8838857587562464624' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/8838857587562464624'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/8838857587562464624'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/09/jperf-performance-testing-for-java.html' title='JPerf - Performance Testing for Java'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry><entry><id>tag:blogger.com,1999:blog-1017709164693452092.post-7867408168996770502</id><published>2007-08-17T18:43:00.001+01:00</published><updated>2007-09-04T12:41:35.969+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='hardware'/><title type='text'>Lenovo 3000 N200 Review</title><content type='html'>I've had a few days to get used to the new laptop now so I thought I'd write a short review that might be useful to others considering buying this model.&lt;br /&gt;&lt;br /&gt;First off, I should point out that this is a no-frills functional laptop rather than a slick designer laptop (and that's definitely reflected in the price). This laptop isn't trying to be a Sony Vaio or a MacBook Pro. However, it doesn't look as dull as its business-class counterpart, the ThinkPad.&lt;br /&gt;&lt;br /&gt;The basic machine specification is: Intel Core 2 Duo 2.0 GHz, 2GB RAM, 160GB HDD (5,400 rpm), 1680 x 1050 resolution, DVD-RW, 4 x USB, 1 x Firewire, 10/100MB Ethernet, Modem, Media card reader, built in web cam and microphone.&lt;br /&gt;&lt;br /&gt;The machine came pre-installed with a rather bewildering amount of software and most of it seemed to load at startup making the machine very slow to boot. Especially worrying was a "ThinkVantage registry monitor" process which took both CPU cores to 100% for several minutes after logging in. After uninstalling most of the pre-installed software, which seems to be aimed at non-technical users, the machine now boots fairly fast and the overall performance is satisfying.&lt;br /&gt;&lt;br /&gt;The keyboard is excellent. One of the best I've used. In fact, this was one of the main reasons I decided to go for this model.&lt;br /&gt;&lt;br /&gt;The screen is slightly disappointing. This is clearly an area where some money was saved. However, it is perfectly adequate. The only real issues are the viewing angle (you really need to look at the screen directly) and the brightness and contrast (the screen seems slightly dark and doesn't compare well to high end laptops I've used in the past).&lt;br /&gt;&lt;br /&gt;The other slight disappointment is that there is no gigabit ethernet. That's not too big a deal since gigabit pcmia cards are inexpensive these days.&lt;br /&gt;&lt;br /&gt;However, at £710+VAT this laptop offers great value for money. If screen quality is important to you then the Thinkpad T61p is the machine to go for in the Lenovo range but this costs around £1,400+VAT. Considering that I'll mostly have the laptop connected to an external LCD display the extra spend just wasn't justified in my case.</content><link rel='replies' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/7867408168996770502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=1017709164693452092&amp;postID=7867408168996770502' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/7867408168996770502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/1017709164693452092/posts/default/7867408168996770502'/><link rel='alternate' type='text/html' href='http://www.codefutures.com/weblog/andygrove/2007/08/lenovo-3000-n200-review.html' title='Lenovo 3000 N200 Review'/><author><name>Andy Grove</name><uri>http://www.blogger.com/profile/13991619444627251911</uri><email>noreply@blogger.com</email></author></entry></feed>