Ranum's Law

ChuckMcM's comment below (emphasis added) made me realized again that why a system failed to work even though technically it was designed correctly within an organization. It was always a people problem rather than a technical issue or as Marcus Ranum put it, "You can’t solve social problems with software".
"This really resonates with me. Once any organization gets to a certain size, getting change to happen is more "social problem" and less "technical problem." When you don't select for people at that stage who can move an entire organization over to a new way of doing things, your organization will stagnate and die. It used to really annoy me at Google when technical evaluators who dismiss those "soft qualities" in candidates. I had a fairly famous Googler tell me point blank that anyone could change the organization by shouting loudly enough. Which I tried to explain was just as false as saying anyone can solve a problem if they write enough code. But alas, they were not ready to hear that at the time."
We, software developers need to realize that technical solution alone can't solve social problem as concluded by Chris Siebenmann below.
"Social engineering is a much more difficult field than computer engineering; it is much easier to build something that works than to build something that people want to use. Solving the technical problem without considering the social ones around it is like designing a beautiful house without bothering to find out where in the world it's going to be located."
He proposed that we should work a technical solution within the social constraints. Additionally, within an organization, in order for a system to work, the management must willing to step in and make the hard decision to change from the top.

Lightdm Login Loop Issue in Ubuntu 13.10

Not sure what's going on but my lappy failed to log me in since yesterday. Symptoms of the issues are:
  • Loop back to login page even for correct password.
  • Guest login works.
  • No issue when switching to gdm.
$ sudo service stop lightdm
$ sudo dpkg-reconfigure lightdm
$ sudo start gdm

Surprisingly, this is quite a common problem as posted which I eventually traced the issue down to incorrect file permission for ~/.Xauthority file. Removing that file and login seems to work.
$ ls -l .Xauthority 
-rw------- 1 root root 57 Feb  25 01:05 .Xauthority

Someone did ask about this .Xauthority file and why file ownership was set to root? Best answer given which I quote here (emphasis added).
"The .Xauthority (not .xAuthority) file can be found in each user home directory and is used to store credentials in cookies used by xauth for authentication of X sessions. Once an X session is started, the cookie is used to authenticate connections to that specific display. You can find more info on X authentication and X authority in the xauth man pages (type man xauth in a terminal).

So, if you are not the owner of this file you can't login since you can't store your credentials there.

This situation usually arises when you execute a GUI application (for instance nautilus) with root permissions by typing sudo nautilus. You can avoid it (for 12.10 and older versions) by invoking the app with gksudo nautilus."

Which I suspect the root cause of this issue is that I always use sudo to start any graphical application where I shouldn't as described in RootSudo wiki entry as quoted below (emphasis added).
"You should never use normal sudo to start graphical applications as Root. You should use gksudo (kdesudo on Kubuntu) to run such programs. gksudo sets HOME=~root, and copies .Xauthority to a tmp directory. This prevents files in your home directory becoming owned by Root. (AFAICT, this is all that's special about the environment of the started process with gksudo vs. sudo)."
If that the case, then why gdm works but not lightdm?

Debian 7 Installation : Part 5 - Graphic Adapter (Nvidia)

Since I have an old legacy Nvidia graphic card that support dual monitors (VGA and DVI port), might as well reuse it again to setup my home development environment. This card will support two DELL E190S LCD monitors, the last of the remaining square size monitor still being manufactured.

Let's follow the wiki instruction to detect, install, and configure this graphic card to the maximum potential output. If you insert the card properly, the original Matrox adapter will be disable, otherwise you'll need to take it out again and reinsert it back.

First, let's find the exact model number. As shown below, the card is GeForce 7300 SE/7200 GS.
$ lspci - nn | grep VGA
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation G72 [GeForce 7300 SE/7200 GS] [10de:01d3] (rev a1)

Alternately, instead of using lspic command, we can use lshw command as well, which show more hardware details.,
$ sudo apt-get install lshw
$ sudo lshw -C display
  *-display               
       description: VGA compatible controller
       product: G72 [GeForce 7300 SE/7200 GS]
       vendor: NVIDIA Corporation
       physical id: 0
       bus info: pci@0000:01:00.0
       version: a1
       width: 64 bits
       clock: 33MHz
       capabilities: pm msi pciexpress vga_controller bus_master cap_list rom
       configuration: driver=nvidia latency=0
       resources: irq:16 memory:d2000000-d2ffffff memory:c0000000-cfffffff memory:d1000000-d1ffffff

