<?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-5689812823462613341</id><updated>2012-01-10T10:20:47.381-08:00</updated><category term='randomness'/><category term='web application'/><category term='Windows XP'/><category term='users'/><category term='cryptography'/><category term='Microsoft'/><category term='SQL'/><category term='JSP'/><category term='hashCode()'/><category term='VirtualBox'/><category term='keepass'/><category term='passwords'/><category term='Hibernate'/><category term='Joshua Bloch'/><category term='environment'/><category term='service'/><category term='comparing Java objects'/><category term='--exclude-dir'/><category term='sed'/><category term='form'/><category term='chrome'/><category term='gnome'/><category term='software development'/><category term='firefox'/><category term='John Yeary'/><category term='css'/><category term='comparing objects'/><category term='&quot;Random Character Generator&quot;'/><category term='metric'/><category term='grep'/><category term='&quot;back button&quot;'/><category term='virtual machine'/><category term='character set'/><category term='Applications'/><category term='typesafe'/><category term='fgrep'/><category term='get'/><category term='Programming in Scala'/><category term='opera'/><category term='safari'/><category term='operating system'/><category term='GoToMeeting'/><category term='Struts'/><category term='form bean'/><category term='MySQL'/><category term='computer disposal'/><category term='software metrics'/><category term='shell scripting'/><category term='Database Reverse Engineering'/><category term='recycling'/><category term='cygwin'/><category term='internet explorer'/><category term='surrogate key'/><category term='Office'/><category term='security'/><category term='update speed'/><category term='quirks mode'/><category term='post'/><category term='bash'/><category term='Java'/><category term='regular expression'/><category term='spacer.gif'/><category term='replace'/><category term='Free Linux PC'/><category term='rest'/><category term='Tomcat'/><category term='antivirus'/><category term='desktop'/><category term='data types'/><category term='Database'/><category term='hard drive'/><category term='generics'/><category term='datatype'/><category term='Linux'/><category term='equals()'/><category term='html'/><category term='session'/><category term='Java Collections'/><category term='Comparable'/><category term='Joel on Software'/><category term='compareTo()'/><category term='Ubuntu'/><category term='Emacs'/><category term='command line'/><category term='symmetry'/><category term='requirements'/><category term='Log4J'/><category term='Deprecated'/><category term='Exceptions'/><category term='subversion'/><title type='text'>Glen Peterson's Blog</title><subtitle type='html'>Musings on programming, web applications, and technology in general</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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>28</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-6674074303996265760</id><published>2012-01-09T08:59:00.000-08:00</published><updated>2012-01-09T09:01:18.346-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='desktop'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='gnome'/><title type='text'>Ubuntu 11.10 "Oneiric" with a Gnome 2 Feel</title><content type='html'>&lt;p&gt;I love, and am maybe even a little addicted to the packages Ubuntu includes in their distributions.  But like many, I am dissatisfied with the new Unity interface because:&lt;ol&gt;&lt;li&gt;I can't open two copies of KeePassX at once&lt;/li&gt;&lt;li&gt;I hate using text search to find applications&lt;/li&gt;&lt;li&gt;It was too hard to create a custom application launcher&lt;/li&gt;&lt;/ol&gt;Hopefully all that will change, but until then, it's just not working for me.&lt;/p&gt;&lt;p&gt;I tried the LXDE desktop, but:&lt;ol&gt;&lt;li&gt;I didn't like the default software (Leafpad text editor, Galculator, LX terminal)&lt;/li&gt;&lt;li&gt;Desktop integration is lacking (no screen-shots, limited drag and drop from one application to another)&lt;/li&gt;&lt;/ol&gt;It's a very new distribution and may need a year or two to catch up.  Certainly LXDE is worth keeping an eye on, but it's not there yet.&lt;/p&gt;&lt;p&gt;What's currently working for me is a "retro" basic Gnome session based on OMG Ubuntu's article, &lt;a href="http://www.omgubuntu.co.uk/2011/12/how-to-make-ubuntu-11-10-look-and-feel-like-gnome-2/" target="_blank"&gt;Make Ubuntu 11.10 Look and Feel Like GNOME 2&lt;/a&gt;.  Thanks to "DigalMan" for pointing it out to me.  That article has a typo though, it's gnome-session-fallback, not gnome-fallback-session.&lt;/p&gt;&lt;p&gt;Also, when they say, "Add ‘ppa:jconti/gnome3‘ to your Software Sources" their instructions don't include how to add the PPA's key, so that future updates are not be applied properly (or at all).  Fortunately, &lt;a href="https://help.launchpad.net/Packaging/PPA/InstallingSoftware"&gt;launchpad.net has instructions&lt;/a&gt;.  The key lines I needed were:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;sudo add-apt-repository http://ppa.launchpad.net/jconti/gnome3/ubuntu&lt;br /&gt;sudo apt-get update&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It's not perfect (the top-bar color's not quite right and the icons space themselves wider apart when hovered over), but I'm basically back with the Ubuntu system I have loved for years, with the latest versions of all software.  If this works in 12.04, I'll probably stick with it.  If not, and LXDE has not improved by that time, I'll guess I'll try Mint.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-6674074303996265760?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/6674074303996265760/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=6674074303996265760' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/6674074303996265760'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/6674074303996265760'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2012/01/ubuntu-1110-oneiric-with-gnome-2-feel.html' title='Ubuntu 11.10 &quot;Oneiric&quot; with a Gnome 2 Feel'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-1305712166226025848</id><published>2011-09-14T06:07:00.000-07:00</published><updated>2011-09-14T06:37:27.224-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Database'/><category scheme='http://www.blogger.com/atom/ns#' term='SQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='Database Reverse Engineering'/><title type='text'>Automatic POJO Generation From a Database</title><content type='html'>&lt;p&gt;Like JPA (or presumably Spring), Hibernate "reverse engineering" tools can generate POJOs (Plain Old Java Objects) from database tables and vice-versa.  Generating database tables from Java code is probably best used as a one-time short-cut, suitable for rapid prototyping.  Because everything in an application is dependent on the database (and not vice-versa), future changes must be made in the database first (and any existing data migrated there first as well), then propagated to all affected parts of the application.&lt;/p&gt;&lt;p&gt;I have found that anything which the "database reverse engineering" process does not generate for me breaks, usually sooner rather than later.  Also that the hardest part of managing maintenance of a large system is the constant refactoring.  To that end, I have developed 2 goals for the database reverse-engineering process:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;It should keep my application in synch with any database changes, automatically updating as much of my application as is practical.&lt;/li&gt;&lt;li&gt;Where #1 is not possible, it should cause any affected areas of my application to generate a compile-time error.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Imagine a database table:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;CREATE TABLE `user` (&lt;br /&gt;  `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;  `last_modifier_id` bigint(20) unsigned DEFAULT NULL,&lt;br /&gt;  `company_id` bigint(20) unsigned NOT NULL,&lt;br /&gt;  `identifier` varchar(64) NOT NULL COMMENT 'PIN.  Unique within a company',&lt;br /&gt;  `first_name_c` varchar(40) DEFAULT NULL,&lt;br /&gt;etc.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Database generation tools normally generate the following sort of fields:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;public class User implements java.io.Serializable {&lt;br /&gt;&lt;br /&gt;private long id;&lt;br /&gt;private User lastModifier;&lt;br /&gt;private Company company;&lt;br /&gt;private String identifier;&lt;br /&gt;private String firstNameC;&lt;br /&gt;etc.&lt;br /&gt;&lt;br /&gt;public long getId() { return id; }&lt;br /&gt;public void setId(long x) { id = x; }&lt;br /&gt;&lt;br /&gt;public User getLastModifier() { return lastModifier; }&lt;br /&gt;public void setLastModifier(User x) { lastModifier = x; }&lt;br /&gt;&lt;br /&gt;public Company getCompany() { return company; }&lt;br /&gt;public void setCompany(Company x) { company = x; }&lt;br /&gt;&lt;br /&gt;public String getIdentifier() { return identifier; }&lt;br /&gt;public void setIdentifier(String x) { identifier = x; }&lt;br /&gt;&lt;br /&gt;public String getFirstNameC() { return firstNameC; }&lt;br /&gt;public void setFirstNameC(String x) { firstNameC = x; }&lt;br /&gt;etc.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I have modified the generator to also generate the following fields for each column in the table (prefixed with SQL_) and for each field in the Java object (prefixed with HQL_):&lt;/p&gt;&lt;pre&gt;&lt;code&gt;public static final String SQL_user = "user"; // table name&lt;br /&gt;public static final String SQL_id = "id";&lt;br /&gt;public static final String SQL_last_modifier_id = "last_modifier_id";&lt;br /&gt;public static final String SQL_company_id = "company_id";&lt;br /&gt;public static final String SQL_identifier = "identifier";&lt;br /&gt;public static final String SQL_first_name_c = "first_name_c";&lt;br /&gt;etc.&lt;br /&gt;&lt;br /&gt;public static final String HQL_User = "User"; // class name&lt;br /&gt;public static final String HQL_id = "id";&lt;br /&gt;public static final String HQL_lastModifier = "lastModifier";&lt;br /&gt;public static final String HQL_company = "company";&lt;br /&gt;public static final String HQL_identifier = "identifier";&lt;br /&gt;public static final String HQL_firstNameC = "firstNameC";&lt;br /&gt;etc.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This allows me to write code like the following:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;Crit&amp;lt;User&amp;gt; dupIdentCrit = Crit.create(User.class);&lt;br /&gt;dupIdentCrit.add(Restrictions.eq(User.HQL_company, this));&lt;br /&gt;dupIdentCrit.add(Restrictions.eq(User.HQL_identifier, identifier));&lt;br /&gt;List&amp;lt;User&amp;gt; dupIdentifierUsers = dupIdentCrit.list();&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Here's an example of building a SQL statement for use with JDBC:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;StringBuilder sqlB = new StringBuilder("select * from " + User.SQL_user +&lt;br /&gt;                                       " where " + User.SQL_company_id +&lt;br /&gt;                                       " = ?");&lt;br /&gt;if (!includeDeleted) {&lt;br /&gt;    sqlB.append(" and " + User.SQL_is_deleted + " is false");&lt;br /&gt;}&lt;br /&gt;sqlB.append(" order by " + User.SQL_last_name_c + ", " +&lt;br /&gt;            User.SQL_first_name_c + ", " +&lt;br /&gt;            User.SQL_middle_name_c);&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When the database changes and I rebuild my objects, I get compile-time errors, showing me every piece of code I need to fix.&lt;/p&gt;&lt;p&gt;I am working on modifying the generator to also generate the following for any varchar or char fields because their maximum length is defined in the database columns:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;public static final int MAX_identifier = 64;&lt;br /&gt;public static final int MAX_firstNameC = 40;&lt;br /&gt;etc.&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;I am considering parsing comments on each column to look for something like: &lt;code&gt;'min: 6'&lt;/code&gt; and generate the following from it:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;public static final int MIN_identifier = 6;&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;That same technique could be used with int columns to define minimum and maximum values which could be used in validation, in the GUI, and in the help of an appliction.  I'm also experimenting with using the HQL_ tokens as the names of the input fields on the GUI screens.&lt;/p&gt;&lt;p&gt;I'm not sure how or even if this would work with stored procedures.&lt;/p&gt;&lt;p&gt;Special thanks to J. Michael Greata, whose interest and suggestions over the past several years have encouraged and shaped the development of this project.  Also to Arash Tavakoli whose post in the LinkedIn Java Architects group inspired me to organize my thoughts into this posting.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-1305712166226025848?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/1305712166226025848/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=1305712166226025848' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/1305712166226025848'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/1305712166226025848'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2011/09/automatic-pojo-generation-from-database.html' title='Automatic POJO Generation From a Database'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-1833949560907097529</id><published>2011-09-08T04:43:00.000-07:00</published><updated>2011-09-15T12:17:20.940-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Exceptions'/><title type='text'>Java Runtime Exceptions</title><content type='html'>&lt;p&gt;I've been enjoying, "&lt;a href="http://oreilly.com/catalog/9780596803742" target="_blank"&gt;Java: The Good Parts&lt;/a&gt;" by Jim Waldo and just finished chapter 3: Exceptions.  At the end of the chapter, Mr. Waldo takes a humorously firm stance against RuntimeExceptions.  Quick review: checked exceptions have to be declared in the method signature and dealt with by the calling code; RuntimeExceptions don't.  Indeed, RuntimeExceptions can be evilly misused.  Yet I believe there is a time and a place for these exceptions.  Knowing when to use them requires an understanding of who is at fault for a particular problem.&lt;/p&gt;&lt;p&gt;The following code sample bit me the other day:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;public static void write(String s) {&lt;br /&gt;    try {&lt;br /&gt;        out.write(s);&lt;br /&gt;    } catch (IOException ioe) {&lt;br /&gt;        throw new IllegalStateException();&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;It is evil (as Mr. Waldo says) for several reasons:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;This method is not being responsible for handling its own problems.  IOExceptions happen for good reasons &lt;em&gt;other&lt;/em&gt; than coding errors - users can delete files, network connections can close, etc.  It could retry the write() or recover some other way.  It could let the original exception bubble up to the caller, or it could report the error some other suitable way (e.g. to the user).&lt;/li&gt;&lt;li&gt;Wrapping a checked exception with a RuntimeException means that this method signature is missing vital information that the caller really needs to know about.  It hides critical details (any problem with the write() now causes an unexpected exception).&lt;/li&gt;&lt;li&gt;Absentmindedly wrapping an exception with another exception serves only to complicate the stack trace - it further hides the cause of the problem.  It's good to wrap an exception if you have critical information to add (e.g. the method handles two streams and you wrap an exception with the message of which stream it applies to).  In the example above, nothing is added.  In fact, this code doesn't wrap an exception, it throws it away and substitutes another which is even worse.&lt;/li&gt;&lt;li&gt;The IllegalStateException is blank, giving the unfortunate caller no idea what went wrong.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;The following shows proper usage of RuntimeExceptions:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/** Creates or returns an existing immutable YearMonth object.&lt;br /&gt;@param year a valid year&lt;br /&gt;@param month a one-based month between 1-12&lt;br /&gt;@return the relevant YearMonth object&lt;br /&gt;*/&lt;br /&gt;public static YearMonth valueOf(int year, int month) {&lt;br /&gt;    if ( (month &amp;lt; 1) || (month &amp;gt; 12) ) {&lt;br /&gt;        throw new IllegalArgumentException("Month must be a positive integer such that 0 &amp;lt; n &amp;lt; 13");&lt;br /&gt;    }&lt;br /&gt;...&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;This works because:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Valid input values are documented clearly.&lt;/li&gt;&lt;li&gt;The inputs are checked at the beginning of the method, before any processing is done.&lt;/li&gt;&lt;li&gt;It throws a RuntimeException to inform the caller that they have made a coding error.&lt;/li&gt;&lt;li&gt;The exception provides information about what the caller did wrong.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You wouldn't want to use a checked exception because it would force the responsible caller's code to check their input values twice:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;YearMonth ym;&lt;br /&gt;if (m &amp;gt; 12) {&lt;br /&gt;    out.write("Month too big");&lt;br /&gt;} else if (m &amp;lt; 1) {&lt;br /&gt;    out.write("Month too small");&lt;br /&gt;} else {&lt;br /&gt;    try {&lt;br /&gt;        ym = YearMonth.valueOf(y, m);&lt;br /&gt;    } catch (Exception e) {&lt;br /&gt;        out.println("month still invalid: " + e.getMessage());&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;The critical nuance here is that RuntimeExceptions should be used to indicate a programming error on the part of the &lt;em&gt;caller&lt;/em&gt; of the function that throws it.  They are generally used in the first few lines of the function to check for invalid input values.  Each RuntimeException should include a description of the problem (not be blank).&lt;/p&gt;&lt;p&gt;RuntimeExceptions can also be used to catch invalid state as follows:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;enum Numb {&lt;br /&gt;    ONE,&lt;br /&gt;    TWO;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;private Numb num = null;&lt;br /&gt;&lt;br /&gt;public void init(Numb n) {&lt;br /&gt;    if (n == null) {&lt;br /&gt;        throw new IllegalArgumentException("init cannot take a null Numb");&lt;br /&gt;    }&lt;br /&gt;    num = n;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public void showNum() {&lt;br /&gt;    if (ONE == num) {&lt;br /&gt;        out.println("1");&lt;br /&gt;    } else if (TWO == num) {&lt;br /&gt;        out.println("2");&lt;br /&gt;    } else {&lt;br /&gt;         throw new IllegalStateException("Unhandled value of Numb or called showNum without initializing the num");&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;When some programmer adds THREE to Numb and doesn't account for the new possible value, it fails hard and fast, making the problem easy to find and fix.&lt;/p&gt;&lt;p&gt;The Java compiler and a good IDE can catch many errors for us, but as a second or third line of defense, RuntimeExceptions are a good way to make code fail hard and fast without forcing the caller to check their input values twice.  Used properly they can make sneaky coding errors obvious.  Used improperly, they can make and obvious errors sneaky.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-1833949560907097529?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/1833949560907097529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=1833949560907097529' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/1833949560907097529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/1833949560907097529'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2011/08/java-runtime-exceptions.html' title='Java Runtime Exceptions'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-8578425263051948718</id><published>2011-08-31T05:55:00.000-07:00</published><updated>2011-09-08T11:49:56.895-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='comparing objects'/><category scheme='http://www.blogger.com/atom/ns#' term='comparing Java objects'/><category scheme='http://www.blogger.com/atom/ns#' term='surrogate key'/><category scheme='http://www.blogger.com/atom/ns#' term='hashCode()'/><category scheme='http://www.blogger.com/atom/ns#' term='Java Collections'/><category scheme='http://www.blogger.com/atom/ns#' term='equals()'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming in Scala'/><title type='text'>Object Mutability</title><content type='html'>I've read a lot lately about making objects immutable whenever possible.  "&lt;a href="http://www.artima.com/shop/programming_in_scala_2ed" target="_blank"&gt;Programming in Scala&lt;/a&gt;" lists Immutable Object Tradeoffs as follows:&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Advantages of Immutable Objects&lt;/h4&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Often easier to reason about because they do not have complex state.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Can be passed freely (without making defensive copies) to things that might try to modify them &lt;/li&gt;&lt;br /&gt;&lt;li&gt;Impossible for two threads accessing the same immutable object to corrupt it.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;They make safe Hashtable keys (if you put a mutable object in a Hashtable, then change it in a way that changes its hashcode, the Hashtable will no longer be able to use it as a key because it will look for that object in the wrong bucket and not find it).&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Disadvantages&lt;/h4&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Sometimes require a large object graph to be copied (to create a new, modified version of the object).  This can cause performance and garbage collection bottlenecks.&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;For most purposes, an object representing a month can be made immutable - February 2003 will never become anything other than what it is.  But a User record is not immutable.  People get married or change their name for other reasons.  Phone numbers, addresses, hair color, height, weight, and virtually every other aspect of a person can change.  Yet the person is still the same person.  This is what surrogate keys model in a database - that everything about a record can change, yet it can still be meaningfully the same record.&lt;br /&gt;&lt;br /&gt;In order to use an object in a hash-backed Collection (in Java), its hashcode must NOT change.  The simplest way to accomplish this is to make the hashcode of a mutable persistent object its surrogate key and to use that key as a primary comparison in the equals method as well (see my older post on &lt;a href="http://glenpeterson.blogspot.com/2010/09/using-java-collections-effectively-by.html"&gt;Implementing equals(), hashcode(), and compareTo()&lt;/a&gt;).&lt;br /&gt;&lt;br /&gt;To make an immutable object, you sometimes need a mutable constructor object, like StringBuilder and String.  StringBuilder allows you to change your object as many times as you want, then get an immutable version by calling toString().  This is clean and safe, but has some small costs in time and memory (transforming the immutable StringBuilder into a new immutable String object, then throwing away the StringBuilder).  An alternative that I have not seen much is to create an immutable interface, extend a mutable interface from it, and then extend your object from that.&lt;br /&gt;&lt;br /&gt;Here's an example based on java.util.List.  Pretend each interface or class is in its own file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;// All the immutable-friendly methods from java.util.List.&lt;br /&gt;// Interfaces like these could easily be retrofitted into&lt;br /&gt;// the existing Java collections framework&lt;br /&gt;public interface ImmutableList {&lt;br /&gt;    int size();&lt;br /&gt;    boolean isEmpty();&lt;br /&gt;    boolean contains(Object o);&lt;br /&gt;    Iterator&amp;lt;E&amp;gt; iterator();&lt;br /&gt;    Object[] toArray();&lt;br /&gt;    &amp;lt;T&amp;gt; T[] toArray(T[] a);&lt;br /&gt;    boolean containsAll(Collection&amp;lt;?&amp;gt; c);&lt;br /&gt;    boolean equals(Object o);&lt;br /&gt;    int hashCode();&lt;br /&gt;    E get(int index);&lt;br /&gt;    int indexOf(Object o);&lt;br /&gt;    int lastIndexOf(Object o);&lt;br /&gt;    ListIterator&amp;lt;E&amp;gt; listIterator();&lt;br /&gt;    ListIterator&amp;lt;E&amp;gt; listIterator(int index);&lt;br /&gt;    List&amp;lt;E&amp;gt; subList(int fromIndex, int toIndex);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;// This interface adds the mutators&lt;br /&gt;public interface java.util.List extends ImmutableList {&lt;br /&gt;    boolean add(E e);&lt;br /&gt;    boolean remove(Object o);&lt;br /&gt;    boolean addAll(Collection&amp;lt;? extends E&amp;gt; c);&lt;br /&gt;    boolean addAll(int index, Collection&amp;lt;? extends E&amp;gt; c);&lt;br /&gt;    boolean removeAll(Collection&amp;lt;?&amp;gt; c);&lt;br /&gt;    boolean retainAll(Collection&amp;lt;?&amp;gt; c);&lt;br /&gt;    void clear();&lt;br /&gt;    E set(int index, E element);&lt;br /&gt;    void add(int index, E element);&lt;br /&gt;    E remove(int index);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class java.util.ArrayList implements List {&lt;br /&gt;    // just as it is now...&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public class MyClass {&lt;br /&gt;    someMethod(ImmutableList&amp;lt;String&amp;gt; ils) {&lt;br /&gt;        // can't change the list&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        List&amp;lt;String&amp;gt; myStrings = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;        myStrings.add("hello");&lt;br /&gt;        myStrings.add("world");&lt;br /&gt;&lt;br /&gt;        someMethod(myStrings);&lt;br /&gt;&lt;br /&gt;        // Totally safe:&lt;br /&gt;        System.out.println(myStrings.get(1));&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This doesn't solve the problem of passing a list to existing untrusted code that might try to change it.  It also doesn't prevent the calling code from modifying myStrings from a separate thread while someMethod is working on it.  But it does provide a way (going forward) for a method like someMethod() to declare that it cannot modify the list.  The programmer of someMethod() cannot compile her code if she tries to modify the list (well, short of using reflection).&lt;br /&gt;&lt;br /&gt;Guaranteed immutability can be critical in writing concurrent code and for keys in hashtables.  Not objects can be made immutable, but many of those objects have immutable surrogate keys that, if used properly, work around the pitfalls of mutability.  Limiting mutability and avoiding common mutable object pitfalls can lead to fewer bugs, easier readability, and improved maintainability.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-8578425263051948718?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/8578425263051948718/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=8578425263051948718' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/8578425263051948718'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/8578425263051948718'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2011/08/object-mutability.html' title='Object Mutability'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-3642751500430863404</id><published>2011-08-25T06:00:00.000-07:00</published><updated>2011-09-08T11:50:54.830-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='typesafe'/><category scheme='http://www.blogger.com/atom/ns#' term='John Yeary'/><title type='text'>Typesafe List in Java 5+ part two...</title><content type='html'>Looks like John's comment yesterday had us both up half the night working on &lt;a href="http://javaevangelist.blogspot.com/2011/08/type-safe-collection-conversion.html" target="_blank"&gt;the same thing&lt;/a&gt;.  I'm posting my version here because it has a parameter that lets the caller determine the trade-off between speed and safety.  When debugging, you can use Check.ALL (or leave it null).  In performance-critical code, you can later set it to NONE.  Check.FIRST is for situations where you just a sanity check - it's fast and better than nothing.&lt;br /&gt;&lt;br /&gt;I've also included some test code and a routine that takes advantage of the String.valueOf() methods to coerce each element of the original list to a String, if necessary, much the way that many dynamically typed languages do.  I wonder if it would be worthwhile to further optimize this method for long lists by dividing the original list into runs of items that do not need casting and copying those runs with stringList.addAll(list.subList(fromIdx, toIdx)) instead of copying one item at a time?&lt;br /&gt;&lt;br /&gt;John's solution uses &lt;code&gt;B.class.isAssignableFrom(a.getClass())&lt;/code&gt; while mine uses &lt;code&gt;a instanceof B&lt;/code&gt;.  I think either works in this case because a List can only hold objects, not primitives.  If we were dealing with primitives, John's solution would handle more cases than mine.  Not sure if there are performance considerations, but I doubt they would be significant.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;import java.util.ArrayList;&lt;br /&gt;import java.util.List;&lt;br /&gt;&lt;br /&gt;public class TypeSafe {&lt;br /&gt;&lt;br /&gt;    public enum Check {&lt;br /&gt;        NONE,&lt;br /&gt;        FIRST,&lt;br /&gt;        ALL;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    @SuppressWarnings({"unchecked"})&lt;br /&gt;    public static &amp;lt;T&amp;gt; List&amp;lt;T&amp;gt; typeSafeList(List list,&lt;br /&gt;                                           Class&amp;lt;T&amp;gt; clazz,&lt;br /&gt;                                           Check safety) {&lt;br /&gt;        if (clazz == null) {&lt;br /&gt;            throw new IllegalArgumentException("typeSafeList() requires a non-null class parameter");&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        // Should we perform any checks?&lt;br /&gt;        if (safety != Check.NONE) {&lt;br /&gt;            if ( (list != null) &amp;&amp; (list.size() &amp;gt; 0) ) {&lt;br /&gt;                for (Object item : list) {&lt;br /&gt;                    if ( (item != null) &amp;&amp;&lt;br /&gt;                         !(item instanceof String) ) {&lt;br /&gt;                        throw new ClassCastException(&lt;br /&gt;                                "List contained a(n) " +&lt;br /&gt;                                item.getClass().getCanonicalName() +&lt;br /&gt;                                " which is not a(n) " +&lt;br /&gt;                                clazz.getCanonicalName());&lt;br /&gt;                    }&lt;br /&gt;                    // Should we stop on first success?&lt;br /&gt;                    if (safety == Check.FIRST) {&lt;br /&gt;                        break;&lt;br /&gt;                    }&lt;br /&gt;                    // Default (Check.ALL) checks every item in the list.&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;        } // end if perform any checks&lt;br /&gt;        return (List&amp;lt;T&amp;gt;) list;&lt;br /&gt;    } // end typeSafeList()&lt;br /&gt;&lt;br /&gt;    public static List&amp;lt;String&amp;gt; coerceToStringList(List list) {&lt;br /&gt;        if (list == null) {&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;        try {&lt;br /&gt;            // Return the old list if it's already safe&lt;br /&gt;            return typeSafeList(list, String.class, Check.ALL);&lt;br /&gt;        } catch (ClassCastException cce) {&lt;br /&gt;            // Old list is not safe.  Make new one.&lt;br /&gt;            List&amp;lt;String&amp;gt; stringList = new ArrayList&amp;lt;String&amp;gt;();&lt;br /&gt;&lt;br /&gt;            for (Object item : list) {&lt;br /&gt;                if (item == null) {&lt;br /&gt;                    stringList.add(null);&lt;br /&gt;                } else if (item instanceof String) {&lt;br /&gt;                    stringList.add((String) item);&lt;br /&gt;                } else {&lt;br /&gt;                    // If this throws a classCastException, so be it.&lt;br /&gt;                    stringList.add(String.valueOf(item));&lt;br /&gt;                }&lt;br /&gt;            }&lt;br /&gt;            return stringList;&lt;br /&gt;        }&lt;br /&gt;    } // end typeSafeList()&lt;br /&gt;&lt;br /&gt;    @SuppressWarnings({"unchecked"})&lt;br /&gt;    private static List makeTestList() {&lt;br /&gt;        List l = new ArrayList();&lt;br /&gt;        l.add("Hello");&lt;br /&gt;        l.add(null);&lt;br /&gt;        l.add(new Integer(3));&lt;br /&gt;        l.add("world");&lt;br /&gt;        return l;&lt;br /&gt;    } // end makeTestList()&lt;br /&gt;&lt;br /&gt;    public static void main(String[] args) {&lt;br /&gt;        List unsafeList = makeTestList();&lt;br /&gt;        List&amp;lt;String&amp;gt; stringList = coerceToStringList(unsafeList);&lt;br /&gt;&lt;br /&gt;        System.out.println("Coerced strings:");&lt;br /&gt;        for (String s : stringList) {&lt;br /&gt;            System.out.println(s);&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        List&amp;lt;String&amp;gt; safeList = typeSafeList(unsafeList,&lt;br /&gt;                                             String.class,&lt;br /&gt;                                             Check.ALL);&lt;br /&gt;&lt;br /&gt;        System.out.println("Safe-casted list:");&lt;br /&gt;        for (String s : safeList) {&lt;br /&gt;            System.out.println(s);&lt;br /&gt;        }&lt;br /&gt;    } // end main()&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-3642751500430863404?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/3642751500430863404/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=3642751500430863404' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/3642751500430863404'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/3642751500430863404'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2011/08/looks-like-johns-comment-yesterday-had.html' title='Typesafe List in Java 5+ part two...'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-4509879066249445065</id><published>2011-08-24T08:13:00.000-07:00</published><updated>2011-08-24T09:24:24.798-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Joshua Bloch'/><category scheme='http://www.blogger.com/atom/ns#' term='data types'/><category scheme='http://www.blogger.com/atom/ns#' term='typesafe'/><category scheme='http://www.blogger.com/atom/ns#' term='generics'/><title type='text'>Checking an Unchecked Cast in Java 5 or Later</title><content type='html'>If &lt;code&gt;query.list()&lt;/code&gt; returns an untyped List, then the following code will produce an ugly warning: "Unchecked Cast: 'java.util.List' to 'java.util.List&amp;lt;String&amp;gt;'&lt;br /&gt;&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; tags = null;&lt;br /&gt;try {&lt;br /&gt;    ...&lt;br /&gt;    tags = (List&amp;lt;String&amp;gt;) query.list();&lt;br /&gt;    ...&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Unfortunately, you can only use @SuppressWarnings on a declaration.  I don't want to add this annotation to my method declaration because my method does a lot more than this one cast and this is a useful warning to detect in the rest of the method.  One solution is to add a new variable to make a declaration to use @SuppressWarnings on:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; tags = null;&lt;br /&gt;try {&lt;br /&gt;    ...&lt;br /&gt;    @SuppressWarnings("unchecked")&lt;br /&gt;    List&amp;lt;String&amp;gt; tempTags = (List&amp;lt;String&amp;gt; query.list();&lt;br /&gt;    tags = tempTags;&lt;br /&gt;    ...&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Now it produces an inspection warning in IDEA 10.5, "Local variable tempTags is redundant."  I should probably just ignore this warning or turn it off altogether, but I wasn't quite comfortable with that either.  In situations like these, I naturally turn to &lt;a href="http://java.sun.com/developer/technicalArticles/Interviews/bloch_effective_08_qa.html" target="_blank"&gt;Joshua Bloch&lt;/a&gt; for guidance and his item #77 - Eliminate Unchecked Warnings has this advice in bold:&lt;br /&gt;&lt;blockquote&gt;If you can't eliminate a warning, and you can prove that the code that provoked the warning is typesafe, then (and only then) suppress the warning with an @SuppressWarnings("unchecked") annotation.&lt;/blockquote&gt;&lt;br /&gt;I could probably prove that my query is only going to return Strings, but it occurred to me that there is a more general way to satisfy all the above requirements:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;@SuppressWarnings({"unchecked"})&lt;br /&gt;private List&amp;lt;String&amp;gt; getListOfStringsFromList(List list) {&lt;br /&gt;    if ( (list != null) &amp;&amp; (list.size() &amp;gt; 0) ) {&lt;br /&gt;        for (Object item : list) {&lt;br /&gt;            if ( (item != null) &amp;&amp; !(item instanceof String) ) {&lt;br /&gt;                throw new ClassCastException("List contained non-strings Elements: " + item.getClass().getCanonicalName());&lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;    return (List&amp;lt;String&amp;gt;) list;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;The method does nothing but make a typesafe cast on a list, so I can @SuppressWarnings on the whole thing.  It throws an undeclared runtime ClassCastException, but only if the caller makes a programming error and this exception would get thrown anyway wherever the list was eventually used if I didn't throw it here.  In short, this method takes a programming error that cannot be caught by the compiler and does the next best thing: &lt;a href="http://en.wikipedia.org/wiki/Fail-fast" target="_blank"&gt;fail-fast&lt;/a&gt;, making the error easy to find.&lt;br /&gt;&lt;br /&gt;Using the above solution hides all that complexity and preserves the intent of the original line of code:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;List&amp;lt;String&amp;gt; tags = null;&lt;br /&gt;try {&lt;br /&gt;    ...&lt;br /&gt;    tags = getListOfStringsFromList(query.list());&lt;br /&gt;    ...&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I thought this was interesting enough to post.  Well, at least interesting enough for people who are considering a &lt;a href="http://java4noobs.blogspot.com/2009/08/java-tattoo-once-show-it-everywhere.html" target="_blank"&gt;Java logo tattoo&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-4509879066249445065?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/4509879066249445065/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=4509879066249445065' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/4509879066249445065'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/4509879066249445065'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2011/08/checking-unchecked-cast-in-java-5-or.html' title='Checking an Unchecked Cast in Java 5 or Later'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-8690407797753675372</id><published>2011-03-09T06:24:00.000-08:00</published><updated>2011-08-24T09:59:54.570-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='update speed'/><title type='text'>Question about MySQL Update speed</title><content type='html'>I originally posted this as a question, but Eric Wood helped me solve it in email, so I've added the solution below.&lt;br /&gt;&lt;br /&gt;The minimum time MySQL with InnoDB tables takes to do an update on a 3Ghz Core 2 Duo runing 64-bit Ubuntu 10.10 is somewhere around 0.06 seconds, though I wonder if hard drive speed could be the gating factor?  My average time using Hibernate is 0.15 seconds.  I think JDBC would approach 0.06 seconds.  This was done using the mysql command line client.  Any thoughts would be appreciated.  Also timings vs. other databases if you have a sense.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;mysql&amp;gt; CREATE TABLE `test` (&lt;br /&gt;    -&amp;gt;   `id` bigint(20) unsigned NOT NULL AUTO_INCREMENT,&lt;br /&gt;    -&amp;gt;   `last_click_date` datetime DEFAULT NULL,&lt;br /&gt;    -&amp;gt;   `is_active` bit(1) NOT NULL DEFAULT b'0' COMMENT 'True if user is still logged in.',&lt;br /&gt;    -&amp;gt;   PRIMARY KEY (`id`),&lt;br /&gt;    -&amp;gt;   UNIQUE KEY `id` (`id`)&lt;br /&gt;    -&amp;gt; ) ENGINE=InnoDB;&lt;br /&gt;Query OK, 0 rows affected (0.21 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; insert into test (last_click_date, is_active) values ('2011-03-07 14:18:16', b'1');&lt;br /&gt;Query OK, 1 row affected (0.11 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; insert into test (last_click_date, is_active) values ('2011-03-07 14:18:16', b'1');&lt;br /&gt;Query OK, 1 row affected (0.11 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; insert into test (last_click_date, is_active) values ('2011-03-07 14:18:16', b'1');&lt;br /&gt;Query OK, 1 row affected (0.06 sec)&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; update test set last_click_date = '2011-03-07 14:18:16' where id = 1;&lt;br /&gt;Query OK, 0 rows affected (0.07 sec)&lt;br /&gt;Rows matched: 1  Changed: 0  Warnings: 0&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; update test set last_click_date = now() where id = 1;&lt;br /&gt;Query OK, 1 row affected (0.06 sec)&lt;br /&gt;Rows matched: 1  Changed: 1  Warnings: 0&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; update test set is_active = b'0' where id = 1;&lt;br /&gt;Query OK, 1 row affected (0.07 sec)&lt;br /&gt;Rows matched: 1  Changed: 1  Warnings: 0&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; update test set is_active = b'0' where id = 1;&lt;br /&gt;Query OK, 0 rows affected (0.02 sec)&lt;br /&gt;Rows matched: 1  Changed: 0  Warnings: 0&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; update test set is_active = b'0' where id = 1;&lt;br /&gt;Query OK, 0 rows affected (0.10 sec)&lt;br /&gt;Rows matched: 1  Changed: 0  Warnings: 0&lt;br /&gt;&lt;br /&gt;mysql&amp;gt; update test set last_click_date = 20110307141816 where id = 1;&lt;br /&gt;Query OK, 0 rows affected (0.06 sec)&lt;br /&gt;Rows matched: 1  Changed: 0  Warnings: 0&lt;br /&gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;It turns out that if I change the table to use the MyISAM engine, all the updates take 0.00 seconds.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-8690407797753675372?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/8690407797753675372/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=8690407797753675372' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/8690407797753675372'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/8690407797753675372'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2011/03/question-about-mysql-update-speed.html' title='Question about MySQL Update speed'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-2438090427055693351</id><published>2011-01-06T08:06:00.000-08:00</published><updated>2011-08-31T07:11:00.486-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='passwords'/><category scheme='http://www.blogger.com/atom/ns#' term='users'/><category scheme='http://www.blogger.com/atom/ns#' term='randomness'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='&quot;Random Character Generator&quot;'/><category scheme='http://www.blogger.com/atom/ns#' term='keepass'/><title type='text'>Top 10 Things a Password Manager Must Have</title><content type='html'>Maybe 1 in 60 of my accounts reports their passwords stolen every year.  For every site that reports a break-in, a few others are probably broken into and don't know it or don't report it.  I would guess that if you have accounts at 30 different sites, you should probably assume that one of them gets broken into every year.  You can't stop people from discovering passwords this way, but if you use a unique, strong password, you can contain the damage so that a hacker cannot leverage the knowledge of one of your passwords to break into your other accounts.&lt;br /&gt;&lt;br /&gt;I just watched &lt;a href="http://nakedsecurity.sophos.com/2010/02/03/choose-strong-password/"&gt;How to choose a strong password&lt;/a&gt; and while that's good advice, most people can neither remember nor type a good password, or at least not more than one or two good passwords.  The only practical way to use a unique, strong password for every site is to use a good password manager.  As such, I'm proposing a Password Manager Feature Manifesto for people to use to compare password managers and decide which one is best for them.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Password Manager Feature Manifesto&lt;/h3&gt;&lt;br /&gt;A password manager needs to do certain things to be worthwhile:&lt;br /&gt;&lt;br /&gt;1.) Store passwords securely, in one place so you can find them, change them, secure them as a unit.  It always seemed to me that storing your passwords in your browser was a little bit like taping your wallet to the outside of your front door - you are putting your valuables in the most vulnerable place.  KeePass (without any plug-ins) is completely separate from your browser.  Browser integration is not necessarily bad, but I think it loose some points from a security perspective.  In any case, the passwords must be secured by a strong master password and encrypted on disk (and maybe in memory when possible too).&lt;br /&gt;&lt;br /&gt;2.) Generate random passwords - people don't manually make strong passwords.  Collecting entropy for the randomness is a huge plus.  KeePass and LastPass both generate strong passwords for you.&lt;br /&gt;&lt;br /&gt;3.) Make it equally easy to store your credit-card number or license key for Photoshop as to store a password to a web site.&lt;br /&gt;&lt;br /&gt;4.) Must be backed up every time you make a change.  LastPass has this built-in.  KeePass must be used with Dropbox and set to save automatically after every change.&lt;br /&gt;&lt;br /&gt;5.) To be shared between multiple computers, e.g. LastPass or KeyPass/Dropbox&lt;br /&gt;&lt;br /&gt;6.) Needs to be relatively easy to use&lt;br /&gt;&lt;br /&gt;7.) To work on all major operating systems (Windows, OS-X, Linux).  I look for this every time I choose software.  I hate being tied to one vendor's operating system or browser.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;If a password manager doesn't do all of those things, I'm not really&lt;br /&gt;interested in it.  One thing that's not important yet, but I bet it&lt;br /&gt;will become critical for most people in the next few years:&lt;br /&gt;&lt;br /&gt;8.) To work on your phone or other mobile device.  Here is where&lt;br /&gt;LastPass may move ahead of KeePass.&lt;br /&gt;&lt;br /&gt;9.) Popular OpenSource software is recommended for security&lt;br /&gt;&lt;br /&gt;And finally, not critical, but the icing on the cake:&lt;br /&gt;&lt;br /&gt;10.) It's free, or at least a reasonable price.&lt;br /&gt;&lt;br /&gt;That leaves KeePass the clear #1 for me and LastPass #2.  LastPass could threaten KeePass if they keep improving on #6 and #8 - specifically, it is very hard to log into sites with LastPass that have the user ID on one screen and password on the next.&lt;br /&gt;&lt;br /&gt;Sadly, no password manager can remember your operating-system login when you boot up your computer, so you have to remember that password yourself.  Also, the master password for your manager.  But for most people that's just 2 passwords to remember and type, and that's fairly do-able.&lt;br /&gt;&lt;br /&gt;I have to thank DigitalMan for his contributions to this article by talking about this with me and sending articles about break-ins, security, and passwords for the past few years, and for encouraging me to improve my own password practices.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-2438090427055693351?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/2438090427055693351/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=2438090427055693351' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/2438090427055693351'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/2438090427055693351'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2011/01/top-10-things-password-manager-must.html' title='Top 10 Things a Password Manager Must Have'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-1272367031037674097</id><published>2010-12-29T06:00:00.000-08:00</published><updated>2011-09-14T06:34:02.504-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='comparing objects'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='comparing Java objects'/><category scheme='http://www.blogger.com/atom/ns#' term='surrogate key'/><category scheme='http://www.blogger.com/atom/ns#' term='hashCode()'/><category scheme='http://www.blogger.com/atom/ns#' term='Comparable'/><category scheme='http://www.blogger.com/atom/ns#' term='Java Collections'/><category scheme='http://www.blogger.com/atom/ns#' term='equals()'/><category scheme='http://www.blogger.com/atom/ns#' term='Programming in Scala'/><category scheme='http://www.blogger.com/atom/ns#' term='compareTo()'/><title type='text'>Using Java Collections Effectively by Implementing equals(), hashCode(), and compareTo()</title><content type='html'>&lt;p&gt;Thanks to attending the local &lt;a href="https://greenjug.dev.java.net/"&gt;Java Users Group&lt;/a&gt; "Oracle® Certified Java™ Programmer Certification Boot Camp" on and off for several years, I've entered the 21st century and begun using Java Collections the way they were intended.  Especially with regard to sorting and eliminating duplicate records, these classes are a god-send in terms of coding-time and run-time efficiency and arguably in terms of readability as well.  But bug-free usage of these classes requires strict adherence to the contract of &lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt;, and often &lt;code&gt;compareTo()&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Even though popular IDEs automatically generate stubs of some of these methods for you, you should still understand how they work, particularly how the three methods work &lt;em&gt;together&lt;/em&gt; because I don't see many IDE's writing meaningful compareTo() methods yet.  Here's what I've learned in the past few years of implementing these methods.  For much of what follows, I am endebted to Joshua Bloch and his book, &lt;a href="http://java.sun.com/docs/books/effective/"&gt;Effective Java&lt;/a&gt;.  Buy it, read it, live it.&lt;/p&gt;&lt;p&gt;In short, make the behavior of &lt;code&gt;equals()&lt;/code&gt;, &lt;code&gt;hashCode()&lt;/code&gt;, and &lt;code&gt;compareTo()&lt;/code&gt; consistent.  I'll cover each in order.&lt;/p&gt;&lt;p&gt;&lt;b&gt;UPDATE 2011-08-31:&lt;/b&gt; As much as possible, you must base these methods on immutable fields.  If you store an object in a collection (e.g. as a key in a HashMap) and it's hashcode changes, you probably won't be able to retrieve it from that hash table anymore!  Thanks to "&lt;a href="http://www.artima.com/shop/programming_in_scala_2ed" target="_blank"&gt;Programming in Scala&lt;/a&gt;" Section 30.2 p. 657.  See also my later post on &lt;a href="http://glenpeterson.blogspot.com/2011/08/object-mutability.html"&gt;Object Mutability&lt;/a&gt;.  &lt;b&gt;END-UPDATE&lt;/b&gt;&lt;/p&gt;&lt;h3&gt;equals()&lt;/h3&gt;&lt;p&gt;&lt;code&gt;a.equals(b)&lt;/code&gt; should return true when a and b represent the same object.  Not that &lt;code&gt;a&lt;/code&gt; and &lt;code&gt;b&lt;/code&gt; are both pigs, or even that they are both pigs named Wilbur, but that they both represent the pig named Wilbur in the story, Charlotte's Web.  Bloch (Item 8) says that the &lt;code&gt;equals()&lt;/code&gt; method must be reflexive, symmetric, transitive and a few other things as well which I won't cover here.  For any non-null value:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;x.equals(x)&lt;/code&gt; must be true.&lt;/li&gt;&lt;li&gt;If &lt;code&gt;x.equals(y)&lt;/code&gt; then &lt;code&gt;y.equals(x)&lt;/code&gt; must be true.&lt;/li&gt;&lt;li&gt;If &lt;code&gt;x.equals(y)&lt;/code&gt; and &lt;code&gt;y.equals(z)&lt;/code&gt; then &lt;code&gt;x.equals(z)&lt;/code&gt; must also be true.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The following should get you off to a good start in writing an equals method that is all of the above.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;@Override&lt;br /&gt;public boolean equals(Object other) {&lt;br /&gt;    // First, the obvious...&lt;br /&gt;    if (this == other) {&lt;br /&gt;        return true;&lt;br /&gt;    }&lt;br /&gt;    if (other == null) {&lt;br /&gt;         return false;&lt;br /&gt;    }&lt;br /&gt;    if ( !(other instanceof MyClass) ) {&lt;br /&gt;        return false;&lt;br /&gt;    }&lt;br /&gt;    // Details...&lt;br /&gt;    final MyClass that = (MyClass) other;&lt;br /&gt;&lt;br /&gt;    // If this is a database object and both have the same surrogate key (id),&lt;br /&gt;    // they are the same.&lt;br /&gt;    if ( (this.getId() != 0) &amp;&amp; (that.getId() != 0) ) {&lt;br /&gt;        return (this.getId() == that.getId());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // If this is not a database object, compare "significant" field here.&lt;br /&gt;    try {&lt;br /&gt;        if ( this.item.equals(that.item) &amp;&amp;&lt;br /&gt;             this.startDate.equals(that.startDate) &amp;&amp;&lt;br /&gt;             this.endDate.equals(that.endDate) ) {&lt;br /&gt;            return true;&lt;br /&gt;        }&lt;br /&gt;    } catch (Exception e) {&lt;br /&gt;        // You may not want to catch exceptions here if that exception&lt;br /&gt;        // indicates an invalid object.  See below.&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // Not proven equal&lt;br /&gt;    return false;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;Be careful not to compare objects that may be invalid.  Consider a rental system where no two people can have overlapping date values for the same rental item, but a user enters the wrong date values and the system builds an object with those values to compare it against the existing objects from the database (to check the validity).  Is the new object the same as the database object whose dates it conflicts with?  You can't make that comparison when one of the objects is invalid.  The coding error here is not that the equals() method is flawed, but rather that you are trying to compare an object before it is valid.&lt;/p&gt;&lt;p&gt;Your equals() method should either compare "significant" fields (the ones that uniquely identify the object) OR &lt;a href="http://en.wikipedia.org/wiki/Surrogate_key"&gt;surrogate keys&lt;/a&gt; - not both!  The danger of providing a field-by-field equals comparison for a database object is that it will work in some cases with invalid objects, but it will not always work, thus I'd rather have it fail fast, than leave me scratching my head when an intermittent bug crops up in production.  For database objects I always use surrogate keys because doing so acknowledges that everything about an object can change over time, yet it is still essentially the same object (The Artist Formerly Known as Prince).  For non-database objects, you must compare the fields that uniquely identify a valid object.&lt;/p&gt;&lt;h3&gt;hashCode()&lt;/h3&gt;&lt;p&gt;Bloch's Item 9 states, "Always override hashCode() when you override equals()".  Giving your hashCode() function an even distribution of values makes the Hashtable-based collections work better.  The following are specifically required (see: &lt;code&gt;&lt;a href="http://download.oracle.com/javase/6/docs/api/java/lang/Object.html#hashCode%28%29"&gt;Object.hashCode()&lt;/a&gt;&lt;/code&gt;):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;x.hashCode()&lt;/code&gt; must always equal &lt;code&gt;y.hashCode()&lt;/code&gt; when &lt;code&gt;x.equals(y)&lt;/code&gt;.  If you break this nothing will work.&lt;/li&gt;&lt;li&gt;It's OK for &lt;code&gt;x.hashCode()&lt;/code&gt; to equal &lt;code&gt;y.hashCode()&lt;/code&gt; when &lt;code&gt;x.equals(y)&lt;/code&gt; is false, but it's good to minimize this.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Once again, surrogate keys can come to your rescue here.  Truncating the row number from the database from a long to an int is a great way to ensure equal distribution of hash buckets in a hash table.  Until you have 2 billion records (2^31 for a signed 32-bit int), they each get their own hash code.  Up to about 4 billion records, you only have 2 records in each hash bucket, and on and on as you add records.  If you don't use surrogate keys, you need to construct an int from the same "significant" fields you used in the above equals method to keep them consistent.  Good luck with that.  Here's the simple solution:&lt;/p&gt;&lt;pre&gt;&lt;code&gt;@Override&lt;br /&gt;public int hashCode() {&lt;br /&gt;    if (this.getId() == 0) {&lt;br /&gt;        return System.identityHashCode(this);&lt;br /&gt;    } else {&lt;br /&gt;        // return (possibly truncated) surrogate key&lt;br /&gt;        return (int) this.getId();&lt;br /&gt;    }&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;If you don't have a surrogate key, sometimes you can fit the other fields into a single int.  For instance, in a date class, you might use the following which gives each day a unique, even human-readable integer representation:&lt;code&gt;&lt;pre&gt;return (this.year * 10000) + (this.month * 100) + this.day;&lt;/pre&gt;&lt;/code&gt;For a class with a lot of booleans you can/should represent them all losslessly in the hashcode:&lt;code&gt;&lt;pre&gt;int ret = (isHappy ? 1 : 0); // 2^0 = 1 = b0001&lt;br /&gt;if (isHungry) {&lt;br /&gt;    ret |= 2; // 2^1 = 2 = b0010&lt;br /&gt;}&lt;br /&gt;if (isSleepy) {&lt;br /&gt;    ret |= 4; // 2^2 = 4 = b0100&lt;br /&gt;}&lt;br /&gt;if (isAntsy) {&lt;br /&gt;    ret |= 8; // 2^3 = 8 = b1000&lt;br /&gt;}&lt;br /&gt;return ret;&lt;/pre&gt;&lt;/code&gt;&lt;h3&gt;compareTo()&lt;/h3&gt;&lt;p&gt;Bloch's Item 12 states that if you are writing a class with an obvious natural ordering (alphabetical, numerical, chronological, etc.) consider implementing &lt;a href="http://download.oracle.com/javase/6/docs/api/java/lang/Comparable.html#compareTo%28T%29"&gt;Comparable&lt;/a&gt;.  I have personally gotten into the most trouble when my compareTo() method was not 100% compatible with my equals() method.  So I added one rule (and it's converse) to Bloch's others:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;If &lt;code&gt;this.equals(that) == true&lt;/code&gt;, then &lt;code&gt;this.compareTo(that)&lt;/code&gt; must return 0.&lt;/li&gt;&lt;li&gt;If &lt;code&gt;this.equals(that) == false&lt;/code&gt;, then &lt;code&gt;this.compareTo(that)&lt;/code&gt; must NOT return 0.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The example below should be fairly self-explanatory and sorts alphabetically by the description field.&lt;/p&gt;&lt;pre&gt;&lt;code&gt;/**&lt;br /&gt; Returns a negative integer, zero, or a positive integer as this object is&lt;br /&gt; less than, equal to, or greater than the specified object (respectively).&lt;br /&gt;&lt;br /&gt; @param that the object to compare to ("that")&lt;br /&gt;&lt;br /&gt; @return -1 means they are in order (this comes before that), 0 means they are equal&lt;br /&gt; (and Sets will eliminate one of them), +1 means to swap them (that comes&lt;br /&gt; before this)&lt;br /&gt;&lt;br /&gt; */&lt;br /&gt;@Override&lt;br /&gt;public int compareTo(MyClass that) {&lt;br /&gt;&lt;br /&gt;    // ensures consistency with equals()&lt;br /&gt;    if (this.equals(that)) {&lt;br /&gt;        return 0;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // compare parents first in a hierarchical structure.&lt;br /&gt;    // If this class can be it's own parent (e.g. a self-joining&lt;br /&gt;    // table) this is much more complicated.  I'll have to&lt;br /&gt;    // make another article on that.  You may also have to&lt;br /&gt;    // check for a null parent, but here's the simplest case:&lt;br /&gt;    int ret = this.getParent().compareTo(that.getParent());&lt;br /&gt;    if (ret != 0) {&lt;br /&gt;        return ret;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if (this.getDescription() == null) {&lt;br /&gt;        if (that.getDescription() != null) {&lt;br /&gt;            // sorts nulls first&lt;br /&gt;            return -1;&lt;br /&gt;        }&lt;br /&gt;    } else if (that.getDescription() == null) {&lt;br /&gt;        // sorts nulls first&lt;br /&gt;        return 1;&lt;br /&gt;    } else {&lt;br /&gt;        // For each test, check and only return a non-zero result&lt;br /&gt;        ret = this.getDescription().compareToIgnoreCase(that.getDescription());&lt;br /&gt;        if (ret != 0) {&lt;br /&gt;            return ret;&lt;br /&gt;        }&lt;br /&gt;        ret = this.getDescription().compareTo(that.getDescription());&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    if (ret != 0) {&lt;br /&gt;        return ret;&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    // If you can't tell, return -1 which means that they are in the right&lt;br /&gt;    // order but unequal - ensures consistency with equals()&lt;br /&gt;    return -1;&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;p&gt;&lt;b&gt;Note:&lt;/b&gt; I have not verified this, but it stands to reason that if you change hashCode() and your object might be used in a Set or Hashtable, you probably need to change SerialVersionUID.  Since a set only calls equals() for objects in the same bucket (ones that have similar hashcodes), you may end up with two of the same objects in the set (one with the old hashCode and one with the new).  I'm not sure if this can happen in practice or not.  Maybe someone will post test code in the comments that proves it one way or the other?&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-1272367031037674097?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/1272367031037674097/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=1272367031037674097' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/1272367031037674097'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/1272367031037674097'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2010/09/using-java-collections-effectively-by.html' title='Using Java Collections Effectively by Implementing equals(), hashCode(), and compareTo()'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-747856548821493959</id><published>2010-12-12T07:27:00.000-08:00</published><updated>2010-12-12T08:57:32.622-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='service'/><category scheme='http://www.blogger.com/atom/ns#' term='metric'/><category scheme='http://www.blogger.com/atom/ns#' term='Joel on Software'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='software metrics'/><title type='text'>Software Development has only One Metric that Matters</title><content type='html'>Having read and thoroughly enjoyed &lt;a href="http://www.amazon.com/More-Joel-Software-Occasionally-Developers/dp/1430209879/" target="_blank"&gt;More Joel on Software&lt;/a&gt; I bought myself &lt;a href="http://www.amazon.com/Joel-Software-Occasionally-Developers-Designers/dp/1590593898/" target="_blank"&gt;Joel on Software&lt;/a&gt; this week and find it to be similarly wonderful.  Both books are basically just hard-copies of his blog and make for entertaining reading even though they are packed with knowledge from decades of successful software development.&lt;br /&gt;&lt;br /&gt;Joel's &lt;a href="http://www.joelonsoftware.com/news/20020715.html" target="_blank"&gt;Measurement&lt;/a&gt; article from 2002 is not his best article, but reading Joel helped me crystallize some vague notions that have been bumping around my head for years.  The aspects of software that are easiest to measure are generally the least valuable measurements.  For instance: lines of code.  The more lines of code, generally the worse your software is; it's bloated and complicated.  In general, the fewer lines of code for the same functionality, the better, though taken to an extreme, you can make something completely illegible and impossible to change without throwing it out and starting over.  How many lines of code are appropriate for the problem you are trying to solve?&lt;br /&gt;&lt;br /&gt;Similarly, increasing complexity usually makes a product buggy, unusable, or both.  But decreasing complexity, taken to an extreme, can make a product useless (it doesn't do what it needs to).  But how do you measure the level of complexity that is "just right" for the problem you are trying to solve?  Bugs is an interesting metric because even though fewer bugs is better, the Heisenberg principle comes into play such that there's no way of measuring bugs without skewing your results.  Scott Adams sums it up beautifully:&lt;br /&gt;http://www.joeindie.com/images/dilbert-minivan.gif&lt;br /&gt;&lt;br /&gt;But there is one metric that combines and trumps all others in just about every meaningful way: Customer satisfaction.  Does the software solve the real-world problem it was intended to for the people who need it most?  That's the only thing that matters.  Not cyclomatic complexity, efferent coupling, or any other measurement that a computer can make on the code directly.  It has meet someone's needs.&lt;br /&gt;&lt;br /&gt;I recently saw &lt;a href="http://www.objectifiedfilm.com/" target="_blank"&gt;Objectified&lt;/a&gt; which was an interesting film.  But I didn't know whether to laugh out loud or stare in horror at one artist who made a robot that required human attention to do what it needed.  The clip shows some woman dressed like a flight attendant leaning down so that this thing could whisper in her ear so that she would know to move it to the other side of the room.  This is exactly how much of our software fails.  The technology we create is supposed to make our lives easier, better, more enriching.  Not make us it's slave.&lt;br /&gt;&lt;br /&gt;How often is the new version of a product a step backward from the old?  I remember one person I worked with actually advertising that with the new version of his architectural component, what used to take you one click now takes you several and makes you wait longer.  How is that supposed to be a good thing?  It's slower and more difficult... why?&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.rackspace.com" target="_blank"&gt;Rackspace&lt;/a&gt; is on to something, realizing that what you get from a hosting company is not servERS, but servICE.  Anyone can set up a few servers.  But the first time your server goes down and your hosting company doesn't or can't respond, you realize that the service is what counts.  Maybe I'm pushing this a little too far here, but I think software development has more in common with a hosting company than a discount store.  That meeting the customer's needs, providing excellent SERVICE is more important than the implementation details of the product.  The software is more like an extension of that service (it serves the customer instead of a human serving the customer) than like a shrink-wrapped product.&lt;br /&gt;&lt;br /&gt;Providing an effective autonomous electronic servant means understanding the customer's need and designing something that meets that need, then communicating that understanding to the people who actually have to build the software.  Get them excited about, or at least involved in solving the customer's actual problem instead of just thinking about some architectural detail or slavishly following a spec.&lt;br /&gt;&lt;br /&gt;Obviously, there are pitfalls.  In &lt;a href="http://www.joelonsoftware.com/articles/fog0000000356.html" target="_blank"&gt;The Iceberg Secret Revealed&lt;/a&gt;, Joel says that "Customers don't know what they want."  And it's true.  In &lt;a href="http://glenpeterson.blogspot.com/2007/12/make-users-happy-by-ignoring.html" target="_blank"&gt;Make Users Happy by Ignoring Requirements&lt;/a&gt; I discuss what I should have called the "Excel Syndrome" where users describe the problem as if Excel were the solution.  It's not.  If it were, they would have made a spreadsheet instead of hiring you.&lt;br /&gt;&lt;br /&gt;One last thing...  When I say, "customer" I don't mean just the people your company serves.  I mean the target audience for your software, which may be inside your company instead of outside it.  When I worked for Fidelity, I worked for a little group called FMTC (now Pyramis) that handled retirement plans for large organizations.  I think the minimum amount to open an account was over a million dollars.  After years of working on the "customer-facing web-site" I learned that the primary users of the site were a handful of customer service people within Fidelity.  Customers would call them up, ask a question, and the internal rep would use the web site to find the answer.  Had we known this up front, we might have designed it very differently.  That was years ago and most people are comfortable logging in and accessing their own account nowadays, but if you are in charge of billions of dollars, you may still have your secretary call the investment company and hand you the phone to get the answer to your question verbally.  No password, no logging in, just "Yes Mr. Big-Wig.  It's at 42 billion and change Mr. Big-Wig.  I'd be happy to explain that for you..."&lt;br /&gt;&lt;br /&gt;In short:&lt;br /&gt;1.) Find out the real need.&lt;br /&gt;2.) Meet it.&lt;br /&gt;3.) Measure your success by asking your customers.&lt;br /&gt;4.) Do it better next time (&lt;a href="http://en.wikipedia.org/wiki/PDCA" target="_blank"&gt;PDCA&lt;/a&gt;).&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-747856548821493959?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/747856548821493959/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=747856548821493959' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/747856548821493959'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/747856548821493959'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2010/12/software-development-has-only-one.html' title='Software Development has only One Metric that Matters'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-3729857218147556271</id><published>2010-04-20T05:53:00.000-07:00</published><updated>2011-08-24T09:54:19.210-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='computer disposal'/><category scheme='http://www.blogger.com/atom/ns#' term='Free Linux PC'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='hard drive'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='recycling'/><category scheme='http://www.blogger.com/atom/ns#' term='environment'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows XP'/><title type='text'>Disposing of Computers Responsibly</title><content type='html'>You don't fall for con games, you use a router with WPA2 wireless, store your passwords securely, keep your operating system and applications updated.  You're safe, right?&lt;br /&gt;&lt;br /&gt;That depends on how you dispose of old hard drives and computers.  If you run Windows, Microsoft recommends cleaning the hard drive with the secure delete command-line application, &lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb897443.aspx"&gt;sdelete&lt;/a&gt;.  Whatever operating system you run, you may prefer to &lt;a href="http://lifehacker.com/5520289/use-an-ubuntu-live-cd-to-securely-wipe-your-pcs-hard-drive"&gt;Use an Ubuntu Live CD to securely wipe your PC's hard drive&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;The Frontline video, &lt;a href="http://www.pbs.org/frontlineworld/stories/ghana804/video/video_index.html"&gt;Ghana: Digital Dumping Ground&lt;/a&gt; not only exposes the human health and environmental costs of disposing of computers, but about 8 minutes in, shows data recovery on those same computers that should make the hair on the back of your neck stand up.&lt;br /&gt;&lt;br /&gt;One of the reasons I got into computer programming is that it eliminates paper, saves trees, and eliminates paper waste.  Electronic devices require electricity, but more importantly, they break or become obsolete very quickly and we replace them.  The impact of our obsession with newer, faster computers and other electronics is astounding.  But even if we think we are recycling, we might be creating new waste problems in far-away lands as the 60 Minutes &lt;a href="http://www.cbsnews.com/video/watch/?id=4586903n"&gt;Electronic Wasteland&lt;/a&gt; video shows (also shows data recovery).&lt;br /&gt;&lt;br /&gt;Anyone who reads this blog knows that when my computer became unacceptably slow running Microsoft Windows XP about 9 months ago and I switched to &lt;a href="http://glenpeterson.blogspot.com/2009/10/settling-in-to-ubuntu-linux.html"&gt;Ubuntu Linux&lt;/a&gt; and have been delighted with that decision.  File operations that took hours on Windows take minutes on Linux.  Everything else seems to run about twice as fast.  So if you care about the impact of disposing of your computer (of if you want to save money), install Linux and keep your hardware twice as long.  My laptop was bought in 1999 and I run Xubuntu on it (a lightweight version of Ubuntu).  It's great for email and web browsing and with a little patience, I can even run a database and a web server on it, even though it only has 500MB memory a 500Ghz processor, and an incredibly slow disk drive.&lt;br /&gt;&lt;br /&gt;Thanks to my occasional involvement with &lt;a href="http://www.uclug.org/index.php/Main_Page"&gt;UCLUG&lt;/a&gt;, I learned of &lt;a href="http://freelinuxpc.org/"&gt;Free Linux PC&lt;/a&gt;.  They are a fantastic organization that takes donations of old computers, installs Linux on them, and gives them to people who don't have a computer!  I've given them a laptop and some memory and I've volunteered at a giveaway at the Greenville Public Library which was a great time.  The recipients were pinching themselves.&lt;br /&gt;&lt;br /&gt;Here's an old-fashioned recipe for improving environmental, human, and security impacts of your computer usage:&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Reduce&lt;/h3&gt;Buy new computers (cell phones, TVs, etc) less often.  Use your old ones longer.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Reuse&lt;/h3&gt;Installing Linux can double the useful life of a Windows computer.  If that's not for you, then donate any still-working equipment to FreeLinuxPC and they will do it for you.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Recycle&lt;/h3&gt;Recycling sometimes works and is probably better than burning your computer on your front lawn (though it might still be burned in Ghana, Taiwan, or China), certainly better than throwing it in the trash.  Even if you don't recycle, if you can find recycled gold watches or other products &lt;em&gt;made&lt;/em&gt; from recycled computers, you are supporting an industry that badly needs encouragement.&lt;br /&gt;&lt;br /&gt;In any case, make sure to wipe the hard drive before disposing of it!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-3729857218147556271?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/3729857218147556271/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=3729857218147556271' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/3729857218147556271'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/3729857218147556271'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2010/04/disposing-of-computers-responsibly.html' title='Disposing of Computers Responsibly'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-8411877636711943161</id><published>2010-04-15T04:35:00.000-07:00</published><updated>2010-04-15T04:52:35.526-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='&quot;back button&quot;'/><category scheme='http://www.blogger.com/atom/ns#' term='post'/><category scheme='http://www.blogger.com/atom/ns#' term='form'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><category scheme='http://www.blogger.com/atom/ns#' term='get'/><title type='text'>POST vs. GET for HTML Form Security (and The Back Button)</title><content type='html'>At the last &lt;a href="https://greenjug.dev.java.net/"&gt;GreenJUG&lt;/a&gt; meeting, we talked about how you should always use POST instead of GET for any secure web sites.  But when you use a POST form on an HTTPS site, navigate away, and click the Back button (on Internet Explorer), you get "The web page you requested is no longer available - try refreshing..."  It essentially breaks the back button which UI designers will tell you never to do.&lt;br /&gt;&lt;br /&gt;So for me, the rule is, for any view-forms (where the form sends selection criteria for what the user wants to view), make them GET.  Such forms tend to submit only the ID number of whatever they are pulling from the database, usually do not involve entering sensitive information, and should work when the users navigates away and uses the back-button.  For update forms (where the user submits new data or changes old data) make them POST because it's good to prevent accidental resubmittion and because people tend to enter private and proprietary information on such forms.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-8411877636711943161?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/8411877636711943161/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=8411877636711943161' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/8411877636711943161'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/8411877636711943161'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2010/04/post-vs-get-for-html-form-security-and.html' title='POST vs. GET for HTML Form Security (and The Back Button)'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-4685495199574454498</id><published>2010-04-13T07:40:00.000-07:00</published><updated>2011-08-31T07:08:39.795-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Applications'/><category scheme='http://www.blogger.com/atom/ns#' term='passwords'/><category scheme='http://www.blogger.com/atom/ns#' term='internet explorer'/><category scheme='http://www.blogger.com/atom/ns#' term='security'/><category scheme='http://www.blogger.com/atom/ns#' term='operating system'/><category scheme='http://www.blogger.com/atom/ns#' term='Microsoft'/><category scheme='http://www.blogger.com/atom/ns#' term='keepass'/><category scheme='http://www.blogger.com/atom/ns#' term='Office'/><title type='text'>Passwords Don't Matter</title><content type='html'>&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Why Passwords Don't Matter&lt;/h3&gt;I set out to write an article about tools to easily manage passwords securely, but when I looked for data on computer crime to encourage people to use better passwords, I discovered a very different story.  Most "computer crime" (according to the FBI) is various forms of scams and con games that used to be carried out in person, over the phone, or through the mail, but are now done through online auctions or email.  Nothing to do with passwords.  This 25-page &lt;a href="http://www.ic3.gov/media/annualreport/2008_IC3Report.pdf"&gt;2008 Internet Crime Report&lt;/a&gt; by the FBI only uses the word "password" twice.&lt;br /&gt;&lt;br /&gt;At least for corporations, the big problem seems to be people using the access they were given to do bad things.  That happens much more often than people hacking into other accounts.&lt;br /&gt;&lt;br /&gt;Computer attacks tend to target applications and the operating system.  If you don't keep up with patches, your password won't matter.  Source: &lt;a href="http://www.sans.org/top-cyber-security-risks/"&gt;The Top Cyber Security Risks&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Contrary to the title of this posting, good password practices are important.  But what's even more important is to:&lt;br /&gt;&lt;br /&gt;1.) Keep your wits about you and cultivate a healthy skepticism before downloading a free game, clicking on an advertisement, or buying something from someone you don't know (e.g. eBay).&lt;br /&gt;&lt;br /&gt;2.) Keep your operating system and applications updated.  Always choose, "Yes, apply updates right now" and "Of course I'll reboot."  Manually check for updates periodically just in case.&lt;br /&gt;&lt;br /&gt;3.) Use a tool like Revo Uninstaller to remove applications you are no longer using.  Especially anything by Adobe, RealPlayer, toolbars (e.g. Yahoo!), and the Microsoft .NET framework.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;When Do Passwords Matter?&lt;/h3&gt;I got an email today saying that a web application I used a single time eight years ago had &lt;a href="https://blogs.apache.org/infra/entry/apache_org_04_09_2010"&gt;suffered a break-in&lt;/a&gt; and warning me that if I used that password for multiple accounts, I should change the passwords to all those accounts.  I have over 120 personal accounts, and God knows how many at my various jobs over the last 8 years - how many of those applications have been compromised?  Kudos to the organization who discovered the break-in AND alerted me.  I think it's safe to assume this is not the only break-in among those 120 applications, nor the only one discovered.&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Minimum Effort Password Management&lt;/h3&gt;I just read a &lt;a href="http://www.boston.com/bostonglobe/ideas/articles/2010/04/11/please_do_not_change_your_password/"&gt;wonderful article in the Boston Globe Online&lt;/a&gt; about the time-wasting, annoying, and mostly useless advice security experts have given us about passwords.  So if you want to be secure with the minimum amount of effort, what is the most important thing?&lt;br /&gt;&lt;br /&gt;I believe using a different unguessable password for every account is the most important password practice because doing so means that all of your other accounts are safe whenever one of them is compromised - and if you use a computer long enough, accounts WILL be compromised.  Some have suggested using X9$bFacebook, X9$bTwitter, X9$bMySpace, but schemes that use the application name, even if it's altered in various ways, are still guessable.&lt;br /&gt;&lt;br /&gt;To manage different passwords for every account, you need a password manager.  Many people use the "remember passwords" feature of their favorite browser.  This is a terrible idea because:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;It means you are storing your most secure data (your passwords) in your least secure application (your browser)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You are going to need to enter activation keys or passwords into software installed on your machine at some point, and you cannot store that in your browser&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When you go to another computer, or try to switch to another brand of browser, you don't have your passwords.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When your hard drive dies, so do all your passwords&lt;/li&gt;&lt;br /&gt;&lt;li&gt;When you die, so does access to your computer and all your passwords.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;So the web browser is not such a good solution.  The best I've found (and thanks to a good friend for pointing me to it) is a free, open-source password manager called KeePass which is available for &lt;a href="http://keepass.info/"&gt;Windows&lt;/a&gt; and &lt;a href="http://www.keepassx.org/"&gt;KeePassX&lt;/a&gt; for Linux, Mac, and Windows.  I use it with a strong master-password and a tool called DropBox to synch it across my computers.  Lifehacker has &lt;a href="http://lifehacker.com/5063176/how-to-use-dropbox-as-the-ultimate-password-syncer"&gt;an article on how to use them together&lt;/a&gt;.  Once a year, I recommend printing out your KeePass database, writing your master password on it (your Dropbox password will be in your KeePass database), sealing the list in a tamper-evident security envelope, and putting it in your safe deposit box.  Then burn last years list (you know, with a match).  When your hard drive dies, you have a backup immediately available on your other computers via Dropbox.  When you die, there are legal proceedings for your next of kin to access your safe deposit box.&lt;br /&gt;&lt;br /&gt;For a less secure, less robust, but easier to use password manager, look at &lt;a href="https://lastpass.com/"&gt;LastPass&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-4685495199574454498?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/4685495199574454498/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=4685495199574454498' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/4685495199574454498'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/4685495199574454498'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2010/02/passwords-dont-matter.html' title='Passwords Don&apos;t Matter'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-5178033877892812964</id><published>2010-02-03T06:55:00.001-08:00</published><updated>2011-08-24T09:56:06.817-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shell scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><category scheme='http://www.blogger.com/atom/ns#' term='regular expression'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='grep'/><category scheme='http://www.blogger.com/atom/ns#' term='command line'/><title type='text'>Grep: Yet Another Reason I Love Bash (the GNU/Linux Born Again Shell)</title><content type='html'>I have to say how incredibly awesome it is to be able to issue a one-liner like the following:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;egrep -w --color --exclude-dir='.svn' '[Bb]usiness [Uu]nits?|BUs?' WEB-INF/jsp/*&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Everywhere the expression Business Unit, business unit, or BU appears in the JSP pages is now listed on my screen in color.&lt;br /&gt;&lt;br /&gt;How does it work?&lt;br /&gt;&lt;br /&gt;&lt;dl&gt;&lt;br /&gt;&lt;dt&gt;egrep&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Run grep (the GNU "find" utility) using Extended (that's where the E comes from) regular expressions.&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;-w&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;I was looking up the word boundary expression in the man page for grep and found that the -w switch forces the match to occur within word boundaries.&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;--color&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Show the file names and matches in a different color (red) from the other text.&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;--exclude-dir='.svn'&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Prevents grep from looking in subversion (source control) directories&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;'[Bb]usiness [Uu]nits?|BUs?'&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Match "business unit" with or without initial caps and with or without an "s" at the end, or match "BU".  These matches have to occur on word-boundaries (because of the -w above) so that ABUSE does not match.&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;WEB-INF/jsp/*&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Run the match against all the files in the WEB-INF/jsp directory.&lt;/dd&gt;&lt;br /&gt;&lt;/dl&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;I pay about $150/year for an individual license for InteliJ IDEA because of it's refactoring, but it still can't do this.  You can pry bash from my cold, dead hands.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-5178033877892812964?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/5178033877892812964/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=5178033877892812964' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5178033877892812964'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5178033877892812964'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2010/02/grep-yet-another-reason-i-love-bash.html' title='Grep: Yet Another Reason I Love Bash (the GNU/Linux Born Again Shell)'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-21331182809077790</id><published>2010-01-05T05:50:00.000-08:00</published><updated>2011-08-31T07:10:11.138-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Applications'/><category scheme='http://www.blogger.com/atom/ns#' term='VirtualBox'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows XP'/><category scheme='http://www.blogger.com/atom/ns#' term='virtual machine'/><title type='text'>My Favorite Free Windows Applications</title><content type='html'>I rely on a small set of free tools (&lt;a href="http://lifehacker.com/5413223/how-to-fix-your-relatives-terrible-computer" target="_blank"&gt;suggested on Lifehacker&lt;/a&gt;) to keep my Windows (virtual) machines (and relative's Windows machines) running smoothly.  They are:&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://www.revouninstaller.com/" target="_blank"&gt;Revo Uninstaller&lt;/a&gt;:&lt;/b&gt; To remove programs, but perhaps more importantly, to nuke junk that auto-starts (click Tools, then the traffic-light icon).  Does a nice job of removing the extra junk that inconsiderate programs leave behind.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://www.ccleaner.com/" target="_blank"&gt;Ccleaner&lt;/a&gt;:&lt;/b&gt; To clean up trash on the hard drive and registry.  I make sure to uncheck cookies for the cleanup in all my browsers.  Longer-term, I will probably mark the cookies I care about as safe and let it nuke the rest.  Then I run the registry cleanup repeatedly until it doesn't find anything any more.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://www.microsoft.com/Security_Essentials/" target="_blank"&gt;Microsoft Security Essentials&lt;/a&gt; (antivirus):&lt;/b&gt; Free, highly rated, low-resource, no hassle.  Time will still tell, but so far, so good.  I've always thought that Microsoft should be the one responsible for protecting their own operating system.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://www.mydefrag.com/" target="_blank"&gt;MyDefrag&lt;/a&gt;:&lt;/b&gt; The "Weekly" script rocks.  I don't run it weekly, but it's amazing what a mess Windows makes of its drive.  It's like an animal soiling it's cage.  MyDefrag really helps boot times.  I'm very impressed.  Note: I do not like the "Monthly" script at all.  Sorting files in name and directory order seems like a total waste of time and disk-life to me.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Honorable Mention:&lt;/h4&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://technet.microsoft.com/en-us/sysinternals/bb897443.aspx" target="_blank"&gt;sdelete&lt;/a&gt;:&lt;/b&gt; A command-line utility from Microsoft that zeros out unused disk space.  This is nice for security reasons, but I use it to keep my virtual disk images really small.  I run "sdelete -c C:"  Then in Linux, "VBoxManage modifyhd &lt;filename&gt; --compact"&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;&lt;a href="http://www.cygwin.com/" target="_blank"&gt;cygwin&lt;/a&gt;&lt;/b&gt;: A Linux command-line emulator for Windows.  I wouldn't live without it, but people wouldn't have a need for it.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;The above programs have helped me tame the most clogged-with-crapware systems and make them secure and responsive again.&lt;br /&gt;&lt;br /&gt;If someone told me that once I ran Linux, I'd think nothing of running several Microsoft operating systems, I would have laughed.  But Windows works great in a VM.  It's a fun toy when you don't have to rely on it to do anything useful.  I use one VM for IE6 (for testing) and the other for IE8 (for programs that don't have Linux equivalents).  Both are running Windows XP SP3.  A Windows 7 VM is probably in my future.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-21331182809077790?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/21331182809077790/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=21331182809077790' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/21331182809077790'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/21331182809077790'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2010/01/my-favorite-free-windows-applications.html' title='My Favorite Free Windows Applications'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-8259587205484306032</id><published>2009-10-30T06:48:00.000-07:00</published><updated>2009-11-21T04:48:37.887-08:00</updated><title type='text'>What is Linux?</title><content type='html'>For those of you who don't know already, Linux is an operating system (like Windows or Mac OS-X), that is built by the public and it's free.*  If you know how to use Windows or OS-X, You can install Linux and be using it with no training in about 30 minutes.  It's made entirely of programs donated to the public good and many distributions come with office applications, games, and web tools already installed.&lt;br /&gt;&lt;br /&gt;There are hundreds of companies, causes, and individuals who package Linux into a "distribution" consisting of the Linux kernel (the guts of the operating system) and a set of software designed for a specific purpose.  For example:&lt;br /&gt;&lt;br /&gt; - Easy-to use desktop: Ubuntu&lt;br /&gt; - Corporate web-server: RedHat&lt;br /&gt; - Free (GNU) software purity: Debian or gNewSense&lt;br /&gt; - Minimal hardware: Puppy&lt;br /&gt; - Run from a CD: PCLinuxOS&lt;br /&gt; - Repair your computer after a crash: SystemRescueCD&lt;br /&gt; - To be just like another distribution: CentOS (like RedHat)&lt;br /&gt; - To protect networks: IPCop&lt;br /&gt; - Breaking into other computers: PHLAK (Professional Hackers Linux Assault Kit)&lt;br /&gt; - Christianity, bible study, and parental control: Ubuntu Christian&lt;br /&gt; - Online learning environment, suitable for classroom use: Edubuntu&lt;br /&gt;&lt;br /&gt;That's a random sampling taken mostly from &lt;a href="http://distrowatch.com/" target="_blank"&gt;Distrowatch&lt;/a&gt;.  There is a lot of crossover between the goals and included packages of various distributions, but this gives you a sense of what's out there.  Because the source code for the entire operating system and most of the software generally distributed with it is free and/or open-source, a programmer can compile their own version and alter it to work with custom hardware such as a new phone, or a robot.&lt;br /&gt;&lt;br /&gt;Interestingly, no-one told anyone to create Linux.  In 1989, Richard Stallman, a visionary programmer who could be called the world's first software activist, created a new kind of software license - the GNU Public License, GPL, or "copyleft.  The GPL guarantees everyone's right to copy, share, modify, and (re-)distribute software.  In 1991, A programmer named Linus Torvalds started working on a version of Unix that he could use on his own computer.  He published it under the GPL and released it on the web and soon many other programmers (or "hackers") contributed changes and improvements.  The work of untold numbers of programmers, writers, graphic artists, testers, and other largely self-selected volunteers, under the GPL and other "open source" licenses, has created Linux in all its flavors - and it continues to evolve. &lt;br /&gt;&lt;br /&gt;Who uses Linux?  We all do.  At least half of all web sites run on Linux servers. Mobile phones such as the Android run Linux. 443 of the top 500 supercomputers run Linux.  Increasingly, personal desktop computers run Linux.  If you aren't familiar with Linux, I encourage you to get yourself a LiveCD of any distribution and give it a try.  Ubuntu is currently the most popular for non-techies.&lt;br /&gt;&lt;br /&gt;For your entertainment, here's a brief history of Linux through advertising (and one spoof):&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=t-L-0s-7-Z0" &gt;Mac Spoof: Upgrading&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=MS-aLOm-6Vc" &gt;I'm a Linux&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a href="http://www.youtube.com/watch?v=0JqtMgiUaf4" &gt;The Heist&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;* Thanks to DigitalMan for helping me come up with a one-line explanation of Linux.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-8259587205484306032?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/8259587205484306032/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=8259587205484306032' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/8259587205484306032'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/8259587205484306032'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2009/10/what-is-linux.html' title='What is Linux?'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-3984257718193461755</id><published>2009-10-29T11:11:00.000-07:00</published><updated>2010-04-20T06:17:03.299-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows XP'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubuntu'/><title type='text'>Settling in to Ubuntu Linux</title><content type='html'>I'm surprised at how much I like Ubuntu Linux better than Windows!  The UI is basically the same, only everything is faster.  Especially when you do anything to a file (write to it, read from it, move it, or delete it), Linux turns hours into Minutes.  When you consider that doing anything useful on a computer generally involves changing a file, that says a lot!&lt;br /&gt;&lt;br /&gt;Printing to a network printer was actually easier on Linux.  Our vendor-supplied proprietary Windows driver hangs the printer every second or third time you try to print a PDF!  I've got my complaints (a few sound limitations and a few software packages that I still have to run in a Windows VM).  But whenever I have to fix someone's Windows computer I pity them.  Even Windows runs better on Linux (in a VM).  That way you can keep working while you reboot, reboot, reboot...&lt;br /&gt;&lt;br /&gt;As I'm writing this, I can hear my wife downstairs using Windows...  "Network Problems!?!  I can't have network problems right now!  Operation failed???"  I feel sorry for her, I've been there myself, but I have to laugh at the background to this blog post.&lt;br /&gt;&lt;br /&gt;I encouraged some people I know who are absolute beginners with computers to buy Macs.  But now I'm not so sure.  Ubuntu Linux is actually easier to navigate in some ways than a Mac.&lt;br /&gt;&lt;br /&gt;Update 2010-04-01: I spent 2 hours trying to copy some pictures from a camera to the hard drive on a Mac and it was awful.  In Ubuntu, your camera gets mounted as a drive and you can drag and drop the pictures wherever you want them.&lt;br /&gt;&lt;br /&gt;The only drawback to running Linux is that some programs only run on Windows.  Screen sharing programs (except for &lt;a href="http://teamviewer.com/download/index.aspx#downloadAreaLinux"&gt;TeamViewer&lt;/a&gt;) all require Windows or Mac.  The latest version of Microsoft Office only runs on Windows, Photoshop only runs on Windows/Mac, and Proprietary VPNs only run on Windows.  Most people don't need those things, but I do, so I have to run &lt;a href="http://glenpeterson.blogspot.com/2009/10/windows-in-virtual-machine.html"&gt;Windows in a Virtual Machine&lt;/a&gt;.  But I only have my VM running about 25% of the time.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-3984257718193461755?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/3984257718193461755/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=3984257718193461755' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/3984257718193461755'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/3984257718193461755'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2009/10/settling-in-to-ubuntu-linux.html' title='Settling in to Ubuntu Linux'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-465687660340596155</id><published>2009-10-22T04:27:00.000-07:00</published><updated>2009-10-29T11:10:57.940-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GoToMeeting'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='antivirus'/><category scheme='http://www.blogger.com/atom/ns#' term='VirtualBox'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows XP'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='virtual machine'/><title type='text'>Windows in a Virtual Machine</title><content type='html'>My Windows XP SP3 CD came in the mail yesterday.  This time around I did a more-or-less default install in VirtualBox with 20GB expanding drive, 1.2GB memory, 24M video memory, and audio disabled.  Instead of Symantec Antivirus, I tried ESET NOD32.  I also disabled Adobe Drive this time because I got some weird messages.  The install and all setup took about 8 hours, much of which was waiting, which is much more reasonable than the 3 days my previous attempt took.&lt;br /&gt;&lt;br /&gt;The new image uses only about 6GB of disk space instead of 15, presumably because it doesn't contain any junk from my old hard drive.  It is immeasurably faster than my 140GB image of my previous hard drive, half-of which was unreadable.  Not sure how much was VM settings, Symantec, or other junk, but I think everything helped.  &lt;br /&gt;&lt;br /&gt;I deleted the old virtual hard drive image.  It took hours because I had a dozen huge "snapshots" I had taken in VirtualBox, each of which took 1-20 minutes to delete.  Before I started, my new 1TB drive was 60% full.  Now it's only 7% full!  What a disaster that was.&lt;br /&gt;&lt;br /&gt;I currently use Windows in VirtualBox a few times a week for the following, but I work mostly in Linux:&lt;br /&gt;&lt;br /&gt;&lt;dl&gt;&lt;dd&gt;Photoshop CS4&lt;/dd&gt;&lt;dt&gt;I use this a lot and it requires 1024x768 resolution for some of the dialogs to work properly (to show the Save button).  Takes a lot of resources, but as much as I love GIMP, it's not a substitute for Photoshop for professional work.&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;A couple of proprietary VPNs&lt;/dd&gt;&lt;dt&gt;The nice thing about running these in a virtual machine is that it lets me check email and access the web from my primary OS while the VPN is connected.&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Citrix GoToMeeting&lt;/dd&gt;&lt;dt&gt;Works great&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Remote Desktop Connection&lt;/dd&gt;&lt;dt&gt;Necessary for work.&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Internet Explorer&lt;/dd&gt;&lt;dt&gt;Necessary for work (for testing)&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Microsoft Office&lt;/dd&gt;&lt;dt&gt;Open Office is a fantastic substitute.  It changes the layout only slightly I think because the available fonts are different on Linux.  I still need the real thing for presentations and testing for work.&lt;/dt&gt;&lt;br /&gt;&lt;dd&gt;Firefox HTML Validator plug-in&lt;/dd&gt;&lt;dt&gt;I really wish there was a Linux version of this!&lt;/dt&gt;&lt;/dl&gt;&lt;br /&gt;&lt;br /&gt;Hopefully, over time, things like GoToMeeting and the Firefox HTML Validator will work on Linux.  I'm still looking for a secure backup strategy for my entire drive.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-465687660340596155?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/465687660340596155/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=465687660340596155' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/465687660340596155'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/465687660340596155'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2009/10/windows-in-virtual-machine.html' title='Windows in a Virtual Machine'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-2732827554054371063</id><published>2009-09-25T13:22:00.000-07:00</published><updated>2011-08-31T07:09:10.235-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='internet explorer'/><category scheme='http://www.blogger.com/atom/ns#' term='safari'/><category scheme='http://www.blogger.com/atom/ns#' term='chrome'/><category scheme='http://www.blogger.com/atom/ns#' term='spacer.gif'/><category scheme='http://www.blogger.com/atom/ns#' term='quirks mode'/><category scheme='http://www.blogger.com/atom/ns#' term='opera'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><category scheme='http://www.blogger.com/atom/ns#' term='css'/><title type='text'>Alternative for spacer.gif in cross-browser standards-compliant html</title><content type='html'>Instead of spacer.gif I use:&lt;br /&gt;&lt;br /&gt;CSS:&lt;br /&gt;&lt;code&gt;.spacer2px{font-size:2px;line-height:2px;}&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;HTML:&lt;br /&gt;&lt;code&gt;&amp;lt;div class="spacer2px"&amp;gt;&amp;amp;#160;&amp;lt;/div&amp;gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Works with &lt;code&gt;&amp;lt;td&amp;gt;&lt;/code&gt; as well.  We have &lt;code&gt;spacer1px, spacer4px, spacer6px&lt;/code&gt;, etc. as needed.    Works the same on all browsers, so long as each HTML page starts with the following, which keeps IE from entering quirks mode and makes it as standards compliant as it gets (requires no blank lines until after the opening HTML tag):&lt;br /&gt;&lt;br /&gt;&lt;code&gt;&lt;pre&gt;&amp;lt;!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN" "http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&amp;gt;&lt;br /&gt;&amp;lt;html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en"&amp;gt;&lt;br /&gt;&lt;br /&gt;    ... headers ...&lt;br /&gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;    &amp;lt;div id="page"&amp;gt;&lt;br /&gt;&lt;br /&gt;    ... content goes here ...&lt;br /&gt;&lt;br /&gt;    &amp;lt;/div&amp;gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;/pre&gt;&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;We currently have two lines of browser-specific code in the entire application, and they are both in the CSS file:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;/* This sets defaults for all elements and helps overcome browser differences */&lt;br /&gt;* {&lt;br /&gt; margin: 0px;&lt;br /&gt; padding: 0px;&lt;br /&gt; box-sizing: border-box;&lt;br /&gt; &lt;strike&gt;/* -moz-box-sizing: border-box; Firefox used to need this */&lt;/strike&gt;&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;/* Add space for scrollbar on IE so it doesn't obscure the page.&lt;br /&gt;This uses the Commented Backslash Hack so only IE can see this: \*/&lt;br /&gt;#page { margin-right: 16px; }&lt;br /&gt;/* End Hack */&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Well, that's my recipe that I've been using for 2-3 years now.  Tested on IE 6, 7, 8, Firefox 2, 3, Safari 3, 4, Chrome, Opera 9, Epiphany, Konqueror and on Windows XP, Mac OS-X, the iPhone, and Linux (Ubuntu/Xubuntu).&lt;br /&gt;&lt;br /&gt;Special thanks to &lt;a href="http://www.quirksmode.org/"&gt;quirksmode.org&lt;/a&gt; for probably being the best resource for these kind of details.  The O'Reilly &lt;a href="http://oreilly.com/catalog/9780596527334/"&gt;CSS: The Definitive Guide&lt;/a&gt; and &lt;a href="http://oreilly.com/catalog/9780596000264/"&gt;HTML &amp; XHTML: The Definitive Guide&lt;/a&gt; books are great for the basic rules.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-2732827554054371063?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/2732827554054371063/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=2732827554054371063' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/2732827554054371063'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/2732827554054371063'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2009/09/alternative-for-spacergif-in-cross.html' title='Alternative for spacer.gif in cross-browser standards-compliant html'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</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-5689812823462613341.post-6665680214196085948</id><published>2009-09-23T08:11:00.000-07:00</published><updated>2011-08-31T07:34:47.105-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='GoToMeeting'/><category scheme='http://www.blogger.com/atom/ns#' term='Linux'/><category scheme='http://www.blogger.com/atom/ns#' term='VirtualBox'/><category scheme='http://www.blogger.com/atom/ns#' term='operating system'/><category scheme='http://www.blogger.com/atom/ns#' term='Microsoft'/><category scheme='http://www.blogger.com/atom/ns#' term='Windows XP'/><category scheme='http://www.blogger.com/atom/ns#' term='Ubuntu'/><category scheme='http://www.blogger.com/atom/ns#' term='Office'/><title type='text'>Moving from Windows XP to Ubuntu Linux this "Weekend"</title><content type='html'>&lt;h3&gt;Sat, Sep 19, 2009&lt;/h3&gt;&lt;br /&gt;&lt;h4&gt;6 PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Bought a Western Digital 1TB Caviar (Green) drive and 4GB memory.  I wanted to keep the old drive untouched in case there were issues with the install.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;10:02 PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;new hard drive installed, Ubuntu install begun.  I almost went with Xubuntu, but the display got messed up in VirtualBox when I applied patches.  I think Ubuntu is going to be more reliable/compatible and that's even more important to me than performance.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;11:53 PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;sent first email from new system using Google Chrome!  I spent one of those 2 hours googling and thinking about partitioning before deciding to let the installer do what it wanted.  Finally I clicked "OK" about 5 times and in 20 minutes it was done.  Ran the updater, rebooted, added and removed some programs; another 20 minutes total.  Really, it couldn't have been easier.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Sun, Sep 20, 2009&lt;/h3&gt;&lt;br /&gt;&lt;h4&gt;10:13 AM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Massive frustration.  So far, it looks like installing Windows is going to be the hardest part of installing Linux.  All I can find is an XP Home edition upgrade CD from 2004.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;Microsoft doesn't sell XP any more.  The Windows 7 beta download program has ended and you can't buy it until October 22nd (or thereabouts).  What you can buy (for $300) is Windows Vista Professional Non-Upgrade with a "free" download of Windows 7 when it's released so you don't have to miss out on upgrading, patching, and being the first to find bugs.  Even if that sounded good, I can't buy it now because the Windows store doesn't open until noon.  Tech support isn't open either.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I decided to follow the instructions here:&lt;br /&gt;&lt;a href="http://www.virtualbox.org/wiki/Migrate_Windows"&gt;http://www.virtualbox.org/wiki/Migrate_Windows&lt;/a&gt;&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;It looked good, but it's not the easiest, or necessarily the best way.  Read on...&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;4:01 PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Booting a 150GB image of my old Windows hard drive.  Woohoo!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;4:06 PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Windows asks for a key code.  I copy it off the sticker on the side of my computer.  It doesn't take it.  I call in and punch the numbers over the pone.  No luck. &lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;It turns out that it wouldn't let me re-register because I have an OEM version that was pre-installed on the drive and the virtual machine had different "hardware" then it was expecting.  Maybe I could have faked the bios, the MAC address, the hardware, and I'm not sure what else in order to satisfy XP that I wasn't trying to install it on a different PC.  I'm installing it on the same PC, so it's legal, but it's in a virtual machine, so I couldn't be sure the hardware would ever match.  At this point I gave up and installed my XP Home Upgrade from 2004 over the XP Pro image because the upgrade has to be installed over an existing version of Windows (apparently it doesn't have to be a validated version).&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;The install kept the files on the C:\ drive, but it overwrote the My Documents folder and left my old home directory inaccessible.  Using CACLs showed it to be owned by WINDOWS NT/my-old-id.  I was able to make it readible, but couldn't delete anything.  I began deleting my now useless applications but it was taking hours.  The solution?  Boot the VM into Ubuntu and delete them from Nautilus.  It took minutes.  How can Microsoft charge $300 for an operating system when a free one has a 100 times faster file system?&lt;p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;8:01 PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Windows XP home is installed and works.  Beginning "Windows Update."&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;8:29 PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Installed vpnc, which connects to a Cisco VPN from the Linux command line.  Nice!  XP is still applying updates.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;9:10PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Windows updates, updates, updates.  50 at once this time.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Mon, Sep 21, 2009&lt;/h3&gt;&lt;br /&gt;&lt;h4&gt;8:24 AM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Windows XP home was finally upgraded to SP3 around midnight last night after many reboots, and lots of clicking and waiting.  Installed VirtualBox Guest Additions this morning which was quite easy.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Noonish&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;Had a work call that wanted to use Citrix GoToMeeting.  I accidentally laughed out loud at the suggestion (it was a little akward).  But within 5 minutes I had the Citrix client installed on Windows and thanks to VirtualBox GuestAdditions I was looking at a full-screen screen-share without any noticeable lag or any issue whatsoever.  Nice!&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;At some point I called Microsoft to find out my options.  This took about 4 calls.  One of which was a voice recognition system that couldn't recognize a thing I said.   later I played "20 questions" with an automated voice response system that put me on hold for 10 minutes.  When I finally got a human, the response was, "If you got your operating system with a computer, call that computer's manufacturer, not us." and "We don't sell Windows XP.  Want to buy Vista?"&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I called Lenovo and they could only offer a CD of the original drive image for $45, but I would have had the same hardware vs. validation issues in VirtualBox.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Tue, Sep 22, 2009&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;4:35 PM&lt;/h4&gt;&lt;br /&gt;&lt;p&gt;I'm basically Done.  XP upgraded so SP3, Office 2003 installed and upgraded to SP3, Norton Internet Security installed and upgraded, 2 VPNs installed on Windows.  I tried to compress the virtual drive image to no avail.  They aren't really designed to have their size changed.  There's inaccessible or otherwise useless little bits of junk spread out on the drive image (some are unmovable by a defragment) which probably kept it from compressing.    At one point, I ran some program on my XP image that was supposed to fill the drive with zeros (to make the image compress better) by making a big enough text file with each character being the null character.  It was supposed to delete the file when finished, but presumably your computer would crash about the same time once the drive was full, but it was running so slowly that I killed it and never got to see.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;I thought I'd just write a little script that piped the small file of zeros many times into a new file until the machine crashed.  But you can't pipe like that in DOS.  I downloaded sdelete instead.   It took about 5 hours for it to write 140GB of zeros.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;While I was waiting for Windows/Office to install, I installed my development environment and got all my Linux apps up and running.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Mon, Oct 5, 2009&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;11:27 AM&lt;/h4&gt;&lt;br /&gt;I've installed Photoshop and Cygwin on Windows in the last week.  They work fine, but Photoshop really needs the full-screen 1024x768.  I might have to get a real graphics card and bigger monitor.&lt;br /&gt;&lt;br /&gt;Had to share encrypted WinZip archives with Windows users.  Installed PeaZip and it encrypted/decrypted zipped/unzipped WinZip files perfectly.  Everything works so much faster than on Windows.  There was definitely pain involved in making the switch, but it's also definitely paying off.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Lessons Learned:&lt;/h3&gt;&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A new hard drive was a great idea.  I could pop the old one out of the machine and be sure I wasn't deleting anything important by accident.  I wouldn't try an upgrade like this without a blank drive.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;A Windows XP Pro SP3 CD would have been a godsend.  I still might get one and reinstall to bring the disk image size down to about 10GB instead of almost 150 and to be rid of any lingering junk on the drive.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Dropbox and KeePass make managing online accounts across machines really, really easy.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;I did the right thing.  I was really unsure about it.  But consider:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;I was able to be up and running on Linux FOR FREE in 2 hours (would have been 1 if I didn't second-guess the installer for an hour).  The XP install took 2 days (would have been 1 if I paid $130 for a disk which Amazon, not Microsoft sells).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Deleting big files on Windows was incredibly slow.  Rebooting into Linux running off a CD (inside the same VirtualBox VM) turned hours into minutes or less.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Support:  If I actually had a support question (not a pre-sales question), I would have had to pay Microsoft $45 to ask it.  The Mac Ads are very funny, but they are accurate in this case.  I can get on Freenode IRC for free and have virtually any (polite, pre-researched, well worded) question answered in about 10 minutes, and probably learn something else interesting while I'm there.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;My whole system is much, much faster.  Startup is in about 20 seconds as opposed to 3 minutes.  Suspend (sleep) takes about 2 seconds, waking up takes 5-10.  The wake-up on Windows often took 5 minutes!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;In this process I expected to discover that Linux wasn't ready for prime time.  Instead I discovered a lot of negative things about Windows and a lot of positive things about Linux!&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;&lt;br /&gt;&lt;p&gt;I'm installing Photoshop now on Windows so a Windows VM is probably here to stay, but I would be very surprised if I ever switched back.&lt;/p&gt;&lt;br /&gt;&lt;br /&gt;&lt;p&gt;If Google comes out with a killer Netbook operating system this fall, more people will use Linux.  If Windows 7 forces developers to rewrite their apps, and if people have to run XP in a virtual machine to use older applications, they can do that just as well on Linux (or Mac) and have a better, faster, more secure host operating system.  All this adds up to Windows having a smaller market share and developers having more reason to come out with a OS-X and/or Linux port of their software.  If they use a toolkit like GTK+ (free) instead of Microsoft Visual Studio (very expensive), it shouldn't be such a big deal to run on all three operating systems.  At least for me, the future looks a lot more like Linux and less like Microsoft.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-6665680214196085948?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/6665680214196085948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=6665680214196085948' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/6665680214196085948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/6665680214196085948'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2009/09/moving-from-windows-xp-to-ubuntu-linux.html' title='Moving from Windows XP to Ubuntu Linux this &quot;Weekend&quot;'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-7540875052053149939</id><published>2009-06-05T09:14:00.001-07:00</published><updated>2009-06-05T10:40:00.310-07:00</updated><title type='text'>Free (GPL) Software Restricts Your Rights!</title><content type='html'>When I say Free Software in this article, I am referring specifically to software released under the GNU Public License (GPL), a license written by Richard Stallman that has revolutionized the software world.  Revolutionized in the sense of completely and indelibly changing it, but also in the sense of starting a movement of people.  I owe great thanks to RMS, his copyleft, and the ideas he set in motion.  I use emacs and cygwin so much every day that I don't even notice when I dream in emacs.  I have created or contributed to a small number of Free or Open-Source Software (F/OSS) projects, but like most professional programmers, I have mostly just helped myself of the bounty of fabulous F/OSS software that exists.&lt;div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;But for all it's talk of freedom, free as in "Free Speech" and free as in "Free Beer", Free (GPL) Software still restricts your rights as a programmer in one very important way that some other Open Source software does not; it cannot be bundled with "non-free" or proprietary software for distribution because the act of doing so turns the proprietary software into free software.  Most professionals get around this either by violating the terms of the GPL (intentionally or through ignorance) or by adopting the Software as a Service (SaaS) model of hosting web applications themselves so that the software does not need to be distributed in order to be used.  Other strategies for making money with Free Software include selling support and packaging (as RedHat does with Linux) or dual-licensing (as Oracle does with MySQL).&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;At my current job we pursued the SaaS model as far as it would take us, but in the end we found we had to distribute our product.  We were unwilling to license our company's only asset (its software) under the GPL, so we removed all GPL code instead.  That was a sad day indeed and it has since been a struggle to keep up with the sheer number of Licenses that F/OSS developers use for their products.  For some minor components we have had to choose tools based more on the license than the quality.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The saving grace for us has been the &lt;a href="http://www.apache.org/"&gt;Apache Software Foundation&lt;/a&gt; and their &lt;a href="http://www.apache.org/licenses/LICENSE-2.0"&gt;license&lt;/a&gt;.  For F/OSS developers on the fence, software released under the Apache License v2.0 can be converted or combined with GPL 3.0 software at any time, but the reverse is not true.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;The trouble with the GPL is a very specific, technical detail which requires some background in order to explain.  Modern computer programs tend to be composed of a number of proprietary and/or open-source components "linked" together to form a whole.  The definition of "linking" varies somewhat between programming languages and platforms and is somewhat disputed, but it basically means what it sounds like - components are developed separately then combined into a single whole.  The trick the GPL plays on programmers is that merely linking to GPL code (without changing it in any way) allows the GPL to extend to all linked code and effectively puts all of it in the public domain.  The Free Software Foundation will tell you that it's fine to link GPL code to proprietary code and it is, so long as you have the right and the desire to change that code into GPL code.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I have made my living writing software and charging for it since 1992.  I am happy to contribute bug fixes and non-core components of software I have written to the Open Source community, but I want the freedom to distribute critical components under the license of my choosing (generally a proprietary license, granting ownership of the code to my employer).  The Apache License (and other Open Source licenses) allow me to do that.  The GPL does not.&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;RMS wrote an article in the latest CACM entitled, "&lt;a href="http://mags.acm.org/communications/200906/?CFID=39214479&amp;amp;CFTOKEN=54882418"&gt;Why 'Open Source' Misses the Point of Free Software.&lt;/a&gt;"  With all due respect to RMS, I have to say I'm glad that it does and I'd like to encourage anyone familiarizing themselves with F/OSS to read several licenses very carefully before choosing one.  The GPL is a classic, but I'm afraid history will show its application is limited to two scenarios:&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;&lt;ol&gt;&lt;li&gt;People who believe that proprietary software is fundamentally wrong and should be eliminated.&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Companies who write software which is much more useful when linked as a component of other software than it is as a stand-alone product.  These companies will pursue a strategy like MySQL where they dual-license their products under the GPL so that the world is free to try, examine, change, and distribute their software for non-commercial purposes.  But those who wish to use the software commercially will have to pay for another version of what might be the same code released under a different license.&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;/div&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;&lt;div&gt;I predict that the GPL will see the most usage by the second camp.  For the rest of us, there are a few hundred other F/OSS licenses to choose from.  A great source for learning about these licenses is the &lt;a href="http://www.opensource.org/licenses"&gt;Open Source Initiative&lt;/a&gt;.  Their &lt;a href="http://www.opensource.org/approval"&gt;approval process&lt;/a&gt; is very instructive for anyone thinking they want to write their own license.  The proliferation of essentially duplicate licenses has run increasingly rampant as more programmers have gotten excited about F/OSS, mostly through the writer's ignorance of an existing license that already does exactly what they want.&lt;/div&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-7540875052053149939?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/7540875052053149939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=7540875052053149939' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/7540875052053149939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/7540875052053149939'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2009/06/free-software-restricts-your-rights.html' title='Free (GPL) Software Restricts Your Rights!'/><author><name>Glen.K.Peterson</name><uri>http://www.blogger.com/profile/01199522537419481981</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-7675119708940141149</id><published>2009-03-09T10:13:00.000-07:00</published><updated>2011-08-31T07:06:12.347-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='symmetry'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='Deprecated'/><category scheme='http://www.blogger.com/atom/ns#' term='equals()'/><title type='text'>Using @Deprecated more liberally</title><content type='html'>I recently created a class and an interface for managing dates as just a month and year (ignoring the day) and called them &lt;code&gt;YearMonth&lt;/code&gt; and &lt;code&gt;YearMonthInterface&lt;/code&gt;.  I then added comparison methods to &lt;code&gt;YearMonth&lt;/code&gt;: &lt;code&gt;greaterThan(YearMonthInterface x)&lt;/code&gt;, &lt;code&gt;lessThan(YearMonthInterface x)&lt;/code&gt;, and &lt;code&gt;equals(YearMonthInterface x)&lt;/code&gt;.  Do you see the problem?&lt;br /&gt;&lt;br /&gt;&lt;code&gt;equals()&lt;/code&gt; overrides &lt;code&gt;Object.equals()&lt;/code&gt; and has a special significance for the Java Collections framework.  According to &lt;a href="http://java.sun.com/javase/6/docs/api/java/lang/Object.html#equals(java.lang.Object)"&gt;the Java SE 6 spec&lt;/a&gt;, &lt;code&gt;equals()&lt;/code&gt; must be reflexive, symmetric, transitive, and consistent, and x.equals(null) should return false.  I had no way of knowing that any class that implemented the YearMonthInterface could satisfy those conditions.  In fact, I already had one implementing class that could never satisfy symmetry: &lt;code&gt;myYearMonth.equals(myOtherClass)&lt;/code&gt; would often not equal &lt;code&gt;myOtherClass.equals(myYearMonth)&lt;/code&gt; because my other class was dependent on another object and could only be equal when its associated objects were also equal.&lt;br /&gt;&lt;br /&gt;I decided to keep the &lt;code&gt;YearMonth.equals(Object o)&lt;/code&gt; method as overriding &lt;code&gt;Object.equals()&lt;/code&gt; so that &lt;code&gt;YearMonth&lt;/code&gt; would work correctly with collections, but create a new method, &lt;code&gt;YearMonth.equalTo(YearMonthInterface x)&lt;/code&gt;, for manual comparisons.  My concern was that I never wanted to mistakenly use &lt;code&gt;equals()&lt;/code&gt; instead of &lt;code&gt;equalTo()&lt;/code&gt;.  The solution?  Deprecate &lt;code&gt;YearMonth.equals()&lt;/code&gt; and provide a comment suggesting the use of &lt;code&gt;equalTo()&lt;/code&gt; instead.  The compiler will now catch any attempt to use the wrong method and the collections framework can still use the &lt;code&gt;equals()&lt;/code&gt; method without problem.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;/**&lt;br /&gt;This is deprecated because it does not compare YearMonthInterface objects,&lt;br /&gt;rather it overrides Object's equals method.  It still works in&lt;br /&gt;collections, but there should be no reason to ever call it directly.&lt;br /&gt;Use equalTo() instead which really compares two YearMonthInterfaces as&lt;br /&gt;you would expect, but doesn't need to be symmetric.&lt;br /&gt;&lt;br /&gt;@param other another YearMonth (or it will return false).&lt;br /&gt;@return true of the YearMonths are equal.&lt;br /&gt;*/&lt;br /&gt;@Deprecated&lt;br /&gt;@Override&lt;br /&gt;public boolean equals(Object other) {&lt;br /&gt;   // Check address and null first for speedy return&lt;br /&gt;   if (this == other) {&lt;br /&gt;       return true;&lt;br /&gt;   }&lt;br /&gt;   if (other == null) {&lt;br /&gt;        return false;&lt;br /&gt;   }&lt;br /&gt;   if ( !(other instanceof YearMonth) ) {&lt;br /&gt;       return false;&lt;br /&gt;   }&lt;br /&gt;   final YearMonth that = (YearMonth) other;&lt;br /&gt;   return (this.year == that.year) &amp;amp;&amp;amp; (this.month == that.month);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;public boolean equalTo(YearMonthInterface that) {&lt;br /&gt;   // Check address and null first for speedy return&lt;br /&gt;   if (this == that) {&lt;br /&gt;       return true;&lt;br /&gt;   }&lt;br /&gt;   if (that == null) {&lt;br /&gt;        return false;&lt;br /&gt;   }&lt;br /&gt;   return (this.year == that.getYear()) &amp;amp;&amp;amp; (this.month == that.getMonth());&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;div&gt;&lt;br /&gt;&lt;/div&gt;This technique would not work if I were to release this class to the public, but for a utility class with limited application, it can prevent me and my coworkers from making a simple but costly mistake.&lt;br /&gt;&lt;br /&gt;For a full explanation of how to write a good &lt;code&gt;equals()&lt;/code&gt; method, see Effective Java Second Edition by Joshua Bloch Chapter 3 (pp 35-36 is specifically about symmetry).  I'm about half way through this book and really enjoying it.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-7675119708940141149?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/7675119708940141149/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=7675119708940141149' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/7675119708940141149'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/7675119708940141149'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2009/03/using-deprecated-more-liberally.html' title='Using @Deprecated more liberally'/><author><name>GlenPeterson</name><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-5689812823462613341.post-5927363527166996553</id><published>2009-02-03T08:38:00.000-08:00</published><updated>2011-08-24T10:04:56.217-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='shell scripting'/><category scheme='http://www.blogger.com/atom/ns#' term='regular expression'/><category scheme='http://www.blogger.com/atom/ns#' term='bash'/><category scheme='http://www.blogger.com/atom/ns#' term='fgrep'/><category scheme='http://www.blogger.com/atom/ns#' term='subversion'/><category scheme='http://www.blogger.com/atom/ns#' term='grep'/><category scheme='http://www.blogger.com/atom/ns#' term='--exclude-dir'/><category scheme='http://www.blogger.com/atom/ns#' term='replace'/><category scheme='http://www.blogger.com/atom/ns#' term='sed'/><title type='text'>New Favorite Find/Replace (bash) shell commands</title><content type='html'>These commands took a while for me to find and I now use them very, very often.  One is to find files in all subdirectories excluding certain directories (in this case, the subversion ".svn" directories).  The second expression replaces regular expressions in all the files that match the find in the first expression:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;# To grep for things excluding subversion .svn directories&lt;br /&gt;find . -path '*/.svn' -prune -o -type f -print | xargs -e fgrep -I &lt;i&gt;&lt;b&gt;PATTERN&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;&lt;br /&gt;# To replace the same (using sed), only in files that grep matched, only in non .svn files.&lt;br /&gt;find . -path '*/.svn' -prune -o -type f -print | xargs -e fgrep -l -I '&lt;i&gt;&lt;b&gt;OLD&lt;/b&gt;&lt;/i&gt;' | xargs sed -i -e 's/&lt;i&gt;&lt;b&gt;OLD&lt;/b&gt;&lt;/i&gt;/&lt;i&gt;&lt;b&gt;NEW&lt;/b&gt;&lt;/i&gt;/g'&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;UPDATE 2009-02-09&lt;/b&gt;&lt;br /&gt;I was browsing at Barnes and Noble over the weekend and the O'Reilly Pocket Guide to Grep mentioned an &lt;code&gt;--exclude-dir&lt;/code&gt; option.  I looked at the man page again and it's there too.  I even googled again and found it right away.  How did I miss it?  That makes things much easier.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;# To grep for things excluding subversion .svn directories&lt;br /&gt;fgrep --exclude-dir='.svn' -rI &lt;i&gt;&lt;b&gt;PATTERN&lt;/b&gt;&lt;/i&gt; *&lt;br /&gt;&lt;br /&gt;# To replace the same (using sed), only in files that grep matched, only in non .svn files.&lt;br /&gt;fgrep --exclude-dir='.svn' -rIl '&lt;i&gt;&lt;b&gt;OLD&lt;/b&gt;&lt;/i&gt;' * | xargs sed -i -e 's/&lt;i&gt;&lt;b&gt;OLD&lt;/b&gt;&lt;/i&gt;/&lt;i&gt;&lt;b&gt;NEW&lt;/b&gt;&lt;/i&gt;/g'&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;UPDATE 2010-03-31&lt;/b&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;# To find whole words, using an even briefer syntax for&lt;br /&gt;# chaining the output of grep into the input for sed.&lt;br /&gt;# Taken from a shell script that sets oldWord and&lt;br /&gt;# newWord, but you get the idea:&lt;br /&gt;sed -i -e "s/\&lt;$oldWord\&gt;/$newWord/g" $(fgrep --exclude-dir='.svn' -wrIl "$oldWord" *)&lt;/code&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-5927363527166996553?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/5927363527166996553/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=5927363527166996553' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5927363527166996553'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5927363527166996553'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2009/02/new-favorite-findreplace-bash-shell.html' title='New Favorite Find/Replace (bash) shell commands'/><author><name>GlenPeterson</name><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-5689812823462613341.post-5626360381085305844</id><published>2008-07-30T07:54:00.000-07:00</published><updated>2011-08-24T10:05:18.660-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='data types'/><category scheme='http://www.blogger.com/atom/ns#' term='datatype'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><title type='text'>Hibernate automatic POJO and mapping file generation from MySQL</title><content type='html'>Let's face it.  Updating Hibernate .hbm.xml and .java files for every database change is phenomenally boring and a waste of time.  It's the sort of thing that makes people switch to Ruby/ActiveRecord.  I have recently undergone an effort to get the Hibernate reverse engineering tools to generate .hbm.xml and .java files as close as possible to what I want.  In theory that should allow me to add new methods to the files in my source tree and still generally get hands-free updates by running the hibernate updater and diffing/patching.  In practice, getting Hibernate to give me exactly what I want is very hard, so in the first week I moved from 3% of my changes being automatically applied to 80%.  In the next month, I moved up to about 95% and it will probably slowly continue to improve.&lt;br /&gt;&lt;br /&gt;The basic outline of the process is simple:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Save the generated files from the last time I ran the generator: &lt;code&gt;mv generatedSchema generatedSchema.1&lt;/code&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;run the generator again and diff the files it generates this time against the ones it generated previously&lt;/li&gt;&lt;br /&gt;&lt;li&gt;patch those changes to my source tree.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;I use the ant task, not the Eclipse integration because I'm in love with the command line.   Assuming that you have read and absorbed all the &lt;a href="http://docs.jboss.org/tools/2.1.0.Beta1/hibernatetools/html/reverseengineering.html" target="_blank"&gt;documentation about hibernate reverse-engineering&lt;/a&gt;, I'll list a few key points from my hibernate.reveng.xml.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;hibernate.reveng.xml&lt;/h3&gt;&lt;br /&gt;For some reason BIGINT was being reverse-engineered as Long (the object) instead of long (the primitive).  I'm not so sure whether my VARCHAR mapping is a good idea...&lt;br /&gt;&lt;br /&gt;I included an example of renaming a column, and a much more useful example of naming a parent-child relationship.  The syntax of the foreign-key mapping was not obvious to me, so I hope you find this example helpful.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;&amp;lt;hibernate-reverse-engineering&amp;gt;&lt;br /&gt; &amp;lt;type-mapping&amp;gt;&lt;br /&gt;  &amp;lt;sql-type jdbc-type="BIGINT" hibernate-type="long" not-null="true"&amp;gt;&amp;lt;/sql-type&amp;gt;&lt;br /&gt;  &amp;lt;sql-type jdbc-type="VARCHAR" length="1" hibernate-type="char" not-null="true"&amp;gt;&amp;lt;/sql-type&amp;gt;&lt;br /&gt; &amp;lt;/type-mapping&amp;gt;&lt;br /&gt; &amp;lt;table-filter match-catalog="mydb" match-name="temp_user" exclude="true"/&amp;gt;&lt;br /&gt; &amp;lt;table catalog="mydb" name="company"&amp;gt; &lt;br /&gt;  &amp;lt;column name="identifier_c" property="coIdentifierC" type="string" /&amp;gt;&lt;br /&gt; &amp;lt;/table&amp;gt;&lt;br /&gt; &amp;lt;table catalog="mydb" name="tree"&amp;gt; &lt;br /&gt;  &amp;lt;foreign-key constraint-name="tree_ibfk_parent"&amp;gt;&lt;br /&gt;   &amp;lt;many-to-one property="parent" /&amp;gt;&lt;br /&gt;   &amp;lt;set property="children" /&amp;gt;&lt;br /&gt;  &amp;lt;/foreign-key&amp;gt;&lt;br /&gt; &amp;lt;/table&amp;gt;&lt;br /&gt;&amp;lt;/hibernate-reverse-engineering&amp;gt;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;pojo/*.ftl Files&lt;/h3&gt;&lt;br /&gt;In addition to these customizations I've made extensive use of the Freemarker POJO templates.  I made a number of very minor changes for personal preference, but one very important change for diff and patch to work.  I added a two-line comment before and after the fields (in PojoFields.ftl) and the get/sets (in PojoPropertyAccessors.ftl):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;// Begin Auto-generated Accessors (by myGenerator project)&lt;br /&gt;// Manual editing is STRONGLY DISCOURAGED below this point!&lt;br /&gt;&lt;br /&gt;...&lt;br /&gt;&lt;br /&gt;// End Auto-generated Accessors (by myGenerator project)&lt;br /&gt;// Manual editing is OK below this point&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Having two lines of comments is very important because:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Hibernate reverse-engineering tools like to shuffle the field order even when you changed one of the related tables&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Diff uses the previous 2-3 lines for comparison to confirm it's found the right place in the file and a two-line comment plus one blank line makes it very happy.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;diff and patch&lt;/h3&gt;&lt;br /&gt;After running the hibernate ant task (and tweaking the output with some canned perl regular expression replacements), I run diff (telling it to ignore the timestamped Hibernate comment):&lt;br /&gt;&lt;pre&gt;&lt;code&gt;diff -I '^\(//\|&amp;lt;!--\) Generated ' -druN generatedSchema.1/ generatedSchema/ &gt;patch.txt&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Often I check what changes were made:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;less patch.txt&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;From my source tree, I do a dry-run of patching the differences.&lt;br /&gt;&lt;pre&gt;&lt;code&gt;cd WEB-INF/src&lt;br /&gt;patch --dry-run -l -p1 &lt;../../../../../scorecardSchemaGen/patch.txt&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;If there are any failures:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;patch those files manually performing any cleanup necessary to make patches more likely to succeed automatically next time&lt;/li&gt;&lt;br /&gt;&lt;li&gt;copy the new &lt;i&gt;generated&lt;/i&gt; version of the &lt;i&gt;manually patched file&lt;/i&gt; from generatedSchema to generatedSchema.1&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Re-run the diff command so that it will find no differences in the generated version(s) of the manually patched file(s).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Perform another dry-run patch&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Repeat until there are no errors in the dry-run&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;Now there will be no failures when you patch for real:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;cd WEB-INF/src&lt;br /&gt;patch -l -p1 &lt;../../../../../scorecardSchemaGen/patch.txt&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Finally, clean up the backup files made by patch:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;rm company/project/section/*.orig&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;MySQL Conventions&lt;/h3&gt;&lt;br /&gt;When choosing optimal types and variable names for the columns in MySQL, one should take into account how Hibernate will reverse-engineer it so as to make sure the resulting files are as close as possible to what you want.  In addition,  MySQL has no "check" function and the MySQL BOOLEAN becomes TINYINT(1) which allows values from -128 to 127.  Here are the subset of data types that are really easy to use with all tools and provide some data integrity by eliminating possibilities for meaningless values.&lt;br /&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;b&gt;Boolean:&lt;/b&gt; Start each name with is_ whenever it makes sense - the code reads more like English that way.  Use positive column/variable names.  E.g. instead of preventUpdate, isNotDirty, or doesntHaveErrors, use allowUpdate, isClean, or isErrorFree.   Declare in MySQL as:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;is_something bit(1) NOT NULL DEFAULT FALSE&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;Note: Java/Hibernate like this but PHP and some other tools may not.  Here are two work-arounds for other tools:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;select if(is_something, 1, 0) from my_table;&lt;/code&gt;&lt;/pre&gt;or&lt;br /&gt;&lt;pre&gt;&lt;code&gt;select if(is_something, true, false) from my_table;&lt;/code&gt;&lt;/pre&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Integers:&lt;/b&gt;Try to only use MySQL (Java) types: INT (int), BIGINT (long), and maybe TINYINT (byte).  The size (the number in parenthesis after the type in MySQL) has no effect that I can see.  The SMALLINT (short) data type requires a trade-off between saving space and limiting possible values and ease of use in Java.  It requires too much type casting for my taste when working with it in Java.  You even have to use casts for situations like:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;i = (short) (i + 1);&lt;br /&gt;myObject.setShort( (short) 0 );&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Unless you require a smaller data type for data integrity or for space considerations, It's easiest to stick with INT.  Stay away from MySQL's MEDIUMINT: It's a 3-byte integer which is probably why Hibernate does not recognize it.  64-bit (8-byte) integers like BIGINT (long) didn't used to even be available on hardware.  The manuals don't make any note of whether they are supported by hardware or not.  Anyone know?&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Strings:&lt;/b&gt; Use VARCHAR with the appropriate length.  That way, it's easier to support utf-8 or other variable-width character encodings if you want to.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Naming:&lt;/b&gt; I started off naming my columns description_c and year_i, but ended up dropping the type-specific suffix because Java and MySQL are both type-safe anyway and the extra letter made the code slightly harder to read.  I still use the word _date for dates because that seems to make the code easier to read (to my eye).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;b&gt;Foreign Keys:&lt;/b&gt; MySQL uses a separate constraint name and foreign key name. The constraint name has to be unique within the database so it's automatically assigned the tablename_ibfk_1 where the "1" is a sequence number incremented for each new constraint. If I drop and recreate a table in development, but merely add a new column in test, then the sequence numbers could be different in different environments and issuing a command to change a constraint in one environment will change a *different* constraint in another environment. This would be really bad. &lt;br /&gt;&lt;br /&gt;To avoid this, I use the following template to declare a foreign key with MySQL InnoDB (the default database, MYISAM ignores the "on-delete cascade" clause): &lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;CREATE TABLE process ( &lt;br /&gt;    ... &lt;br /&gt;    company_id BIGINT UNSIGNED NOT NULL, &lt;br /&gt;    constraint `process_fk_company` FOREIGN KEY (company_id) &lt;br /&gt;        REFERENCES company(id) on delete cascade, &lt;br /&gt;    ... &lt;br /&gt;) ENGINE=InnoDB;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That way I'm sure that the constraint name (which is what I have to reference in the Hibernate file) will not change.  It's confusing because MySQL syntax mixes foreign key column names and constraint names.  E.g. &lt;code&gt;DROP FOREIGN KEY &lt;i&gt;constraint-name&lt;/i&gt;&lt;/code&gt;.  To find what constraint name is used for a given foreign key, use the SHOW CREATE TABLE command. Constraint names in mysql look like `tablename_ibfk_1`&lt;/li&gt;&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-5626360381085305844?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/5626360381085305844/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=5626360381085305844' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5626360381085305844'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5626360381085305844'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2008/07/mysql-data-types-and-naming-for-use.html' title='Hibernate automatic POJO and mapping file generation from MySQL'/><author><name>GlenPeterson</name><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-5689812823462613341.post-6652975467191701242</id><published>2008-03-22T07:37:00.000-07:00</published><updated>2011-08-24T10:06:04.138-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='web application'/><category scheme='http://www.blogger.com/atom/ns#' term='rest'/><category scheme='http://www.blogger.com/atom/ns#' term='form bean'/><category scheme='http://www.blogger.com/atom/ns#' term='session'/><category scheme='http://www.blogger.com/atom/ns#' term='Struts'/><title type='text'>RESTful Form Beans with Struts 1</title><content type='html'>&lt;h3&gt;What Struts is Good At&lt;/h3&gt;Struts works great for view-only pages, particularly when you need to build those pages from the database.  But there are a number of subtle issues with update pages which can probably best be solved with a RESTful solution.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;REST and state&lt;/h3&gt;The database holds persistent state.  My understanding is that one of the concepts of REST is that there is rarely a reason to cache that state on the server and/or the browser/client when it's already stored semi-permanently in the database.  It seems that the session on the server only needs to hold state that doesn't belong in the database.  That would be data specific to what the user is currently viewing or doing.  Some examples are:&lt;br /&gt;&lt;ul&gt;&lt;li&gt;Logging in - that is usually (and probably best) accomplished by establishing  a session on the server and storing a cookie on the client containing a token that the client's browser sends with every request instead of resending their user-id and password.  The token tells the server which session is associated with that user.  That way you aren't repeatedly sending the user ID and password over the wire.  If someone eavesdrops on the session and steals the token, it expires after a certain period so that an attacker will (theoretically) not be able to log back in.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Other session-specific state.  This is for global-controls analogous to the caps-lock key on the keyboard.  All the other controls (keys) are still available to you, but their behavior is slightly different because the state (caps-lock) has changed.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;h3&gt;Session Timeout&lt;/h3&gt;Sessions need to time out eventually (usually in 30 minutes or so for security reasons).  End-users find this incredibly annoying.  A little JavaScript can soften the blow:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;setTimeout(alert('Your session will expire (silently) in 2 minutes...'), 1680000);&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;This is still annoying, and not very secure, since the point of having sessions expire is to prevent an unauthorized person from accessing a still-valid session.  To a hacker, this is essentially a pop-up, advertising: "You can hijack someone's session if you act now!".&lt;br /&gt;&lt;br /&gt;If you're application is mostly RESTful, storing only the login information on the server, you can pop up a login window to allow them to log-back in without loosing whatever they are working on.  This is secure because the user ID and password are required.  Also, the user does not have to respond within a certain period because they are effectively logging in and creating a new session.&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;if (confirm('Your session has expired.  Log back in to continue your work?')) {&lt;br /&gt;    loginWin = window.open("pre_login.do");&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Since you do not rely on server side state or cookies for anything but the login, the user can get a form and fill it out in one session, then submit it in a completely different session, so the login pop-up works.  If you have a little bit of state on the server and you are clever, you can enhance this solution to send a setting or two from your server-side state from the old session to the login page so that the new session will match the old.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Struts Problem 1: Clearing session-scoped Form Beans&lt;/h3&gt;Form beans are session scoped by default.  A user GETting the form in preparation for POSTing a new record will see whatever data was left over in the form bean from the last record they edited.  You need to work around this by figuring out when the user is going to add a new record and clearing the fields in the form.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Struts Problem 2: Session expiration with session-scoped form beans&lt;/h3&gt;Sooner or later the session will time out and the form bean will be completely lost.  Writing an Action and pairing it with a log-back-in JavaScript function becomes prohibitively ugly from a maintenance and security standpoint if even a few different fields are required for a few different screens.  As far as I can see, there is no reasonable work-around for this scenario, which is why REST is so useful.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Struts Problem 3: Request-scoped Form Beans and the Struts validator&lt;/h3&gt;When there is a failed validation on a form bean, the model isn't invoked, so only the fields that the HTML form posted are filled in from the request.  Any extra data (breadcrumbs, related records from the database, or other variable text that provides context for the update taking place) is lost unless that data was also built into fields in the form &amp;lt;input type="hidden"... /&amp;gt;.  But REST is supposed to make things simpler and more lightweight.  Building breadcrumbs into URLs requires escaping any special characters, increases both the request and response sizes, and is generally a very ugly solution.  Storing these fields in client side cookies is possible too, but very complicated.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;A RESTful solution&lt;/h3&gt;REST precludes any unnecessary server state and as mentioned above, the validator is not RESTful.  So you'll need to declare actions: &lt;pre&gt;&lt;code&gt;&amp;lt;action ... validate="false" scope="request" /&amp;gt;&lt;/code&gt;&lt;/pre&gt;All validation must take place on the action, not the form bean (see Problem 3, above).&lt;br /&gt;&lt;br /&gt;Query the database for all the data to build the screen with each request.  Any extra load on the database is small because this amounts to making extra queries only for failed validations.  The catch is that you need to keep track of whether any data on the form bean is dirty (user has changed it) or clean.  Remember, a user can clear a field, so just checking for null or 0 doesn't mean that the data is unchanged.&lt;br /&gt;&lt;br /&gt;Fortunately, we already know whether the form data is dirty or not by the method the user used on the request.&lt;br /&gt;&lt;br /&gt;&lt;dl&gt;&lt;dt&gt;GET without a database record number&lt;/dt&gt;&lt;dd&gt;Clean: Show the user a blank form so that they can add (POST) a new record.&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;GET with a database record number&lt;/dt&gt;&lt;dd&gt;Clean: Show the user the current values of the record from the database so that they can update (PUT) the existing record.&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;PUT&lt;/dt&gt;&lt;dd&gt;Dirty.  If input passes validation, update existing record with changes.  If it doesn't pass, return the dirty form plus any "extra" data from the DB for building the screen.&lt;/dd&gt;&lt;br /&gt;&lt;dt&gt;POST&lt;/dt&gt;&lt;dd&gt;Dirty.  If input passes validation, check that the record doesn't exist already as the user can resubmit a post by pressing the back button.  This resubmitted post should either be ignored or treated as a put (see above) since the record already exists.  If it is a valid POST add a new record to the database.  If the input is invalid, return the dirty form plus any "extra" data from the DB for building the screen.&lt;/dd&gt;&lt;/dl&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; HTML only supports GET (all links) and POST (only in &amp;lt;form method="post"...&amp;gt;).  Not surprisingly, most browsers don't support the other methods mentioned in the HTTP spec either.  I'm sure people are signing petitions calling for an overhaul of HTML and all browsers, but it's really not an issue to send an extra parameter to designate the "method" of each request.  Your application may require some methods that HTTP does not provide, in which case you'd have to send a "method" parameter anyway.&lt;br /&gt;&lt;br /&gt;In order for the extra data from the DB to be refreshed on a request-scoped form, the action still needs to be invoked.  Something like the following would probably be a good template for Actions:&lt;br /&gt;&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Check that the user is logged in.  This might better be done in a servlet, but it needs to be done.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Check for required parameters (like the unique identifier of the record you are editing).  Send failures to a Not Authorized page.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Check that the user is authorized to do what they are trying to do.  Send failures to a Not Authorized page.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Validate any other parameters that would make the request just invalid - essentially assertions that your UI is working properly go here.  Send failures to a Not Authorized page.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Create your ActionMessages object to hold user-friendly validation errors&lt;/li&gt;&lt;br /&gt;&lt;li&gt;User input validation.  Accumulate friendly error messages in your errors object.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If there are no errors and the method is POST or PUT, perform update.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;If the form is not dirty - fill form fields with data from database.  Build or rebuild other data for the screen to show the user and put it on the form bean.  Or you might create a new form bean and forward to a different screen after a successful update.&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;&lt;br /&gt;That list is a starting point, but I always code Actions to minimize the number and extent of database queries, so that sometimes shuffles the later stages in that list.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Advantages:&lt;/h3&gt;&lt;ul&gt;&lt;li&gt;You don't have to worry about clearing old data from the form bean (you get a new form bean with each request).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You don't need to encode any "extra" data (breadcrumbs, etc.) into the form to make it get included in the request (it's reread from the database).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can log back in elegantly, without loosing what you were working on (all necessary data is reread from the database or carried as form data in the request).&lt;/li&gt;&lt;br /&gt;&lt;li&gt;You can use any objects you want for building the screen - you are not limited to Strings and other primitive types.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;This requires less code than any of the other ways I tried solving these issues.&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Disadvantages&lt;/h3&gt;You can't use the Struts validator.  All your validation must be done in the action.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;So far, this is the neatest solution I can come up with to all of the above issues.  And it can be as RESTful as the design of your application allows (uses a minimum of server-side state).  It fails to leverage the struts validator, but I was never eager to perform that much XML configuration anyway.  Struts 1 has been around for a long time.  People ask much more of a web application now than they used to.  Maybe Struts 2 has some easier shortcuts for these same issues?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-6652975467191701242?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/6652975467191701242/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=6652975467191701242' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/6652975467191701242'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/6652975467191701242'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2008/03/searching-for-restful-solution-with.html' title='RESTful Form Beans with Struts 1'/><author><name>Glen Peterson</name><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-5689812823462613341.post-5828601234060540027</id><published>2008-01-25T12:17:00.000-08:00</published><updated>2011-08-31T07:31:27.014-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='JSP'/><category scheme='http://www.blogger.com/atom/ns#' term='Java'/><category scheme='http://www.blogger.com/atom/ns#' term='cygwin'/><category scheme='http://www.blogger.com/atom/ns#' term='Log4J'/><category scheme='http://www.blogger.com/atom/ns#' term='character set'/><category scheme='http://www.blogger.com/atom/ns#' term='Tomcat'/><category scheme='http://www.blogger.com/atom/ns#' term='Hibernate'/><category scheme='http://www.blogger.com/atom/ns#' term='Struts'/><category scheme='http://www.blogger.com/atom/ns#' term='Emacs'/><title type='text'>Converting a Web Application to UTF-8</title><content type='html'>&lt;h4&gt;Overview:&lt;/h4&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://java.sun.com/developer/technicalArticles/Intl/HTTPCharset/"&gt;Java-Web-Application Character Encoding Overview&lt;/a&gt; - this is a great overview.  It answers the questions, "why bother" and "what's involved".&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8"&gt;UTF-8 and Unicode FAQ for Unix/Linux&lt;/a&gt; - A more detailed overview of Unicode in general.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.unicode.org/versions/Unicode4.0.0/ch03.pdf"&gt;The Unicode Standard: Conformance&lt;/a&gt; (PDF) - See section 3.9 Unicode Encoding Forms.  Table 3-5 explains UTF-8 encoding at a binary level, which is really where you have to go to understand it.  Table 3-4 is UTF-16.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.columbia.edu/kermit/utf8.html"&gt;UTF-8 Sampler&lt;/a&gt; a great collection of text in many different languages.&lt;/li&gt;&lt;li&gt;&lt;a href="http://www.unicode.org/charts/PDF/Unicode-5.0/U50-10900.pdf"&gt;Phonecian (Range 10900-1091F)&lt;/a&gt; (PDF) - Some high sample-characters&lt;/li&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;MySQL&lt;/h4&gt;MySQL supports only characters from the Basic Multilingual Plane (BMP or Plane 0) which is U+0000 - U+FFFF.  The Plane 0 characters all take 2-bytes in UCS-2 or 1-3 bytes in UTF-8.&lt;br /&gt;&lt;br /&gt;Source: &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/charset-unicode.html"&gt;MySQL 5.0 Reference Manual 8.1.8. Unicode Support&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IMPORTANT:&lt;/b&gt; MySQL's default collation for each characterset (not just utf8) ends with a "_ci" which stands for Case Insensitive.  This is probably just what you want for everything *except* passwords digests (I'm assuming no-one stores plaintext passwords in databases any more).  If a 43 character Base64 digest were stored in a case-insensitive column, it would be roughly 5 billion times more vulnerable to hash collisions.  That's bad.  Using utf8 for user IDs increases security by allowing tens of thousands of characters in the user ID instead of the mere 100 or so safe ASCII characters.  Decide whether you want johnDoe and JohnDoe to log into the same account and choose your collation for the user ID column accordingly.&lt;br /&gt;&lt;br /&gt;As of this writing, the only case-sensitive utf8 collation is utf8_bin.  To find out what collations your installation supports issue:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;show collation like 'utf8%';&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Don't use utf8_bin for fields that you will sort for the user because letters A-Z come before a-z in this collation, but it is the only collation to use for utf8 case sensitivity.  Specify character set and collation explicitly on your password hash column (and optionally the user ID column) so that you are free to change the character set of the database and tables in the future without accidentally locking any users out of your system:&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;user_identifier varchar(30) character set 'utf8' not null;&lt;br /&gt;password_hash varchar(44) character set 'utf8' collate 'utf8_bin' not null;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;More information on character sets and collations is available on the &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/charset-general.html" target="_blank"&gt;Character Sets and Collations in General&lt;/a&gt; page of the MySQL manual.&lt;br /&gt;&lt;br /&gt;When you change the character set of a table in MySQL, every column is explicitly set to take the *old* character set - even if the columns in your tables all previously used whatever the default was! You will need to issue ALTER TABLE statements for every column, or dump the database to file and replace all the character set references. I did the latter with three text replacements:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;mysqldump -p -u root --single-transaction my_db &gt; ~/backup_$(date --iso-8601).sql&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Change table-specific character sets (always present)&lt;br /&gt;Replace:&lt;code&gt;" DEFAULT CHARSET=latin1"&lt;/code&gt;&lt;br /&gt;With:&lt;code&gt;" DEFAULT CHARSET=utf8"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Remove any column-specific character sets (less common)&lt;br /&gt;Replace:&lt;code&gt;" character set latin1"&lt;/code&gt;&lt;br /&gt;With:&lt;code&gt;""&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Change all char's to varchar's because char has to store 3 bytes/character in utf8 while varchar can store 1 for short characters&lt;br /&gt;Replace:&lt;code&gt;" char("&lt;/code&gt;&lt;br /&gt;With:&lt;code&gt;" varchar("&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;You might want to search for "latin1" and see if anything else needs to be changed.  Then follow the instructions above and everything will be fine.  Hopefully this will save someone else a day's worth of trouble.&lt;br /&gt;&lt;br /&gt;Change configuration settings in the system-wide /etc/mysql/my.cnf file (or .my.cnf or my.ini file on Windows):&lt;br /&gt;&lt;br /&gt;&lt;pre&gt;&lt;code&gt;[client]&lt;br /&gt;default-character-set=utf8&lt;br /&gt;[mysql]&lt;br /&gt;default-character-set=utf8&lt;br /&gt;[mysqld]&lt;br /&gt;default-character-set=utf8&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Restart MySQL.&lt;br /&gt;&lt;br /&gt;Login to MySQL and &lt;code&gt;use&lt;/code&gt; the database in question.  Enter the following:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;alter database db_name character set utf8;&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Test the settings:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;mysql&amp;gt;show variables;&lt;br /&gt;&lt;br /&gt;character_set_client            | utf8&lt;br /&gt;character_set_connection        | utf8&lt;br /&gt;character_set_database          | utf8&lt;br /&gt;character_set_filesystem        | binary&lt;br /&gt;character_set_results           | utf8&lt;br /&gt;character_set_server            | utf8&lt;br /&gt;character_set_system            | utf8&lt;br /&gt;character_sets_dir              | C:\Program File&lt;br /&gt;collation_connection            | utf8_general_ci&lt;br /&gt;collation_database              | utf8_general_ci&lt;br /&gt;collation_server                | utf8_general_ci&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Log out and re-load the database:&lt;br /&gt;&lt;code&gt;mysql -p -u root my_db &amp;lt;~/backup_$(date --iso-8601).sql&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Cygwin&lt;/h4&gt;&lt;a href="http://www.okisoft.co.jp/esc/utf8-cygwin/"&gt;UTF-8 Cygwin&lt;/a&gt; seems to add support for Japanese characters, but I can't tell if it supports UTF-8 up to U-FFFF or higher.  Maybe I just don't have the fonts on my system to see it!&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Log4J&lt;/h4&gt;&lt;code&gt;log4j.appender.appenderName.encoding=UTF-8&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Source: a comment in email from&lt;a href="http://don.dwoske.com/"&gt;Don Dwoske&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Hibernate:&lt;/h4&gt;In hibernate.cfg.xml, set&lt;code&gt;hibernate.connection.url&lt;/code&gt; to:&lt;br /&gt;&lt;code&gt;jdbc:mysql://host:port/db?autoReconnect=true&amp;amp;amp;useUnicode=true&amp;amp;amp;characterEncoding=UTF-8&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Source:&lt;a href="http://www.whirlycott.com/phil/2006/05/28/more-utf-8-head-thumping-with-hibernate-3/"&gt;More UTF-8 head-thumping with Hibernate 3&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Struts:&lt;/h4&gt;&lt;a href="http://ianpurton.com/struts-utf-8-and-form-submissions/"&gt;http://ianpurton.com/struts-utf-8-and-form-submissions/&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;JSP:&lt;/h4&gt;Add the following attributes to the&lt;code&gt;&amp;lt;%@page %&amp;gt;&lt;/code&gt; tag:&lt;br /&gt;&lt;code&gt;pageEncoding="UTF-8" contentType="text/html; charset=UTF-8"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Source:&lt;a href="http://wiki.apache.org/tomcat/Tomcat/UTF-8"&gt;Tomcat/UTF-8&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Tomcat:&lt;/h4&gt;In&lt;code&gt;$CATALINA_HOME/conf/server.xml&lt;/code&gt; add the following attribute to the&lt;code&gt;&amp;lt;Connector&amp;gt;&lt;/code&gt; tag:&lt;br /&gt;&lt;code&gt;URIEncoding="UTF-8"&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Source:&lt;a href="http://wiki.apache.org/tomcat/Tomcat/UTF-8"&gt;Tomcat/UTF-8&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Java&lt;/h4&gt;In your Java code you need to replace:&lt;br /&gt;&lt;code&gt;s.getBytes()&lt;/code&gt;&lt;br /&gt;with:&lt;br /&gt;&lt;code&gt;s.getBytes("UTF-8")&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Source: a comment in email from&lt;a href="http://don.dwoske.com/"&gt;Don Dwoske&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;b&gt;IMPORTANT:&lt;/b&gt; You *especially* need to convert bytes that are being sent to your SHA message digest algorithm because otherwise any non-latin characters will come out as "invalid", and they will all have the same hash!&amp;#160; Perform the UTF-8 conversion above, and you will have no problems.&amp;#160; All the ASCII characters keep their old encoding, so existing users' passwords still work.&amp;#160; Not sure what happens to high ASCII characters.&amp;#160; If that's an issue for you, you'll have to check.&lt;br /&gt;&lt;br /&gt;Java seems to support the 16 Supplimentary Planes that take an extra 16-bit character to represent.&lt;br /&gt;   Source:&lt;a href="http://jcp.org/en/jsr/detail?id=204"&gt;JSR 204: Unicode Supplementary Character Support&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;MySQL only supports the BMP (Basic Multilingual Plane or "Plane 0") so I'm going to have to intercept these "high" UTF-8 characters in Java and put the replacement character in the database instead:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;safeString = inString.replaceAll("[^\u0000-\uFFFF]", "\uFFFD");&lt;/code&gt;&lt;br /&gt;&lt;br /&gt;Note: U+FFFD is � - the Replacement Character "used to represent an incoming character whose value is unknown or unrepresentable in Unicode".  Source:&lt;a href="http://www.unicode.org/charts/PDF/UFFF0.pdf"&gt;Specials Range FFF0-FFFF&lt;/a&gt; (PDF).  This shows up as a blank box in Internet Explorer (it's supposed to be a white question-mark in a black diamond).  I'm sure IE will support it eventually.&lt;br /&gt;&lt;br /&gt;I made a routine to clean all my string inputs from the user:&lt;br /&gt;  &lt;br /&gt;&lt;pre&gt;&lt;code&gt;private static final Pattern highUnicodePattern = Pattern.compile("[^\u0000-\uFFFF]");&lt;br /&gt;&lt;br /&gt;public static String cleanInputStr(String inStr) {&lt;br /&gt;    if (inStr == null) {&lt;br /&gt;        return null;&lt;br /&gt;    }&lt;br /&gt;    String trimmedStr = inStr.trim();&lt;br /&gt;    if (trimmedStr.length()&lt; 1) {&lt;br /&gt;  return null;&lt;br /&gt; }&lt;br /&gt;&lt;br /&gt; Matcher highUnicodeMatcher = highUnicodePattern.matcher(trimmedStr);&lt;br /&gt; return highUnicodeMatcher.replaceAll("\uFFFD");&lt;br /&gt;}&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As just shown, I filter any characters above U+FFFF, so I don't have to worry about characters that take more than 2 bytes of storage.  I know what you are saying: "But, U+FFFF takes THREE bytes to store in UTF-8, wouldn't it take more than 2 bytes in UTF-16?"  Maybe, but Java stores it as 2 bytes, so the Java String.length() still works.  If you support characters above U+FFFF, you'll need to replace all your string-length checks:&lt;br /&gt;&lt;br /&gt;&lt;code&gt;int charCount = testString.length();&lt;/code&gt;&lt;br /&gt;with this:&lt;br /&gt;&lt;pre&gt;&lt;code&gt;int oldCount = testString.length();&lt;br /&gt;int charCount = testString.codePointCount(0, oldCount);&lt;/code&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Source:&lt;a href="http://java.sun.com/mailers/techtips/corejava/2006/tt0822.html"&gt;Strings - Core Java Technologies Technical Tips&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;h5&gt;Notes:&lt;/h5&gt;There are also some characters that are affected by the neighboring characters (accents and such).  I'm not thoroughly convinced that I can count characters by counting the number of times I press the arrow keys.&lt;br /&gt;&lt;br /&gt;Even with the Java character-counting hack above, I thought for a while that JavaScript in FireFox and Safari counted more characters than Java in certain circumstances.&amp;#160; Then I discovered that I had line-feeds in my test data, and they were being counted by Java, but not by JavaScript (probably because they wouldn't render in the HTML) - so this "problem" wasn't related to UTF-8 after all!&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Emacs&lt;/h4&gt;On Windows, GNU Emacs supports all the Unicode characters I could test on it of the box and XEmacs doesn't support unicode at all.  For the past 8 years I've been using XEmacs because that's what I started with and I never saw a significant enough difference between GNU and X to bother switching.  Ladies and Gentlemen, I am making the switch!&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Note:&lt;/b&gt; Once you get your .emacs file switched over, make a backup or be careful not to open XEmacs, because it will *nuke* your old .emacs file and leave a new version converted to XEmacs in its place!  That alone is so annoying that I don't ever want to switch back.&lt;br /&gt;&lt;br /&gt;&lt;code&gt;describe-current-coding-system&lt;/code&gt; is a handy function to see what emacs thinks it's doing!  Emacs UTF-8 support is not 100% at the time of this writing (4-byte characters sometimes flicker and change), but it does pretty well.  The GNU Emacs site says that even better UTF-8 support is planned for the next release.&lt;br /&gt;  &lt;br /&gt;&lt;h4&gt;Fixed-width Windows Fonts&lt;/h4&gt;I downloaded Everson Mono Unicode and it really supports every character, but I found it unacceptably hard to read the English/code in Emacs when I made it small enough for coding.&lt;br /&gt;&lt;br /&gt;Courier New and Lucida Console seem the best of the ones that come with Windows XP English.  They are both missing big chunks of UTF-8, but they have better support than any of the others I found (at least they show some Greek, Russian, Turkish, Arabic, Hebrew, Chinese, Japanese, and Korean).  I picked Courier to use with Emacs (does anything else really matter?) because it's smaller and seems a little easier to read on the screen to me.  Maybe I'm just used to it.&lt;br /&gt;&lt;br /&gt;&lt;h4&gt;Humor&lt;/h4&gt;From xkcd: &lt;a href="http://xkcd.com/380/"&gt;Emoticon&lt;/a&gt; (read the mouse-over)&lt;br /&gt;&lt;br /&gt;&lt;a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/"&gt;&lt;img alt="Creative Commons License" style="border-width: 0pt;" src="http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-5828601234060540027?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/5828601234060540027/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=5828601234060540027' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5828601234060540027'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5828601234060540027'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2008/01/mysql-utf8-character-set-conversion.html' title='Converting a Web Application to UTF-8'/><author><name>GlenPeterson</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-5689812823462613341.post-5788200026658919459</id><published>2007-12-12T08:35:00.000-08:00</published><updated>2008-03-26T19:47:32.601-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='requirements'/><category scheme='http://www.blogger.com/atom/ns#' term='users'/><category scheme='http://www.blogger.com/atom/ns#' term='software development'/><title type='text'>Make Users Happy by Ignoring Requirements</title><content type='html'>People talk about the system they want you to build in the language of what they have now.  It is natural to not want to give anything up.  Writing web applications, I hear over and over that people want to be able to sort by every column, have column headings print out on every page, filter by any value...  That's Microsoft Excel - and it is a truly incredible tool for doing all of those things.&lt;br /&gt;&lt;br /&gt;On the other hand, most business systems (as opposed to software systems) are dependent on communication to make people with different skills work together.  In general, if you can meet those needs, you can always add a CSV download later if people still want to "slice and dice" data in Excel.  I have had consistently good results from ignoring the "Excel on the Web" requirements and asking my clients instead to describe the people, the tasks, and the communication involved in their processes.&lt;br /&gt;&lt;br /&gt;I try to break a process into the sets of tasks that each person (or type of user) needs to perform.  The usual goal is to show someone only the data they really need to perform their task(s), and give them the tools they need to accomplish that task on the same screen.  Sometimes, similar tasks can share a screen or multiple screens might be required for a task, but the goal is to compartmentalize and specialize the business system into units that use common sets of data and functionality.&lt;br /&gt;&lt;h3&gt;Example 1: Ignoring User Requirements&lt;/h3&gt;Below are the requirements I was given for a year-long project:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A list of the documents&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Columns showing which one's are ready, which ones are late, and who is responsible for each&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Sort by any column&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Filter by any column&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Print out with column-headings on the top of every page...&lt;/li&gt;&lt;br /&gt;&lt;/ol&gt;Sounds like Excel, no?  I only actually met requirement #1, yet the client was delighted.  Why?  Because the list of Excel features I was given tipped me off that the requirements were not well thought-through.&lt;br /&gt;&lt;br /&gt;Here is a list of &lt;span style="font-style: italic;"&gt;real&lt;/span&gt; requirements that a team of us had to dig for:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;A draft document needs to be written by an analyst&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The document must be reviewed and approved&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Financial reports need to be produced by a system&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Financial reports need to be approved by a person&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The documents and financials need to be combined into various client reports&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The client reports need to be printed&lt;br /&gt;&lt;/li&gt;&lt;li&gt;The client reports need to be mailed&lt;br /&gt;&lt;/li&gt;&lt;li&gt;A manager needs to keep track of all of the above&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Developers understand systems and the benefits and limitations of your technology. Businesspeople know what they need to accomplish, but are often dimly aware of the business systems they use to achieve their goals.  Bridging that communication gap is the hardest part of writing good software.&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Example 2: Ignoring Systems Requirements&lt;/h3&gt;Stated requirements for a reporting web site that I worked on (on-and-off) for 6 years:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;high availability&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Load balancing&lt;br /&gt;&lt;/li&gt;&lt;li&gt;portal&lt;br /&gt;&lt;/li&gt;&lt;li&gt;pluggable, snappable, replaceable software components&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Thin, rich client&lt;/li&gt;&lt;li&gt;Fully denormalized reusable data source&lt;/li&gt;&lt;li&gt;Dashboarding&lt;/li&gt;&lt;li&gt;3-tiered communication layer&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;Today, you could add to those requirements:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;stateless (REST-ful)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;SOA (Service-oriented architecture)&lt;br /&gt;&lt;/li&gt;&lt;li&gt;AJAX&lt;br /&gt;&lt;/li&gt;&lt;li&gt;Web 2.0 (or 3.0, or whatever)&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;What the business actually needed was:&lt;br /&gt;&lt;ol&gt;&lt;li&gt;Show quarterly reports as soon as the data was approved to show&lt;/li&gt;&lt;li&gt;Secure&lt;/li&gt;&lt;li&gt;Support a maximum of 20 simultaneous users&lt;/li&gt;&lt;li&gt;Available 8AM-8PM Eastern (US) time&lt;/li&gt;&lt;li&gt;Show some other marketing information&lt;br /&gt;&lt;/li&gt;&lt;/ol&gt;&lt;br /&gt;One web server would have been more than adequate, but we had 2.  The slow part was actually some of the queries/reports in the database.  The only part of the site we ever plugged/snapped/reused were some hand-coded HTML pages and the login code.  If we had known enough to discover the actual needs of the users, we could have saved at least 50% of the effort and used some of that time to further optimize the long-running queries, or even redesign the database to make the queries simple enough that they wouldn't need optimization.&lt;br /&gt;&lt;br /&gt;Well, 20/20 hindsight is easy.  That's how you learn...&lt;br /&gt;&lt;br /&gt;&lt;h3&gt;Conclusion&lt;/h3&gt;When requirements read like a winning "Buzzword Bingo" card, it's almost a sure sign they weren't thought out very carefully.  Time spent digging for real requirements pays off in both medium and long-term system usefulness and cost savings.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-5788200026658919459?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/5788200026658919459/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=5788200026658919459' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5788200026658919459'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5788200026658919459'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2007/12/make-users-happy-by-ignoring.html' title='Make Users Happy by Ignoring Requirements'/><author><name>GlenPeterson</name><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-5689812823462613341.post-5529353133063811295</id><published>2007-09-30T18:45:00.000-07:00</published><updated>2011-08-31T07:11:38.520-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='passwords'/><category scheme='http://www.blogger.com/atom/ns#' term='cryptography'/><category scheme='http://www.blogger.com/atom/ns#' term='randomness'/><category scheme='http://www.blogger.com/atom/ns#' term='&quot;Random Character Generator&quot;'/><title type='text'>On Generating Random Keys for Use in Cryptography</title><content type='html'>Computers are deterministic state machines - totally incapable of producing anything random.  They employ Pseudo-Random Number Generators: functions that appear to produce random output with respect to their input.  Good Pseudo-Random Number Generators (PRNGs) can produce thousands of values a second with a surprising degree of entropy.  But even the best PRNG is limited by the variability of its “seed” or input values, and none have perfectly even distribution or an absence of patterns in their output.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Bits of Entropy:&lt;/b&gt;&lt;br /&gt;In cryptography, key size, measured in bits (or powers of 2), represents the number of keys which must be tested to break an encrypted message by brute force.  But encryption algorithms are generally “lossy” because, being deterministic, they have some degree of predictability and/or collisions of different keys producing the same output values.  Some algorithms are “lossier” than others and there are many statements in the literature to the effect that a 1024-bit key using one algorithm is just as secure as an 80-bit key using another.  That means one algorithm is 1.49×10&lt;sup&gt;248&lt;/sup&gt; times more secure than the other.  Despite common misuse in advertising claims, bits are the "standard" unit of entropy.  In this writing, they represent a mathematically derived number of equally likely possible combinations, expressed in powers of 2.  Not the computer space used to store a value generated with a PRNG.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Dirty Secrets of Pseudo-Random Number Generators:&lt;/b&gt;&lt;br /&gt;Given the same initial input, a PRNG will not only produce the same output value, but will produce the same &lt;em&gt;sequence&lt;/em&gt; of values every time, and that sequence is generally your automatically generated cryptographic key or password!  The "moving parts" inside most PRNGs are 64-bit and 32-bit registers and this limits their entropy.  But PRNGs are often more limited by the precision of their input or seed.  Since the few somewhat random processes in the computer tend to produce clusters of values, they are unsuitable as random number seeds.  The only part of the computer that produces an even dispersion of values is the system clock, which measures milliseconds.&lt;br /&gt;&lt;br /&gt;There are only 8.64×10&lt;sup&gt;7&lt;/sup&gt; milliseconds in a day.  That's about 26 bits of entropy.  Using the 95 characters on a US-keyboard, a 5-character truly-random password is more secure!  So why not use a longer time frame?  There are 3.1×10&lt;sup&gt;11&lt;/sup&gt; milliseconds in 10 years which is only 38 bits of entropy – and who has a password older than 10 years?  Most people generate their keys at work, between the hours of 9-5 M-F which is only one fourth of the hours in the week, so you might be losing about 2 bits of entropy there.&lt;br /&gt;&lt;br /&gt;So even if you have a 64-bit random number generator, unless you can get a seed with more than 36 bits of entropy, you only get 36 bits of entropy in your key.  That’s the same number of significant digits in a truly random 6-character key (95&lt;sup&gt;6&lt;/sup&gt;).  How many significant digits of randomness does any computer generated encryption key contain?  No more than the significant digits of entropy used to create it times the number of PRNG algorithms that could have been used.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;Links:&lt;/b&gt;&lt;br /&gt;&lt;ul&gt;&lt;li&gt;&lt;a href="http://www.cigital.com/papers/download/developer_gambling.php"&gt;How We Learned to Cheat at Online Poker: A Study in Software Security&lt;/a&gt; - If you liked my article here, you will probably like this article even more.  One of my favorite reads on the web!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.ftp.cl.cam.ac.uk/ftp/users/rja14/tr500.pdf"&gt;The Memorability and Security of Passwords -  Some Empirical Results&lt;/a&gt; - Using initial letters and punctuation from pass-phrases is as easy to remember as the most commonly hacked English words, but is as hard to crack as truly random characters... until cracking tools learn what to look for.  In the meantime, this is the best advice I've seen on telling an end-user how to construct a secure password.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://egd.sourceforge.net/"&gt;Entropy Gathering Daemon&lt;/a&gt; - A great way to seed a random-number generator.  The only complaint I've heard is that EGD can block, waiting for more entropy if you use it heavily in software such as a web server.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://www.keepass.info/"&gt;KeePass&lt;/a&gt; password store - has an entropy-gatherer built-in for generating random passwords... Nice!&lt;/li&gt;&lt;br /&gt;&lt;li&gt;&lt;a href="http://en.wikipedia.org/wiki/RANDU"&gt;RANDU&lt;/a&gt; Wikipedia article about an infamous random number generator.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Humor from xkcd: &lt;a href="http://xkcd.com/221/"&gt;Random Number&lt;/a&gt;, &lt;a href="http://xkcd.com/257/"&gt;Code Talkers&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;a rel="license" href="http://creativecommons.org/licenses/by-nc-sa/3.0/"&gt;&lt;img alt="Creative Commons License" style="border-width: 0pt;" src="http://i.creativecommons.org/l/by-nc-sa/3.0/88x31.png" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/5689812823462613341-5529353133063811295?l=glenpeterson.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://glenpeterson.blogspot.com/feeds/5529353133063811295/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://www.blogger.com/comment.g?blogID=5689812823462613341&amp;postID=5529353133063811295' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5529353133063811295'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/5689812823462613341/posts/default/5529353133063811295'/><link rel='alternate' type='text/html' href='http://glenpeterson.blogspot.com/2007/09/dirty-secrets-of-pseudo-random-number.html' title='On Generating Random Keys for Use in Cryptography'/><author><name>GlenPeterson</name><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>