Next, we need to detect the Graphic Processing Unit (GPU) to make a recommendation for the matching drivers to be used. I used to set it up on two LCD monitors few years back and driver support should be stable and good enough.
$ sudo apt-get install nvidia-detect
$ nvidia-detect 
Detected NVIDIA GPUs:
01:00.0 VGA compatible controller [0300]: NVIDIA Corporation G72 [GeForce 7300 SE/7200 GS] [10de:01d3] (rev a1)
Your card is supported by the default drivers and version 173.
It is recommended to install the
    nvidia-glx
    package.

Following the wiki instruction again, let's install the necessary packages for this graphic card. If you haven't add the non-free repository, type this command to add the non-free repository.
$ cat 'deb http://http.debian.net/debian/ wheezy main contrib non-free' \
| sudo tee /etc/apt/source.list.d/wheezy-non-free.list

Note that I don't modified the default repository list file (/etc/apt/sources.list) as it is easier to put every repository details in /etc/apt/source.list.d folder and rename it to something else if want to disable it. This is a matter of preferences.

Finally, we will update the update the package index files and install the necessary packages.
$ sudo aptitude update
$ sudo aptitude r install linux-headers$(uname r|sed 's,[^]*[^**]*-,,') nvidia-kernel-dkms

Again, we don't use the default /etc/X11/xorg.conf file anymore but instead put it as separate file in /etc/X11/xorg.conf.d folder. Is easy to toggle a configuration file using such mechanism. More on this later when we want to setup for dual monitors.
$ sudo mkdir /etc/X11/xorg.conf.d
$ echo -e 'Section "Device"\n\tIdentifier "My GPU"\n\tDriver "nvidia"\nEndSection' \
| sudo tee /etc/X11/xorg.conf.d/20-nvidia.conf

Lastly, before we reboot the machine, we need to install the GUI software to manage the graphic card settings. See the screenshot below.
$ sudo apt-get install nvidia-settings
$ sudo nvidia-settings
$ sudo reboot

Debian 7 Installation : Part 4 - Graphic Adapter (Matrox)

Checking the VGA display adapter used by the machine. Surprising to find the long forgotten graphic card manufacturer, Matrox on this machine.
$ lspci | grep -i VGA
01:00.0 VGA compatible controller: Matrox Graphics, Inc. MGA G200 AGP (rev 03)

Instead of using the default basic VESA driver which is quite slow (you can see the screen slowly refresh itself), we opt for the exact vendor driver instead.

Following the configuration instructions by pc-freak, we need to rebuild and install vendor driver for getting the best optimized performance from this legacy card.
$ sudo apt-get install --yes xserver-xorg-video-mga
$ sudo dpkg -i /usr/src/modules/mga-vid/debian/mga-vid-source_2.6.32-1_i386.deb
$ sudo apt-get install --yes mga-vid-common
$ sudo reboot

Debian 7 Installation : Part 3 - Root Account and Wifi Configuration

Continue with previous post on the setup of the new Debian installation.

During the installation, I chose to disable the root account and hence the first user will use sudo to perform administrative tasks. A practice popularized by Ubuntu.

However, if you want to set the root password, just simple type command below to replace the default empty password.
$ sudo passwd root

Next step, settings up my Wifi dongle so I can move this server anywhere around the house instead of limiting myself to certain location using wired connection.

Using the lsusb command to find the brand and model of the Wifi adapter.
$ lsusb

Unfortunately, you will need install special non-free binary firmware for the RTL8188CUS Wifi adapter to work.
$ echo "deb http://http.debian.net/debian/ wheezy main contrib non-free" \
| sudo tee /etc/apt/sources.list.d/wheeze-non-free.list
$ sudo apt-get update
$ sudo apt-get install firmware-realtek
$ sudo reboot

Sadly, the default package repository is just dog slow, updating speed was pathetically at 725B/s!

Debian 7 Installation : Part 2 - Burning ISO Image to USB Thumbdrive

Continue with previous post on Debian installation. The CD image was created using isohybrid technology, which can write to both CD ROM and USB thumbdrive. However, since most laptop these days don't have CD ROM anymore, we've to write the ISO image into a USB thumbdrive and do our installation
through this media.

Plug your thumbdrive in and check for the device name through any of these commands. Typically the thumbdrive device name start with /dev/sdb, /dev/sdc, or the next sequence. For my case, the device name is /dev/sdc.
$ dmesg
$ df -h
$ mount

Write bootable ISO image to the USB thumbdrive using the dd command. Be patient as it takes a while to finish both commands.
$ sudo dd if=debian-7.4.0-amd64-CD-1.iso of=/dev/sdc bs=4M
$ sync

Take the the USB drive and boot the machine using it. Before that, please remember to configure your BIOS to boot from removable media first. If you encounter this error message "isolinux.bin missing or corrupt" when booting from thumbdrive, then you've wrote the image to the thumbdrive incorrectly. Double check the dd command in step 2 again as the device name is /dev/sdc and not the partition name /dev/sdc1.

On a side note, I prefer Debian to Ubuntu installer as it is more verbose in explaining the reason of the installation procedure. The Ubuntu installer has been dumbed down or made easier for beginner user.

Debian 7 Installation : Part 1 - ISO Image Verification

Due to unforeseen reason, I need to revive back my unused server and install Debian 7 (Wheezy), the latest stable version on it. It has been a while since I last use Debian as my primary GNU/Linux distro compare to its siblings, Ubuntu. Some notes regarding the installation procedure.

Download the ISO CD 1 image as well as the signed checksum files (for verification) from the cdimage site. I opted to use the first CD image which we will later burn into the USB thumbdrive as installation media.
$ wget http://cdimage.debian.org/debian-cd/7.4.0/amd64/iso-cd/debian-7.4.0-amd64-CD-1.iso
$ wget http://cdimage.debian.org/debian-cd/7.4.0/amd64/iso-cd/SHA512SUMS
$ wget http://cdimage.debian.org/debian-cd/7.4.0/amd64/iso-cd/SHA512SUMS.sign

Due to recent exposure to keysigning, is a good practice validate the ISO image using the checksum tool and verify the authenticity of the ISO image. It took me a while of googling to find the proper step-by-step guide (in Chinese but the instructions are quite obvious) to validate and verify the downloaded iso images. Funny how the official documentation does not even has these instruction.

First, let's verify the checksum file to confirm this image was built by the authorized people. As the error message below shown, we're missing the public key to verify the signed checksum.
$ gpg --verify SHA512SUMS.sign SHA512SUMS

gpg: Signature made Isnin 10 Feb 2014 02:31:31  MYT using RSA key ID 6294BE9B
gpg: Can't check signature: public key not found

Find and add the required public key that signed this checksum file. We can obtain this public key from Debian's own key server. Take note of the last line where this key is still not fully valid or trustworthy enough according to the PGP trust model.
$ gpg --keyserver http://keyring.debian.org --recv-keys 6294BE9B

gpg: requesting key 6294BE9B from hkp server http://keyring.debian.org
gpg: key 6294BE9B: public key "Debian CD signing key " imported
gpg: 3 marginal(s) needed, 1 complete(s) needed, PGP trust model
gpg: depth: 0  valid:   1  signed:   0  trust: 0-, 0q, 0n, 0m, 0f, 1u
gpg: Total number processed: 1
gpg:               imported: 1  (RSA: 1)

To be safe, just to confirm the aforementioned public key had been added locally. The fingerprint shown below should exists in Debian CDs authentication and verification page.
$ gpg --fingerprint 6294BE9B                                                                                                                                        
pub   4096R/6294BE9B 2011-01-05
Key fingerprint = DF9B 9C49 EAA9 2984 3258  9D76 DA87 E80D 6294 BE9B
uid                  Debian CD signing key 
sub   4096R/11CD9819 2011-01-05

Let's verify our downloaded checksum file (SHA512SUMS) using the added Debian CD signing key file (SHA512SUMS.sign).
$ gpg --verify SHA512SUMS.sign SHA512SUMS
gpg: Signature made Isnin 10 Feb 2014 02:31:31  MYT using RSA key ID 6294BE9B
gpg: Good signature from "Debian CD signing key "
gpg: WARNING: This key is not certified with a trusted signature!
gpg:          There is no indication that the signature belongs to the owner.
Primary key fingerprint: DF9B 9C49 EAA9 2984 3258  9D76 DA87 E80D 6294 BE9B

However, as the message above indicates, the signature is valid but identify is unverified. Let's show the list of people who had signed this Debian CD signing key.
$ gpg --list-sigs 6294BE9B
pub   4096R/6294BE9B 2011-01-05
uid                  Debian CD signing key 
sig          1B3045CE 2011-01-07  [User ID not found]
sig          3442684E 2011-01-05  [User ID not found]
sig          A40F862E 2011-01-05  [User ID not found]
sig          C542CD59 2011-01-05  [User ID not found]
sig          63C7CC90 2011-01-05  [User ID not found]
sig 3        6294BE9B 2011-01-05  Debian CD signing key 
sub   4096R/11CD9819 2011-01-05
sig          6294BE9B 2011-01-05  Debian CD signing key 

Import these public key with their corresponding names and email addresses.
$ gpg --recv-keys 1B3045CE 3442684E A40F862E C542CD59 63C7CC90
$ gpg --list-sigs 6294BE9B

pub   4096R/6294BE9B 2011-01-05
uid                  Debian CD signing key 
sig          1B3045CE 2011-01-07  Colin Tuckley 
sig          3442684E 2011-01-05  Steve McIntyre 
sig          A40F862E 2011-01-05  Neil McGovern 
sig          C542CD59 2011-01-05  Adam D. Barratt 
sig          63C7CC90 2011-01-05  Simon McVittie 
sig 3        6294BE9B 2011-01-05  Debian CD signing key 
sub   4096R/11CD9819 2011-01-05
sig          6294BE9B 2011-01-05  Debian CD signing key 

Alternatively, you can find the list of users that signed the public key 6294BE9B by using debian-keyring package.
$ sudo apt-get install debian-keyring
$ gpg --keyring /usr/share/keyrings/debian-keyring.gpg -kvv 6294BE9B

You can only verify the identity of the Debian CD signing through the concept
of Web of Trust [7] by going through the list of people above either by signing
their public key (which you've meet them in real life or trust them through
fingerprint exchange) or ask them directly.

Lastly, let's check ISO image file for correctness and corruption.
$ sha512sum -c SHA512SUMS debian-7.4.0-amd64-CD-1.iso 2> /dev/null | grep debian-7.4.0-amd64-CD-1.iso
debian-7.4.0-amd64-CD-1.iso: OK

How to Spend Less?

2014 is going to be a bleak financial year for most of us in .my due to recent price hikes in toll, electricity, petrol, and others. But most important changes is the introduction of Goods and Services Tax (GST), which will come into effect starting on April 1, 2015.

Hence, for us who are still living by paycheck to paycheck, is wise to spend wisely and aware of our purchases. Occasionally, due to work/life-related stress and idiocy or personality traits, we tend to succumb to temptation and spend impulsively. Which inevitably leads to buyer's remorse, causing more pressure and then the circle of pain continues.

Discussion at Reddit gave us a few tips on how to led a frugal life and don't easily spend away your money. Notable tips which inspired me.

1. Think over before making any purchase more than 100.
Divide it by 50 and you'll get the number of days for you to think over before making the purchase. For example an Iphone 5S 16G retail price is around 2,370. Divide that amount by 50 and you have 47 days to think through over whether you really want that mobile phone. Some may argue that you can pay by by credit card with zero interest installment. Well you can if you only going to buy that phone only and nothing else.

Furthermore, what you really need is a mobile phone. You may need a smart phone but do you really need an Iphone?

2. Don't go grocery shopping on empty stomach.
I will add that don't argue with significant other on empty stomach as well. Why ? Hunger depletes your willpower and you tends to say or make regrettable decision or purchases.

3. Buy quality items.
Redditor JarlesV3 offered a simple way to dictate his purchasing decision. Basically is how you intend to use the item. Is it for long term (durable) or just short while (consumable)? For the former, for example, if you run a lot, spend the best money you can on best running shoe for best comfort and to prevent injury. Be careful not to overpaying for brand rather than quality. For the later, spend more if you want quality and healthy food instead of instant noodle.

4. Money earned is hours worked.
Imagine you're earning 3,000 per month. After all the compulsory deductions, you nett pay is roughly around 2655. Divide that by 26 working days and basically you're earning 102 per day. Minus this amount with other expenditure like transport, rental, and others. You may end up with nett pay of 50 per day. Now if you want to buy that Iphone we mentioned just now, basically you've to work 47 days or 423 hours (if you work 9 hours per day) of your life just to afford that mobile phone.

5. 52-week money challenge.
Don't have enough money at year end to pay for your car insurance or vacation? Why not try this method. Start with first week, save 1. For the subsequent week, add 1 to the previous month. At the end of the year, you will save a surprising amount of 1,378. Or you can do the reverse way, start with 52 and start reducing 1 from previous month.

In short, be discipline and well aware of your spending. Know what you really need and what you want to resist any temptation.

Find Package Dependency During Source Code Compilation

Found this sandboxing tool, mbox (really crappy name) via HN. Sandboxing is like a container which isolates and assign limited resources for a guest program. Similarly, is like another form of virtualization.

Since there is not Debian package, I had to clone the source and try to compile it. But still, the tool still can't run in Ubuntu due to a memory issue which is way beyond me to fix it.
$ ./mbox ls
Stop executing pid=22065: It's not allowed to call mmap on 0x400000
Sandbox Root:
> /tmp/sandbox-22061

Since the Debian package is missing, we've to resolve to code compilation according to the README.

Clone the source from github, configure, and compile it.
$ git clone https://github.com/tsgates/mbox
$ cd src
$ cp {.,}configsbox.h
$ ./configure
$ make

Unfortunately, you should encounter error during compilation as certain required libraries are missing. That why having a .deb package is useful as we can check the dependency list of a package. For example to find a dependency of ack program which I've just installed.

Install the ack program
$ sudo apt-get install ack

Go to the cached location where all the Debian packages were stored.
$ cd /var/cache/apt/archives

Check the dependency of the program and install those required packages.
$ sudo dpkg -I ack_1.39-12_amd64.deb | grep -i depends
Depends: libc6 (>= 2.4)

Or you can forcefully install by letting apt-get resolves the unmet dependencies.
$ sudo apt-get install -f ack_1.39-12_amd64.deb

Now, how can we find the dependency list through source code compilation? The only way I know is to rerun the configure script, capture the output, filter all those line with 'no' keyword, and lastly find those packages with those missing header file. Example as shown below.

Capture the output of the configuration script.
$ ./configure | tee configure.log

Find those header file, sample line given.
$ cat configure.log | grep no
checking libaio.h usability... no

Find the package that contains this file libaio.h. But first we must install apt-file program.
$ sudo apt-get install apt-file
$ sudo apt-file update
$ apt-file search libaio.h
libaio-dev: /usr/include/libaio.h

Install the found package.
$ sudo apt-get install libaio-dev

Repeat for other missing header files as well. Note that certain header files are supposed to be not found as the script was checking for platform specific header files.

Stay Focus, Stay Healthy, and Stay Relevant

"For a long time there’s been a fad in our industry of having open workspaces. While being right next to someone and being able to just look over and ask a question is ideal for communication, it can be the opposite for concentration. Headphones with loud music don’t solve the problem either. What I believe works best is quiet. Can you imagine taking a final exam in college with someone blasting music? You can’t concentrate at your best when any sort of external stimuli is demanding some of your attention. It needs to be quiet, and free of any visual distraction as well. People walking by, a television, anything like this should be avoided for you to stay in the zone."

Via HN. Strongly concurred with the poster on the important of elimination of distraction for a programmer. Even blasting yourself with music is also another form of distraction or a substitution of your environment noise. Any programmers who claim that they are productive in a open floor plan office is just plain bullshitting. As the example of taking an exam shown, any cognitive activities needs absolute zero or minimum distraction. That why some of us is far more productive after working hour or during midnight.

On a related note, besides optimizing your daily routine as a developer, you need to take charge of your programming career and be responsible of your learning and health, especially when you're over 35-years old. At this second phase of your career, priorities changed as time and stamina are limited resources. There is only so much you can do within this lifetime, minus all the sleep, rest, idleness, or procrastination. Focus on learning new stuff to sustain that programming passion. Unless necessary, stop working on the same stuff years after years (same technology stack but different business domain), do something new, take risk, even if might affect your bread and butter.

In short, stay healthy, stay relevant, and be a generalist rather than a specialist. Open yourself to different programming languages or paradigms. Find a way to sustain your love and don't starve yourself along the way.

Great User Experience (UX) Design

Was trying to buy a book online, fascinate by the whole ordering process. Some ui patterns are design to assist the consumer while others are carefully crafted to encourage more spending.


1. Upon resetting the password, the system will delete all stored addresses and credit cards information. While this may seem troublesome, but it does prevent information leaking if your account was ever breached.

2. Separation of credit card and Paypal payment method. Far more easier and safer. At least your credit card information is stored in just one location instead of multiple sites.

3. Encourage more spending and creating good feeling when you can support your favourite charity for every online purchase. Another method to encourage more purchasing.

4. Similarly to (3), notice how the keywords free and save were being used to encourage more purchasing.


On Grep

Interesting write-up on the history of the grep command, which stands for g/re/p (globally search a regular expression and print). But off course, as a developer, this has been replaced with ack [3], which was succeed by Ag, the Silver Searcher.

Java, the predictable language

"Java is neither a good nor a bad language. It is a mediocre language, and there is no struggle. In Haskell or even in Perl you are always worrying about whether you are doing something in the cleanest and the best way. In Java, you can forget about doing it in the cleanest or the best way, because that is impossible. Whatever you do, however hard you try, the code will come out mediocre, verbose, redundant, and bloated, and the only thing you can do is relax and keep turning the crank until the necessary amount of code has come out of the spout. If it takes ten times as much code as it would to program in Haskell, that is all right, because the IDE will generate half of it for you, and you are still being paid to write the other half.
...
So yes, I enjoyed programming in Java, and being relieved of the responsibility for producing a quality product. It was pleasant to not have to worry about whether I was doing a good job, or whether I might be writing something hard to understand or to maintain. The code was ridiculously verbose, of course, but that was not my fault. It was all out of my hands."
-- Mark Dominus, Why I Like Java (emphasis added)
Not sure he being sarcastic or stating the truth, but the predictability of Java is why this language triumphs in the education sector and in developing Enterprise software. Not to mention heavy marketing and evangelism when the language was first introduced. Furthermore, "nobody ever got fired for picking Java".

Centering a div in 2014

Sad but true, yet in 2014, we still need to resolve to ridiculous hacks to center a bloody div tag. Changes in the web standard still moving at a slow pace, mostly due to the monopoly and high Internet Explorer browser market share.

Not to mention the fixed or sticky footer. Sigh.

Spotify Installation in Ubuntu 13.10

To reduce the distraction of office noise and find more interesting music, I've recently installed Spotify, the digital music service. Compare to the default GNU/Linux instruction, you can try a different approach on step 1 without using any editor and the Unix tee command. Steps as follows:

Add the repository details to our software repositories list. Note we're using the tee command to bypass the limitation of the sudo command to pipe standard input directly to a file.

$ echo deb http://repository.spotify.com stable non-free \
| sudo tee /etc/apt/sources.list.d/spotify.list

Add their public key so we can verify the downloaded software packages.
$ sudo apt-key adv --keyserver keyserver.ubuntu.com --recv-keys 94558F59

Update your package repository and install the client.
$ sudo apt-get update
$ sudo apt-get install spotify-client

By the way, although the recommendation system seems primitive, the client did suggest quite a few songs which were new to me. Example is Miles Davis's In a Silent Way, my current favourite song before I start coding.

Latest and Greatest or Just Stay Stagnate

"I don't actually agree with this. It's a cop-out. Some people will just say "Well, this procedural mess I've been copy-pasting since 1996 works for me, so I'll keep using that. Make an informed decision about what's the best option. That may or may not be to learn a new technology. But don't take "works for you" to mean you should not bother to progress in your learning. Too many PHP programmers sit back and stagnate."
-- mattaugamer
There is always an exception to any rules or advises given. It's considered a best modern practice to write you web application using a framework rather than flat PHP script. However, taking into consideration of the talent and time available, sadly you have to live with the flat PHP approach.

There is nothing wrong with that provided that you don't have to maintain the code base. If you're stuck maintaining the legacy stuff which will slowly hinder your learning progress, career advancement, and suffer the consequences of technical debts made by others, you're left with four choices. Put up and shut up, rewrite it using modern approach, delegate to another developer and never touch it again, or lastly just move on.

Java, the Predictable Language

"Java is neither a good nor a bad language. It is a mediocre language, and there is no struggle. In Haskell or even in Perl you are always worrying about whether you are doing something in the cleanest and the best way. In Java, you can forget about doing it in the cleanest or the best way, because that is impossible. Whatever you do, however hard you try, the code will come out mediocre, verbose, redundant, and bloated, and the only thing you can do is relax and keep turning the crank until the necessary amount of code has come out of the spout. If it takes ten times as much code as it would to program in Haskell, that is all right, because the IDE will generate half of it for you, and you are still being paid to write the other half."
...
"So yes, I enjoyed programming in Java, and being relieved of the responsibility for producing a quality product. It was pleasant to not have to worry about whether I was doing a good job, or whether I might be writing something hard to understand or to maintain. The code was ridiculously verbose, of course, but that was not my fault. It was all out of my hands."

-- Mark Dominus, Why I Like Java (emphasis added)
Not sure he being sarcastic or stating the truth, but the predictability of Java is why this language triumphs in the education sector and in developing Enterprise software. Not to mention heavy marketing and evangelism when the language was first introduced. Furthermore, "nobody ever got fired for picking Java".