<?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-427258967255390991</id><updated>2012-01-25T08:29:59.033+08:00</updated><category term='postupload'/><category term='client certificate'/><category term='waterbot'/><category term='postgresql'/><category term='support'/><category term='javascript'/><category term='web'/><category term='bugs'/><category term='glassfish'/><category term='desire hd'/><category term='fonts'/><category term='xargs'/><category term='jersey'/><category term='hosting'/><category term='wine'/><category term='open source'/><category term='scribd'/><category term='proprietary software'/><category term='opentype'/><category term='whine'/><category term='ebook'/><category term='application server'/><category term='belkin'/><category term='soil moisture'/><category term='find'/><category term='sparkfun'/><category term='ejb'/><category term='shell'/><category term='bob'/><category term='downgrade'/><category term='truetype'/><category term='internet'/><category term='humidity'/><category term='script'/><category term='windows'/><category term='cdi'/><category term='x509'/><category term='jaxrs'/><category term='cmd'/><category term='usability'/><category term='database'/><category term='backup'/><category term='patch'/><category term='arduino'/><category term='linux'/><category term='scripting'/><category term='datarecovery'/><category term='arduno'/><category term='embedded'/><category term='uploadify'/><category term='tech'/><category term='radio'/><category term='jsf2'/><category term='java'/><category term='seam'/><category term='php'/><category term='srv'/><category term='tutorial'/><category term='htc'/><category term='cyanogenmod'/><category term='music'/><category term='iinet'/><category term='embedded-glassfish'/><category term='txt'/><category term='root'/><category term='pdf'/><category term='oracle'/><category term='netbeans'/><category term='publishing'/><category term='electronics'/><category term='microcontroller'/><category term='android'/><category term='desktop'/><category term='dns'/><category term='jpa'/><category term='kobo'/><category term='servers'/><category term='sensor'/><category term='jboss'/><category term='drupal'/><category term='javaee6'/><category term='qt'/><category term='fontforge'/><category term='ereader'/><category term='webapp'/><category term='atmel'/><category term='notetoself'/><category term='crypto'/><title type='text'>Beware of soapy frogs</title><subtitle type='html'>Yet another tech geek weblog with a focus on technical Linux and open source, particularly server-side, on Java EE, PostgreSQL, and more.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>83</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-8243404624561638494</id><published>2012-01-23T09:41:00.013+08:00</published><updated>2012-01-25T08:28:38.911+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='htc'/><category scheme='http://www.blogger.com/atom/ns#' term='downgrade'/><category scheme='http://www.blogger.com/atom/ns#' term='cyanogenmod'/><category scheme='http://www.blogger.com/atom/ns#' term='desire hd'/><category scheme='http://www.blogger.com/atom/ns#' term='android'/><category scheme='http://www.blogger.com/atom/ns#' term='root'/><title type='text'>I've had it with HTC - thanks for the rescue, CyanogenMod + AAHK</title><content type='html'>&lt;p&gt;HTC pushed an Android 2.3.5 update to my Vodafone Australia-branded HTC Desire HD. There was no changelog, and along with the Android update it turns out I get a new version of HTC Sense (yay?) with all sorts of animations I can't turn off and extra bloat.&lt;/p&gt;

&lt;p&gt;Great work HTC, you made the phone faster, then ruined it with more pointless animation. At least the "no window animations" setting used to work in the old version...&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;The new phone version is:&lt;/p&gt;

&lt;pre&gt;
Model number: HTC Desire HD A9191
Android version: 2.3.5
HTC Sense version: 3.0
Software number: 3.12.178.2
Kernel version: 2.6.35.10-g931a37e htc-kernel@and18-2 #1 WEd Nov 9 14:04:03 CST 2011
Baseband version: 12.65.60.29U_26.14.04.28_M
Build number: 3.12.178.2 CL200874 release-keys
&lt;/pre&gt;

&lt;p&gt;(from "settings-&gt;about phone")&lt;/p&gt;

&lt;p&gt;I'd love to downgrade it (rooting it if necessary to do so) then reflash it with a sensible firmware from Cyanogen etc. Unfortunately, Vodafone/HTC seem to have broken existing root methods on the device with the latest update. It's not like this is &lt;i&gt;my phone that I purchased outright&lt;/i&gt; or anything, so why should I be able to do anything with it? &lt;i&gt;GRR!&lt;/i&gt;.&lt;/p&gt;

&lt;p&gt;&lt;b style="color: dark-red;"&gt;IMPORTANT: IF YOU ARE CONSIDERING ROOTING AND REFLASHING YOUR PHONE, UNDERSTAND THAT IT WILL PROBABLY VOID ALL WARRANTIES AND MAY DESTROY YOUR PHONE IF YOU MAKE A MISTAKE OR IT DOESN'T WORK PROPERLY WITH YOUR MODEL!&lt;/b&gt; This is not a guide, it's just a report of what worked for me.&lt;/p&gt;

&lt;p&gt;psneuter reports:&lt;/p&gt;

&lt;pre&gt;
$ /data/local/tmp/psneuter
/data/local/tmp/psneuter
Failed to set prot mask (Inappropriate ioctl for device)
&lt;/pre&gt;

&lt;p&gt;Fre3vo reports:&lt;/p&gt;

&lt;pre&gt;
$ ./data/local/tmp/fre3vo
./data/local/tmp/fre3vo
fre3vo by #teamwin
Please wait...
$
&lt;/pre&gt;

&lt;p&gt;... never successfully getting root.&lt;/p&gt;

&lt;p&gt;I've also tried the &lt;a href="http://www.htcdev.com/bootloader/"&gt;official HTC bootloader unlock tool&lt;/a&gt; for the Desire HD. It's been released for my Vodafone firmware version, so it should work, but it reports "unsupported firmware version". Sigh. Even if the bootloader unlock worked, I couldn't actually reflash the phone without temporary root access or a direct flashing tool that doesn't require using the phone to modify its own recovery partition.&lt;/p&gt;

&lt;p&gt;So, what's a pissed-off phone owner to do?&lt;/p&gt;

&lt;p&gt;Investigation suggests I may be able to directly reflash the device with tools called odin3 (a leaked Samsung android flashing tool) or &lt;a href="http://www.glassechidna.com.au/products/heimdall/"&gt;the libusb-based Heimdall&lt;/a&gt;, but I'm having a hard time finding suitable images for the Desire HD, or much information about it. When I reboot my HD into what I think is download mode (bootloader menu -&gt; recovery) heimdall can't see the device - whether I'm using the libusb drivers or the default drivers. Odin doesn't seem to see it either, and it looks like both are designed primarily for Samsung phones.&lt;/p&gt;

&lt;p&gt;Stuck!&lt;/p&gt;

&lt;p&gt;The long term solution is, as noted below, buy from a less closed and restrictive vendor. If I wanted a locked down device I would've bought an iPhone, it would've at least had more than a half day's battery life.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;PS: The next person who posts a guide/walkthrough/howto without &lt;b&gt;listing the version number of the latest firmware for which it is known to work&lt;/b&gt; is going to find themselves in a world of pain if I ever find them. The amount of stunningly bad writing on this topic is incredible.&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPDATE:&lt;/b&gt; A downgrade was successful with the &lt;a href="http://forum.xda-developers.com/showthread.php?t=1259821"&gt;AAHK (Advanced ACE Hack Kit)&lt;/a&gt;, after which I could root the phone with &lt;code&gt;fre3vo&lt;/code&gt;:&lt;/p&gt;

&lt;pre&gt;
C:\Users\Craig\Downloads\Downgrade_v3\Downgrade&gt;adb shell
$ /data/local/tmp/fre3vo -debug -start FBB00000 -end FFFFFFFF
/data/local/tmp/fre3vo -debug -start FBB00000 -end FFFFFFFF
fre3vo by #teamwin
Please wait...
Attempting to modify ro.secure property...
fb_fix_screeninfo:
  id: msmfb
  smem_start: 802160640
  smem_len: 3145728
  type: 0
  type_aux: 0
  visual: 2
  xpanstep: 0
  ypanstep: 1
  line_length: 1920
  mmio_start: 0
  accel: 0
fb_var_screeninfo:
  xres: 480
  yres: 800
  xres_virtual: 480
  yres_virtual: 1600
  xoffset: 0
  yoffset: 0
  bits_per_pixel: 32
  activate: 16
  height: 106
  width: 62
  rotate: 0
  grayscale: 0
  nonstd: 0
  accel_flags: 0
  pixclock: 0
  left_margin: 0
  right_margin: 0
  upper_margin: 0
  lower_margin: 0
  hsync_len: 0
  vsync_len: 0
  sync: 0
  vmode: 0
Buffer offset:      00000000
Buffer size:        8192
Scanning region fbb00000...
Potential exploit area found at address fbb7f800:1800.
Exploiting device...

C:\Users\Craig\Downloads\Downgrade_v3\Downgrade&gt;adb shell
#
&lt;/pre&gt;

&lt;p&gt;Unfortunately, ClockworkMod Recovery doesn't seem to work, though it's theoretically now flashed onto the phone. The phone still boots into regular Android recovery. I suspect that AAHK hasn't successfully unlocked the bootloader to S-OFF, probably because gfree seems to fail:&lt;/p&gt;

&lt;pre&gt;
# ./gfree -f
./gfree -f
--secu_flag off set
--cid set. CID will be changed to: 11111111
--sim_unlock. SIMLOCK will be removed
Section header entry size: 40
Number of section headers: 44
Total section header table size: 1760
Section header file offset: 0x000138b4 (80052)
Section index for section name string table: 41
String table offset: 0x000136fb (79611)
Searching for .modinfo section...
 - Section[16]: .modinfo
 -- offset: 0x00000a14 (2580)
 -- size: 0x000000cc (204)
Kernel release: 2.6.35.10-gd2564fb
New .modinfo section size: 204
Attempting to power cycle eMMC... Failed.
Module failed to load: No such file or directory
&lt;/pre&gt;

&lt;p&gt;&lt;b&gt;UPDATE:&lt;/b&gt; It looks like the radio downgrade failed the first time around. Re-running AAHK successfully downgraded the radio and got me ClockworkMod recovery on reboot into recovery mode. Victory! CM install in progress.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPDATE:&lt;/b&gt; CM 7 installed, but got into a reboot loop on startup. To fix this I rebooted back into clockworkmod recovery and used it to erase the cache and user data. A reboot then succeeded, dropping me into a clean Cyanogenmod install - without, of course, any of my data.&lt;/p&gt;

&lt;p&gt;I then grabbed the &lt;a href="http://wiki.cyanogenmod.com/index.php?title=Latest_Version/Google_Apps"&gt;latest Google Apps version&lt;/a&gt;, put it in the root of the SD card, and rebooted into recovery using Rom Manager's "reboot into recovery" mode, where I used "install zip from sdcard" to install the apps and did another "wipe data / factory reset".&lt;/p&gt;

&lt;p&gt;After that, a reboot bought me into a Cyanogen environment with the Google apps, Market, etc. Phew!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8243404624561638494?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8243404624561638494/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2012/01/ive-had-it-with-htc.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8243404624561638494'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8243404624561638494'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2012/01/ive-had-it-with-htc.html' title='I&apos;ve had it with HTC - thanks for the rescue, CyanogenMod + AAHK'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-1032645328693083849</id><published>2012-01-20T10:50:00.006+08:00</published><updated>2012-01-20T11:40:33.648+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='atmel'/><category scheme='http://www.blogger.com/atom/ns#' term='radio'/><category scheme='http://www.blogger.com/atom/ns#' term='microcontroller'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='soil moisture'/><title type='text'>Atmel ATtiny with built-in 433MHz (US: 310MHz) transmitter!</title><content type='html'>&lt;p&gt;While researching parts for my soil moisture sensors I stumbled across these awesome Atmel microcontrollers:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc4551.pdf"&gt;ATAM862-4
Microcontroller 
with UHF ASK/FSK 
Transmitter (433MHz non-US band)&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc4554.pdf"&gt;ATAM862-3 
Microcontroller 
with UHF 
ASK/FSK 
Transmitter (310MHz-330MHz US band)&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I was so excited I had to share. At about AU$8 each, these little beasties might make building wireless soil moisture sensors so much easier it's just not funny. The main problem is going to be ordering them, since Jaycar and Element14 don't carry them, and DigiKey has them as non-stock components with 4000 unit minimum volumes. They're 4-bit 8051-architecture micros so they're not going to be compatible with the ATmega or ATtiny range, so I lose the advantage of having the same arch on sensor and control system. For something as relatively simple as sampling an analog temperature and humidity sensor that may not be a big problem.&lt;/p&gt;

&lt;p&gt;It may still land up being easier to use an ATtiny for the analog sensor controller   and digital sensor data transmitter, so I can use (mostly) the same software tools as for the ATmega on the control board. I could then hook the sensor's ATtiny up to either some wiring for wired service, or to an RF transmitter IC for wireless operation without much if any change to the sensor codebase.&lt;/p&gt;

&lt;p&gt;Atmel also have a family of &lt;a href="http://atmel.com/dyn/products/devices.asp?category_id=171&amp;family_id=651&amp;subfamily_id=1629&amp;source=left_nav"&gt;RF receiver ICs&lt;/a&gt; (with matching tx modules or transceivers available) so I might be able to avoid the need for a breakout board / shield for the RF receiver support and just make it an optional component in the base design. Things like &lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc4596.pdf"&gt;the ATA5723
/ATA5724/ATA5728&lt;/a&gt; and the &lt;a href="http://www.atmel.com/dyn/resources/prod_documents/doc4596.pdf"&gt;ATA5745
/ATA5746&lt;/a&gt; RF receiver ICs could be awfully handy at about AU$4 each ... if I can find someone who'll sell them to me in less than 1,500 unit quantities. If not, there are lots of other highly integrated 433MHz RF receivers and transmitter ICs out there.&lt;/p&gt;

&lt;p&gt;The ATA8204P3-TKQY looks particularly suitable; it's a slower and cheaper unit without UHF, but that shouldn't be a biggie for my use. It's cheaper than any of the other units except the ATA8202-PXQW 19 on digi-key, and should do the job fine. It's surface mount so it won't be assembly-friendly, though. An alternative might be the &lt;a href="http://search.digikey.com/au/en/products/ALPHA-RX433S/ALPHA-RX433S-ND/2000718"&gt;ALPHA-RX433S from RF-Solutions&lt;/a&gt; as that's packaged as a little module that'd be a bit saner to solder up.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1032645328693083849?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1032645328693083849/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2012/01/atmel-attiny-with-built-in-433mhz-us.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1032645328693083849'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1032645328693083849'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2012/01/atmel-attiny-with-built-in-433mhz-us.html' title='Atmel ATtiny with built-in 433MHz (US: 310MHz) transmitter!'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8902547238880937413</id><published>2012-01-17T12:45:00.008+08:00</published><updated>2012-01-19T13:58:34.774+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='arduino'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='soil moisture'/><category scheme='http://www.blogger.com/atom/ns#' term='sensor'/><title type='text'>DIY DC soil moisture sensor - early test successful</title><content type='html'>&lt;img style="float:left; margin:0 10px 10px 0;cursor:pointer; cursor:hand;width: 240px; height: 320px;" src="http://2.bp.blogspot.com/-LzFYtpUTDW8/TxUK2fu50wI/AAAAAAAAAC0/eZyz-ZqHRTg/s320/1ykwh.jpg" border="0" alt="Laptop connected to Arduino connected to flowerpot" title="Laptop, microcontroller, flowerpot full of dirt. Just another normal day around here." id="BLOGGER_PHOTO_ID_5698472834854671106" /&gt;

&lt;p&gt;On the the progressive difficulty scale of home built soil moisture sensors the bottom of the ladder is a DC soil conductivity sensor that uses simple resistivity measurement.&lt;/p&gt;

&lt;p&gt;It took a couple of hours build one of those last night, most of which was spent incompetently attempting to produce a decent solder joint on steel wire and on the cleaned heads of galvanized nails. Anyone who can use a soldering iron without being a hazard to themselves and those around them should be able to whip something like this up in a few minutes.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;First step: Simplest of the simple DC resistive sensor&lt;/h2&gt;

&lt;p&gt;After looking at the &lt;a href="http://gardenbot.org/howTo/soilMoisture/"&gt;GardenBot&lt;/a&gt; design by Andrew Frueh, I landed up using something between his newer voltage-flipping design and his basic capacitor-based design - something that probably shares the faults of both, but was good enough for a quick and dirty experiment.&lt;/p&gt;

&lt;p&gt;It's a simple circuit and it works, producing analog input values across a wide range that correlated well with soil moisture in my test flowerpot. It's also a stupid circuit that's really just a simple first-try toy, and isn't something I'd ever consider deploying in practice.&lt;/p&gt;

&lt;p&gt;The 10k resistor is there for current limiting in case of a short; the 300kΩ to 3MΩ resistor is for you to tune the voltage range to fit your sensor probe length, spacing, etc so you get a good range of values. I landed up using a 630kΩ resistor for a pair of ~1mm steel wire probes about 10cm long. Your needs will vary.&lt;/p&gt;

&lt;img style="margin: 0.5em; height: 320px; float: right;" src="http://1.bp.blogspot.com/-7dJ3kuGSRvw/TxUDieokhsI/AAAAAAAAACo/ca1RT8W3m6o/s320/Diagram1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5698464794380895938" /&gt;

&lt;p&gt;If you don't want this simple circuit always-on (and always electrolysing - ouch!) you can use a digital pin in OUTPUT mode instead of the +3.3V rail, keeping it LOW except when you want to take a reading. Note that pin 13 has an LED and resistor inline, so you may want to use a different pin.&lt;/p&gt;

&lt;h2&gt;Next step: AC resistive sensor&lt;/h2&gt;

&lt;p&gt;I'm determined to avoid using two digital pins like Andrew has done for his bidirectional current sensor, so I need to produce low voltage AC. For the next version I'll be experimenting with generating a low voltage ultra-low-current AC square wave, probably using an &lt;a href="http://hyperphysics.phy-astr.gsu.edu/hbase/electronic/square.html"&gt;astable multivibrator&lt;/a&gt; (also &lt;a href="http://en.wikipedia.org/wiki/Schmitt_trigger#Use_as_an_oscillator"&gt;on Wikipedia&lt;/a&gt;) so I can synchronize my sensor reads to the oscillator easily.&lt;/p&gt;

&lt;p&gt;Bizarrely it might be cheaper to do this with an IC containing an array of Schmitt triggers or with a &lt;a href="http://www.kpsec.freeuk.com/555timer.htm"&gt;555 IC&lt;/a&gt; like the &lt;a href="http://au.element14.com/stmicroelectronics/ne555n/ic-timer/dp/1467742"&gt;NE555&lt;/a&gt; or &lt;a href="http://au.element14.com/texas-instruments/tlc555cp/ic-timer-cmos-dip8-555/dp/1103036?in_merch=Featured%20Texas%20Instruments%20products"&gt;TLC555CP&lt;/a&gt; (used like &lt;a href="http://www.allaboutcircuits.com/vol_6/chpt_8/2.html"&gt;this&lt;/a&gt;) than with discrete transistor logic!&lt;/p&gt;

&lt;p&gt;Either way, the oscillator will have to be embedded as local electronics near the sensor to avoid RF on the long cables out to the sensors, but that shouldn't be a big problem with simple circuitry like this.&lt;/p&gt;

&lt;p&gt;Keep an eye out for the next post with work on AC/oscillator based resistive sensors.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ringerc/scrapcode/blob/master/arduino_avr/Arduino_Resistive_Soil_Moisture_Sensor_1.ino"&gt;https://github.com/ringerc/scrapcode/blob/master/arduino_avr/Arduino_Resistive_Soil_Moisture_Sensor_1.ino&lt;/a&gt;&lt;/p&gt;


&lt;script src="http://gist-it.appspot.com/github/ringerc/scrapcode/raw/master/arduino_avr/Arduino_Resistive_Soil_Moisture_Sensor_1.ino"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8902547238880937413?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8902547238880937413/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2012/01/diy-dc-soil-moisture-sensor.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8902547238880937413'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8902547238880937413'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2012/01/diy-dc-soil-moisture-sensor.html' title='DIY DC soil moisture sensor - early test successful'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-LzFYtpUTDW8/TxUK2fu50wI/AAAAAAAAAC0/eZyz-ZqHRTg/s72-c/1ykwh.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-2330399579146294030</id><published>2012-01-16T11:57:00.016+08:00</published><updated>2012-01-17T14:03:01.675+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='waterbot'/><category scheme='http://www.blogger.com/atom/ns#' term='soil moisture'/><category scheme='http://www.blogger.com/atom/ns#' term='humidity'/><title type='text'>Interested in soil moisture sensors and irrigation control? Start with the UF/IFAS virual extension series</title><content type='html'>&lt;p&gt;I've been having ... "fun" ... trying to find a way to build an affordable network of soil moisture sensors that don't require too much looking after.&lt;/p&gt;

&lt;p&gt;It's harder than you'd think, but &lt;a href="http://vfd.ifas.ufl.edu/gainesville/irrigation/index.shtml"&gt;this UF/IFAS Virtual Extension series on soil moisture and irrigation&lt;/a&gt; has made it a lot easier to understand the different approaches and sensor types. It'll help you understand the differences between resistive and capacitive soil moisture measurement, introduce alternatives like tensiometers, etc. This is important whether you plan to DIY your sensors or buy off the shelf.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Unfortunately, the article series says very little about how to get your hands on working soil moisture sensors without your wallet screaming for mercy. I'm still working on that bit.&lt;/p&gt;

&lt;p&gt;Promising options that need further investigation include:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;img src="http://vegetronix.com/Products/VH400/VH400-Vertical.jpg" height="60" width="10" style="float: right; margin: 0.5em;"/&gt;&lt;a href="http://vegetronix.com/"&gt;Vegetronix (USA)&lt;/a&gt; VH400: Capacitive probe (?), US$35 each, shipping cost to non-USA addresses is extortionate. Not yet tested.&lt;/li&gt;

&lt;li&gt;&lt;img src="http://au.element14.com/productimages/farnell/standard/1566264-40.jpg" height="60" width="40" style="float: right; margin: 0.5em;"/&gt;&lt;a href="http://sensing.honeywell.com/humidity%20sensors%20line%20guide_009034-3-en_final.pdf"&gt;Honeywell HCH-1000 Hydrogrometer&lt;/a&gt;: At &lt;a href="http://au.element14.com/honeywell-s-c/hch-1000-002/sensor-humidity-capacitive/dp/1566265"&gt;$5&lt;/a&gt; to &lt;a href="http://au.element14.com/honeywell-s-c/hch-1000-001/sensor-humidity-capacitive/dp/1566264"&gt;$7 ea&lt;/a&gt;, promising. Available &lt;a href="http://au.element14.com/jsp/level5/module.jsp?moduleId=en/505708.xml"&gt;cased or uncased&lt;/a&gt;. See &lt;a href="http://www.farnell.com/datasheets/34209.pdf"&gt;HCH-1000 datasheet&lt;/a&gt;. They're even on pins for our soldering sanity. As these seem to be designed for air humidity I'm not sure they're going to be suitable, but at this price it's worth a go. &lt;b&gt;To test.&lt;/b&gt;&lt;/li&gt;

&lt;li&gt;&lt;a href="http://blog.ringerc.id.au/2012/01/diy-dc-soil-moisture-sensor.html"&gt;DIY DC resistive soil moisture sensor&lt;/a&gt;: Sensitive to impurities in the water, like salt levels, and quite temperature sensitive. Prone to electrolysis causing corrosion of the electrodes. &lt;a href="http://blog.ringerc.id.au/2012/01/diy-dc-soil-moisture-sensor.html"&gt;Tested successfully&lt;/a&gt; but too crude for real-world use.&lt;/li&gt;

&lt;li&gt;DIY AC resistive soil moisture sensor: As the DC version, but less prone to corrosion in exchange for being a bit trickier to build (or using up extra I/O pins for the lazy-man's version). Yet to test.&lt;/li&gt;

&lt;li&gt;DIY &lt;a href="http://vfd.ifas.ufl.edu/gainesville/irrigation/capacitance_probe.shtml"&gt;capacative soil moisture sensor&lt;/a&gt;: These are less sensitive to soil composition and water impurities, but appear to be very hard to get right. Yet to test.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Less promising or totally non-viable options include:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;DIY &lt;a href="http://vfd.ifas.ufl.edu/gainesville/irrigation/neutron_probe.shtml"&gt;neutron probe&lt;/a&gt;: Just kidding. Don't even think about it.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.senviro.com/index.html"&gt;Senviro&lt;/a&gt; sensors. Senviro claims to be focused on low-cost sensors, but their website is useless. When I called them (Jan 2102) I was told that the company has been essentially mothballed for the last year and a half, though they're looking at picking it back up to focus on long range wireless sensors for park/golf course/industrial use, focused on high durability and long life at a price of "hundreds not thousands". Still not really in our range; not testing.&lt;/li&gt;
&lt;li&gt;&lt;img src="http://www.holmanindustries.com.au/products/Wireless%20Soil%20Moisture%20Monitor%20and%20Probe/files/Monitor%20and%20Probe%20front%20image.JPG" height="60" width="30" style="float: right; margin: 0.5em;"/&gt;&lt;a href="http://www.holmanindustries.com.au/products.asp?product=Wireless%20Soil%20Moisture%20Monitor%20and%20Probe"&gt;Holman WS7880 433MHz wireless soil moisture sensor&lt;/a&gt;: A Western Australian supplied sensor, presumably a rebadge of something made overseas. Available as sensor and base station bundle or as stand-alone sensor, since each base station supports 3 sensors. At AU$17 (Bunnings) these aren't cheap but they're not that expensive compared to many of the other options. They require near-line-of-sight and won't work through multiple walls (as specified, even through one). The sensors take 2xAA batteries. They're a big and white and might be a theft/vandalism risk, plus they're not that pretty, but at least they'll be hard to accidentally mow over if installed in lawn. I think they're using resistive metering. I bought a couple for testing and they work well when used as designed. Being able to use these from a microcontroller - either by repurposing the base station or by directly receiving the 433MHz RF signals from the base stations - would be exciting and fun, but is beyond the ability and resources I have at the moment without at least some documentation. I have some but won't be testing them with my microcontroller unless I can find out more about their radio protocol.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://content.honeywell.com/sensing/prodinfo/humiditymoisture/"&gt;Honeywell HIH-6131 series Hydrogrometer&lt;/a&gt; (&lt;a href="http://au.element14.com/honeywell-s-c/hih-6131-021-001/humidity-temperature-sensor/dp/1961622?in_merch=New%20Products"&gt;eg HIH-6131-021-001 @ AU$27 ea for 10+&lt;/a&gt;): i2c-enabled sensor with on-chip signalling, optional temperature sensor integration. See also &lt;a href="http://sensing.honeywell.com/products/humidity_sensors?Ne=2308&amp;N=3217"&gt;this product page&lt;/a&gt;. I'm not sure even the filtered versions can cope with immersion, they're more designed for air humidity. Not that cheap ($28 and waaay up) or suitable, so untested. Pity, as i2c bus support would be great.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://sensing.honeywell.com/humidity%20sensors%20line%20guide_009034-3-en_final.pdf"&gt;Honeywell HIH-4000/HIH-4100/HIH-4200/HIH-4201 series  Hydrogrometers&lt;/a&gt;: Simple voltage-output sensor. Still not less than $25 ea in 10-unit quantities, filtered variants over $35. No tested.&lt;/li&gt;
&lt;li&gt;HIH-4602: Thermisisistor. Over $100 ea, so totally unsuitable, not further investigated.&lt;/li&gt;
&lt;li&gt;&lt;a href="http://www.sensirion.com/en/01_humidity_sensors/01_humidity_sensor_sht10.htm"&gt;Sensirion SHT10/SHT11/SHT15/SHT71/SHT75&lt;/a&gt;: The "low cost" SHT10 is still over AU$60 anywhere I can find it, so I have no plans to test this for home irrigation control. Not tested.&lt;/li&gt;
&lt;li&gt;&lt;a href="https://www.silabs.com/support/pages/support.aspx?ProductFamily=Humidity+Sensors"&gt;Silicon Labs humidity sensors&lt;/a&gt; don't actually appear to exist. Their site lists the category but no datasheets, and none of their catalog vendors carry them. Not tested, possible vapourware.&lt;/li&gt;
&lt;li&gt;Various scientific/industrial integrated soil moisture measurement systems: Seriously expensive, usually hundreds or thousands of dollars. When looking for sensors, this is mostly what you find. Obviously not tested.&lt;/li&gt;
&lt;li&gt;Various Tensiometers: Not only are soil tensiometers expensive, but they don't work well in dry soil and require a lot of babying. They're totally unsuitable for irrigation control where they'll be left in place for long periods. Not tested.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I'm planning follow-up posts as I try different DIY meter designs. I'll include lists of articles I found during research and used as references, so hopefully it'll help others doing similar work down the track even if they all get given the big red X at the end of testing.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-2330399579146294030?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://vfd.ifas.ufl.edu/gainesville/irrigation/index.shtml' title='Interested in soil moisture sensors and irrigation control? Start with the UF/IFAS virual extension series'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/2330399579146294030/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2012/01/interested-in-soil-moisture-sensors-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2330399579146294030'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2330399579146294030'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2012/01/interested-in-soil-moisture-sensors-and.html' title='Interested in soil moisture sensors and irrigation control? Start with the UF/IFAS virual extension series'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4113273143780073580</id><published>2012-01-14T21:40:00.028+08:00</published><updated>2012-01-19T12:00:56.356+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='arduino'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='waterbot'/><category scheme='http://www.blogger.com/atom/ns#' term='sensor'/><title type='text'>Using a RHT03 (aliases: RHT-22, DHT22, AM2302) temperature/humidity sensor from Arduino</title><content type='html'>&lt;img src="http://dlnmh9ip6v2uc.cloudfront.net/images/products/10167-01_i_th.jpg" style="float: right; margin: 1em;"/&gt;

&lt;p&gt;I picked up a &lt;a href="http://littlebirdelectronics.com/products/humidity-and-temperature-sensor-dht22"&gt;nice compact little temperature and relative humidity sensor called the &lt;code&gt;RHT03&lt;/code&gt;&lt;/a&gt; for a project from &lt;a href="http://littlebirdelectronics.com/"&gt;Little Bird Electronics&lt;/a&gt;. It and very similar parts appear to go by the names &lt;code&gt;RHT-22&lt;/code&gt;, &lt;code&gt;DHT-22&lt;/code&gt; and &lt;code&gt;AM2302&lt;/code&gt;. You can find the part at SparkFun, Adafruit, etc too.&lt;/p&gt;

&lt;p&gt;It took a lot more work to get it working than I expected, so I thought I'd write it up here for anyone else who is looking into it. There's sample code at the end of this post, but you should probably read the details because this is a quirky beast.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPDATE:&lt;/b&gt; I since found a library &lt;a href="https://github.com/nethoncho/Arduino-DHT22"&gt;on GitHub: nethoncho/Arduino-DHT22&lt;/a&gt; that does a better job more simply and compactly. It works fine with my sensor. It needed some changes for Arduino 1.0 and some further tweaks to work how I wanted, so I've uploaded a fork here: &lt;a href="https://github.com/ringerc/Arduino-DHT22"&gt;https://github.com/ringerc/Arduino-DHT22&lt;/a&gt;.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Given that it's clearly a pretty commonplace part, I thought reading it couldn't be too tricky. Foolish me!Its &lt;a href="http://dlnmh9ip6v2uc.cloudfront.net/datasheets/Sensors/Weather/RHT03.pdf"&gt;datasheet almost in engrish&lt;/a&gt; and the &lt;a href="http://www.sparkfun.com/datasheets/Sensors/Temperature/DHT22.doc"&gt;sample code&lt;/a&gt; (warning: Word document) is pretty horrid. The sensor communicates over a custom (non-Dallas-compatible) 1-wire interface where the lengths of low and high pulses convey information. Parts of the datasheet are practically fiction.&lt;/p&gt;

&lt;p&gt;Particular things to note when working with this sensor are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;When requesting a sensor read, you may have to &lt;i&gt;actively&lt;/i&gt; pull the signal line up to +5V for 20-40μs after your initial 1 - 10ms low pulse.&lt;/li&gt;
&lt;li&gt;The datasheet is a lie. In particular, instead of a neat 50μs low signal before sending a bit, my sensor may pull the line low for anything from 35 to 75μs. Similarly, 0-bit high pulses may be from 10-40μs instead of the documented 22-26μs, and 1-bit high pulses may be 60-85μs instead of 70μs.&lt;/li&gt;
&lt;li&gt;Even when you're being tolerant of timing weirdness, you'll probably get the odd bad read. Adding a cap across the +5VDC input (as mentioned in the datasheet) doesn't seem to help, so it's probably not power fluctuation. Be prepared to re-try reads and remember to wait at least 2s between reads.&lt;/li&gt;
&lt;li&gt;(Didn't trouble me, but): Remember to let the sensor settle for at least 2s after power-on before attempting a read, and at least 2s between reads.&lt;/li&gt;
&lt;li&gt;Your code must be extremely fast to avoid missing state transitions when capturing the bit stream, whether you're using a looping or interrupt-driven capture method. I landed up buffering the stream to an array of pulse lengths, then interpreting it after the full stream was buffered. This would be less of an issue if the timings from the sensor weren't so erratic to start with.&lt;/li&gt;
&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;Having finally got it working, I thought I'd post some sample code. &lt;b&gt;It's very much that - sample code - not a library suitable for direct re-use. In particular, &lt;i&gt;this code does not handle millisecond counter roll-over at all&lt;/i&gt;.&lt;/b&gt; Reads during which the millisecond counter rolls over will fail. That said, at least the checksum is verified and negative temperatures are handled correctly. (By the way, a USB cable coming out of the freezer door seal looks &lt;i&gt;really&lt;/i&gt; weird).&lt;/p&gt;

&lt;p&gt;You should really look at the GitHub project for DHT-22 linked to above rather than using this code.&lt;/p&gt;

&lt;p&gt;To try the code with an Arduino Uno R3 (ATMEGA328 based), connect the sensor's I/O wire (pin 2) to digital pin 2 on the Arduino. Connect sensor pin 1 to +5V and sensor pin 3 and 4 to GND. Theoretically you should be able to let pin 3 float, but I've seen reports that some sensors have their internal ground on pin 3 instead of pin 4, so it won't hurt to ground both.&lt;/p&gt;

&lt;p&gt;I use pin 2 on the Arduino because it's one of the two pins that support external interrupts on the basic Arduino boards. Pin 3 will also work, just change &lt;code&gt;sensorPin&lt;/code&gt; in the example code.&lt;/p&gt;

&lt;p&gt;You could make the code work without requiring interrupts, either by busy-waiting in a loop to detect level changes or by using the &lt;a href="http://arduino.cc/en/Reference/pulseIn"&gt;&lt;code&gt;pulseIn&lt;/code&gt;&lt;/a&gt; function to read pulse lengths. I haven't implemented that, but it shouldn't be hard. You can re-use all the code except &lt;code&gt;readSensor&lt;/code&gt;, which must be rewritten to capture the &lt;code&gt;timings&lt;/code&gt; array a non-interrupt-driven way. The trick is making it fast enough.&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ringerc/scrapcode/blob/master/arduino_avr/RHT03_DHT22_humidity_temp_sensor.c"&gt;https://github.com/ringerc/scrapcode/blob/master/arduino_avr/RHT03_DHT22_humidity_temp_sensor.c&lt;/a&gt;&lt;/p&gt;

&lt;script src="http://gist-it.appspot.com/github/ringerc/scrapcode/raw/master/arduino_avr/RHT03_DHT22_humidity_temp_sensor.c"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4113273143780073580?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4113273143780073580/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2012/01/using-rht03-aliases-rht-22.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4113273143780073580'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4113273143780073580'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2012/01/using-rht03-aliases-rht-22.html' title='Using a RHT03 (aliases: RHT-22, DHT22, AM2302) temperature/humidity sensor from Arduino'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-6797486554257170402</id><published>2012-01-12T21:50:00.012+08:00</published><updated>2012-01-19T12:07:34.723+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='arduno'/><title type='text'>Extending Arduino example CIRC-05 to use hardware SPI control</title><content type='html'>&lt;p&gt;For kicks, I've extended the basic Arduino &lt;a href="http://en.wikipedia.org/wiki/Shift_register"&gt;shift register&lt;/a&gt; LED control example &lt;code&gt;&lt;a href="http://www.oomlout.com/a/products/ardx/circ-05"&gt;CIRC-05&lt;/a&gt;&lt;/code&gt; from &lt;a href="http://www.oomlout.com/"&gt;www.oomlout.com&lt;/a&gt; to use the Arduino's hardware SPI routines instead of software signalling.&lt;/p&gt;

&lt;p&gt;As someone who has done very little with low-level electronics and who didn't know what SPI even was until today, this was embarrassingly easy. Kudos to the excellent Arduino libraries and the great documentation for making this simple.&lt;/p&gt;

&lt;p&gt;I'm posting the re-written example for &lt;code&gt;CIRC-05&lt;/code&gt; here. It has the original software-based control as well as support for SPI, so you can see how similar the methods are.&lt;/p&gt;

&lt;p&gt;(BTW, if you were wondering what a "latch" is in the IC, see &lt;a href="http://www.play-hookey.com/digital/experiments/rtl_bistable.html"&gt;this example&lt;/a&gt;.)&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;The only change required to the example circuit is that you must use pins 11 and 13 instead of pins 2 and 3, as the Arduino hardware SPI support has MOSI locked to pin 11 and CLK locked to pin 13. It also uses pin 12 for MISO, but we're not using that in this example.&lt;/p&gt;

&lt;p&gt;Use output 11 where the wiring diagram says use output 2, and use output 13 instead of 3. No other changes are required.&lt;/p&gt;

&lt;p&gt;The actual circuit on the breadboard is not altered in any way.&lt;/p&gt;

&lt;p&gt;Here's the sketch code:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ringerc/scrapcode/blob/master/arduino_avr/Arduino_Tutorial_CIRC05_hardware_spi.c"&gt;https://github.com/ringerc/scrapcode/blob/master/arduino_avr/Arduino_Tutorial_CIRC05_hardware_spi.c&lt;/a&gt;&lt;/p&gt;

&lt;script src="http://gist-it.appspot.com/github/ringerc/scrapcode/raw/master/arduino_avr/Arduino_Tutorial_CIRC05_hardware_spi.c"&gt;&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-6797486554257170402?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.oomlout.com/a/products/ardx/circ-05' title='Extending Arduino example CIRC-05 to use hardware SPI control'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/6797486554257170402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2012/01/extending-arduino-example-circ-05-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/6797486554257170402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/6797486554257170402'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2012/01/extending-arduino-example-circ-05-to.html' title='Extending Arduino example CIRC-05 to use hardware SPI control'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4671729492028192109</id><published>2012-01-12T10:39:00.006+08:00</published><updated>2012-01-12T11:12:50.698+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='electronics'/><category scheme='http://www.blogger.com/atom/ns#' term='sparkfun'/><category scheme='http://www.blogger.com/atom/ns#' term='arduno'/><title type='text'>SparkFun Inventors Kit CIRC-03 - Motor not working (spinning)? It's an error in the instructions</title><content type='html'>&lt;p&gt;I've started playing with some basic tutorial/toy electronics stuff using the Arduino platform and the "SparkFun Arduino Inventors' Kit" (hardly "inventors'", but anyway...) after picking it up as part of an order from the awesome outfit &lt;a href="http://littlebirdelectronics.com/"&gt;Little Bird Electronics&lt;/a&gt;. While generally good, I've hit an interesting issue with the kit that's worth documenting for anyone else who has it.&lt;/p&gt;

&lt;p&gt;The short version: If you're using the SparkFun kit that specifies a 10kΩ resistor and the test circuit doesn't work (the motor won't spin) you might need to use a lower valued resistor between the transistor and pin 9 of the Arduino board.&lt;/p&gt;

&lt;p&gt;If this is the case, you'll find that when you flick the motor's drive around with your fingers so it spins, sometimes it'll spin down slowly and sometimes it'll stop suddenly, depending on whether the Arduino is currently trying to drive it or not.&lt;/p&gt;

&lt;p&gt;Check for all the usual errors before assuming the issue described here is what's wrong with your circuit. You might've reversed the flyback diode, made a poor connection on a power rail, etc etc.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;The SparkFun kit for &lt;code&gt;CIRC-03&lt;/code&gt; ("Spin Motor Spin - Transitor &amp;amp; Motor") is based on &lt;a href="http://oomlout.com/a/products/ardx/circ-03"&gt;Ardx CIRC03&lt;/a&gt; but it has one difference that looks like it may be a significant error.&lt;/p&gt;

&lt;p&gt;The SparkFun instructions specify a 10kΩ resistor but, at least with the motor supplied in my kit, the test circuit won't actually work with that much resistance between the &lt;code&gt;P2N2222AG&lt;/code&gt; (actually marked &lt;code&gt;P2N222A18&lt;/code&gt; in my kit) transistor's base pin and pin 9 of the Arduino Uno board. Replacing the 10kΩ resistor with a 2.2kΩ resistor - like that specified by the ardx instructions - fixes the problem, allowing the transistor to pass enough current to let the motor spin.&lt;/p&gt;

&lt;p&gt;A quick read suggests that &lt;code&gt;P2N2222AG&lt;/code&gt; and &lt;code&gt;P2N222218&lt;/code&gt; are functionally equivalent, with different date codes or serial numbers, so that shouldn't be the cause of the issue. I'm not too sure on this point, though.&lt;/p&gt;

&lt;p&gt;If you bought the kit and don't do any other electronics stuff you may not have a 2.2kΩ (red-red-red) resistor or anything vaguely similar lying around. If so, you can always connect up 6 (or so) of the 330Ω resistors supplied in the kit in series to achieve roughly the same resistance. Remember that to get them connected in series you must put the input of one and the output of another alone on a breadboard row. Connecting five or six of them will give you a sort of stair-step or saw-tooth pattern where you have resistors connecting, say, F14-G13, F13-G12, F12-G11, F11-G10, F10-G9 and F9-G8. You can then plug the lead from the transistor base pin into F8 and the lead from Arduino pin 9 into the other end of the resistor series at G14. If your resistors have long leads and you don't want to clip them you have to space everything out a bit more than this to avoid shorts across all those resistor wires; this is just an example of how it can work.&lt;/p&gt;

&lt;p&gt;To make sure my motor wasn't just defective I bridged it across the +5V and -GND rails. This probably wasn't a smart thing to do without a resistor in series with the motor; I got away with it, but you might not.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4671729492028192109?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4671729492028192109/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2012/01/sparkfun-inventors-kit-circ-03-motor.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4671729492028192109'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4671729492028192109'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2012/01/sparkfun-inventors-kit-circ-03-motor.html' title='SparkFun Inventors Kit CIRC-03 - Motor not working (spinning)? It&apos;s an error in the instructions'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-7629730123574008924</id><published>2011-12-11T19:10:00.006+08:00</published><updated>2011-12-11T19:30:27.173+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='xargs'/><category scheme='http://www.blogger.com/atom/ns#' term='find'/><category scheme='http://www.blogger.com/atom/ns#' term='cmd'/><category scheme='http://www.blogger.com/atom/ns#' term='windows'/><category scheme='http://www.blogger.com/atom/ns#' term='shell'/><category scheme='http://www.blogger.com/atom/ns#' term='script'/><category scheme='http://www.blogger.com/atom/ns#' term='scripting'/><title type='text'>Windows command line survival: findfiles, find+xargs's dim cousin</title><content type='html'>&lt;p&gt;Sometimes, we have to use the Microsoft Windows &lt;code&gt;cmd.exe&lt;/code&gt; command line. Maybe it's at a client site or on a user machine where installing PowerShell, Cygwin or Mingw isn't appropriate. Maybe you're on a domain member so locked down by group policy you can't do anything if you want to. Either way, you have to use &lt;code&gt;cmd.exe&lt;/code&gt;, and you're swearing.&lt;/p&gt;

&lt;p&gt;I find out about the odd useful command that makes the Windows shell more usable, and I'll be trying to post them here. The first is &lt;code&gt;forfiles&lt;/code&gt;, a basic alternative to &lt;code&gt;find -exec&lt;/code&gt; or &lt;code&gt;find|xargs&lt;/code&gt;.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;If you're missing &lt;code&gt;find -exec&lt;/code&gt; (or &lt;code&gt;xargs&lt;/code&gt;) it turns out that Windows provides &lt;code&gt;forfiles&lt;/code&gt; to do a limited subset of the work that &lt;code&gt;find -exec&lt;/code&gt; can do. This'll help with simple jobs like flattening out a deep directory tree into a flat folder of files, running a command recursively on every file in a directory, etc.&lt;/p&gt;

&lt;p&gt;For example, here's how to find every TrueType font file within the deep directory tree &lt;code&gt;C:\deeptree&lt;/code&gt; and copy the files to &lt;code&gt;C:\flat&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;
forfiles /s /p C:\deeptree /M *.ttf /c "cmd /c move @file C:\flat"
&lt;/pre&gt;

&lt;p&gt;You &lt;i&gt;really&lt;/i&gt; want to use an absolute path when you invoke &lt;code&gt;move&lt;/code&gt; here, as it's invoked with the file's parent directory as its cwd, not the directory you ran the findfiles command in. Use a relative path and you'll just land up renaming all your files - which is a handy trick, but not what you intended.&lt;/p&gt;

&lt;p&gt;In reality you'll also want to use more appropriate paths than folders off the root of C:\ . Beware spaces in pathnames, and remember that Windows has totally different rules to *nix in this area.&lt;/p&gt;

&lt;p&gt;This particular case can be just as easily accomplished by doing a search for &lt;code&gt;*.ttf&lt;/code&gt; from the parent directory, selecting all, copying, and pasting into an empty directory. More complex uses of this command like recursive renames or command invocations, not so much.&lt;/p&gt;

&lt;p&gt;See &lt;code&gt;forfiles /?&lt;/code&gt; for help.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-7629730123574008924?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7629730123574008924/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/12/windows-command-line-survival-findfiles.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7629730123574008924'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7629730123574008924'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/12/windows-command-line-survival-findfiles.html' title='Windows command line survival: findfiles, find+xargs&apos;s dim cousin'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-5139654047211257869</id><published>2011-12-11T18:22:00.011+08:00</published><updated>2012-01-19T11:40:29.507+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='opentype'/><category scheme='http://www.blogger.com/atom/ns#' term='fonts'/><category scheme='http://www.blogger.com/atom/ns#' term='truetype'/><category scheme='http://www.blogger.com/atom/ns#' term='fontforge'/><category scheme='http://www.blogger.com/atom/ns#' term='script'/><title type='text'>Bulk conversion of OpenType (OTF) to TrueType (TTF) or Type 1(PFA/PFB) fonts using FontForge</title><content type='html'>&lt;p&gt;FontForge is a great tool for converting fonts. I needed to convert a &lt;i&gt;lot&lt;/i&gt; of OpenType fonts to TrueType or Type 1 format to use them with Apache FOP, as FOP doesn't yet support OpenType fonts with CFF glyph formats. Doing the conversion in one FontForge script didn't work well, because (a) it leaked memory until it got OOM-killed and (b) it only used one CPU. So I wrote a helper shell script to control the job - posted here in case anyone else needs it later.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;The script:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Runs multiple FontForge processes ($CPUS) for faster conversion;&lt;/li&gt;
&lt;li&gt;Limits each FontForge process to converting 10 fonts before a new one is spawned, limiting memory leaks while avoiding too much launch overhead&lt;/li&gt;
&lt;li&gt;Filters the conversion log to eliminate messages I'm not so interested in seeing. You can tune the filter (awk script) to your taste.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;You can grab it &lt;a href="https://github.com/ringerc/scrapcode/blob/master/scripts/bulk_convert_fonts.sh"&gt;here&lt;/a&gt; (&lt;a href="https://raw.github.com/ringerc/scrapcode/master/scripts/bulk_convert_fonts.sh"&gt;raw text download&lt;/a&gt;)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5139654047211257869?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5139654047211257869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/12/bulk-conversion-of-opentype-otf-to.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5139654047211257869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5139654047211257869'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/12/bulk-conversion-of-opentype-otf-to.html' title='Bulk conversion of OpenType (OTF) to TrueType (TTF) or Type 1(PFA/PFB) fonts using FontForge'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-8326595878090539104</id><published>2011-12-07T13:41:00.006+08:00</published><updated>2011-12-07T14:20:41.779+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='postgresql'/><title type='text'>PostgreSQL: Great even when you can see the warts</title><content type='html'>&lt;p&gt;I’m a very happy &lt;a href="http://postgresql.org/"&gt;PostgreSQL&lt;/a&gt; user, and was interested to read a &lt;a href="http://facility9.com/2011/12/ten-reasons-postgresql-is-better-than-sql-server/"&gt;recent post by an MS SQL Server DBA advocating PostgreSQL&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This post is a response, enumerating areas where Pg may not be so hot after all. You might say it's my tendency to always see the negative, or perhaps it's just my desire to inform others of the downsides of something so the loud advocacy doesn't leave them feeling let-down when the reality hits. I'd like to think it's the latter. Telling someone how it is from the start is much better than showing them the rosy-goggles view until they're committed. So, from an enthusiastic PostgreSQL user who spends a lot of time helping out on the mailing lists, here's a list of some of the warts and quirks I'm aware of, so you don't have to find out about them the hard way:&lt;/p&gt;

&lt;p&gt;This post is based on PostgreSQL 9.0, as I haven't really got into 9.1 yet. Each version improves a great deal, so don't be surprised if some of these issues are gone by 9.2.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;ul&gt;

&lt;li&gt;&lt;b&gt;Limited CPU concurrency&lt;/b&gt;. Pg can run many queries on many different CPUs, but one query can only run on one CPU at a time. If you have one really big, CPU-expensive query, Pg can’t complete it faster by using multiple CPUs. There are some limitations on concurrent disk use, too, but they’re much less significant. None of this affects workloads with larger numbers of smaller queries, where Pg easily maxes out the machine.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;No query queueing or admission control (though connection pools help a lot).&lt;/b&gt; Pg can’t natively be told to “run no more than 10 queries concurrently, queuing connections in submission order”; every connection can be actively executing a query at the same time. Too many actively working connections lead to contention for resources, excess context switches, etc and slow overall throughput. Every connection has its own query executor, so even high (many hundreds) idle connection counts can be expensive in synchronization overhead and in RAM, plus they tend to lead to "stampeding herd" problems when the DB or app is restarted. Pg has a “sweet spot” for number of actively working connections that varies depending on the hardware, and it’s usually best to use a connection pool to queue queries and stop it exceeding that sweet spot. Good &lt;a href="http://wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling"&gt;connection pools&lt;/a&gt; like &lt;a href="www.pgpool.net/"&gt;PgPool-II&lt;/a&gt; and &lt;a href="http://pgfoundry.org/projects/pgbouncer/"&gt;PgBouncer&lt;/a&gt; are available, so this is more of a wart than a real problem.&lt;/li&gt;

&lt;li&gt;PostgreSQL has &lt;b&gt;no true stored procedures&lt;/b&gt; and &lt;b&gt;no autonomous transactions&lt;/b&gt;. PL/PgSQL functions always run within a transaction; they cannot commit that transaction or create others. (They *can* use savepoints via EXCEPTION blocks, though). dblink can be used as a workaround, but has its own issues. It looks like MS SQL Server may have the same limitation, but I’m not up to date with MS SQL Server and wanted to point this out in case it doesn’t.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;All-or-nothing built-in replication&lt;/b&gt;. &lt;a href="http://wiki.postgresql.org/wiki/Replication,_Clustering,_and_Connection_Pooling"&gt;3rd party replication options&lt;/a&gt; are not affected by this. You can’t currently control which databases or tables get replicated when you’re using the built-in wal-shipping or streaming replication, it’s all or nothing. This can be a pain if you have a busy but unimportant database and a less-busy but very important database, where replication requirements differ. You can work around it by using 3rd party replication tools. Alternately, you can run two PostgreSQL instances, but they can’t use the same shared memory segment so you’ll pay a RAM cost for it, plus they have to be on different ports or IPs.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;No automatic savepoints&lt;/b&gt;. If one statement in a transaction fails, the whol transaction is automatically rolled back. You can’t pick it up again from the last successful statement unless you’ve been issuing SAVEPOINT statements before each statement that might fail. Savepoints have a performance cost, too, so this can slow things down as well as being a pain. In practice Pg’s behaviour is usually a good thing, but it’s occasonally a real pain.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Limited XML functionality&lt;/b&gt;. Support for in-db XSLT in particular is currently in an add-on module with known issues.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Limited performance monitoring&lt;/b&gt; and somewhat primitive performance monitoring tools.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;work_mem is per-sort (etc) not per-connection&lt;/b&gt;, so it’s hard to give a query or set of queries the maximum possible RAM to work with w/o risking exceeding available resources or pushing too much cached disk data out. In practice this isn’t too big an issue, but it’s a bit ugly.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Incompatible upgrades&lt;/b&gt;. Until PostgreSQL 8.4, you used to have to do a full dump and reload whenever you wanted to do a major release upgrade, which was a frequently raised argument against PostgreSQL for larger sites. pg_upgrade fixes this for later releases, making upgrades much less painful, but major release upgrades still often contain gotchas or backward compatibility breaks that may require code changes to some applications. On the upside, changes are generally to make common mistakes more obvious or harder to make, fix bugs or design issues, and generally improve the system; they're not made casually. Backward-incompatible changes are also always documented in the release notes.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;In other words: Pg isn’t perfect. It has quirks, and you should be aware of them. Despite that, it’s *REALLY* good for lots of workloads, and the price is hard to argue with. The generally really helpful and friendly community doesn’t hurt, either.&lt;/p&gt;

&lt;p&gt;The very incomplete list of things I love about using Pg is:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;b&gt;Continuous improvement&lt;/b&gt;. Every major release adds something that's really handy for a project you're working on or fixes some frustrating limitation or quirk.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;The community&lt;/b&gt; is friendly, open and very helpful. There are lots of people on the mailing lists and on Stack Overflow who're happy to help anyone who's made at least a minimal effort to explain their problem and their environment. Responses are usually detailed and helpful.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Devs are part of the community&lt;/b&gt;. Some of the core dev team are active on the user mailing lists - it's not uncommon to see a question posted, followed half an hour later by something like: "Yup, that's a bug; fixed in c6e3ac11b60ac4a8942ab964252d51c1c0bd8845 in head and backported to 9.0 and 8.4 for the next minor release". PostgreSQL is one of the only places where I've seen a bug found, reported, and fixed within half an hour.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;User contribution, involvement and influence&lt;/b&gt;. Got a bug that really annoys you or a feature you really need? You can quite possibly fix it, interest someone else in fixing it, or pay someone else to fix it. I've written fixes for issues that annoy me and had them accepted, so I speak from experience. If you're not a coder, you can learn, or you can pay someone  who already knows the codebase to work on an issue for you. Many major PostgreSQL features are developed through commercially sponsored work by consultants and the model appears to work well.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Support options.&lt;/b&gt; You have a choice in which organisations you use for support, ranging from "none, I'll do it myself" to &lt;a href="http://www.postgresql.org/support/professional_support/"&gt;a variety of consultants and companies&lt;/a&gt;. This means you're not stuck with a single complacent support provider who knows you can't go anywhere else if you don't like the price or the service. If you're not paying for support you can't demand anyone else fix an issue for you - but then when was the last time you had any luck getting Oracle or MS to fix a bug in software you &lt;i&gt;were&lt;/i&gt; paying support for? With Pg, you can (via consultants or direct participation) get an important fix into the next patch release, and &lt;i&gt;include it in your own builds until then&lt;/i&gt; so you have it immediately.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Extensibility&lt;/b&gt;. The ability to add your own data types, procedural languages, etc comes in very handy when you're doing more than writing accounting software.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;Stability and a focus on correctness&lt;/b&gt;. PostgreSQL doesn't eat your data, and if it's not sure if something's safe it'll throw an error rather than going ahead and making it up as it goes along. This is &lt;i&gt;really&lt;/i&gt; nice when you care about your data.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;&lt;a href="http://www.postgresql.org/docs/"&gt;Great documentation&lt;/a&gt;&lt;/b&gt; makes a big difference to how nice Pg is to use day-to-day, as does the excellent &lt;code&gt;psql&lt;/code&gt; command line client with its good error messages, good UI and built-in help.&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/427258967255390991-8326595878090539104?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8326595878090539104/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/12/postgresql-great-even-when-you-can-see.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8326595878090539104'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8326595878090539104'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/12/postgresql-great-even-when-you-can-see.html' title='PostgreSQL: Great even when you can see the warts'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-2630120015928653737</id><published>2011-07-27T09:25:00.004+08:00</published><updated>2011-07-28T20:42:54.644+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>No further work on Kobo</title><content type='html'>&lt;p&gt;I've stopped work on the Kobo software and dev env.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;The Kobo is a fun platform but there isn't much point developing addons and plugins, because only a tiny proportion of Kobo owners will even realize it's a computer, let alone one they can enhance.&lt;/p&gt;

&lt;p&gt;Most of the things I want to improve about the Kobo require access to the source code for the Nickel eReader application that runs on it. Kobo are not releasing this code - not even under NDA or other restrictive terms that permit fixes to be sent back to them without permitting distribution of the code to others.&lt;/p&gt;

&lt;p&gt;The rest of the things I'd like to do need the QWS (Qt Window System) driver that's currently distributed only as a statically linked component of nickel. Without this I can't write my own interactive Kobo apps unless I hijack nickel's startup with a qt plugin hook. Doing it that way is ugly, inefficient, and prone to breakage, plus it renders the device useless as an eReader.&lt;/p&gt;

&lt;p&gt;Given this, working on Kobo isn't going to be a productive use of my time. I learned a bit about ARM embedded systems and cross compiling, but I think I'll be leaving it at that.&lt;/p&gt;

&lt;hr/&gt;

&lt;p&gt;&lt;b&gt;Update&lt;/b&gt; to address Dan C's comments:&lt;/p&gt;

&lt;p&gt;I'm not giving up on Kobo at all... just on developing fixes and enhancements for it.&lt;/p&gt;

&lt;p&gt;As for the company's loss/missed opportunity: it's really not that simple.&lt;/p&gt;

&lt;h2&gt;Kobo's product isn't the hardware&lt;/h2&gt;

&lt;p&gt;Kobo's product isn't really the hardware, it's their software, brand, publisher relationships, website, and bookseller relationships. Their hardware is really generic - it's the same, or very nearly the same, as several other eReader devices and is made by a 3rd party. &lt;/p&gt;

&lt;p&gt;The original Kobo was a netronix device, and I think the wifi is too. The Linux port and platform software on it was developed by Netronix, too, using an ARM SDK produced by CodeSourcery.&lt;/p&gt;

&lt;p&gt;What makes the Kobo different is the Qt-based user interface Kobo inc built - "Nickel" - and its helpers. This software is the main differentiator they have. If they release it under a full OSS license, they enable competitors (other booksellers, other hardware makers, etc) to easily rebrand it and bundle it on their own generic eReader devices. Kobo is already fighting two juggernauts - Amazon and Apple - in this market, and will probably be up against Google soon too. They can't afford to enable small clone-competititors to use their stuff without contributing back.&lt;/p&gt;

&lt;p&gt;GPLv3 isn't enough, either, because a rebrand could contribute and publish all their code but still direct sales to a different book store. There go the revenues Kobo relies on.&lt;/p&gt;

&lt;p&gt;What they'd need to do is a "source available" release rather than an "open source" release as we know it these days. One where you can obtain the source, edit it, rebuild the app, etc, but cannot distribute it on your own devices, change the target bookstore, etc.&lt;/p&gt;

&lt;p&gt;They've chosen not to do that. There may be good reasons why. Auditing sources for release to the public is expensive, even if it'll be released only under a restrictive license or even under an NDA. They'd also have to potentially fight enforcement actions against competitors who infringed despite the terms. Then there's the issue of DRM and Adobe Digital Editions, which quite likely has its own restrictive terms, probably including restrictions on release of sources for apps that use it.&lt;/p&gt;

&lt;h2&gt;Taking patches and dealing with random "developers" can be lots of work&lt;/h2&gt;

&lt;p&gt;Even if it were easy to release the sources, it's not clear that they'd see all that much benefit for the work. How much contribution would they really get? I speak from experience when I say that vetting and reviewing submitted patches is often harder than writing the code yourself. That's especially the case in environments people are less familiar with (like embedded linux developed using Linux desktops/workstations), projects with smaller communities, C++ projects using particular toolkits like Qt, etc.&lt;/p&gt;

&lt;p&gt;Now add the inevitable group of enthusiastic-but-incompetent newbie developers who want you to hold their hand all the way. Mix in a few abrasive personalities who know they're always right, a few angry self-righteous demanders with a severe attitude of entitlement, and a couple of right idiots. You get the potential for a huge time sink that takes devs away from real work or requires dedicated people to pay to take care of it.&lt;/p&gt;

&lt;h2&gt;The plugin API was a non-starter&lt;/h2&gt;

&lt;p&gt;The alternative, of a plugin API, was pretty much a waste of time.&lt;/p&gt;

&lt;p&gt;Writing a good plugin API is way harder than just opening up the code... which is plenty hard enough. You have to document the API, maintain compatibility across releases, keep the build system and SDK up to date, handle the additional tech support issues created by buggy plugins, etc etc. An app not originally designed for plugins requires *massive* work for plugins to be useful - I learned that one the hard way while working on Scribus.&lt;/p&gt;

&lt;p&gt;Given that the Kobo is very much a consumer device where the vast majority of its users won't realize it is an embedded computer at all, let alone a hackable Linux-based one, and you get a whole lot of work for nearly zero return. Anyway, anyone likely to be building plugins is more likely to be writing smartphone or tablet apps.&lt;/p&gt;

&lt;h2&gt;So it's &lt;i&gt;really&lt;/i&gt; not that simple&lt;/h2&gt;

&lt;p&gt;In their position, I would've wanted to release the code under NDA or a restrictive source-available license to interested devs who could show they already had a handle on the SDK and the platform. I'm not them, though, and I'm not in possession of all the info. They may've had good reasons for staying completely closed. Alternately, it's quite possible that the tech types wanted to open up but the lawyers/PHBs vetoed because of fear or misunderstanding. We'll never know.&lt;/p&gt;

&lt;p&gt;At least if they'd opened up Nickel under a restrictive source-available license they'd get bug fixes contributed back. A plugin API would just be a time sink for them, to no benefit.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-2630120015928653737?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://blog.ringerc.id.au/2011/01/enabling-telnet-and-ftp-access-to-kobo.html?showComment=1298332830434#c5062204372919528782' title='No further work on Kobo'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/2630120015928653737/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/07/no-further-work-on-kobo.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2630120015928653737'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2630120015928653737'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/07/no-further-work-on-kobo.html' title='No further work on Kobo'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-1871295846521854460</id><published>2011-07-14T08:07:00.018+08:00</published><updated>2011-07-15T09:36:04.853+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bugs'/><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss'/><category scheme='http://www.blogger.com/atom/ns#' term='cdi'/><title type='text'>Java EE 6 - Traps, pitfalls and warts list</title><content type='html'>&lt;p&gt;Java EE 6 has the potential to be a great platform. It just needs plenty of cleanup, implementation bug fixing, and real-world use to get there.&lt;/p&gt;

&lt;p&gt;One of the bigger issues is the significant array of spec inconsistencies and oversights that leave parts of the system working (or not working) in ways very different to one might expect. These issues can greatly increase debugging time and take important time away from productive development and into chasing issues in appservers, frameworks, and the specifications themselves.&lt;/p&gt;

&lt;p&gt;I thought I'd make a list of some of the ones I've hit so far. I'm really hoping we'll see a Java EE 6.1 - a fixes-only revision that, unlike EE 7, focuses on polish and usability over adding new features. These are things I think are important to see covered by it.&lt;/p&gt;

&lt;p&gt;The shortlist, explained in detail below:&lt;/p&gt;

&lt;li&gt;You can't &lt;a href="http://docs.jboss.org/weld/reference/1.1.0.Final/en-US/html/injection.html"&gt;&lt;code&gt;@Inject&lt;/code&gt;&lt;/a&gt; into a &lt;a href="http://download.oracle.com/javaee/6/api/javax/faces/convert/FacesConverter.html"&gt;&lt;code&gt;@FacesConverter&lt;/code&gt;&lt;/a&gt;. Use &lt;a href="http://seamframework.org/Seam3/FacesModule"&gt;Seam 3 Faces&lt;/a&gt; to work around this and hope JSF 2.1 fixes it in Java EE core.&lt;/li&gt;

&lt;li&gt;You can't &lt;code&gt;@Inject&lt;/code&gt; into a JPA 2.0 &lt;code&gt;EntityListener&lt;/code&gt;. Do a &lt;a href="http://download.oracle.com/javaee/6/api/javax/enterprise/inject/spi/BeanManager.html"&gt;direct BeanManager lookup from JNDI&lt;/a&gt; or use &lt;a href="http://seamframework.org/Seam3/Solder"&gt;Seam 3 Solder&lt;/a&gt;'s &lt;a href="http://docs.jboss.org/seam/3/solder/latest/api/org/jboss/seam/solder/beanManager/package-summary.html"&gt;BeanManagerAware&lt;/a&gt; to kind of hack around this; it's ugly. Lack of JPA 2.0 lifecycle hooks for EntityListener classes means &lt;a href="http://seamframework.org/Seam3/PersistenceModule"&gt;Seam 3 Persistence&lt;/a&gt; can't fix this one.&lt;/li&gt;

&lt;li&gt;Completely avoid all use of &lt;a href="http://download.oracle.com/javaee/6/api/javax/annotation/ManagedBean.html"&gt;&lt;code&gt;javax.annotation.ManagedBean&lt;/code&gt;&lt;/a&gt;. It was obsolete before release, is only inconsistently recognised, &lt;a href="http://download.oracle.com/javaee/6/api/javax/faces/bean/ManagedBean.html"&gt;duplicates JSF2 functionality&lt;/a&gt; that's also obsoleted by CDI's &lt;a href="http://download.oracle.com/javaee/6/api/javax/inject/Named.html"&gt;&lt;code&gt;@Named&lt;/code&gt;&lt;/a&gt;, and &lt;a href="http://weblogs.java.net/blog/cayhorstmann/archive/2009/12/23/javaxfacesbeanmanagedbean-dead-arrival?force=120"&gt;should simply be removed&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;Weld (CDI) before version 1.1.1 &lt;a href="http://java.net/jira/browse/GLASSFISH-13040"&gt;is&lt;/a&gt; &lt;a href="http://java.net/jira/browse/GLASSFISH-15735"&gt;extremely&lt;/a&gt; &lt;a href="http://java.net/jira/browse/GLASSFISH-16318"&gt;buggy&lt;/a&gt;. If using Glassfish 3.0, 3.0.1, or 3.1 you need to update weld-osgi-bundle to 1.1.1. See &lt;a href="http://seamframework.org/Seam3/CompatibilityHome#H-UpgradingWeldInGlassFish31"&gt;these instructions for Seam 3&lt;/a&gt;.&lt;/li&gt;

&lt;li&gt;Mapping of application-local &lt;code&gt;resource-ref&lt;/code&gt; resource names in &lt;code&gt;web.xml&lt;/code&gt; to a global resource name defined in &lt;code&gt;glasfish-web.xml&lt;/code&gt; or &lt;code&gt;jboss-web.xml&lt;/code&gt; &lt;a href="http://java.net/jira/browse/GLASSFISH-17024"&gt;doesn't work for persistence.xml jta datasource references and isn't supposed to work&lt;/a&gt;. Ignore all the examples and &lt;a href="http://download.oracle.com/docs/cd/E18930_01/html/821-2417/beaww.html"&gt;official documentation&lt;/a&gt; that suggest it should, they work with direct JDBC and with Spring but &lt;i&gt;not&lt;/i&gt; with JPA 2.0.&lt;/li&gt;

&lt;li&gt;If you encounter mysterious silent failures or null pointer exceptions, you probably forgot to include &lt;a href="http://seamframework.org/Documentation/WhatIsBeansxmlAndWhyDoINeedIt"&gt;&lt;code&gt;beans.xml&lt;/code&gt;&lt;/a&gt;. Your container will not warn you about this at deployment or runtime even if CDI annotations are present on classes.&lt;/li&gt;

&lt;p&gt;Many of these workarounds require the use of Seam 3 (ie: "let's finish the work EE 6 started") modules. Because of Glassfish bugs, some additional work is required to use Seam 3 on Glassfish versions prior to the yet-to-be-released 3.2. See &lt;a href="http://blog.ringerc.id.au/2011/05/using-seam-3-with-glassfish-31.html"&gt;this previous post&lt;/a&gt; for some information.&lt;/p&gt;

&lt;p&gt;In addition to the genuine issues with EE 6, there's also the learning curve to contend with. A while ago I wrote a &lt;a href="http://blog.ringerc.id.au/2010/08/java-ee-6-is-not-product-you-can.html"&gt;broad conceptual overview of EE 6&lt;/a&gt; intended for those coming to it cold, rather than migrating from EE 5 or from Seam. I felt it was necessary because of the &lt;a href="http://blog.ringerc.id.au/2010/08/im-not-smart-enough-to-use-java-ee.html"&gt;struggle I had getting started with EE 6&lt;/a&gt; and the &lt;a href="http://blog.ringerc.id.au/2010/08/java-ee-6-doesnt-just-work-in-even.html"&gt;bugs that bit me and confused me during the learning process&lt;/a&gt;. Perhaps it'll benefit others here or even provide insight into areas of the documentation and &lt;a href="http://download.oracle.com/javaee/6/tutorial/doc/"&gt;tutorial&lt;/a&gt; that would benefit from improvement.&lt;/p&gt;

&lt;p&gt;Now, for the details:&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;CDI vs legacy&lt;/h2&gt;

&lt;p&gt;The Contexts and Dependency Injection (CDI, JSR299) spec was integrated into Java EE 6 quite late in the process. Most of the other specs were developed concurrently with little or no awareness of CDI and of how significant it'd be in Java EE 6. This shows in several areas that really impact usability and learning time:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;&lt;b&gt;JavaServer Faces 2 includes its own dependency injection framework&lt;/b&gt;, introduced in Java EE 6 alongside CDI. This uses the javax.faces.bean.ManagedBean annotation and the javax.faces.bean scope annotations @RequestScoped, @SessionScoped and @ApplicationScoped. These directly duplicate functionality in CDI &lt;i&gt;but only work within JavaServer Faces&lt;/i&gt;, creating a fair bit of confusion.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;CDI doesn't work everywhere, and fails silently where unavailable&lt;/b&gt;. Because it was introduced quite late, there are places like JPA 2.0 EntityListener classes, JSF 2.0 @FacesConverter classes, etc where injection is not performed. Because CDI never sees these classes, &lt;i&gt;injection fails silently with no error message or other indication&lt;/i&gt; so the confused programmer just gets NullPointerExceptions on some injection sites. Needless to say, this doesn't aid learning Java EE 6. It wastes a lot of development time with finding workarounds and additional maintenance, too.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;In areas where CDI is unavailable there is often no good workaround&lt;/b&gt; for obtaining access to application resources like (say) an @ApplicationScoped pool of converters for some resource. Some libraries, like JSF2, have lifecycle hooks that permit partial injection support - for example, Seam 3 Faces adds injection but not @PostConstruct support for @FacesConverter. For some other libraries, like JPA 2.0 implementations, there aren't any hooks to use, so user classes have to do their own direct bean lookups using BeanManager. Getting a reference to the BeanManager requires a use of an add-on library like Seam 3 Solder or lots of messing with JNDI lookups. Using the BeanManager once you have a reference to it is messy, poorly documented and inefficient. Seam 3 Solder's BeanManagerAware class (hack) helps a little, but is still a very ugly approach.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;EJB 3.1 EJBs have their own annotations that look similar to CDIs but have very different semantics.&lt;/b&gt; The annotations @Singleton, @Stateful and @Stateless declare EJBs, which have container-managed transactions and locking, interface remoting, and more. They also have subtly different lifecycle and scope rules to those for CDI managed beans (@ApplicationScoped, @SessionScoped and @RequestScoped). The EJB vs CDI managed bean differences are quite difficult for the newcomer to understand, especially given the apparent paucity of side-by-side comparision documentation covering both CDI and EJB 3.1 together.&lt;/li&gt;

&lt;li&gt;&lt;b&gt;There are &lt;i&gt;two&lt;/i&gt; copies of the ManagedBean annotation&lt;/b&gt;, because of an effort to "standardize" bean annotations that occurred parallel to CDI. The javax.annotation.ManagedBean copy is confusing, only works in some of the places where the javax.faces.bean copy does, and should never have been included in the JDK in the first place because it was &lt;i&gt;already obsoleted by cdi by the time of release&lt;/i&gt;.&lt;/li&gt;

&lt;/ul&gt;

&lt;p&gt;How should these issues be solved?&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;Move javax.faces.bean and javax.annotation.ManagedBean into a separate "JavaEE6-legacy" spec jar, and flag them @Deprecated. New projects should not see these annotations at all.&lt;/li&gt;

&lt;li&gt;Make CDI work almost everywhere, and make the exceptions explicit. This requires spec and implementation changes in some places to define lifecycle hooks and hookable factories for helpers like FacesConverters and JPA 2.0 EntityListeners.&lt;/li&gt;

&lt;li&gt;Where CDI isn't available for performance or other reasons, ensure that the CDI implementation is &lt;i&gt;required to report a warning or error at deployment time if annotation scanning finds CDI annotations in classes where they cannot be effective&lt;/i&gt;. For example, an @Entity class annotated @RequestScoped or containing @Inject fields should be an error at deployment time. It won't always be possible to detect, but would be a good start.&lt;/li&gt;

&lt;li&gt;Provide a civilized way to access and use the BeanManager from code that still can't get it any other way, via a standard BeanManagerLocator helper class and helper methods in BeanManager for common bean lookup tasks. Legacy code isn't going to go away in a hurry, and not all code can be written to run only within a container; some of it has to work outside a managed container environment too. CDI's purist approach has hurt usability here.&lt;/li&gt;

&lt;li&gt;Make the mixing of CDI and EJB annotations an error at annotation processing time during deployment.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;JSF2 and JAX-RS have incompatible access methods for HttpServletRequest etc&lt;/h2&gt;

&lt;p&gt;Another area where CDI's late introduction shows is in the completely different approach JAX-RS takes to injection. JAX-RS uses method argument injection, not instance variable injection. If you want access to (eg) HttpServletRequest in JAX-RS, you add a method parameter of type HttpServletRequest that's annotated @Context. That's all well and good if you're only writing JAX-RS code. But what if you want to write a class that's callable from JAX-RS and from JSF2 pages?&lt;/p&gt;

&lt;p&gt;&lt;a href="http://blog.ringerc.id.au/2011/03/javaserver-faces-jsf2-and-jax-rs.html"&gt;You have to write two different versions of each method&lt;/a&gt;. One that uses JAX-RS method argument injection to get the HttpServletRequest (or whatever else you need) and another that uses FacesContext to obtain it via getExternalContext().&lt;/p&gt;

&lt;p&gt;You can't just &lt;code&gt;@Inject HttpServletRequest req;&lt;/code&gt; as a member variable, because CDI injection doesn't know how to inject &lt;code&gt;HttpServletRequest&lt;/code&gt;. You have to use framework-specific methods to obtain it.&lt;/p&gt;

&lt;p&gt;Thankfully, the &lt;a href="http://seamframework.org/Seam3/ServletModule"&gt;Seam 3 Servlet&lt;/a&gt; library largely resolves this issue by introducing a servlet filter that captures HttpServletRequest and stores it in a threadlocal exposed to injection via a CDI producer. &lt;b&gt;This should become part of Java EE 6.1&lt;/b&gt;.&lt;p&gt;

&lt;h2&gt;JNDI naming is confusing and hard to debug&lt;/h2&gt;

&lt;p&gt;Despite ongoing efforts into EJB 3.1 and Java EE 6, JNDI naming shows its messy app-server specific history strongly. Different app servers have different naming schemes. It's hard to get a clear picture from within an app of what the JNDI name tree contains from the app's perspective - which, with Java EE 6's resource-ref mapping and module/component scopes is now different to the app server's perspective.&lt;/p&gt;

&lt;p&gt;A few changes would really help here:&lt;/p&gt;

&lt;ul&gt;

&lt;li&gt;Require &lt;i&gt;everything&lt;/i&gt; within an app to perform lookups up the component-&gt;module-&gt;app-&gt;global scope hierarchy. Right now there are some exciting exceptions (&lt;a href="http://java.net/jira/browse/GLASSFISH-17024"&gt;like &lt;code&gt;persistence.xml&lt;/code&gt;&lt;/a&gt;) that make the java ee 6 scopes much less useful, and much &lt;i&gt;much&lt;/i&gt; more confusing, than they should be.&lt;/li&gt;

&lt;li&gt;Define some static helper methods on JNDI contexts that recursively dumped a context to an outstream. Yes, they're fairly easy to write, but would be rather helpful for people trying to get an initial handle on issues, especially those who started with Java EE 6 who consequently have little direct contact with or experience with JNDI.&lt;/li&gt;

&lt;li&gt;Define portable global JNDI naming for all resource types, not just EJB 3.1s, so app servers are guaranteed to offer a consistent name for a given resource in a given place.&lt;/li&gt;

&lt;/ul&gt;

&lt;h2&gt;Related to the above: persistence.xml ignores resource-ref mappings&lt;/h2&gt;

&lt;p&gt;An important specific case of JNDI issues is with persistence.xml. Different containers like to name resources differently and different deployments have different needs. That's why web.xml has the resource-ref element for declaring an app-internal name for a resource that must be defined, and why container-specific deployment descriptors have elements for mapping referenced resources to global jndi names in the container.&lt;/p&gt;

&lt;p&gt;Unfortunately, persistence.xml cannot use those mappings. The persistence context is initialized outside module/component scope and must use global mappings. At least, &lt;a href="http://java.net/jira/browse/GLASSFISH-17024"&gt;that's what the Glassfish folks say&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
The resource-ref element that you refer to above defines an indirection within a component name space. A PersistenceUnit (EMF) is not initialized within a component and hence always uses the global name space to look up data sources.
&lt;/blockquote&gt;

&lt;p&gt;Again, we have a case where a useful feature in Java EE 6 only works in some places, some of the time. Again, I wasted hours and hours wondering what I was doing wrong, writing test cases and filing bugs before being told that, actually, it's not supposed to work.&lt;/p&gt;

&lt;p&gt;What &lt;i&gt;does&lt;/i&gt; work - in glassfish - is defining an app-local data source within web.xml using the &amp;lt;data-source/&amp;gt; element. This is, unlike defining data sources in &lt;code&gt;glassfish-web.xml&lt;/code&gt; and &lt;code&gt;jboss-web.xml&lt;/code&gt;, theoretically portable. For an example of a datasource defined this way, see &lt;a href="http://blogs.oracle.com/enterprisetechtips/entry/datasource_resource_definition_in_java"&gt;this article&lt;/a&gt;. Define the datasource in the &lt;code&gt;java:app/env&lt;/code&gt; namespace, eg as &lt;code&gt;java:app/env/your-ds-name&lt;/code&gt;, and reference it from &lt;code&gt;persistence.xml&lt;/code&gt; as &lt;code&gt;java:app/env/your-ds-name&lt;/code&gt;. It works for me on Glassfish 3.2 build 09 ... but &lt;a href="https://issues.jboss.org/browse/AS7-1277"&gt;of course that means it has to trigger an apparent bug in JBoss AS 7&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Configuration and deployment descriptors are within the application archive&lt;/h2&gt;

&lt;p&gt;In Java EE 6, application configuration lives within the application .war / .ear, in xml deployment descriptors or sometimes even in annotations on the Java code its self. That's great for quick and easy testing, but severely limiting for production.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;You have to unpack and edit a war to change deployment-specific details&lt;/b&gt; like:&lt;/p&gt;

&lt;ul&gt;&lt;li&gt;principal-to-role/user mappings in glassfish-web.xml;&lt;/li&gt;&lt;li&gt;resource-ref mappings in (glassfish|jboss)-web.xml;&lt;/li&gt;&lt;li&gt;datasources defined in web.xml or in code using Java EE 6 standard annotations;&lt;/li&gt;&lt;li&gt;datasources, jms resources, javamail sessions, etc defined in glassfish-resources.xml;&lt;/li&gt;&lt;li&gt;... and more.&lt;/li&gt;&lt;/ul&gt; 

&lt;p&gt;This makes it nearly impossible to distribute a signed war for any non-trivial app using standard EE 6 configuration mechanisms, because the app must be unpacked and edited before deployment. It's also a maintenance and support nightmare. Even better, it requires every user to maintain config patches and merge them whenever upstream code changes.&lt;/p&gt;

&lt;p&gt;Currently, there is no standard deployment-time mechanism for overriding bundled settings in web.xml, [container]-resources.xml, [container]-web.xml, etc with deployment specifics like local group names, local database hosts and ports, etc. A mechanism like this would greatly improve the utility of concepts like resource-ref based indirection and principal-to-role mappings.&lt;/p&gt;

&lt;p&gt;Containers should also &lt;a href="http://java.net/jira/browse/GLASSFISH-16278"&gt;allow web admin console based management of things like role mappings post-deployment&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Glassfish 3.0 was released prematurely, and has buggy CDI until 3.1.1&lt;/h2&gt;

&lt;p&gt;Glassfish 3.0 had a sufficient number of major CDI bugs that it was practically unusable with Java EE 6 using CDI. Even Glassfish 3.1 needs a manual update of &lt;code&gt;weld-osgi-bundle.jar&lt;/code&gt; to weld 1.1.1 be usable with Seam 3 Solder, Seam 3 Faces, etc, the use of which is necessary to paper over some of the nastier holes in the EE 6 specs.&lt;/p&gt;

&lt;p&gt;See my previous posts for lists of CDI bugs in Glassfish, etc.&lt;/p&gt;

&lt;p&gt;While not a Java EE 6 spec issue as such, the &lt;a href="http://blog.ringerc.id.au/2010/08/java-ee-6-doesnt-just-work-in-even.html"&gt;quality problems&lt;/a&gt; with Glasfish early on were a major problem for me and I suspect they caused a significant number of interested people to simply drop the platform and move elsewhere. I persevered, kept reporting bugs, and kept on chasing beta releases, but I've wasted &lt;i&gt;weeks&lt;/i&gt; of development time chasing container and platform bugs that I should've spent on my application code. Most people can't afford to do that.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1871295846521854460?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1871295846521854460/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/07/java-ee-6-traps-pitfalls-and-warts-list.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1871295846521854460'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1871295846521854460'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/07/java-ee-6-traps-pitfalls-and-warts-list.html' title='Java EE 6 - Traps, pitfalls and warts list'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-9045962906475535869</id><published>2011-07-13T13:03:00.006+08:00</published><updated>2011-07-15T07:58:57.190+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='jboss'/><category scheme='http://www.blogger.com/atom/ns#' term='cdi'/><title type='text'>Someone else in Java EE 6 pain</title><content type='html'>&lt;p&gt;I'm not alone!&lt;/p&gt;

&lt;p&gt;&lt;a href="http://hwellmann.blogspot.com/2011/06/java-ee-6-server-comparison-conclusion.html"&gt;Harald Wellmann writes about his experience moving a Tomcat-based app to Glassfish, JBoss AS 6, and Resin&lt;/a&gt;. Warts and all.&lt;/p&gt;

&lt;p&gt;I think Java EE 6 really needs more real-world attitude and less hype. More bug fixing and more spec tidying, less focus on the next whizz-bang feature. It's nice to see somebody else who's had similarly painful experiences with it. Perhaps I'm not stupid after all, I simply make the foolish mistake of expecting released software like Glassfish 3.0.1 to have most features fairly solid.&lt;/p&gt;

&lt;p&gt;Harald also notes that &lt;a href="http://hwellmann.blogspot.com/2010/11/cdi-major-risk-factor-in-java-ee-6.html"&gt;CDI was a huge problem with Java EE 6 until quite recently, largely because Glassfish 3.0 was half-baked and under-tested&lt;/a&gt;; &lt;a href="http://hwellmann.blogspot.com/2010/07/jpa-20-frustration.html"&gt;JPA 2.0 is prone to exciting implementation differences and bugs&lt;/a&gt;, and more.&lt;/p&gt;

&lt;p&gt;My favourite quote:&lt;/p&gt;

&lt;blockquote&gt;

&lt;p&gt;&lt;b&gt;Legacy&lt;/b&gt;&lt;/p&gt;
&lt;p&gt;Backward compatibility is a good thing for veteran users of a framework. Users don't want to change all of their application code just for upgrading to a new framework version.&lt;/p&gt;

&lt;p&gt;On the other hand, &lt;i&gt;backward compatibility can be extremely confusing to new users&lt;/i&gt;: There's two or three solutions for the same kind of problem, &lt;i&gt;the legacy one is not marked as legacy or deprecated clearly enough&lt;/i&gt;, tutorials and example code still use the old style, and you can only resort to your favourite search engine or to trial and error to make things work consistently.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;(Emphasis mine)&lt;/p&gt;

&lt;p&gt;That's a perfect description of some of the problems I encountered when trying to get a handle on the released standard Java EE 6 and Glassfish 3.0, which I foolishly assumed was a stable platform to build my app on as I moved into the Java EE world&lt;/p&gt;

&lt;p&gt;Java EE 7 really needs to &lt;i&gt;modularize the backward compatibility crap into separate archives&lt;/i&gt; so that apps can depend on either &lt;code&gt;javaee-7&lt;/code&gt; or &lt;code&gt;javaee-7-legacy&lt;/code&gt; as appropriate. That way we'll finally be free of the two copies of the obsolete-before-release @ManagedBean annotation and all the other horrible legacy crap.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-9045962906475535869?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://hwellmann.blogspot.com/2011/06/java-ee-6-server-comparison-conclusion.html#more' title='Someone else in Java EE 6 pain'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/9045962906475535869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/07/someone-else-in-java-ee-6-pain.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/9045962906475535869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/9045962906475535869'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/07/someone-else-in-java-ee-6-pain.html' title='Someone else in Java EE 6 pain'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1091224540832838321</id><published>2011-06-25T17:13:00.011+08:00</published><updated>2011-07-15T08:01:25.102+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='application server'/><title type='text'>Java EE application servers - learning from the past</title><content type='html'>&lt;p&gt;&lt;i&gt;(Edit 15 July 2011: JBoss AS 7 is here, and brings a huge improvement to class loading and memory management. It's not full isolation, but it limits the exposed contact surface between app server and app greatly, and massively improves class loading. Brilliant!)&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;If you've used a Java EE application server like Glassfish or JBoss AS for long, you will have experienced 
&lt;a href="http://blogs.oracle.com/fkieviet/entry/classloader_leaks_the_dreaded_java"&gt;
classloader leaks&lt;/a&gt;, though you may not have realized it.&lt;/p&gt;

&lt;p&gt;If you've ever seen the error &lt;code&gt;java.lang.OutOfMemoryError: PermGen space&lt;/code&gt; at deploy-time, read the linked article above. If you have ever worked on the JVM, on app servers, or on EE applications, please read on.&lt;/p&gt;

&lt;p&gt;Even if you haven't hit classloader leaks, you should be aware of the &lt;a href="http://www.mhaller.de/archives/140-Memory-leaks-et-alii.html"&gt;various ways Java EE applications can cause memory leaks in the server&lt;/a&gt; and what to do about them.&lt;/p&gt;

&lt;p&gt;For those coming here for help fixing an immediate issue with their app: Read the links above, and &lt;a href="http://blogs.oracle.com/sundararajan/entry/jhat_s_javascript_interface"&gt;this article&lt;/a&gt; on using jhat's JavaScript interface to find likely leaks. More fancy JavaScript OQL tricks are &lt;a href="http://blogs.oracle.com/sundararajan/entry/permanent_generation_analysis_with_oql"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;Background: classloader leaks&lt;/h2&gt;

&lt;p&gt;Classloader leaks are one of several symptoms of a design flaw in all current application servers. The memory space of the server is not clearly separated from that of applications running on the server. The Java Security Manager (if enabled) prevents apps from casually reaching into the appserver's memory to patch and tweak things. Unfortunately, this doesn't help the application server free all the memory used by an app when undeploying (killing and uninstalling) an application.&lt;/p&gt;

&lt;p&gt;Any class in the application server its self, the core Java libraries, or additional libraries deployed to the app server for use by all classes may cause a classloader leak. All it has to do is hold a regular reference (rather than a WeakReference) to an application-defined class in an object that isn't destroyed when the app is undeployed. The app defined class in turn holds a reference to the classloader that loaded it. That reference keeps the app's old classloader alive, preventing that memory from being freed when the app is undeployed.&lt;/p&gt;

&lt;p&gt;Since the Java libraries were not designed with application servers in mind, they're full of places where exactly that can happen. The article linked to in the intro shows one such case in java.util.logging .&lt;/p&gt;

&lt;p&gt;One could try to clean up all places in the Java libraries where references to user-defined code may be held, but it's going to be a losing battle. Worse, it won't help protect the app server from libraries like JDBC drivers that typically get deployed to the server for the use of all apps.&lt;/p&gt;

&lt;p&gt;Even if you did that, you still wouldn't have solved the problem. Classloader leaks are not the only possible kind of leak, they're just one of the most common and troublesome. Classes and classloaders exist in the PermGen space in the JVM, which is generally of fairly restricted size. This tends to make the leaks more obvious because the app server starts throwing OutOfMemory exceptions when an app is redeployed. &lt;/p&gt;

&lt;p&gt;Another big problem area is libraries that use ThreadLocal storage for caches. It seems like a great idea, as using a ThreadLocal means you don't have locking overheads and delays. Unfortunately, in an application server environment things like HTTP threads are pooled &lt;i&gt;between all applications on the server&lt;/i&gt;. If you have 200 HTTP threads and one of your applications likes to keep a 5MB ThreadLocal cache, even if that app isn't used very often you'll eventually land up with 1GB of memory used by those caches!&lt;/p&gt;

&lt;p&gt;My understanding is that it's hard for the application server to clean up such caches.  It can't tell which application owns which ThreadLocal data so it doesn't know what to get rid of at undeploy time.&lt;/p&gt;

&lt;h2&gt;Lessons from the past&lt;/h2&gt;

&lt;p&gt;Rather than trying to manually patch up every leaked reference and fix every library that uses a ThreadLocal cache, why not learn from operating system design?&lt;/p&gt;

&lt;p&gt;Old operating systems used to share the same memory space between the OS "kernel" such as it was and the application(s). Apps could freely reach into kernel space using a simple pointer. A mistake in pointer arithmetic or numerous other errors could cause the app to trample all over OS memory. Perhaps more importantly the app had to carefully release any resources it acquired from the OS, otherwise the OS wouldn't regain the use of them until it was rebooted.&lt;/p&gt;

&lt;p&gt;Modern systems protect themselves from processes running on them by isolating each process in a separate memory segment. The process cannot access the kernel's memory or that of other processes directly. Access to other processes is done via the kernel, and access to the kernel is done via a very restricted interface (usually a trap). Not only does this have huge advantages for security and protection against accidental memory corruption, but &lt;i&gt;it means the OS can free all the memory used by a process by dropping the memory segment(s) it allocated for that process&lt;/i&gt;. It doesn't have to care about individually cleaning up each little string, struct and class allocated by the process, and &lt;i&gt;it doesn't care if the application has memory leaks&lt;/i&gt; because they're confined to the application's process space so the leaked memory is recovered along with everything else.&lt;/p&gt;

&lt;h2&gt;Applying the lessons to EE app servers&lt;/h2&gt;

&lt;p&gt;How does this apply to Java EE application servers? Two ways. First: Cleaning up guest apps in detail and trusting them to behave properly is a losing battle, you need ways to brutally sweep away the remanants when you terminate an app. Second: Modern OSes already provide the required tools by running JVMs in protected memort spaces.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Future Java application servers should run each application in its own JVM&lt;/b&gt;, using efficient IPC mechanisms for server/client communication. References to appserver-provided objects held by the client would be proxies that used the IPC mechanism to do their work. Because no IPC mechanism is perfectly efficient, this means that some of the application server code would probably be run as a library within the application's JVM. For example, CDI/Weld would need to run in the app's space, as would any JDBC drivers, Facelets libraries, JAX-RS, etc.&lt;/p&gt;

&lt;p&gt;To keep things reasonably efficient, the app server kernel would need to be able to pass sockets (HTTP connections etc) to client JVMs to avoid the overhead of copying HTTP replies via the master instance, but that's possible in most if not all OSes.&lt;/p&gt;

&lt;p&gt;By clearly defining where the application server core ends and the application begins, it'd be possible to finally fix classloader leaks and all the other issues of the shared process space once and for all.&lt;/p&gt;

&lt;p&gt;I'm well aware that this would have performance consequences, and that lots of work would be required to restructure app servers to be able to run this way. It's highly likely that core JVM changes would be required too. Nonetheless, if JavaEE app servers are ever to become as dependable as the OSes they run on, it's going to have to happen.&lt;/p&gt;

&lt;p&gt;Right now, the common workaround to the problem is to run each application in its own application server domain, running on its on JVM listening on its own ports, etc. When apps are re-deployed, the app server domain is restarted to free leaked classes. A front-end server is usually employed to redirect HTTP requests to the appropriate app server(s). This model is even less efficient than the one I describe above, because it prevents app server instances from sharing &lt;i&gt;anything&lt;/i&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1091224540832838321?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1091224540832838321/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/06/java-ee-application-servers-learning.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1091224540832838321'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1091224540832838321'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/06/java-ee-application-servers-learning.html' title='Java EE application servers - learning from the past'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-371846690131522326</id><published>2011-06-24T18:03:00.005+08:00</published><updated>2011-06-24T18:22:59.691+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='postgresql'/><category scheme='http://www.blogger.com/atom/ns#' term='oracle'/><title type='text'>Database preferences and product selection methodolgy</title><content type='html'>&lt;p&gt;I recently stumbled across an &lt;a href="http://www.blogger.com/comment.g?blogID=8351103828469419334&amp;postID=2502018713249988460"&gt;interesting weblog&lt;/a&gt; post by a DBA who expresses a strong preference for Oracle over PostgreSQL. I thought I'd respond to it with a few thoughts here, not so much because of the opinion expressed as the reasons given for it.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;There are certainly plenty of areas where Oracle is a better technical choice than Pg for the job, so someone preferring Oracle isn't overly surprising or controversial. What surprises me is that the author expressed such a blanket opinion without apparent concern for use-cases or needs, instead appearing to assert that Oracle is universally better and that Pg is a second-rate Oracle clone. The preference seems to be not so much for technical reasons or need-fit reasons as political/ideological ones.&lt;/p&gt;

&lt;h2&gt;Product selection, values, and selection methods&lt;/h2&gt;

&lt;p&gt;I think this is a worldview/culture difference as much as anything. If you or your management want "somebody to sue if it breaks" and a single organization to assign blame to, Pg isn't for you. If your management always wants to buy "the market leader" irrespective of other concerns, Pg isn't for you. If you want to use the same product for absolutely every conceivable project, no matter that consistency costs you, then Pg isn't for you. For the latter case Oracle might not be either.&lt;/p&gt;

&lt;p&gt;On the other hand, if your organization is willing and able to evaluate its needs and requirements then select products based on those needs, you might find that Pg is a good fit for some (though perhaps not all) the roles or projects you need a DB for. This might mean that you need to support more than one database if you find that you have a job Pg isn't well suited to. There certainly are things Pg doesn't do well. On the other hand, it also means that you may be able to save eye-popping sums in license fees by using Pg in roles where it &lt;i&gt;is&lt;/i&gt; a good fit.&lt;/p&gt;

&lt;p&gt;For example: If you want a multi-master shared-storage cluster on a SAN, Pg is a poor fit for that role. Similarly, if you want really strong XML and XSLT support in-database, Pg will probably not be good for you. On the other hand, if you need a high-performance master plus some read-only replicas for a system handing geographic information, that's one of the many areas where Pg is an ideal fit. For tasks where reliability and good performance are important and a solid relational database is required, Pg is a strong contender. &lt;i&gt;If you determine what you need you can make informed decisions about what product(s) fit those needs&lt;/i&gt; on a case-by-case basis.&lt;/p&gt;

&lt;h2&gt;Support&lt;/h2&gt;

&lt;p&gt;As for support - personally, I've had nothing but bad experiences with single-vendor support. I've had particularly horrible experiences with the "support" offered by Adobe, by Quark, and by a few more specialized newspaper-industry products used at my work. I'm reduced to begging them for a fix, which they will provide according to their determination of the problem's importance and on their schedule. Support contracts have - in my experience - surprisingly little effect on this. Not only can I not fix a bug/fault in most big single-vendor software myself, but the vendors never provide debugging symbol data, so I can't even hook up a debugger and get a backtrace to analyse a crash to see what's causing it so I can try to work around it locally. Microsoft is one of the few vendors who provide debug symbols, and even then not for all their products.&lt;/p&gt;

&lt;p&gt;By contrast, when working with Pg I've usually seen bugs reported by me or others fixed in hours, not days. For crashes (and yes, crash bugs do turn up) I can send off a complete backtrace showing exactly what went wrong and where, simplifying support and bug fixing immensely. In one case (multiple keys in a JDBC keystore using client cert auth) I landed up fixing it myself and sending in a patch that was included in the next version. I would never have been able to do that without the sources and debug symbols.&lt;/p&gt;

&lt;p&gt;Sometimes a fix will hit git master minutes after a mailing list post points an issue out, typically along with additional regression tests to catch it if it comes up again. A few key committers (you know who you are) are absolute heroes in this area. Of course, responses do depend on whether there's anyone interested in the problem, how busy everybody is, etc ... and if you're using Pg for free you have no right to demand anything more.&lt;/p&gt;

&lt;p&gt;Because you may not want to be dependent on the attention span of mailing list readers, paid support is available from numerous outfits, including several run by major Pg core contributors. By signing up with them, you get to assert your priorities and do things on your timelines.&lt;/p&gt;

&lt;p&gt;What that means is that you have solid support guarantees if you want them, but the freedom not to take them if you don't need them for a particular deployment role.&lt;/p&gt;

&lt;h2&gt;Pg as a poor Oracle clone&lt;/h2&gt;

&lt;p&gt;I'm afraid this is just misinformed. Pg *does* try to maintain a degree of Oracle compatibility for ease of porting and because users want it, but it is not and has never been an Oracle clone. Please see &lt;a href="http://www.postgresql.org/about/history"&gt;http://www.postgresql.org/about/history&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;In conclusion&lt;/h2&gt;

&lt;p&gt;Personally, I think product ideology is dangerous, no matter which product is your idol. Select according to your needs.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-371846690131522326?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.blogger.com/comment.g?blogID=8351103828469419334&amp;postID=2502018713249988460' title='Database preferences and product selection methodolgy'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/371846690131522326/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/06/database-preferences-and-product.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/371846690131522326'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/371846690131522326'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/06/database-preferences-and-product.html' title='Database preferences and product selection methodolgy'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-3686936512961790936</id><published>2011-05-31T08:58:00.003+08:00</published><updated>2011-05-31T09:11:56.534+08:00</updated><title type='text'>Solo sysadmin/coder</title><content type='html'>&lt;p&gt;Are you a solo coder who doubles as a sysadmin or some other role(s) in your organization?&lt;/p&gt;

&lt;p&gt;I share your pain - and I want to share these articles with you, because if you struggle with it too they will change how you look at it.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;a href="http://www.codinghorror.com/blog/2007/06/in-programming-one-is-the-loneliest-number.html"&gt;Coding Horror: In programming, One is the loneliest number&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://blog.asmartbear.com/burn-out.html"&gt;Burn-out&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://muddylemon.com/2011/05/depression-burn-out-and-writing-code/"&gt;Depression, burn-out and writing code&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;The Coding Horror article was written by Jeff Atwood, a damn fine developer and co-founder of Stack Overflow / Stack Exchange, which to me gives it a bit of added oompf.&lt;/p&gt;

&lt;p&gt;While you're at it, read &lt;a href="http://www.makinggoodsoftware.com/2011/05/23/top-7-programmers-bad-habits/"&gt;this article on programmer's bad habits&lt;/a&gt; ... because you probably do at least one of them. I know I do. They're counterproductive and just make you feel worse, so being aware of them helps.&lt;/p&gt;

&lt;p&gt;Of course, nothing is as good as saying "I'm dropping all my other roles, you can hire someone else for them. I'm just programming now, that way I can actually finish the projects I started two years ago." I only have to hold out another two months before the new guy at work is ready to take over :-)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-3686936512961790936?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/3686936512961790936/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/05/solo-sysadmincoder.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3686936512961790936'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3686936512961790936'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/05/solo-sysadmincoder.html' title='Solo sysadmin/coder'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-7917548447798220912</id><published>2011-05-25T10:39:00.010+08:00</published><updated>2011-05-25T11:41:55.901+08:00</updated><title type='text'>Using Seam 3 with Glassfish 3.1</title><content type='html'>&lt;p&gt;&lt;a href="http://seamframework.org/Seam3"&gt;Seam 3&lt;/a&gt; builds on top of the Java EE 6 standards, filling in holes and omissions in the functionality provided by the specs and providing important enhancements that many programmers would otherwise find themselves implementing themselves. &lt;a href="http://seamframework.org/Seam3/Solder"&gt;Seam 3 Solder&lt;/a&gt; in particular addreses some of the frustrating limitations in the CDI and Java EE 6 specs, providing well-engineered and easily re-used solutions to common problems.&lt;/p&gt;

&lt;p&gt;Being a JBoss project, Seam 3 is unsurprisingly better tested on the JBoss application server. However, its goals explicitly cover portability and it's supposed to work on Glassfish and - where possible - even on servlet containers like Tomcat and Jetty. Perhaps unsurprisingly, Glassfish doesn't get as much attention , so it's a bit harder to use Seam 3.0.0.Final on Glassfish than it is on JBoss. It appears that it was also harder for the Seam 3 folks to get quick fixes for the &lt;i&gt;many&lt;/i&gt; Glassfish bugs they found into Glassfish than it was for them to get fixes into JBoss AS, so many of the fixes for issues found in Seam 3 won't hit a Glassfish stable release until 3.2 comes out, if then.&lt;/p&gt;

&lt;p&gt;In the mean time, there are some quirks to work around. The Seam 3 project documents Glassfish 3.1 compatibility issues &lt;a href="http://seamframework.org/Seam3/CompatibilityHome"&gt;here&lt;/a&gt; and you should read that document before continuing.&lt;/p&gt;

&lt;p&gt;Many of the issues are solved pretty easily once you know what to do and where to look. It's well worth using Seam 3 instead of rolling your own solutions to many of the problems it tackles, so read on.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;


&lt;h2&gt;Update Weld (the CDI implementation) in Glassfish&lt;/h2&gt;

&lt;p&gt;Some of the bugs were in Weld. Those have been fixed, but Glassfish has not yet been updated to the latest Weld release. You can, and should, update Glassfish to the latest Weld yourself by updating &lt;code&gt;weld-osgi-bundle.jar&lt;/code&gt; according to &lt;a href="http://seamframework.org/Seam3/CompatibilityHome#H-UpgradingWeldInGlassFish31"&gt;these instructions on the Seam Glassfish 3.1 compatibility page&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;This will resolve several issues immediately, and will get you side-benefits like improved error messages from Weld in some previously hard-to-debug cases.&lt;/p&gt;


&lt;h2&gt;Work around Glassfish's over-strict class scanner&lt;/h2&gt;

&lt;p&gt;The biggest issue you will run into is a Glassfish limitation (&lt;a href="http://java.net/jira/browse/GLASSFISH-14808"&gt;GLASSFISH-14808&lt;/a&gt;) that causes Glassfish to try to resolve a reference to a class that is supposed to be hidden by the &lt;a href="http://docs.jboss.org/seam/3/solder/latest/api/org/jboss/seam/solder/core/Veto.html"&gt;&lt;code&gt;@Veto&lt;/code&gt;&lt;/a&gt; annotation and to &lt;a href="http://seamframework.org/Seam3/CompatibilityHome#H-OverzealousClassScanner"&gt;abort deployment when the resolution fail&lt;/a&gt;s. Because of this problem, dependencies of Seam 3 modules that are supposed to be optional become mandatory on Glassfish 3.1.&lt;/p&gt;

&lt;p&gt;There doesn't seem to be any listing of the dependencies of a given module when deployed under Glassfish 3.1, so it's a bit of a guessing game. One thing that will help you a lot, though, is the Seam 3 project's &lt;a href="http://seamframework.org/Seam3/BuildSystemArchitecture"&gt;Maven infrastructure&lt;/a&gt;, which provides the &lt;a href="http://anonsvn.jboss.org/repos/seam/dist/trunk/pom.xml"&gt;Seam 3 BOM&lt;/a&gt; as a central place to manage dependencies rather than doing it directly in the parent POM. This wonderful choice allows 3rd party Maven projects to import the Seam 3 BOM into their &lt;code&gt;dependencyManagement&lt;/code&gt; sections, making it trivial to use the correct versions of the correct optional dependencies and to use all the right versions of all the Seam 3 modules together.&lt;/p&gt;

&lt;p&gt;To use the Seam 3 BOM, import it into your DependencyManagement section.&lt;/p&gt;

&lt;pre&gt;
&amp;lt;project ...&amp;gt;
    ...
    &amp;lt;dependencyManagement&amp;gt;
        &amp;lt;dependencies&amp;gt;
             &amp;lt;dependency&amp;gt;
                &amp;lt;groupId&amp;gt;org.jboss.seam&amp;lt;/groupId&amp;gt;
                &amp;lt;artifactId&amp;gt;seam-bom&amp;lt;/artifactId&amp;gt;
                &amp;lt;version&amp;gt;${seam.version}&amp;lt;/version&amp;gt;
                &amp;lt;scope&amp;gt;import&amp;lt;/scope&amp;gt;
                &amp;lt;type&amp;gt;pom&amp;lt;/type&amp;gt;
             &amp;lt;/dependency&amp;gt;
             ...
        &amp;lt;/dependencies&amp;gt;
    &amp;lt;/dependencyManagement&amp;gt;
&amp;lt;/project&amp;gt;
&lt;/pre&gt;

&lt;p&gt;You may now specify Seam 3 dependencies without an explicit version, as if you'd declared them in your &lt;code&gt;dependencyManagement&lt;/code&gt; section directly. For example, with Seam 3 Faces:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;dependency&amp;gt;
   &amp;lt;groupId&amp;gt;org.jboss.seam.faces&amp;lt;/groupId&amp;gt;
   &amp;lt;artifactId&amp;gt;seam-faces-api&amp;lt;/artifactId&amp;gt;
   &amp;lt;!-- Override the Seam 3 BOM managed version, because Seam 3 Faces 3.0.1 is required to use Seam 3 Faces on Glassfish 3.1 --&amp;gt;
   &amp;lt;version&amp;gt;3.0.1.Final&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;dependency&amp;gt;
   &amp;lt;groupId&amp;gt;org.jboss.seam.faces&amp;lt;/groupId&amp;gt;
   &amp;lt;artifactId&amp;gt;seam-faces-impl&amp;lt;/artifactId&amp;gt;
   &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
   &amp;lt;!-- Override the Seam 3 BOM managed version, because Seam 3 Faces 3.0.1 is required to use Seam 3 Faces on Glassfish 3.1 --&amp;gt;
   &amp;lt;version&amp;gt;3.0.1.Final&amp;lt;/version&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;!-- Required by Seam Faces (via Seam Solder and/or Seam International) on GF 3.1, but not explicitly depended on --&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;joda-time&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;joda-time&amp;lt;/artifactId&amp;gt;
    &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&amp;lt;!-- Required by Seam Faces on GF 3.1, but not explicitly depended on --&amp;gt;
&amp;lt;dependency&amp;gt;
    &amp;lt;groupId&amp;gt;com.ocpsoft&amp;lt;/groupId&amp;gt;
    &amp;lt;artifactId&amp;gt;prettyfaces-jsf2&amp;lt;/artifactId&amp;gt;
    &amp;lt;scope&amp;gt;runtime&amp;lt;/scope&amp;gt;
&amp;lt;/dependency&amp;gt;
&lt;/pre&gt;

&lt;p&gt;Using the Seam 3 BOM won't magically add the optional dependencies that're required on Glassfish 3.1, but it'll make managing them easier. You can still override versions specified by the BOM, as shown above where we force the use of Seam 3 Faces 3.0.1 because Seam 3 Faces 3.0.0.Final didn't work quite right on Glassfish 3.1.&lt;/p&gt;

&lt;p&gt;If you deploy a project that uses Seam 3 to Glassfish and deployment fails with classNotDef errors, most likely you need to add another optional dependency that turns out not to be optional on GF 3.1. Look up which artifact the missing class is in and add it. Make sure to add them as runtime dependencies so you don't accidentally introduce a real dependency on them in your code!&lt;/p&gt;

&lt;p&gt;For example, if I deploy Seam 3 Faces without the above added dependencies I'll see errors like:&lt;/p&gt;

&lt;pre&gt;
SEVERE: Class [ org/joda/time/DateTimeZone ] not found. Error while loading [ class org.jboss.seam.international.datetimezone.DefaultDateTimeZoneProducer ]
...
SEVERE: Exception while loading the app
SEVERE: Exception while loading the app : org/joda/time/DateTimeZone
java.lang.ClassNotFoundException: org.joda.time.DateTimeZone
 at org.glassfish.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1518)
        ...
...
WARNING: The log message is null.
&lt;/pre&gt;

&lt;p&gt;if JodaTime is not added as a dependency. Similarly:&lt;/p&gt;

&lt;pre&gt;
Error occurred during deployment: Exception while loading the app : com/ocpsoft/pretty/faces/spi/ConfigurationProvider. Please see server.log for more details.

SEVERE: Exception while loading the app
WARNING: The log message is null.
SEVERE: Exception while loading the app : com/ocpsoft/pretty/faces/spi/ConfigurationProvider
java.lang.ClassNotFoundException: com.ocpsoft.pretty.faces.spi.ConfigurationProvider
 at org.glassfish.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1518)
 at org.glassfish.web.loader.WebappClassLoader.loadClass(WebappClassLoader.java:1368)
        ...
&lt;/pre&gt;

&lt;p&gt;if PrettyFaces isn't added.&lt;/p&gt;


&lt;h2&gt;Weird BeanManager exceptions&lt;/h2&gt;

&lt;p&gt;On deployment I also sometimes encounter an incredibly verbose 300+ line spew of exceptions including key lines:&lt;p&gt;

&lt;pre&gt;
CONFIGURATION FAILED! Failed to locate BeanManager using any of these providers: org.jboss.seam.solder.beanManager.DefaultJndiBeanManagerProvider(11), org.jboss.seam.solder.beanManager.ServletContainerJndiBeanManagerProvider(10). Please see server.log for more details.

org.jboss.seam.solder.beanManager.BeanManagerUnavailableException: Failed to locate BeanManager using any of these providers: org.jboss.seam.solder.beanManager.DefaultJndiBeanManagerProvider(11), org.jboss.seam.solder.beanManager.ServletContainerJndiBeanManagerProvider(10)
 at org.jboss.seam.solder.beanManager.BeanManagerLocator.getBeanManager(BeanManagerLocator.java:91)
 ...

SEVERE: PWC1306: Startup of context /myapproot failed due to previous errors
SEVERE: PWC1305: Exception during cleanup after start failed
org.apache.catalina.LifecycleException: PWC2769: Manager has not yet been started
 at org.apache.catalina.session.StandardManager.stop(StandardManager.java:872)
 at org.apache.catalina.core.StandardContext.stop(StandardContext.java:5509)
 ...
&lt;/pre&gt;

&lt;p&gt;Stopping and re-starting Glassfish then re-deploying tends to resolve the issue, and it only happens after another failed deployment. It's not clear why it happens yet.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-7917548447798220912?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7917548447798220912/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/05/using-seam-3-with-glassfish-31.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7917548447798220912'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7917548447798220912'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/05/using-seam-3-with-glassfish-31.html' title='Using Seam 3 with Glassfish 3.1'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-110817824607994141</id><published>2011-04-11T14:19:00.004+08:00</published><updated>2011-04-11T14:37:23.763+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='pdf'/><category scheme='http://www.blogger.com/atom/ns#' term='web'/><category scheme='http://www.blogger.com/atom/ns#' term='scribd'/><category scheme='http://www.blogger.com/atom/ns#' term='publishing'/><title type='text'>Scribd is amazing</title><content type='html'>&lt;p&gt;I've recently been looking for better ways to publish online editions of the newspaper I work for. We don't have a full content managed workflow internally, nor do we use automatic layout, so CMS-driven cross-publshing isn't currently an option. We're stuck with finding something to do with the PDFs that come out of the print production process for the forseeable future.&lt;/p&gt;

&lt;p&gt;Scribd appears to be a much more impressive option than direct PDF publishing or tools like flexpaper &amp;amp; pdf2swf. The HTML5 viewer is amazing. Note how all the elements are selectable, columns have been correctly recognised in the pdf text, etc?&lt;/p&gt;

&lt;p&gt;Here's an example.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;This is the iframe-based embedding view, showing the HTML5 document viewer. There's also the option for native (iframe-free) embedding, but unfortunately it's Flash-only at the moment.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.scribd.com/full/52574170?access_key=key-1wsbpml1bzprj7xnubtc"&gt;View fullscreen&lt;/a&gt;&lt;/p&gt;

&lt;a title="View POST Newspaper 2011-04-02 Small on Scribd" href="http://www.scribd.com/doc/52574170/POST-Newspaper-2011-04-02-Small" style="margin: 12px auto 6px auto; font-family: Helvetica,Arial,Sans-serif; font-style: normal; font-variant: normal; font-weight: normal; font-size: 14px; line-height: normal; font-size-adjust: none; font-stretch: normal; -x-system-font: none; display: block; text-decoration: underline;"&gt;POST Newspaper 2011-04-02 Small&lt;/a&gt;&lt;iframe class="scribd_iframe_embed" src="http://www.scribd.com/embeds/52574170/content?start_page=1&amp;view_mode=list&amp;access_key=key-1wsbpml1bzprj7xnubtc" data-auto-height="true" data-aspect-ratio="0.654545454545455" scrolling="no" id="doc_46253" width="100%" height="600" frameborder="0"&gt;&lt;/iframe&gt;&lt;script type="text/javascript"&gt;(function() { var scribd = document.createElement("script"); scribd.type = "text/javascript"; scribd.async = true; scribd.src = "http://www.scribd.com/javascripts/embed_code/inject.js"; var s = document.getElementsByTagName("script")[0]; s.parentNode.insertBefore(scribd, s); })();&lt;/script&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-110817824607994141?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/110817824607994141/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/04/scribd-is-amazing.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/110817824607994141'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/110817824607994141'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/04/scribd-is-amazing.html' title='Scribd is amazing'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-5275312745725537900</id><published>2011-03-31T15:31:00.011+08:00</published><updated>2011-09-02T07:30:31.281+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='embedded-glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><title type='text'>Useful things I've discovered about Glassfish (especially embedded glassfish) lately</title><content type='html'>&lt;p&gt;I've been working with / struggling with Glassfish a lot lately. The documentation leaves much to be desired, so there are a few bits and pieces you might not know that might come in handy.&lt;/p&gt;

&lt;p&gt;These notes refer to Glassfish 3.1.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;Glassfish properties&lt;/h2&gt;

&lt;p&gt;Use &lt;code&gt;asadmin list-commands&lt;/code&gt; to list supported commands, and &lt;code&gt;asadmin help commandname&lt;/code&gt; for details. Read all of &lt;code&gt;asadmin help&lt;/code&gt; since it contains important info the subcommand references should mention, but don't.&lt;/p&gt;

&lt;p&gt;Use &lt;code&gt;asadmin list *&lt;/code&gt; to list all Glassfish properties. This is important when working on embedded, because most configuration can only be done using &lt;code&gt;asadmin set&lt;/code&gt;. In embedded glassfish you can run this on your embedded instance using:&lt;/p&gt;

&lt;pre&gt;
commandRunner = glassfish.getService(CommandRunner.class);
CommandResult result = commandRunner.run("list","*");
System.err.println("Got command result: " + result.getOutput());
&lt;/pre&gt;

&lt;p&gt;Use the &lt;code&gt;get&lt;/code&gt; and &lt;code&gt;set&lt;/code&gt; commands to manipulate the objects you discover with &lt;code&gt;list&lt;/code&gt;.&lt;/p&gt;

&lt;h2&gt;Creating users in a file realm&lt;/h2&gt;

&lt;p&gt;Creation of users non-interactively - for batch/script use - is possible using the &lt;code&gt;asadmin create-file-user&lt;/code&gt; command. It's not possible to pass a user password to the command directly because it rejects the --AS_ADMIN_USERPASSWORD argument with "passwords not allowed on command line". You must create an asadmin password file that contains the &lt;i&gt;new user password to set as well as the admin password required to use the admin command&lt;/i&gt;. See &lt;code&gt;asadmin help&lt;/code&gt; for details on the password file's weirdness and on the &lt;code&gt;AS_ADMIN_USERPASSWORD&lt;/code&gt; variable. &lt;code&gt;help create-file-user&lt;/code&gt; doesn't mention it at all.&lt;/p&gt;

&lt;h2&gt;maven-embedded-glassfish-plugin&lt;/h2&gt;

&lt;p&gt;The &lt;code&gt;maven-embedded-glassfish-plugin&lt;/code&gt; is very limited. It doesn't offer facilities to deploy a &lt;code&gt;glassfish-resources.xml&lt;/code&gt;, run &lt;code&gt;asadmin&lt;/code&gt; commands pre/post deploy, etc. If you're using container provided resources, avoid it and run an embedded Glassfish instance directly in your application or tests instead. The Glassfish embedded API (see below) is pretty easy to use and unlike the Maven plugin gives you the flexibility to invoke &lt;code&gt;asadmin&lt;/code&gt; commands to set up your domain to fit your needs.&lt;/p&gt;

&lt;p&gt;There are references to a different maven plugin with a slightly different name out there. It's not the same thing, doesn't exist in Glassfish 3, and won't do what you want.&lt;/p&gt;

&lt;h2&gt;Documentation for the embedded glassfish 3.1 api&lt;/h2&gt;

&lt;p&gt;The Glassfish 3.1 embedded documentation is here: &lt;a href="http://embedded-glassfish.java.net/nonav/apidocs/org/glassfish/embeddable/GlassFish.html"&gt;http://embedded-glassfish.java.net/nonav/apidocs/org/glassfish/embeddable/GlassFish.html&lt;/a&gt;. Most of the documentation has been &lt;i&gt;removed&lt;/i&gt; from the Oracle Glassfish Embedded guide. The API has changed significantly from 3.0, so the 3.0 guide is just misleading.&lt;/p&gt;

&lt;h2&gt;&lt;code&gt;asadmin&lt;/code&gt; from embedded&lt;/h2&gt;

&lt;p&gt;If you run your own standalone embedded glassfish, you can invoke &lt;code&gt;asadmin&lt;/code&gt; commands using the &lt;a href="http://embedded-glassfish.java.net/nonav/apidocs/org/glassfish/embeddable/CommandRunner.html"&gt;CommandRunner&lt;/a&gt;. However, a frustrating bug caused by a feature removed without considering the impact on embedded means you can't create file users for a file realm this way. See &lt;a href="http://java.net/jira/browse/GLASSFISH-16277"&gt;http://java.net/jira/browse/GLASSFISH-16277&lt;/a&gt;. I don't have a solution for this issue yet.&lt;/p&gt;

&lt;h2&gt;REST admin&lt;/h2&gt;

&lt;p&gt;Glassfish has a REST driven admin interface in addition to &lt;code&gt;asadmin&lt;/code&gt; and the web console. This can be a powerful facility for automation, but is almost totally undocumented. Exploring it using `curl' can be informative, because it's based on JAX-RS so paths can report the supported subpaths, etc.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5275312745725537900?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5275312745725537900/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/03/useful-things-ive-discovered-about.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5275312745725537900'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5275312745725537900'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/03/useful-things-ive-discovered-about.html' title='Useful things I&apos;ve discovered about Glassfish (especially embedded glassfish) lately'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-5508839199115674692</id><published>2011-03-11T14:54:00.017+08:00</published><updated>2011-07-22T12:55:10.032+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='jersey'/><category scheme='http://www.blogger.com/atom/ns#' term='jaxrs'/><title type='text'>JavaServer Faces (JSF2) and JAX-RS don't play well together</title><content type='html'>&lt;p&gt;I've been working with JavaServer Faces 2 (JSF2) since shortly after its release, and it continues to be an annoying mix of excellent and frustrating to work with. It's theoretically a great technology, especially with CDI (contexts and dependency injection) even despite the holes and quirks in the spec. There are just two wee problems.&lt;/p&gt;

&lt;p&gt;First, unlike many of the other EE specs, the JSF2 spec really isn't written to be useful for developers intending to use JSF2 in their apps. It's very much a specification not a reference, and I found it hard to read even as a specification. Because I was getting started with JSF2 in the very early days of EE 6, I seem to remember that the now very helpful &lt;a href="http://download.oracle.com/javaee/6/tutorial/doc/"&gt;Java EE Tutorial&lt;/a&gt; wasn't EE6 ready yet or was incomplete, so I didn't have that as a basis for learning JSF2 and EE6 either. Perhaps my memory is just failing me. In any case, I often landed  up stumbling through Google-land trying to find out details that I thought should be obvious and easy to find in the spec or other reference documentation. At the time, Google-land was full of outdated references to JSF 1, lots of migration guides from Spring to EE 6, and plenty of other things to side-track a confused newbie. All in all, it was &lt;i&gt;really&lt;/i&gt; hard to get going with JSF2 alone.&lt;/p&gt;

&lt;p&gt;Second, the JSF2 spec was clearly concocted with no thought of or co-operation with the JAX-RS spec, and it shows. They don't work well together, and it's really, really frustrating because they should be so useful in combination.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Edit:&lt;/i&gt; Part of the issue here appears to be that I was trying to use JSF2 just for the Facelets templating language, expecting to be able to interoperate with JAX-RS where possible. JSF2 is a framework and doesn't isn't really designed to enable you to easily step outside the abstractions imposed by the framework - not least because you shouldn't need to. I was probably going about this the wrong way in a square-peg, round-hole kind of way, and shouldn't have been trying to use JSF2 in the first place. That's not made particularly obvious by the docs, though.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;Documentation&lt;/h2&gt;

&lt;p&gt;How do you insert the context path of your servlet into the page? The answer is to use the &lt;code&gt;contextPath&lt;/code&gt; property of the implicit &lt;code&gt;request&lt;/code&gt; property - but good luck finding that out unless you already know about it, or you've somehow managed to wade through the JSF 2.0 spec to find it. I was using FacesContext to obtain it via a very roundabout route before I finally found out about the implicit request object. There's plenty of information about the &lt;code&gt;pageContext&lt;/code&gt; object from the older JSP standard, but that's defunct in JSF2.&lt;/p&gt;

&lt;p&gt;Reading &lt;a href="http://one-size-doesnt-fit-all.blogspot.com/2007/03/some-explicit-language-about-implicit.html"&gt;this&lt;/a&gt; might help you with JSF implicit variables. I wouldn't recommend trying to read the JSF2 spec its self as a reference and learning aid, though, it's really focused on spec implementers and isn't especially accessible to JSF2/facelets users. This one, at least.&lt;/p&gt;

&lt;h2&gt;JSF2 doesn't mesh well with JAX-RS&lt;/h2&gt;

&lt;p&gt;The other thing that's increasingly frustrating is the mismatch between JAX-RS (Jersey etc) and JSF2. They have different injection points in different lifecycles, making it hard to make a single class cleanly handle both JSF2 and JAX-RS requests. For example, if your method needs access to the HttpServletRequest, in JAX-RS you add a &lt;code&gt;@Context HttpServletRequest request&lt;/code&gt; method parameter, which JAX-RS will automagically pass when calling your method. In JSF2 you access it via &lt;code&gt;(HttpServletRequest)FacesContext.getInitialContext().getExternalContext().getRequest()&lt;/code&gt;. These two mechanisms are incompatible, and mean that you have to write two wrapper methods to call the same underlying code depending on how the call came in. Needless to say, that's annoying - and it's enshrined in the JAX-RS and JSF2 specs.&lt;/p&gt;

&lt;p&gt;While you can call the JAX-RS method using EL method calls like &lt;code&gt;${myClass.myMethod(request)}&lt;/code&gt; you can't use that as a writable EL property. You land up writing SIX methods (JSF2 getter, JSF2 setter, JAX-RS getter, JAX-RS setter, private impl getter, private impl setter) instead of just annotating the two real implementation methods and letting injection take care of it.&lt;/p&gt;

&lt;p&gt;Instead of something like the (non-working) code:&lt;/p&gt;

&lt;pre&gt;
@Named
@Path("/test")
@RequestScoped
public class Something {

  @Inject private HttpServletRequest currentRequest;

  @Path("/property")
  @Produces("text/plain")
  public String getProperty() {
    // do the work here
  }

};
&lt;/pre&gt;

&lt;p&gt;we land up writing something like:&lt;/p&gt;

&lt;pre&gt;
@Named
@Path("/test")
@RequestScoped
public class Something {

  @Path("/property")
  @Produces("text/plain")
  public String getPropertyJAXRS(@Context HttpServletRequest request) {
    return getPropertyImpl(request);
  }

  public String getProperty() {
  getPropertyImpl((HttpServletRequest)FacesContext.getCurrentInstance().getExternalContext().getRequest());
  }

  private String getPropertyImpl(HttpServletRequest request) {
    // Do the work here
  }

};
&lt;/pre&gt;

&lt;p&gt;This &lt;i&gt;might&lt;/i&gt; be fixable with a CDI extension to provide consistent injection of things like the HttpServletRequest, but Seam Servlet (which should theoretically do it) &lt;a href="https://issues.jboss.org/browse/SEAMSERVLET-29"&gt;fails to deploy to Glassfish with an NPE&lt;/a&gt;. Yay!&lt;/p&gt;

&lt;p&gt;Of course, someone will chime up and say "you shouldn't need to do that, stay within the framework". Unfortunately web frameworks like JSF2 have large feature holes that sometimes force you to go to a lower level, especially if you need to inter-operate with other tools or frameworks. How do you get to the application's initial context parameters via facelets-only or jax-rs-only API, for example? You can't, you need to access the ServletContext to do that. How do you accept a multipart/mixed file upload with JSF2? Or exchange JSON data with a client?&lt;/p&gt;

&lt;p&gt;Another problem is the difficulty of accessing the JSF2 context to do things like map outcomes to URLs from JAX-RS methods when you want to do something like send a redirect from a JAX-RS POST request to a JSF2 page.&lt;/p&gt;

&lt;h2&gt;Results&lt;/h2&gt;

&lt;p&gt;The end result is that whenever I need to add web services capabilities to my beans, I land up converting them to pure JAX-RS beans and converting my JSF2 xhtml across to plain XHTML+JavaScript with JQuery. So much pain just goes away when I'm not trying to mix both systems.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5508839199115674692?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5508839199115674692/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/03/javaserver-faces-jsf2-and-jax-rs.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5508839199115674692'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5508839199115674692'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/03/javaserver-faces-jsf2-and-jax-rs.html' title='JavaServer Faces (JSF2) and JAX-RS don&apos;t play well together'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-3845011067643766019</id><published>2011-03-11T12:45:00.004+08:00</published><updated>2011-03-11T13:02:06.689+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='postupload'/><category scheme='http://www.blogger.com/atom/ns#' term='uploadify'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='webapp'/><title type='text'>File upload webapp</title><content type='html'>&lt;p&gt;I recently became frustrated with the lack of easily self-hosted file upload app options. I needed to replace my employer's reliance on giant email attachments with something that wouldn't melt our mail server - &lt;i&gt;and our clients could still understand how to use.&lt;/i&gt; This unfortunately ruled out the classic options like anonymous FTP.&lt;/p&gt;

&lt;p&gt;In the end I wrote a &lt;a href="https://github.com/ringerc/postupload"&gt;new web application&lt;/a&gt; to accept files from clients over http and save them to a shared folder on the internal network. Many of the building blocks like &lt;a href="http://uploadify.com"&gt;uploadify&lt;/a&gt; were available, but there wasn't much around in terms of complete applications to do the job. Instead, people seemed to be using services like YouSendIt, Box.net, DropSend, etc ... which have downsides including:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Inability to provide a canned recipient list, or high per-user per-month fees to support one&lt;/li&gt;
&lt;li&gt;Inability to customise the site's appearance and integrate it into your domain - without paying high fees for an "enterprise" version&lt;/li&gt;
&lt;li&gt;The need to download files once the client has sent them to the provider, often one-by-one via a clumsy web interface&lt;/li&gt;
&lt;li&gt;Windows-only client applications required to do batch downloads of files. Some services have Mac versions, but only one had a Linux client suitable for our advertising thin clients.&lt;/li&gt;
&lt;li&gt;Lack of a web services API - unless you pay high fees for the "enterprise" version.&lt;/li&gt;
&lt;li&gt;Lack of no-flash fallback upload methods&lt;/li&gt;
&lt;li&gt;Sometimes iffy browser compatibility&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;I wanted something really simple for clients - and staff - to use, that simply dropped files into a shared folder as they were uploaded and sent a notification email to staff when the upload was finished. Now that I've finished it, I'm releasing the result as open source software for the use of anybody who wants it. You can get it here:&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ringerc/postupload"&gt;https://github.com/ringerc/postupload&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;It's a Java EE 6 project that runs on Glassfish. Don't stress if you've never used any of that before, if you only know PHP, etc. The client-side is all familiar HTML and JavaScript, and the server side isn't especially complicated code. Detailed instructions for installing and running Glassfish are provided.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-3845011067643766019?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/3845011067643766019/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/03/file-upload-webapp.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3845011067643766019'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3845011067643766019'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/03/file-upload-webapp.html' title='File upload webapp'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-8064295111601899020</id><published>2011-02-26T12:00:00.018+08:00</published><updated>2011-03-15T19:00:27.883+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bugs'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Kobo and long-unfixed bugs</title><content type='html'>&lt;p&gt;&lt;i&gt;(This post is part of a &lt;a href="http://soapyfrogs.blogspot.com/search/label/kobo"&gt;series&lt;/a&gt; on the internals of the Kobo and Kobo Wifi eReaders. The rest of the posts are technically focused; this one was just prompted by increasing frustration with the device.)&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;I really like the Kobo. It has great hardware, a great price, and some pretty nice software running on it. Unfortunately, it also has some annoying bugs and limitations - &lt;span style="text-decoration:line-through;"&gt;many of which wouldn't be too bad,if only there was any sign they'd ever get fixed/resolved.&lt;/span&gt; some but not all of which have now been fixed in the 1.9 firmware.&lt;/p&gt;

&lt;p&gt;I feel a little bad paying the Kobo out about these issues, because the Kobo folks were really great to deal with and really responsive early on. I've been absolutely blown away by how far beyond narrow-interpretation GPL requirements they've gone with their source releases (including full build scripts and config files) and how quick they were to update their releases when asked about them. Please keep in mind that overall &lt;i&gt;the Kobo is an excellent eReader&lt;/i&gt; and the Kobo guys deserve our support for being one of the depressingly few companies who fully accept, understand and &lt;i&gt;act on&lt;/i&gt; the obligations that fall on them in exchange for the free use of GPL-licensed software.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;(You can't) Go To Page (fixed in 1.9)&lt;/h2&gt;

&lt;p&gt;There's no way to jump to a page in a book by entering the page number, though the Kobo Wifi uses a handy on-screen keyboard for the online store that'd be ideal for the job. The inability to jump straight to page wouldn't be so bad, except that...&lt;/p&gt;

&lt;h2&gt;Reading position loss sucks&lt;/h2&gt;

&lt;p&gt;... it loses your page when it runs out of power. It tends to run out of power suddenly and without warning, which doesn't help. Indications are that `nickel', the reader app, doesn't checkpoint your reading position to the SQLite database periodically, it only does it on explicit shutdown or when you go to the home screen.&lt;/p&gt;

&lt;p&gt;Factor in the fact that you can't jump straight to a page number, and you discover just how tedious it is to press "next page" enough times to get back to where you were. It's even worse in a book with long chapters, or with no chapter structure at all.&lt;/p&gt;

&lt;h2&gt;Which of the set I just added scrambled its brains?&lt;/h2&gt;

&lt;div style="float:right; padding: 0.5em; width: 320px;"&gt;
&lt;img src="http://www.postnewspapers.com.au/~craig/webfiles/kobo-blank.JPG" /&gt;
&lt;p&gt;&lt;i&gt;How many files are really on this Kobo? Tip: more than "no books found".&lt;/i&gt;&lt;/p&gt;
&lt;/div&gt;

&lt;p&gt;&lt;i&gt;(This issue updated 28 Feb to reflect new information; see comments)&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;At one point book I added to the unmodified orignal Kobo I had at that point caused it to crash during startup and reboot, failing to add that book to the database or recognise any new content added after that book was added&lt;a href="#sub1"&gt;&lt;sup&gt;*&lt;/sup&gt;&lt;/a&gt;. It rebooted normally into the usual `nickel' reading screen, but didn't see any of the new content. As I'd added a fair few books in that one update, I &lt;b&gt;had no idea which book was the problem and no way to find out&lt;/b&gt;. I had to do a laborious binary search where I add/remove half the books at a time, slowly narrowing down the culprit. Tedious beyond belief, especially when it turns out you have more than one book that triggers a bug in Kobo's epub parsing.&lt;/p&gt;

&lt;p&gt;To fix this, Nickel needs to write a checkpoint file indicating which book it's about to process, then remove it after successfully processing that book. If it starts up and discovers a checkpoint file in place, it knows it crashed while processing that book and should not try to process it again; instead it carries on with the next book. That way, the Kobo could tell you which books it failed to process, and let you read the rest of the ones you just added.&lt;/p&gt;

&lt;p&gt;(Update: I recently had what I thought was another case of this, but even after a factory reset it still couldn't find any books, even previously working ones. A restore from a backup copy of my SD card fixed it. On balance, it's most likely that I broke something subtly ... somehow. The rest of the issues described here affect a stock Kobo, though, with no modifications.)&lt;/p&gt;

&lt;h2&gt;I'm not currently reading that (fixed in 1.9)&lt;/h2&gt;

&lt;p&gt;The Kobo keeps track of a "currently reading" list. This rapidly grows to include a long list of books you've looked at, decided you don't want to read right now, and left. You can't remove them from this list without clearing the SQLite database completely or deleting the book off the Kobo. A cosmetic issue, but it becomes an irritation to have many, many "1% read" books in your collection.&lt;/p&gt;

&lt;h2&gt;Dammit, which is the next book?&lt;/h2&gt;

&lt;p&gt;You know how series are very popular with authors and publishers these days? They bring in a lot more money, because people are willing to pay more for three (or five, or eight, or the godamm wheel of endless time ogod-make-it-stop-i-beg-you) books than just one huge one? And, frankly, they can be carted around in pieces rather than making you tote around something the size of the Necronomicon that could double as a potent self-defense weapon?&lt;/p&gt;

&lt;p&gt;Well, there are lots of series out there. And the Kobo has absolutely no idea that they exist. Each book is individual and complete in and of its self to the Kobo, which doesn't seem to think that you REALLY want to read that six book series in order. I hope you have a smartphone to look up the Wikipedia entry on the series and find out which book is next, or you'll be doing plenty of guesswork.&lt;/p&gt;

&lt;p&gt;Most ePub novels contain series and other metadata, but the Kobo doesn't use most of it.&lt;/p&gt;

&lt;h2&gt;Quote me once&lt;/h2&gt;

&lt;p&gt;The quotes are a cool idea. They're also hard-coded into Nickel, and there's only a small list of them. After a while, they may begin to drive you just a little bit batty. Why not put them in an XML file that's loaded at launch or on first use, so a bit more variety becomes possible? Or let us hide them? This doesn't bug me much, but it drives my girlfriend (who has a gen1 kobo) up the wall.&lt;/p&gt;

&lt;h2&gt;Don't just whine about it, do something!&lt;/h2&gt;

&lt;p&gt;I'd love to fix some of these. I'm a competent C++/Qt programmer, and I already have an ARM development environment set up for the Kobo that lets me compile binaries for the Kobo, including plugins for `nickel' (the Qt-based eReader app). Unfortunately, the &lt;a href="https://github.com/kobolabs/Kobo-Reader"&gt;KoboLabs GitHub repo&lt;/a&gt; doesn't contain any headers for Nickel and the plugin API its self is largely useless without them. Even with a usable plugin API, it's not clear that I could fix or improve many of these without the full sources to Nickel. So, really, all I &lt;i&gt;can&lt;/i&gt; do is whine about it and hope the Kobo guys will get around to fixing some of these issues.&lt;/p&gt;

&lt;p style="text-color:grey"&gt;&lt;a name="sub1"&gt;*&lt;/a&gt;Only once (the second time I thought it'd happened turned out to be a different problem) but it was very frustrating to track the issue down.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8064295111601899020?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8064295111601899020/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/02/kobo-and-long-unfixed-bugs.html#comment-form' title='19 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8064295111601899020'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8064295111601899020'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/02/kobo-and-long-unfixed-bugs.html' title='Kobo and long-unfixed bugs'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>19</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-392905474967458758</id><published>2011-02-25T11:21:00.001+08:00</published><updated>2011-02-25T11:24:23.746+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='bugs'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='php'/><category scheme='http://www.blogger.com/atom/ns#' term='postgresql'/><title type='text'>Drupal 7, PostgreSQL, unserialize, and bytea_output</title><content type='html'>&lt;p&gt;I just found out that PHP 5.3's PostgreSQL PDO driver (as used by Drupal) is broken by the Postgresql 8.0 transition from octal to hex encoding for bytea. It passes the raw hex through to the app, including the leading x , causing PHP's unserialize() function to choke.&lt;/p&gt;

&lt;p&gt;I'm using Drupal with Apache and PostgreSQL on Windows (sigh) because I'm writing up a step-by-step hand-holding guide for someone who needs to do some testing against our Drupal database. I wouldn't be using that configuration voluntarily ;-)&lt;/p&gt;

&lt;p&gt;Drupal doesn't check that bytea_output is set to 'escape' as a workaround or do a sanity test to detect this fault, so the results are ... interesting:&lt;/p&gt;

&lt;pre&gt;
Notice: unserialize(): Error at offset 0 of 27 bytes in variable_initialize() (line 749 of C:\Program Files\Apache Software Foundation\Apache2.2\htdocs\drupal-7.0\includes\bootstrap.inc).
&lt;/pre&gt;

&lt;p&gt;If anybody else lands up whacking their head against this for a while: just&lt;/p&gt;

&lt;pre&gt;
ALTER DATABASE drupal SET bytea_output = 'escape'
&lt;/pre&gt;

&lt;p&gt;to have your sanity restored.&lt;/p&gt;

&lt;p&gt;The amount of breakage being seen in drivers really makes me wonder if the the octal to hex transition should've been done with an opt-in from drivers during negotiation or an explicit SET, rather than just making it globally default. But, hey, hindsight.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-392905474967458758?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/392905474967458758/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/02/drupal-7-postgresql-unserialize-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/392905474967458758'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/392905474967458758'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/02/drupal-7-postgresql-unserialize-and.html' title='Drupal 7, PostgreSQL, unserialize, and bytea_output'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-3011457035111983502</id><published>2011-02-04T14:03:00.009+08:00</published><updated>2011-03-11T13:05:58.309+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='uploadify'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='jersey'/><title type='text'>Handling file uploads with Java EE 6 / JAX-RS / Glassfish / Uploadify</title><content type='html'>&lt;p&gt;Joeri Sykora &lt;a href="http://tiainen.sertik.net/2009/10/easy-file-upload-in-java-using-jersey.html"&gt;wrote&lt;/a&gt; about using Jersey extensions to JAX-RS to handle file uploads in Java EE 6. His example is extremely handy, but needs some updates to handle Jersey 1.5.&lt;/p&gt;

&lt;p&gt;&lt;i&gt;Update:&lt;/i&gt; I've now put together a complete and self contained Java EE upload application using Uploadify, which you can grab from &lt;a href="https://github.com/ringerc/postupload"&gt;github.com/ringerc/postupload&lt;/a&gt;. See &lt;a href="http://blog.ringerc.id.au/2011/03/file-upload-webapp.html"&gt;http://blog.ringerc.id.au/2011/03/file-upload-webapp.html&lt;/a&gt;&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;h2&gt;Jersey 1.5 and jersey-multipart's &lt;code&gt;@FormDataParam&lt;/code&gt;&lt;/h2&gt;
&lt;p&gt;Jersey 1.5 advises that the jersey-multipart contrib module be used to handle uploads, using the &lt;code&gt;multipart/form-data&lt;/code&gt; content type and &lt;a href="http://jersey.java.net/nonav/apidocs/1.5/contribs/jersey-multipart/com/sun/jersey/multipart/FormDataParam.html"&gt;&lt;code&gt;@FormDataParam&lt;/code&gt;&lt;/a&gt; annotation.&lt;/p&gt;

&lt;p&gt;Joeri's JAX-RS code may be adapted to read:&lt;/p&gt;

&lt;pre&gt;
@Path("/file")
public class FileHandler {

  @POST
  @Path("/upload")
  @Consumes("multipart/form-data")
  @Produces("text/plain")
  public String uploadFile(
          @FormDataParam("file") InputStream file,
          @FormDataParam("file") FormDataContentDisposition fileInfo) {

    // your code here to copy file to destFile
    System.err.println("Received file: " +  fileInfo.getFileName() + " as " + file);

    return "1";
  }

}
&lt;/pre&gt;

&lt;p&gt;... so that the file receipt is handled more efficiently.&lt;/p&gt;

&lt;h2&gt;Determining the correct path to the upload handler&lt;/h2&gt;

&lt;p&gt;Determining the URL to the upload handler isn't trivial. JAX-RS doesn't understand &lt;code&gt;../&lt;/code&gt; paths, so &lt;code&gt;faces/../rest/file/upload&lt;/code&gt; isn't the same as &lt;code&gt;rest/file/upload&lt;/code&gt; to it. We can't easily create an absolute path, either, because the application is deployed in a configurable context root inside the Java EE server.&lt;/p&gt;

&lt;p&gt;The easiest way to do it is build an absolute URL where you substitute the context root in. Assuming your JAX-RS servlet is configured to handle &lt;code&gt;rest/*&lt;/code&gt; in &lt;code&gt;web.xml&lt;/code&gt; and your upload handler is at &lt;code&gt;file/upload&lt;/code&gt; in JAX-RS, you might use:&lt;/p&gt;

&lt;pre&gt;
$(function() {
  $('#file_upload').uploadify({
    'script' : '#{facesContext.externalContext.request.contextPath}/rest/file/upload',
    // blah blah blah rest of uploadify config 
  });
});
&lt;/pre&gt;

&lt;h2&gt;Handling form data on a page that accepts Uploadify file uploads&lt;/h2&gt;

&lt;p&gt;An additional wrinkle is that Joeri's method isn't suitable for use where you want to have a &amp;lt;h:form&amp;gt; submit form data after all files are uploaded. If you attach the uploadify &lt;code&gt;uploadifyUpload()&lt;/code&gt; call to a JSF &lt;code&gt;commandLink&lt;/code&gt; or &lt;code&gt;commandButton&lt;/code&gt; you'll find that the form submits before all the files upload. This is because &lt;code&gt;uploadifyUpload()&lt;/code&gt; is asynchronous, returning before all files are sent.&lt;/p&gt;

&lt;p&gt;To get your form submission to wait until all files are sent, you'll probably want to have your submit button (which should be an ordinary non-jsf button) call &lt;code&gt;uploadifyUpload()&lt;/code&gt; ... then do nothing more. You can attach an &lt;code&gt;onAllComplete()&lt;/code&gt; event handler to uploadify that invokes a hidden JSF2 &lt;code&gt;commandLink&lt;/code&gt; when all the uploads are sent.&lt;/p&gt;

&lt;p&gt;For example, add this to your uploadify ctor arguments:&lt;/p&gt;

&lt;pre&gt;
'onAllComplete' : function(event,data) {
  // Escape due to css selector interpreation of colon
  // see http://docs.jquery.com/Frequently_Asked_Questions#How_do_I_select_an_element_by_an_ID_that_has_characters_used_in_CSS_notation.3F
  $('#upload\\:hiddenLink').trigger('click');
}
&lt;/pre&gt;

&lt;p&gt;and use a jsf form something like this:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;h:form id="upload"&gt;
  &amp;lt;-- uploadify field --&amp;gt;
  &amp;lt;input id="file_upload" name="file_upload" type="file" /&amp;gt;
  &amp;lt;-- Put your JSF2 input fields in here --&amp;gt;
  &amp;lt;input type="button" value="Clear Queue"  onclick="$('#file_upload').uploadifyClearQueue();"/&amp;gt;
  &amp;lt;input type="button" value="Submit Queue" onclick="$('#file_upload').uploadifyUpload();"/&amp;gt;
  &amp;lt;h:commandLink id="hiddenLink" style="display:none; visibility: hidden;"
    action="#{fileUpload.doUploadsSuccessfulAction}"
    value="doIt!"/&amp;gt;
&amp;lt;/h:form&amp;gt;
&lt;/pre&gt;

&lt;p&gt;... where &lt;code&gt;String FileUpload.doUploadsSuccessfulAction()&lt;/code&gt; is a suitable managed bean that's ready to process the submitted form data.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-3011457035111983502?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/3011457035111983502/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/02/handling-file-uploads-with-java-ee-6.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3011457035111983502'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3011457035111983502'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/02/handling-file-uploads-with-java-ee-6.html' title='Handling file uploads with Java EE 6 / JAX-RS / Glassfish / Uploadify'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1561159658610863286</id><published>2011-02-04T11:48:00.002+08:00</published><updated>2011-02-04T11:50:39.247+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='cdi'/><title type='text'>How to get the context path (context root) in jsf2</title><content type='html'>&lt;p&gt;Use:&lt;/p&gt;

&lt;pre&gt;
#{facesContext.externalContext.request.contextPath}
&lt;/pre&gt;

&lt;p&gt;For some insane reason, none of the methods that should work would work when invoked via a &lt;code&gt;@RequestScoped&lt;/code&gt; object with &lt;code&gt;@Inject @FacesContext&lt;/code&gt; - they all merrily returned the empty string. More stupid CDI/JSF brokenness.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1561159658610863286?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1561159658610863286/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/02/how-to-get-context-path-context-root-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1561159658610863286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1561159658610863286'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/02/how-to-get-context-path-context-root-in.html' title='How to get the context path (context root) in jsf2'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-3116848507419039341</id><published>2011-01-31T13:29:00.010+08:00</published><updated>2011-01-31T21:11:03.241+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Setting up usb gadget serial (g_serial) on Kobo Wifi</title><content type='html'>&lt;p&gt;The Kobo Wifi (apparently) has an onboard serial port, but it doesn't have any header pins let alone a usable socket. Using it will require bulldog clips at best, more likely soldering some pins in. Either way you have to open the case &lt;i&gt;and keep it open while using the port&lt;/i&gt; which is inconvenient if you like to actually use your Kobo. In any case, I can't find any documentation for the pinout.&lt;/p&gt;

&lt;p&gt;Telnet over wifi works and can be enabled without opening up the device - but tends to go down quite a bit, as it's not really intended for this use. It's hopelessly unsuitable for running a GDB remote debugger over.&lt;/p&gt;

&lt;p&gt;It's fairly simple to modify the Kobo Wifi (and presumably the original Kobo, though I haven't tried it) to add support for serial-over-usb and/or ethernet-over-usb, both of which are &lt;i&gt;much&lt;/i&gt; easier to work with than physical serial and much better than telnet/ftp over wifi. Enabling USB serial gadget support seems to interfere with the USB gadget mass storage system used to export the file system to a host computer, though, so don't make the change permanent unless you like to keep your library on an SD card. It looks like the 2.6.33-rc1 and newer kernels may contain support for multiple gadgets running at once, but the Kobo is on 2.6.28 and it's unlikely to be worth the effort of an update.&lt;/p&gt;

&lt;p&gt;To get usb serial mode working, you must build a new kernel for the Kobo and copy the modules from that kernel over to the Kobo. You might want to &lt;code&gt;make menuconfig&lt;/code&gt; and enable &lt;code&gt;CONFIG_USB_CDC_COMPOSITE&lt;/code&gt; in &lt;code&gt;drivers -&amp;gt; usb support -&amp;gt; usb gadget -&amp;gt; CDC Composite Device&lt;/code&gt; if you'd like to have simultaneous support for Ethernet and serial gadget mode, which can be really handy for debugging. See &lt;code&gt;documentation/README.kernel&lt;/code&gt; in the KoboLabs git repository for how to build a new kernel and modules. I've asked them to pull it, so it should be there soon. You should not actually need to install the new kernel, as the modules you build should be compatible with the old kernel already on the device.&lt;/p&gt;

&lt;p&gt;To install the modules, install them to some temporary &lt;code&gt;MOD_INSTALL_PATH&lt;/code&gt; like &lt;code&gt;/tmp/arm&lt;/code&gt; then &lt;code&gt;cd /tmp/arm&lt;/code&gt; and &lt;code&gt;tar cvzf KoboRoot.tgz lib&lt;/code&gt;. Copy the &lt;code&gt;KoboRoot.tgz&lt;/code&gt; file to &lt;code&gt;.kobo/KoboRoot.tgz&lt;/code&gt; on the device's onboard user-accessible flash storage and reboot to install the update. After a reboot, you can telnet in and &lt;code&gt;modprobe g_serial&lt;/code&gt; (or &lt;code&gt;g_cdc&lt;/code&gt; if you compiled the mixed serial/ethernet module) to enable USB serial support. To make the serial port useful you need to run &lt;code&gt;/sbin/getty -L ttyGS0 115200 vt100&lt;/code&gt; to listen for logins. This is easily wrapped in a shell script you can invoke via telnet, or via an autorun hook you add in &lt;code&gt;/etc/init.d/rcS&lt;/code&gt; that runs a script off an add-in SD card if one is found. The same script can be used to bring up the &lt;code&gt;usb0&lt;/code&gt; ethernet device if you're using &lt;code&gt;g_cdc&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;If you want to make usb serial gadget support start at every boot (thus disabling the ability to manage the internal card library over USB - this will be annoying!), add this line to &lt;code&gt;/etc/inittab&lt;/code&gt; on the device:&lt;/p&gt;

&lt;pre&gt;
ttyGS0::askfirst:/sbin/getty -L ttyGS0 115200 vt100
&lt;/pre&gt;

&lt;p&gt;... and add this line to &lt;code&gt;/etc/init.d/rcS&lt;/code&gt; after "/bin/mount -t sysfs" :&lt;/p&gt;

&lt;pre&gt;
/sbin/modprobe g_serial
&lt;/pre&gt;

&lt;p&gt;If you'd prefer to have serial and ethernet, and made the kernel config change to enable it as described above, replace &lt;code&gt;g_serial&lt;/code&gt; with &lt;code&gt;g_cdc&lt;/code&gt; in the line above.&lt;/p&gt;

&lt;p&gt;Once you've loaded the g_serial module one way or another and started a getty, you can connect the Kobo to your PC. When prompted for what to do, say "keep reading". You'll discover that the Kobo appears as a USB serial port that you can connect a terminal to. On a Linux host it should be &lt;code&gt;/dev/ttyACMx&lt;/code&gt; (&lt;code&gt;/dev/ttyACM0&lt;/code&gt; if you have no other USB serial ports); check &lt;code&gt;dmesg&lt;/code&gt; to be sure. You can use a terminal program like &lt;code&gt;minicom&lt;/code&gt;, &lt;code&gt;gtkterm&lt;/code&gt; or &lt;code&gt;picoterm&lt;/code&gt; to talk to the port. The login is "root" and there is no password unless you set one later. You can transfer files using zmodem's rx command if you need to, use the usb ethernet module, or ftp them over wifi.&lt;/p&gt;

&lt;p&gt;Unlike telnet over wifi, this serial console stays up no matter what antics &lt;code&gt;nickel&lt;/code&gt; performs, and doesn't require the device to be open. It's similarly easy to use &lt;code&gt;g_ether&lt;/code&gt; to expose an Ethernet interface or &lt;code&gt;g_cdc&lt;/code&gt; to do both ethernet and serial at once. It's just a real pain that using them prevents the &lt;code&gt;g_file_storage&lt;/code&gt; module used by the Kobo to manage the onboard memory from working!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-3116848507419039341?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/3116848507419039341/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/setting-up-usb-gadget-serial-gserial-on.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3116848507419039341'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3116848507419039341'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/setting-up-usb-gadget-serial-gserial-on.html' title='Setting up usb gadget serial (g_serial) on Kobo Wifi'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8850468372742457937</id><published>2011-01-28T20:21:00.004+08:00</published><updated>2011-01-28T20:41:31.936+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='qt'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Compiling Qt plugins for Kobo Wifi</title><content type='html'>&lt;p&gt;The Kobo Wifi's main user interface application, &lt;code&gt;nickel&lt;/code&gt;, is a Qt Embedded app. It drives the ePaper display via a Linux framebuffer interface and some ioctl calls to trigger display refreshes. This is abstracted away from the app behind a QWS (Qt Window System) driver called &lt;code&gt;broadsheet_ioctl&lt;/code&gt; that's statically linked into &lt;code&gt;nickel&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;Because the QWS driver is only in &lt;code&gt;nickel&lt;/code&gt;, we can't really write stand-alone Qt applications to run on the Kobo without reimplementing it. However, the &lt;code&gt;nickel&lt;/code&gt; app has a simple plugin interface you can use to load your own code as a shared library.&lt;/p&gt;

&lt;p&gt;To do this, you need a &lt;a href="http://soapyfrogs.blogspot.com/2011/01/preparing-development-environment-for.html"&gt;full Kobo development environment&lt;/a&gt;, including a cross-compiled Qt Embedded build that matches the one used on the Kobo. Once you have that, you can build apps according to the template provided by &lt;code&gt;examples/poker&lt;/code&gt;, then drop the shared library produced into &lt;code&gt;/usr/local/Kobo&lt;/code&gt; on the device to get it to load.&lt;/p&gt;

&lt;p&gt;You now have some totally useless code running on your Kobo. Congratulations. There's no way to invoke it. For now, I'm testing by replacing the poker plugin with my own code, but it's still painful to access it so it's not desirable for real-world use. It may prove necessary to introspect the Qt widget tree or even runtime patch classes to get useful functionality loaded by plugins without having access to the &lt;code&gt;nickel&lt;/code&gt; sources.&lt;/p&gt;

&lt;p&gt;If your plugin doesn't load, it may be helpful to telnet into your Kobo and create a script like this, called (eg) &lt;code&gt;/usr/local/relaunch-nickel.sh&lt;/code&gt;&lt;/p&gt;

&lt;pre&gt;
#!/bin/sh
# Kill off the old nickel
pkill nickel
# Clear plugin cache
rm -f /mnt/onboard/.kobo/Trolltech.conf
# Restart nickel
export PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/lib:
export NICKEL_HOME=/mnt/onboard/.kobo
export LD_LIBRARY_PATH=/usr/local/Kobo
export QWS_KEYBOARD=netronix
export INTERFACE=eth0
export LANG=en_US.UTF-8
QT_DEBUG_PLUGINS=1 /usr/local/Kobo/nickel -qws -display broadsheet_ioctl &gt;/mnt/onboard/.kobo/nickel.log 2&gt;&amp;1 &amp;
&lt;/pre&gt;

&lt;p&gt;When executed, this script will restart nickel, causing it to write a debug log to the .kobo directory on the user flash and print detailed information about plugin loading. This might help you figure out what's going on. &lt;code&gt;nickel&lt;/code&gt; prints a fair few warnings during normal startup, so don't be thrown by that.&lt;/p&gt;

&lt;p&gt;When developing for Qt on Kobo, the &lt;a href="http://trinity.pearsoncomputing.net/docs/qt4/qt-embedded-envvars.html"&gt;Qt for Embedded Linux Environment Variables&lt;/a&gt; document may be helpful, though it doesn't mention &lt;code&gt;QT_DEBUG_PLUGINS&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;It can also be helpful to determine how Qt was built on your target device. On the Kobo Wifi:&lt;/p&gt;

&lt;pre&gt;
$ strings libQtCore.so | grep 'Build key'
Build key:           arm linux g++-4 full-config
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8850468372742457937?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8850468372742457937/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/compiling-qt-plugins-for-kobo-wifi.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8850468372742457937'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8850468372742457937'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/compiling-qt-plugins-for-kobo-wifi.html' title='Compiling Qt plugins for Kobo Wifi'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-95645040083648592</id><published>2011-01-27T18:30:00.007+08:00</published><updated>2011-01-31T11:40:29.567+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Taking a disk image of the Kobo Wifi without opening the device</title><content type='html'>&lt;p&gt;Once you've &lt;a href="http://soapyfrogs.blogspot.com/2011/01/enabling-telnet-and-ftp-access-to-kobo.html"&gt;enabled telnet&lt;/a&gt; on your Kobo Wifi, you can use it to transfer a disk image of the Kobo's entire firmware image on the internal MicroSD card. This will give you a backup copy of your Kobo's firmware on your computer.&lt;/p&gt;

&lt;p&gt;You can't restore the firmware image without physically opening your Kobo if you mess things up badly enough for it not to boot, but hopefully you won't do that. The Kobo seems to be amazingly hard to break unless you corrupt its file systems, overwrite its kernel, etc. Anyway, this way you can avoid opening the Kobo until/unless you ever need a total firmware restore.&lt;/p&gt;

&lt;h2&gt;Imaging the Kobo's firmware over wifi&lt;/h2&gt;

&lt;p&gt;To image the disk of a running system, we need all its file systems in read only mode. This turns out to be trivial to achieve with the Kobo, though it requires terminating the main eReader process ("nickel") so you'll want to reboot after you've done it. During the process, the screen will display whatever was on it before you ran the commands to image the system and none of the keys will respond. Just:&lt;/p&gt;

&lt;pre&gt;
pkill nickel
mount -o ro,remount /mnt/onboard
mount -o ro,remount /
nc -l 9984 &lt; /dev/mmcblk0
&lt;/pre&gt;

&lt;p&gt;Now, take note of the IP address printed by the commands above and use it in this command on your main computer:&lt;/p&gt;

&lt;pre&gt;
nc IP_OF_KOBO 9984 &gt; kobo_internal_microsd.img
&lt;/pre&gt;

&lt;p&gt;The disk imaging process will take a while over wifi, and won't show any progress. Mine took about twenty minutes. If you want progress indication, open a second terminal and run:&lt;/p&gt;

&lt;pre&gt;
watch "du -ms kobo_internal_microsd.img"
&lt;/pre&gt;

&lt;p&gt;... to get a count in megabytes of data transferred so far.&lt;/p&gt;

&lt;p&gt;Finally, reboot the kobo to bring it back to normal. You can do this by pressing and holding the power button for 6+ seconds, or by typing control-C then "reboot" and enter into the telnet command line on the kobo.&lt;/p&gt;

&lt;p&gt;The image saved on your computer should be 1977614336 bytes for a Kobo Wifi with a 2GB internal card (~1.1GB user accessible memory).&lt;/p&gt;

&lt;p&gt;If you have any issues, try a different port number with &lt;code&gt;nc&lt;/code&gt;.&lt;/p&gt;

&lt;pre&gt;
^C
reboot
&lt;/pre&gt;

&lt;p&gt;Once you have the Kobo's disk image, you can mount the partitions within it to examine them, and you can extract some individual components of the unpartitioned space in the card.&lt;/p&gt;

&lt;h2&gt;Imaging the Kobo's firmware to an SD card&lt;/h2&gt;

&lt;p&gt;In principle you can also image the Kobo's firmware to an SD card instead of using wifi. This will be much easier if you don't run Linux. You need a 2GB or larger card.&lt;/p&gt;

&lt;p&gt;To image the Kobo's firmware to an SD card you can telnet in to the kobo and make the file systems read-only as above. Make sure you unmount the external sd card &lt;code&gt;/mnt/sd&lt;/code&gt; if it is mounted. Then, instead of getting the IP address and running netcat in listen mode, you can just `dd' the internal mmc card to the external sd card, `sync' to force it to flush, and `reboot'.&lt;/p&gt;

&lt;p&gt;Alternately, you may prefer to leave the external SD card mounted, remount everything else read only, then run:&lt;/p&gt;

&lt;pre&gt;
dd if=/dev/mmcblk0 | gzip &gt; /mnt/sd/kobo.img.gz
&lt;/pre&gt;

&lt;p&gt;... to make a compressed image (slow!) on the file system on the SD card. This will be easier to work with under Windows, which doesn't work well with raw devices.&lt;/p&gt;

&lt;h2&gt;Examining the firmware image&lt;/h2&gt;

&lt;p&gt;gunzip the image if it's compressed. Either back up a copy of the firmware image that you will not touch, or make the firmware image read-only with &lt;code&gt;chattr +i&lt;/code&gt;.&lt;/p&gt;

&lt;p&gt;You may now mount the three file systems on the image using this little script - or by hand by using "fdisk -l" to dump the partition table, then calculating the offsets to pass to losetup. If you want to use my script, I make no promises that it won't eat your system and your cat, so be careful. Save the following to "kobomount.sh" and mark it executable:&lt;/p&gt;

&lt;pre&gt;
#!/bin/bash
set -e -u
if test $# -ne 1 ; then
  echo "Usage: $0 firmware.img"
  exit 1
fi
IMG=$1
i=0
sudo -v
for offset in $(sfdisk -d kobo_2gb_microsd_image_v174.img | grep start | cut -d : -f 2 | awk '{print $2}' | sed 's/,//g' |grep -v ^0$); do
  sudo losetup --offset $(( $offset * 512 )) /dev/loop$i "$IMG"
  sudo mkdir -p /mnt/kobo/$i
  sudo mount -o ro /dev/loop$i /mnt/kobo/$i
  ((i++))||true
done
&lt;/pre&gt;

&lt;p&gt;You may then aim it at your firmware image and it'll mount the contained partitions on &lt;code&gt;/mnt/kobo/0&lt;/code&gt; (the recovery partition), &lt;code&gt;/mnt/kobo/1&lt;/code&gt; (the main OS) and &lt;code&gt;/mnt/kobo/2&lt;/code&gt; (the user flash partition). All will be read-only.&lt;/p&gt;

&lt;p&gt;You may also want to extract some blobs from the unpartitioned space at the start of the image. That's where the kernel, boot splash image, etc live. I don't have official documentation on the layout of this space, but reading the upgrade scripts in /etc/init.d suggests that these values should be about right. They are UNVERIFIED except for the boot splash image, which I've been able to edit and replace.&lt;/p&gt;

&lt;pre&gt;
IMG=kobo_2gb_microsd_image_v174.img
dd if=$IMG of=serialnumber.bin bs=512 count=1 skip=1
dd if=$IMG of=hwconfig.bin bs=512 count=1 skip=1024 count=2
# The image that ships with the kobo is &lt; 470 512 byte blocks long. It's not clear
# if something else takes the space between the end of the image and the start of
# the epson display binaries, if it's dead space, or if there's room for bigger
# images.
dd if=$IMG of=bootlogo.bmp bs=512 skip=1026 count=470 
dd if=$IMG of=epson_display_setup.bin bs=512 count=1 skip=1920 count=8
dd if=$IMG of=epson_waveform.bin bs=512 count=1 skip=1928 count=$((2048 - 1928))
dd if=$IMG of=kernel.bin bs=512 count=1 skip=2048 count=$((7564-2048))
&lt;/pre&gt;

&lt;h2&gt;The Kobo's kernel&lt;/h2&gt;

&lt;p&gt;We can now examine the existing kernel to learn more about it. &lt;a href="http://linux.derkeiler.com/Mailing-Lists/Kernel/2004-03/5506.html"&gt;This post&lt;/a&gt; tells us
how to extract the kernel (but it's more easily done with
scripts/extract-ikconfig from a kernel tree)&lt;/p&gt;

&lt;p&gt;The hard way (which lets us extract the image, not just the config) in brief:&lt;/p&gt;

&lt;pre&gt;
od -A d -t x1 kernel.bin | grep '1f 8b 08 00'
# Note the (decimal) offset, correct for offset of deflate header into line
dd if=kernel.bin bs=1 skip=0013076 | zcat &gt; vmlinux
&lt;/pre&gt;

&lt;p&gt;The easy way to dump the config:&lt;/p&gt;

&lt;pre&gt;
scripts/extract-ikconfig $HOME/kobo/kobofirmwaredump/kernel.img
&lt;/pre&gt;

&lt;p&gt;Boot loading is apparently performed by RedBoot (2.0 according to shipped
sources), which should offer tools like `fis list' to report on the structure
of the redboot-managed areas of flash storage. Building redboot is a nightmare,
so I haven't investigated this yet.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-95645040083648592?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/95645040083648592/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/taking-disk-image-of-kobo-wifi-without.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/95645040083648592'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/95645040083648592'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/taking-disk-image-of-kobo-wifi-without.html' title='Taking a disk image of the Kobo Wifi without opening the device'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://img2.blogblog.com/img/b16-rounded.gif'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-2412355355601641612</id><published>2011-01-27T17:10:00.011+08:00</published><updated>2011-06-21T08:50:49.122+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Enabling telnet and ftp access to the Kobo Wifi</title><content type='html'>&lt;p&gt;The Kobo Wifi has this handy 802.11 radio that it really only uses for online shopping. Not only is it calling out to be used for RSS feeds and web browsing, but it is also a really handy tool for developing on the device without having to crack it and solder serial port headers onto the board.&lt;/p&gt;

&lt;p&gt;We can gain telnet and ftp access to the device quite trivially; see the pre-made KoboRoot.tgz patch linked to below for the really easy way.&lt;/p&gt;

&lt;p&gt;If you want to roll your own patch or you're using a different firmware revision, there isn't much to do. The Kobo already has busybox's telnetd, ftpd and inetd on it. All that's necessary is to:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;Edit /etc/inittab and add the lines:
&lt;pre&gt;
::sysinit:/etc/init.d/rcS2
::respawn:/usr/sbin/inetd -f /etc/inetd.conf.en
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;Create /etc/init.d/rcS2 with the content:
&lt;pre&gt;
#!/bin/sh
mkdir -p /dev/pts
mount -t devpts devpts /dev/pts
/usr/sbin/inetd /etc/inetd.conf
&lt;/pre&gt;
and run &lt;code&gt;chmod a+x /etc/init.d/rcS2&lt;/code&gt; to flag it executable.
&lt;/li&gt;
&lt;li&gt;create /etc/inetd.conf with the content:
&lt;pre&gt;
# service_name sock_type proto flags user server_path args
21 stream  tcp     nowait  root    /bin/busybox ftpd -w -S  /
23 stream tcp nowait root /bin/busybox telnetd -i
&lt;/pre&gt;
&lt;/li&gt;
&lt;li&gt;tar our new /etc into KoboRoot.tgz&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;&lt;i&gt;(Edited for safer method suggested by tjm)&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;KoboRoot.tgz may then be used to update the device's operating system by putting it in the &lt;code&gt;.kobo&lt;/code&gt; directory on the user-accessible flash.&lt;/p&gt;

&lt;p&gt;Because it's not designed to be accessible from the outside world, the Kobo doesn't have any root password set. Telnet and FTP access as root will be offered &lt;b&gt;with a blank password&lt;/b&gt; by default. You can change that once you telnet in if you like, by typing "passwd" at the root prompt. This won't affect the device's normal operations, only telnet/ftp access.&lt;/p&gt;

&lt;p&gt;I created a canned &lt;a href="http://www.postnewspapers.com.au/~craig/webfiles/kobowifi-telnet-enable-patch/KoboRoot.tgz"&gt;&lt;code&gt;KoboRoot.tgz&lt;/code&gt;&lt;/a&gt; (&lt;i&gt;for firmware 1.7 ONLY&lt;/i&gt;) with these changes to save you the hassle of making them yourself. It was made from the initscripts in &lt;b&gt;firmware release 1.7.4;&lt;/b&gt; if you use it with any other firmware and it fails to boot you'll have to do a factory reset. Because there are no binaries in the patch, only a modified &lt;code&gt;/etc/init.d/rcS&lt;/code&gt; and a new &lt;code&gt;/etc/inetd.conf&lt;/code&gt;, you can easily verify that the code isn't malicious. It has two optional features you can uncomment in &lt;code&gt;/etc/init.d/rcS&lt;/code&gt; to (a) syslog to user flash for debugging, and (b) run a user-defined script from &lt;code&gt;.kobo/rc.sh&lt;/code&gt; every boot. Both are commented out so they do nothing unless you edit the script to enable them.&lt;/p&gt;

&lt;p&gt;Unlike modifying the boot splash screen, this is a pretty safe change as it's completely erased by a factory reset of the device.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-2412355355601641612?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/2412355355601641612/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/enabling-telnet-and-ftp-access-to-kobo.html#comment-form' title='21 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2412355355601641612'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2412355355601641612'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/enabling-telnet-and-ftp-access-to-kobo.html' title='Enabling telnet and ftp access to the Kobo Wifi'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>21</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-7932684720890976869</id><published>2011-01-27T14:41:00.009+08:00</published><updated>2011-01-27T15:05:14.989+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Changing the Kobo's boot image</title><content type='html'>&lt;p&gt;&lt;img style="float:right;" src="http://4.bp.blogspot.com/_5Tomk_SO2aA/TUEWvAWov8I/AAAAAAAAAAw/NyMhIf_v0iw/s320/kobo-splash.jpg" border="0" alt="Image of Kobo with modified splash screen" id="BLOGGER_PHOTO_ID_5566755611211251650" /&gt;The Kobo's boot splash image is stored in unpartitioned space on the internal MicroSD flash. It's an 600x800 4-bit greyscale Windows bitmap (not RLE compressed) starting 1026 512-byte sectors from the beginning of /dev/mmcblk0 (the onboard SD).&lt;/p&gt;

&lt;p&gt;&lt;b&gt;Before trying to modify the boot splash image or anything else on the onboard SD card, make a backup of your Kobo's firmware by disk-imaging the internal SD card.&lt;/b&gt; If you don't back it up and you damage the kernel or bootloader you won't even be able to factory reset your device; it'll be useless.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;If you use a bitmap that is too large you will render your kobo useless&lt;/b&gt;, so be careful. I've verified a bitmap of 240120 bytes, saved from Adobe Photoshop CS2, to work on my Kobo Wifi.&lt;/p&gt;

&lt;p&gt;`file logo.bmp' should report &lt;code&gt;PC bitmap, Windows 3.x format, 600 x 800 x 4&lt;/code&gt;&lt;/p&gt;

&lt;p&gt;I haven't found any unix/linux based tools that will write 4-bit greyscale Windows Bitmap images yet, so Photoshop is your best bet. Recommendations appreciated.&lt;/p&gt;

&lt;p&gt;The boot splash image may be replaced by putting a compatible bitmap on the user-accessible onboard fat32 partition. It must be located at &lt;code&gt;.kobo/upgrade/logo.bmp&lt;/code&gt;. Once placed, disconnect the kobo. It'll detect the upgrade files automatically, apply them, and reboot. On reboot, it'll show the new splash.&lt;/p&gt;

&lt;p&gt;(Sorry for the horrible phone-camera pic)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-7932684720890976869?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7932684720890976869/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/changing-kobos-boot-image.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7932684720890976869'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7932684720890976869'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/changing-kobos-boot-image.html' title='Changing the Kobo&apos;s boot image'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_5Tomk_SO2aA/TUEWvAWov8I/AAAAAAAAAAw/NyMhIf_v0iw/s72-c/kobo-splash.jpg' height='72' width='72'/><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-4967165490827399144</id><published>2011-01-25T10:15:00.005+08:00</published><updated>2011-01-27T11:22:32.081+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Preparing a development environment for the Kobo Wifi</title><content type='html'>&lt;p&gt;First, I'd like to thank the KoboLabs members for being  so helpful and responsive as I try to get up and running with Kobo development. They've pushed several tools, config files, scripts etc to the KoboLabs repository, all of which have drastically reduced the amount of time it's taken to get up and running and largely eliminated the need for reverse-engineering work. This is incredibly unusual for an embedded company, and &lt;i&gt;absolutely wonderful&lt;/i&gt;. I was already impressed by their ongoing releases of new firmwares for the original Kobo after the release of the Wifi, as most companies just drop supports old products. Now I'm blown away. Please tell people how damn impressive the Kobo folks are with their after-sales product support and maintenance; I know I will be.&lt;/p&gt;

&lt;p&gt;In fact, this whole document has been rendered largely unnecessary by improvements to the KoboLabs code drops. They've posted their build scripts and configurations. To get started, just clone the git repo and read documentation/README.&lt;/p&gt;

&lt;h2&gt;Overview&lt;/h2&gt;

&lt;p&gt;You will need to download the CodeSourcery toolchain used for Kobo development, then build the libraries the Kobo uses so that you can link your new executables against them. Qt Embedded, with its dependencies, is particularly crucial. The latest GitHub code drop includes the Qt Embedded configuration used on the Kobo, letting you compile a compatible version of Qt.&lt;/p&gt;

&lt;h2&gt;Installing Sourcery G++&lt;/h2&gt;

&lt;p&gt;Download &lt;a href="http://www.codesourcery.com/sgpp/lite/arm/portal/release1293"&gt;Sourcery G++ 2010q1-202 4.4.1&lt;/a&gt; for your platform and run the installer.&lt;/p&gt;

&lt;p&gt;If you're on an x64 RPM-based Linux distro, you may need to install a 32-bit userspace to be able to run the tools. x64 Debian/Ubuntu users will have to install them in the 32-bit chroot, and do all future steps described here within that chroot.&lt;/p&gt;

&lt;pre&gt;
cd ~/Downloads
chmod a+x arm-2010q1-202-arm-none-linux-gnueabi.bin
./arm-2010q1-202-arm-none-linux-gnueabi.bin
&lt;/pre&gt;

&lt;p&gt;Accept the default install path of $HOME/CodeSourcery/Sourcery_G++_Lite when prompted. Accept all other installer defaults.&lt;/p&gt;

&lt;p&gt;You now need to symlink your tools to new names, as some of the Kobo makefiles etc expect the tools to be named arm-linux-x where they're named arm-linux-gnuegabi-none-x by CodeSourcery. To do this:&lt;/p&gt;

&lt;pre&gt;
cd ~/CodeSourcery/Sourcery_G++_Lite/bin
for f in arm-none-linux-gnueabi-* ; do ln -s $f arm-linux-${f:23}; done
&lt;/pre&gt;

&lt;p&gt;If all is configured properly, after a logout &amp; login or after running "bash -l" you should now be able to type "arm-linux-g++" and get the response "arm-linux-c++: no input files".&lt;/p&gt;

&lt;h2&gt;Compiling libraries&lt;/h2&gt;

  &lt;p&gt;Now that we have a toolchain in place, including gcc, binutils, glibc, and all of that provided for us by the wonderful folks at &lt;a href="http://codesourcery.com"&gt;CodeSourcery&lt;/a&gt;, we can get on with the real work of preparing our dev environment.&lt;/p&gt;

  &lt;p&gt;Clone a copy of the KoboLabs git repository to get the required sources and patches:&lt;/p&gt;

&lt;pre&gt;
mkdir ~/kobo
cd ~/kobo
git clone git://github.com/kobolabs/Kobo-Reader.git koboreader
&lt;/pre&gt;

  &lt;p&gt;This will take a while, as it's downloading the sources to a lot of libraries the Linux kernel used on the Kobo, and more.&lt;/p&gt;

&lt;p&gt;Once you've cloned the repository, it should be easy to get going. A reasonable option might be:&lt;/p&gt;

&lt;pre&gt;
echo "DEVICEROOT=$HOME/kobo/fs" &gt; ~/kobo/build/build-config-user.sh
mkdir ~/kobo/tmp
cd ~/kobo/tmp
../koboreader/build/build-all.sh
&lt;/pre&gt;

&lt;p&gt;... which will use the Kobo build scripts to compile all the libraries and install them in DEVICEROOT, which you set to $HOME/kobo/fs . The unpacked sources and temporary object files will be stored in ~/kobo/tmp , so your git clone of the kobo sources doesn't get messed up by the build.&lt;/p&gt;

&lt;p&gt;Now you need to build Qt, which you can do by following the instructions in documentation/README.trolltech&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4967165490827399144?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4967165490827399144/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/preparing-development-environment-for.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4967165490827399144'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4967165490827399144'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/preparing-development-environment-for.html' title='Preparing a development environment for the Kobo Wifi'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-1208901789861313808</id><published>2011-01-23T23:08:00.006+08:00</published><updated>2011-01-25T10:15:49.790+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ereader'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Running your own code on the Kobo</title><content type='html'>&lt;p&gt;The 1.7.4 update tarball (see http://soapyfrogs.blogspot.com/2011/01/getting-kobo-update-urls.html) contains a new copy of &lt;code&gt;/etc/init.d/rcS&lt;/code&gt;. Reading it tells us how to replace files on the Kobo's root file system, giving us a way to replace rcS with a patched version that calls out to a shell script on the more easily writable FAT32 main flash, so we can tinker with the system with minimal risk. This initial change is risky, though, as you might render your Kobo useless with a typo!&lt;/p&gt;

&lt;h2&gt;Replacing files on the Kobo Wifi's root file system&lt;/h2&gt;

&lt;p&gt;The Kobo Wifi's &lt;code&gt;/etc/init.d/rcS&lt;/code&gt; startup script tests for the presence of a number of files within the &lt;code&gt;.kobo&lt;/code&gt; subdirectory of the main fat32 partition.&lt;/p&gt;

&lt;p&gt;Two notable files are &lt;code&gt;.kobo/Kobo.tgz&lt;/code&gt; and &lt;code&gt;.kobo/KoboRoot.tgz&lt;/code&gt;. If these files exist, they will be extracted into &lt;code&gt;/usr/local/kobo&lt;/code&gt; or &lt;code&gt;/&lt;/code&gt; (the root directory) respectively. The system uses this mechanism to apply incremental updates.&lt;/p&gt;

&lt;p&gt;We can exploit this mechanism to insert our own files onto the device's main memory or &lt;i&gt;very carefully&lt;/i&gt; replace existing files. If you screw it up you might render your Kobo useless and possibly irreparable, so don't try anything unless you don't mind breaking it.&lt;/p&gt;

&lt;p&gt;You're much better off copying the firmware from the internal microSD card to a 2G SD card then booting off the external SD by holding "enter" down during power-on. Getting the image for this requires physically opening the Kobo to get to it, unless someone finds an image of it hosted by Kobo somewhere, though.&lt;/p&gt;

&lt;p&gt;BTW, the rcS script also looks for &lt;code&gt;.kobo/upgrade&lt;/code&gt; and if found, runs /etc/init.d/upgrade-wifi.sh . This is probably the hook for a full upgrade installation. Don't mess with it.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1208901789861313808?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1208901789861313808/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/running-your-own-code-on-kobo.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1208901789861313808'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1208901789861313808'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/running-your-own-code-on-kobo.html' title='Running your own code on the Kobo'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1017139890383327806</id><published>2011-01-23T23:00:00.004+08:00</published><updated>2011-01-23T23:07:42.809+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Getting Kobo update URLs</title><content type='html'>&lt;p&gt;Whenever the Kobo eReader connects to wifi, it sends a HTTP/XML request to the Kobo severs with some details about the device, including the current firmware version. The server sends a HTTP/XML response containing various URLs for the Kobo store, etc.&lt;/p&gt;

&lt;p&gt;If the firmware version sent by the Kobo isn't the same as the current Kobo firmware,  the server also includes the URL for a software update in the HTTP/XML post.&lt;/p&gt;

&lt;p&gt;This is apparently only an incremental patch on the main firmware, not a full firmware download, so I still need a full firmware image and will have to extract the MicroSD card from the hardware to get it.&lt;/p&gt;

&lt;h2&gt;The 1.7.4 update&lt;/h2&gt;

The URL for the 1.7.4 update is:

&lt;a href="
http://download.kobobooks.com/firmwa...rade-1.7.4.zip"&gt;http://download.kobobooks.com/firmwa...rade-1.7.4.zip&lt;/a&gt;

&lt;p&gt;Examination of it tells us some useful things that help open the hardware up some more. See the next post.&lt;/p&gt;

&lt;h2&gt;How to get the current firmware URL from the server&lt;/h2&gt;

&lt;p&gt;You can find out the update URL by capturing the wifi traffic of a Kobo with an older firmware when it connects to the network. Use wireshark with the capturing machine's wifi card in monitor mode, or capture the traffic at the router. If you capture off wifi, you'll need to enable 802.11 decryption in Wireshark's protocols-&gt;802.11 preferences and enter the key of your network. &lt;/p&gt;

&lt;p&gt;In your network capture, look for a request like:&lt;/p&gt;

&lt;pre&gt;
POST /MobileRequest.ashx HTTP/1.1
&lt;/pre&gt;

&lt;p&gt;and in the HTTP/XML response to it you'll find something like:&lt;/p&gt;

&lt;pre&gt;
&amp;lt;UpdateURL&amp;gt;http://download.kobobooks.com/firmwares/kobo2/kobo2-upgrade-1.7.4.zip&amp;lt;/UpdateURL&amp;gt;
&lt;/pre&gt;

&lt;p&gt;If you don't have a Kobo with an old firmware, you can achieve the same effect by recording the POST request from an up-to-date Kobo, saving it, modifying it so that &amp;lt;ApplicationVersion&amp;gt;1.7.4&amp;lt;/ApplicationVersion&amp;gt; instead reads (eg) &amp;lt;ApplicationVersion&amp;gt;1.6.0&amp;lt;/ApplicationVersion&amp;gt;. Because changing the length of the request will corrupt it, you're best off using `sed' to do it. Eg:&lt;/p&gt;

&lt;pre&gt;
sed -e '/^Accept-Encoding/d' -e 's/1.7.4/1.6.0/g' request.txt &amp;gt; request-mod.txt
nc mobile.kobobooks.com &amp;lt; request-mod2.txt &amp;gt; response-mod2.txt
&lt;/pre&gt;

&lt;p&gt;The command deletes the request header that asks the server to gzip the response (for easier reading) and changes the version the "Kobo" announces its self to be running when it connects to the server.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://soapyfrogs.blogspot.com/search/label/kobo"&gt;(post series)&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1017139890383327806?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1017139890383327806/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/getting-kobo-update-urls.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1017139890383327806'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1017139890383327806'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/getting-kobo-update-urls.html' title='Getting Kobo update URLs'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-95483751380463110</id><published>2011-01-23T12:03:00.020+08:00</published><updated>2011-01-31T13:44:23.339+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='ereader'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='embedded'/><category scheme='http://www.blogger.com/atom/ns#' term='kobo'/><title type='text'>Kobo and Kobo Wifi exploration and enhancement</title><content type='html'>&lt;p&gt;&lt;i&gt;This article is part of an &lt;a href="http://soapyfrogs.blogspot.com/search/label/kobo"&gt;extended series&lt;/a&gt; on Kobo development and investigation&lt;/i&gt;&lt;/p&gt;

&lt;p&gt;I just got myself a new Kobo Wifi, after spending a year drooling over my girlfriend's Kobo 1st gen. I immediately felt the impulse to fiddle3 come over me, resulting in this collection of information about the hardware, firmware, etc as I research what I can do with it. The result is this &lt;a href="http://soapyfrogs.blogspot.com/search/label/kobo"&gt;series of posts&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There's lots to be learned about it from the sticky threads in the &lt;a href="http://www.mobileread.com/forums/forumdisplay.php?f=223"&gt;Kobo section of the MobileRead forums&lt;/a&gt;. I also strongly recommend reading their &lt;a href="http://wiki.mobileread.com/wiki/Kobo_eReader"&gt;wiki article on the Kobo&lt;/a&gt;, including the FAQ.&lt;/p&gt;

&lt;h2&gt;About the Kobo&lt;/h2&gt;

&lt;p&gt;The Kobo is a great device - it has a few flaws, but may good points that greatly outweigh those flaws. The original Kobo was based on the &lt;a href="http://wiki.mobileread.com/wiki/EB-600"&gt;Netronix EB600&lt;/a&gt;, the same board as the Cool-er reader, Astak Mentor Lite, booq, and numerous other devices. Netronix's &lt;a href="http://www.netronixinc.com/product_e-book.htm"&gt;website&lt;/a&gt; is scary-bad, but their products are pretty nice. It's not 100% clear if the Kobo Wifi is still an EB600 product - the kernel refers to the "imx357" - but it's certainly Netronix.&lt;/p&gt;

&lt;p&gt;Kobo good points include price, format support, gorgeous eInk screen, light weight and nice feel, and a nice-to-use UI. It's also &lt;i&gt;extremely moddable&lt;/i&gt;. It uses a nice standard USB mass storage interface for transferring books so you don't need dedicated vendor crapware applications. With the -wifi you can buy books directly on the device, and with the old version you can use the Adobe Digital Editions app if you don't want to use the Kobo application to buy DRM'd books. Support for DRM-free books is perfect, and is just a matter of dropping them onto the flash.&lt;/p&gt;

&lt;p&gt;Bad points include somewhat slow menu navigation (some say page turns too, but I find those just fine), lack of full-justified text display option, useless Bluetooth in the 1st gen, and &lt;i&gt;the inability to remove books from the "currently reading" list&lt;/i&gt;. It also has a long pause after unplugging it when you've added books, during which it scans them and adds them to the internal SQLite catalog. It's a pity the Kobo Desktop application can't be used for this while it's connected, though it's wonderful that the device can do it its self and doesn't rely on such an application.&lt;/p&gt;

&lt;p&gt;Later firmwares improve some prior issues dramatically, allowing things like charging while reading, and are strongly recommended. The latest firmware is out for both the Kobo and Kobo Wifi; they haven't dropped support for the old generation of hardware, which they deserve real credit for. The use of FAT32 means it risks file system corruption if it's unplugged while transferring books - but this will only happen if you mistreat it, won't cause the device to fail to boot or anything, and is trivial to fix.&lt;/p&gt;

&lt;p&gt;The Kobo is massively improved by the use of the free and open source &lt;a href="http://calibre-ebook.com/"&gt;Calibre&lt;/a&gt; e-Book management software, which is vastly superior to the Kobo Desktop application in every way. You don't need any management app at all (thanks to USB mass storage support) but Calibre is wonderful for document conversion and cleanup, management, automatic conversion of newsfeeds to ebooks, etc.&lt;/p&gt;

&lt;h2&gt;Kobo modding - hardware/software info&lt;/h2&gt;

&lt;p&gt;First: &lt;b&gt;Do not try to reflash your Kobo with firmwares from other EB600 devices.&lt;/b&gt; Reports from the mobileread.com forums suggest that this will render your device nonfunctional and make it impossible to restore the original firmwares, though others report having successfully converted a Kobo into a Cool-ER Reader. If you try it, make sure you can afford to replace your eReader or are a JTAG wizard before you try converting it to the &lt;a href="http://www.mobileread.com/forums/showthread.php?t=102044"&gt;Cool-ER firmware&lt;/a&gt; or anything like that. At the very least, make sure you take a disk image of its MicroSD card first.&lt;/p&gt;

&lt;p&gt;There is much to be learned about the Kobo and many other devices on the &lt;a href="http://www.mobileread.com/"&gt;MobileRead&lt;/a&gt; forums. I'm &lt;code&gt;ringerc&lt;/code&gt; there, though I'm not a big forum user so you're better off commenting here or emailing me.&lt;/p&gt;

&lt;p&gt;The kobo's kernel, operating system, and user data is stored on an onboard 2GB MicroSD card. There's also a full size externally accessible SD card slot for add-on storage. There are three partitions on the internal microSD - a recovery root FS (ext3), the main root FS (ext3) and the user data partition (fat32). The fat32 partition is exported over USB as a mass storage device when the device is connected in "manage library" mode. The MicroSD card has some unpartitioned space before the recovery partition, where it stores the boot splash screen (800x600x4 Windows BMP), kernel, bootloader, hardware config data, epson display setup data, and probably more.&lt;/p&gt;

&lt;p&gt;Because everything is on MicroSD, it's wonderful to hack on. Just pop it open, disk image the MicroSD card using a MicroSD adapter in a PC, and you can recover from pretty much whatever screwup you might make without attacking a flash chip with a soldering iron or learning your JTAG voodoo.&lt;/p&gt;

&lt;p&gt;The Kobo (original) apparently presents a root console via its &lt;a href="http://openinkpot.org/wiki/Device/EB600/ComPortPinout"&gt;internal 3.3V RS232-like serial port headers&lt;/a&gt;, though examination of the Kobo Wifi's board suggests that it may lack these headers. This should allow full access if you're willing to crack your Kobo case - possibly literally, because it's made for easy assembly not easy opening, and is by all accounts a pain to open. I had no real problems opening my Kobo Wifi carefully using a butter knife to pop open all the clips around the edge of the front bezel, but I don't recommend that you try this unless you don't mind the risk of breaking it.&lt;/p&gt;

&lt;p&gt;I've been informed by someone working on the Kobo that there is no "trusted boot" with signed kernel and bootloader. The Kobo uses u-boot (the Kobo Wifi may use Redboot; not sure yet) and a regular Linux kernel (2.6.28 for the -wifi). Kernel and u-boot sources, along with some userspace helper apps and other things of interest can be found in the &lt;a href="https://github.com/kobolabs/Kobo-Reader"&gt;Kobo git repository&lt;/a&gt; on GitHub. It's a binary drop of tarballs in git (ugh); there's no version history and no way to differentiate Kobo mods from the original Netronix-supplied kernel etc, but at least it's there. Props to &lt;a href="https://github.com/gtalusan"&gt;gtalusan&lt;/a&gt; for posting the updated sources for the new Kobo Wifi model less than a day after I asked him about them. He's also been &lt;i&gt;wonderful&lt;/i&gt; about posting updates, fixes, config files etc as I get further into setting up a Kobo development environment.&lt;/p&gt;

&lt;h3&gt;The numerous ways of updating the Kobo firmware and root file system&lt;/h3&gt;

&lt;p&gt;The Kobo has an amazing number of different ways to perform firmware updates, plus several ways to apply small incremental patches to the root file system contents.&lt;/p&gt;

&lt;p&gt;Smaller firmware patches are applied by putting a file called KoboRoot.tgz in the .kobo directory of the main onboard FAT32 flash. Upon reboot, this is untarred into the / directory then deleted. Another file named Kobo.tgz (also in .kobo) is untarred into /usr/local/Kobo if found, making for somewhat safer updates when only the Kobo application is being changed. There also seems to be a mechanism to update the Kobo firmware using the .kobo/update file, but I recommend staying away from that.&lt;/p&gt;

&lt;p&gt;Firmware updates to the Kobo may be performed by copying a new firmware archive file to a separate piece of FAT32-formatted flash. The device then uses that file to rewrite its firmware. There's no direct flashing over USB to worry about. The Kobo Wifi can download new firmwares over wifi instead of having them dropped on it via USB mass storage. Both the Kobo and Kobo Wifi can also be updated via the Kobo Desktop application or by a simple "cp" of the firmware file by putting the device in firmware upload mode then connecting it over USB.&lt;/p&gt;

&lt;p&gt;USB firmware upload mode (which seems to use the minimalist linux recovery partition on the microUSB) is entered by turning the device off and unplugging it from USB, then holding down the Menu key then pressing power. Continue to hold Menu until "Initializing USB partition" appears on the screen. The device will show "Please connect your eReader to USB" on the display. Doing so will mount an empty 200MB FAT32 volume labeled "KoboUpgrade" from a USB mass storage volume from "Netchip Technology" (usb 0525:a4a5). You may safely unmount and unplug the Kobo without copying a firmware file to the partition; it will briefly report that it's "updating software" then reboot back into the old firmware with all your books etc unchanged.&lt;/p&gt;

&lt;p&gt;Kobo firmware updates can &lt;i&gt;also&lt;/i&gt; be performed via the SD card. Some Canadian users received SD cards with firmware updates for the original Kobo. Reports suggest that it prefers &lt;= 2GB SD cards for updates (not SDHC). Instructions &lt;a href="http://bordersau.zendesk.com/entries/234241-how-do-i-update-my-kobo-firmware-if-i-am-using-linux"&gt;here&lt;/a&gt;. I have not tested this. &lt;b&gt;Do not try to install firmwares for other eReaders on your Kobo unless you don't mind rendering it permanently non-functional&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;The Kobo can &lt;a href="http://www.mobileread.com/forums/showpost.php?p=1152496&amp;postcount=20"&gt;boot off an SD card&lt;/a&gt; - just hold down the middle button of the D-PAD ("enter") during power-on, until the first five black boxes of the boot progress bar have filled in. This can be used to test new firmwares without rewriting the onboard flash.&lt;/p&gt;

&lt;p&gt;I've yet to build a new firmware for the Kobo, though I've been using the KoboRoot.tgz patch method on mine in an attempt to get telnet and FTP access over wifi. Figuring out how it's put together isn't too hard, but it's unlikely I'll be able to get the rights to distribute new firmwares, so using the Kobo's firmware patch mechanism is likely to be safer and more useful.&lt;/p&gt;

&lt;p&gt;The Kobo Wifi runs on an arm6 CPU on a Motorola MX35 3-Stack compatible board. It has 128MB of RAM.&lt;/p&gt;

&lt;pre&gt;
[root@(none) /]# cat /proc/cpuinfo 
Processor       : ARMv6-compatible processor rev 3 (v6l)
BogoMIPS        : 530.84
Features        : swp half thumb fastmult vfp edsp java 
CPU implementer : 0x41
CPU architecture: 6TEJ
CPU variant     : 0x1
CPU part        : 0xb36
CPU revision    : 3

Hardware        : Freescale MX35 3-Stack Board
Revision        : 35120
Serial          : 0000000000000000
&lt;/pre&gt;


&lt;h2&gt;What runs on the Kobo&lt;/h2&gt;

&lt;p&gt;The Kobo Wifi runs Linux 2.6.28, glibc 2.11.1, Busybox v1.17.1, and Qt Embedded 2.6.2 among other things. A fuller list can be found in the KoboLabs git repository.&lt;/p&gt;

&lt;p&gt;The busybox configuration is fairly complete, though it lacks a bbconfig command. The full list is easily gained by running "busybox" without arguments on the Kobo, so I won't reproduce it here.&lt;/p&gt;

&lt;p&gt;The sd8xxx wireless driver is loaded as a module. Everything else seems to be built in to the kernel, but module support is present.&lt;/p&gt;

&lt;p&gt;The Kobo Wifi binaries were developed with &lt;a href="http://www.codesourcery.com/sgpp/lite/arm/portal/release1293"&gt;CodeSourcery Sourcery G++ Lite 2010q1-202 4.4.1&lt;/a&gt;. To build plugins for the Kobo Qt application, rebuild Kobo binaries, or build your own, you will need these tools.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-95483751380463110?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/95483751380463110/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/kobo-and-kobo-wifi-hacking.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/95483751380463110'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/95483751380463110'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/kobo-and-kobo-wifi-hacking.html' title='Kobo and Kobo Wifi exploration and enhancement'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-3796999860051192886</id><published>2011-01-02T11:00:00.006+08:00</published><updated>2011-06-15T18:18:10.312+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='patch'/><category scheme='http://www.blogger.com/atom/ns#' term='postgresql'/><title type='text'>PostgreSQL automatic crash dumps for windows</title><content type='html'>&lt;p&gt;It seems my &lt;a href="http://archives.postgresql.org/message-id/4CAB4294.2070104@postnewspapers.com.au"&gt;first real* PostgreSQL patch&lt;/a&gt; has been accepted and committed! By the time patch review was done I think it was almost more Magnus Hagander's patch than mine, but I'm still very happy to have put it together and seen it through into &lt;a href="https://github.com/postgres/postgres/commits/43a5c23bb3a60eaaf77f802ef6be08e7c7e08670/src/backend/port/win32/crashdump.c#"&gt;Pg mainline&lt;/a&gt;. Thanks very much to Marcus for testing, enhancing and committing the patch.&lt;/p&gt;

&lt;p&gt;PostgreSQL 9.1 will now have automatic crash dump generation under Windows. That'll allow Windows users to run their production sites with crash dumps so we can do post-mortem debugging without holding up their server or requiring them to run it under a debugger. That gives us a much better chance of tracking down the cause of hard-to reproduce or intermittent faults. To activate the crash dump collection feature, just create a &lt;code&gt;crashdumps&lt;/code&gt; directory in the data directory and grant the &lt;code&gt;postgres&lt;/code&gt; user (or whatever user your server runs under) "full control" of that folder in Properties-&gt;Security. No configuration file changes or server re-starts are needed.&lt;/p&gt;

&lt;p&gt;Helpfully, this change will also let the majority who don't know what Visual Studio or &lt;code&gt;windbg.exe&lt;/code&gt; are send crash dumps off to someone more experienced with the tools already configured. There should be much less need for &lt;a href="http://wiki.postgresql.org/wiki/Getting_a_stack_trace_of_a_running_PostgreSQL_backend_on_Windows"&gt;this page&lt;/a&gt; - and while it took me long enough to write, I'll be glad to see it rendered unnecessary.&lt;/p&gt;

&lt;p style="color: grey;"&gt;* The one-liner fix for the X.509 client certificate validation bug doesn't really count, despite the truly epic amount of &lt;a href="http://postgresql.1045698.n5.nabble.com/BUG-5468-Pg-doesn-t-send-accepted-root-CA-list-to-client-during-SSL-client-cert-request-tt2124692.html"&gt;convincing&lt;/a&gt; required to get the patch applied.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-3796999860051192886?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/3796999860051192886/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2011/01/postgresql-automatic-crash-dumps-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3796999860051192886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3796999860051192886'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2011/01/postgresql-automatic-crash-dumps-for.html' title='PostgreSQL automatic crash dumps for windows'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-6971872161561324623</id><published>2010-11-01T08:57:00.011+08:00</published><updated>2010-11-01T13:47:18.849+08:00</updated><title type='text'>Kogan KGN1080P32VAA - Avoid this TV and any other with a CultraView CV119MA mainboard</title><content type='html'>&lt;p&gt;The Kogan KGN1080P32VAA TV is a lemon. Avoid it, and Kogan.&lt;/p&gt;

&lt;h2&gt;Support&lt;/h2&gt;

&lt;p&gt;Before I go on, I want to note that Kogan's support were responsive, if not helpful, about these issues. They acknowledged the EDID defect, and while they won't fix it or anything else that's wrong with the TV they did credit me $40 for my trouble, showing some sign they accept their error. Kogan have been much better than, say, Belkin when it comes to supporting their products. Given that they still haven't actually fixed anything, that says scary things about the industry, doesn't it? Anyway, here's why you should avoid this product, anything else by CultraView, and possibly anything else by Kogan:&lt;/p&gt;

&lt;h2&gt;720p EDID on a 1080p panel, mainboard programmed wrong&lt;/h2&gt;

&lt;p&gt;When I bought the KGN1080P32VAA I expected (and got) a 1080p TV with cheap and nasty LCD panel; this is, after all, a $600 1080p TV. Fine, you get what you pay for, and it's still a 1080p full HD panel.&lt;/p&gt;

&lt;p&gt;What I didn't expect was an incorrectly programmed mainboard that sends the EDID for a 720p TV rather than a 1080p TV, making the TV not "full hd" at all (and in fact worse than native 720p due to scaling) for anything that respects the EDID! What does this say about quality control and product testing? I certainly didn't expect Kogan to have no interest in issuing a firmware update to fix it, given that the &lt;a href="http://www.cultraview.com/EnProductShow.asp?ID=82"&gt;TV's mainboard, by physical examination determined to be the CultraView CV119MA&lt;/a&gt; is still in production and supports firmware updates via USB key. CultraView don't respond to queries and don't offer their tools or firmware for download without a username and password to their vendor/partner FTP site. Kogan say:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;We have tried to get a firmware upgrade for you TV however we cannot get solution for this issue.&lt;/p&gt;
&lt;p&gt;I would still suggest you to use the VGA as you get Full HD resolution on VGA without any issue.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;Alas, there's only one VGA input and associated 3.5mm audio port. I didn't buy a TV with only one (working) input. Additionally, at 1920x1080 the (analog) VGA looks ... kind of nasty. This is partly explained by the picture issue described later.&lt;/p&gt;

&lt;h2&gt;HDMI audio and overscan&lt;/h2&gt;

&lt;p&gt;The problems with HDMI don't end with the EDID, though. The CultraView CV119MA, as shipped by Kogan, turns on &lt;a href="http://en.wikipedia.org/wiki/Overscan"&gt;overscan&lt;/a&gt; on &lt;i&gt;digital inputs&lt;/i&gt; when it detects a HDMI audio signal. No HDMI audio, no overscan. HDMI audio, overscan. This means you can't get decent picture quality and HDMI audio at the same time - you have to endure the image butchery of overscan and overscan compensation, or have no audio on that input. Overscan &lt;i&gt;makes no sense&lt;/i&gt; for digital inputs, where a perfect 1:1 pixel representation is sent and there are no scan line fringes, etc, to deal with. Sadly overscan does need to be an option because some older/broken digital output devices produce images with overscan for compatibility with ancient TVs, but should never be the default, and is something you must be able to disable. It certainly shouldn't be controlled by whether or not HDMI audio is being sent!&lt;/p&gt;

&lt;p&gt;It's possible to disable overscan by disabling HDMI audio - if your device supports this. For my media PC, that requires telling the nVidia driver to completely ignore the EDID so it doesn't detect the HDMI audio capability of the output. I had to override the EDID anyway because the TV sends a totally bogus EDID with only 1080i and 720p resolutions over HDMI. There are a couple of ways to override the EDID though - I found that overriding just the resolutions left me with an overscanned 1080p image, while overriding the whole EDID got me a 1:1 pixel sharp-ish 1080p image. The difference: HDMI audio.&lt;/p&gt;

&lt;p&gt;The TV doesn't offer a "1:1 pixel" mode in its zoom/aspect list, nor in its menus. The only way to control overscan is via HDMI audio. I'm gobsmacked.&lt;/p&gt;

&lt;h2&gt;Vendors unwilling to fix anything&lt;/h2&gt;

&lt;p&gt;Neither Kogan nor CultraView will supply a firmware update, or the tools to create one using the vendor customisation tools CultraView offer to vendors. Neither will supply the documentation for the mainboard, which has "PC DEBUG" (JTAG?) and "PC INPUT" (DB9 serial headers? Maybe?) on it and has updatable firmware. Examination of a firmware image I found on the 'net confirms that it's capable of being reflashed via USB.&lt;/p&gt;

&lt;h2&gt;Brightness controls pixel values not backlight&lt;/h2&gt;

&lt;p&gt;The problems don't end with a misprogrammed EDID and idiotic overscanning behaviour there, though. When you change the "brightness" setting in the TV, it doesn't adjust the backlight, it scales the pixel values down toward the black end, so you lose contrast and get horrid banding. While this can be written off as a "cheap TV" problem, it's one I never even imagined might be possible.&lt;/p&gt;

&lt;h2&gt;Horizontal blur at native 1080p&lt;/h2&gt;

&lt;p&gt;Worse: Even now that I've finally achieved a non-overscanned image at the panel's native 1080p resolution, the image is still crap. By creating a few stippled and striped b&amp;w test patterns, I was able to determine that horizontal lines are sharp and clear - so there's no vertical scaling or distortion. An array of 1-pixel wide b&amp;w vertical lines, though, are blurred and smudged when displayed on the TV, as if they've been scaled down then up, or up then down, before display.&lt;/p&gt;

&lt;p&gt;The test image, unscaled, appears below. If your LCD display is set to native resolution and your browser isn't scaling the image, this image should appear as a block of fine vertical lines.&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.postnewspapers.com.au/~craig/webfiles/vpixels-med.png"/&gt;&lt;/p&gt;

&lt;p&gt;... and here's a photo of what you see on the Kogan TV. The connected machine was running Linux, but I see the same problem under Windows 7's (to use Media Center) over HDMI:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.postnewspapers.com.au/~craig/webfiles/kogan_tv_blur2.png"/&gt;&lt;/p&gt;

&lt;p&gt;Here's an image I scaled down by 5%, then back up to original size, which demonstrates a similar effect:&lt;/p&gt;

&lt;p&gt;&lt;img src="http://www.postnewspapers.com.au/~craig/webfiles/vpixels-med-mangled.png"/&gt;&lt;/p&gt;

&lt;p&gt;Imagine what this does to text, typefaces, and most kinds of pattern. Argh!&lt;/p&gt;

&lt;h2&gt;It's just too crap&lt;/h2&gt;

&lt;p&gt;Before you say "It's a TV, not a computer monitor", let me quote from Kogan's product page for their &lt;a href="http://preview.tinyurl.com/29hbeyf"&gt;new model 32" 1080p HD TV, the &lt;code&gt;1080P-BD32&lt;/code&gt;&lt;/a&gt;:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;&lt;b&gt;32"/ 81cm Full HD panel (1920x1080)&lt;/b&gt;&lt;br/&gt;
Full High Definition panel ensuring crystal clear TV with Progressive Scan up to 1080P. Doubles up as an excellent computer monitor.
&lt;/blockquote&gt;

&lt;p&gt;While that text did not appear on the product page for my TV, it shows that Kogan, like me, think that it's reasonable to use modern HDTVs as monitors. In any case, I'm using mine for a lounge room media PC, not a regular PC, and just want it to work! I don't expect amazing quality, but actually displaying the pixels I send it and doing so with working HDMI audio surely isn't too much to ask?&lt;/p&gt;

&lt;p&gt;After hours of work and several conversations with Kogan, I finally have the TV running at its rated 1920x1080 (1080p) resolution without horribly butchering the image with overscan. Of course, to get that far I had to override the EDID and disable HDMI audio. If there's no HDMI audio signal the TV plays the 3.5mm input on all HDMI channels - but that means you have only one input for all your channels, making the HDMI combined audio/video feature spectacularly useless, and rendering the TV effectively capable of only one distinct A+V input among from the 2 HDMI and 1 VGA ports. Needless to say, this sucks.&lt;/p&gt;

&lt;p&gt;At this point the TV is annoying me enough that I'm planning on gutting my old laptop and using its 1080p capable LVDS output to drive the TV's panel directly, bypassing the TV mainboard. That way I'll be able to see how much of this is the panel and how much is that CultraView mainboard. If the panel is OK, I'll grab a little ARM or Via Nano board with a couple of HDMI inputs and a high-res LVDS output and build that into the case as an embedded media PC, getting rid of the CultraView part entirely.&lt;/p&gt;

&lt;p&gt;Well, I've certainly learned one thing. I won't be buying from Kogan again &lt;i&gt;unless I can test the product in person with my equipment before buying.&lt;/i&gt; They assemble products from 3rd party parts - which I knew. They don't push issues back to those suppliers and get fixes for them, which I didn't know and didn't expect. Poor show, Kogan.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-6971872161561324623?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/6971872161561324623/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/11/kogan-kgn1080p32vaa-avoid-this-tv-and.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/6971872161561324623'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/6971872161561324623'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/11/kogan-kgn1080p32vaa-avoid-this-tv-and.html' title='Kogan KGN1080P32VAA - Avoid this TV and any other with a CultraView CV119MA mainboard'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-7368005415944879742</id><published>2010-10-16T13:41:00.002+08:00</published><updated>2010-10-16T13:44:31.013+08:00</updated><title type='text'>Are you a programmer+sysadmin+support person at a small company? You need to read this.</title><content type='html'>&lt;p&gt;I'm a sysadmin, developer of in-house software, and end-user tech support person at a small company. I wear a lot of hats, and do everything from Java EE applications to data recovery from crashed user laptops. If you can't open that email attachment or get to that website, I'm your man.&lt;/p&gt;

&lt;p&gt;Needless to say, this makes the programming side rather challenging. I've always thought of interruption as the enemy of effective programming, and I've observed my effectiveness falling as the rate of interruption increases.&lt;/p&gt;

&lt;p&gt;&lt;a href="
http://www.stevestreeting.com/2010/09/04/work-2-0/"&gt;This article&lt;/a&gt; gives me reason to reconsider that idea, and consider trying to plan for and work with interruption instead. I cannot recommend it enough. &lt;/p&gt;

&lt;p&gt;&lt;a href="
http://www.stevestreeting.com/2010/09/04/work-2-0/"&gt;http://www.stevestreeting.com/2010/09/04/work-2-0/&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-7368005415944879742?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.stevestreeting.com/2010/09/04/work-2-0/' title='Are you a programmer+sysadmin+support person at a small company? You need to read this.'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7368005415944879742/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/10/are-you-programmersysadminsupport.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7368005415944879742'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7368005415944879742'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/10/are-you-programmersysadminsupport.html' title='Are you a programmer+sysadmin+support person at a small company? You need to read this.'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8228363944575249492</id><published>2010-10-15T11:33:00.004+08:00</published><updated>2010-10-15T22:57:59.989+08:00</updated><title type='text'>On SSDs</title><content type='html'>&lt;p&gt;I suspect that some of the ideas behind the design of SSD drives as currently sold are rather flawed. &lt;b&gt;Update:&lt;/b&gt; See the end of the article for an alternative viewpoint, though.&lt;/p&gt;

&lt;p&gt;These drives are embedded computers, with their own CPUs, RAM, firmware, etc. They are much more complicated than they should be for a simple storage device, and are more like mini RAID controllers than they are like hard disks.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;The reason things are done this way is because of the dire state of direct flash memory support in most OSes. NTFS, HFS+, Ext3/4, etc are all focused on rotating media. They try to minimize seeks and fragmentation, don't care about grouping writes/overwrites together, etc. For solid-state flash-based storage to work well with these file systems and the OSes that use them, the drives need to do a lot of work behind the scenes to cope with the file system's access patterns.&lt;/p&gt;

&lt;p&gt;In an attempt to work around the mismatch between flash storage and rotating-media-file systems, current SSDs also sometimes cheat and do things like claim to have flushed their buffers to persistent storage when they really haven't, or ignore write-ordering barriers, both of which can cause horrifying data corruption for database workloads if power is lost suddenly. (Most SSDs don't suffer from this problem - high-end enterprise drives have big capacitors that give them time to write out their caches before losing power, and most consumer drives respect the OS's request to flush their write cache. Test carefully before trusting one, though.)&lt;/p&gt;

&lt;p&gt;Existing flash-oriented file systems like JFFS2 are (a) not widely availible on major platforms, and (b) severely limited in the maximum size of media they can handle, because of the requirement to scan the media to build in-memory metadata before becoming usable. This has led to the release of SSDs oriented toward use with existing file systems - which *can't* be accessed directly for use with intelligent, flash-aware file systems.&lt;/p&gt;

&lt;p&gt;That's the bit that drives me nuts. None of the consumer and few of the enterprise SSDs can be lobotomized to present a dumb pass-through direct-access interface for OSes, HW raid controllers, etc that know how to use the flash directly. Because of this, we're stuck with the frustrating situation where file systems focused on rotating media are being optimized to work better on flash storage that's trying to pretend to be rotating media. This makes it hard to build and test, let alone adopt, file systems that're actually designed for flash!&lt;/p&gt;

&lt;p&gt;With 64-bit (OK, physically 48-bit) machines, we should really just be able to map the flash storage directly into host address space and let the OS play with it that way. With an IOMMU (universal these days) it can be done safely and securely, and you'd benefit from the host's memory barrier support etc. Alternately, a simple direct PCI-E interface to access the flash wouldn't be too much to ask. This strange way of doing it by pretending to be a SATA hard disk appears to be wasteful, expensive, and SLOW. It also makes it nigh-impossible to do interesting things with flash SSDs, like use them as write cache for software RAID.&lt;/p&gt;

&lt;p&gt;It's not showing any signs of getting better, either. You'd think that one or more SSD manufacturers would be working with Microsoft, at least, to provide an NTFS port optimized for direct use on flash, so they could avoid including all that *expensive* non-flash-chip hardware on their SSDs.&lt;/p&gt;

&lt;hr/&gt;


&lt;p&gt;&lt;b&gt;Update:&lt;/b&gt; Arguably the complexity and abstraction of SSDs is an extension of the existing trend in hard drives. First, HDDs went from electronics-offboard MFM and RLL drives to electronics-onboard IDE drives. Drives later gained logical block addressing (LBA) to hide the drive's internal layout from the OS, so the old c/h/s mappings became meaningless and the OS lost all visibility into the drive's internal layout, having to trust the drive to use a sensible block mapping. Shortly thereafter drives began to transparently remap bad sectors in firmware, so the OS didn't have to be aware of failed sectors or map them at the file system level. Command queuing and reordering arrived a while thereafter, letting the drive make intelligent decisions about the order in which it executes requests. Now modern HDDs even have extensive self reporting and analysis tools like S.M.A.R.T . A spinning-disk HDD is already a small computer, and provides a useful abstraction of storage away from the OS so the OS doesn't have to understand the fine details of every drive.&lt;/p&gt;

&lt;p&gt;Viewing things this way, perhaps the complexity of SSDs is a natural evolution. Perhaps the SSD firmware and PLC will simply do a better job of wear-leveling and write-ordering than the host file system ever can. Maybe on-board supercapacitor-protected write-back cache offers features that can't be matched at the host level. Perhaps it's a case of  "right tool for the job" and sensible levels of abstraction. Keeping write-back cache close to the storage it is cache for is a particularly compelling advantage, and one that'd be particularly hard to match with host-controlled flash.&lt;/p&gt;

&lt;p&gt;Personally, I think it's probably partway between the two. SSDs will have to expose more and more SATA/SAS extensions to allow host OSes and file systems better visibility into their innards and to gain better control. Host file systems will have to become more aware of SSDs and more able to adapt their usage patterns. Perhaps SSDs that plug straight into PCI-E and present an ACHI to the host without any real SATA bus in the middle will become common. Much like RAID controllers, modems, and many other things have slowly moved from hardware to running on the host CPU, so much of the SSD workload is likely to move to the OS driver level over time. On the other hand, a few things like safe write-back caching are probably always going to be better done in the individual SSD rather than the host, much as current ATA drives map logical addresses to C/H/S internally. It's just going to take a while for the division of responsibilities to settle out. The difficulty of plugging new file systems into existing OSes makes it initially more appealing to do everything in hardware, but over time that'll shift as flash gets cheaper and the control hardware becomes a bigger proportion of overall cost, pushing SSD manufacturers to move more and more control logic from expensive hardware into cheap drivers on the host OS.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8228363944575249492?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8228363944575249492/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/10/on-ssds.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8228363944575249492'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8228363944575249492'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/10/on-ssds.html' title='On SSDs'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-4158907500436142452</id><published>2010-10-04T11:42:00.004+08:00</published><updated>2010-10-04T11:44:45.693+08:00</updated><title type='text'>Notes on gdb use</title><content type='html'>&lt;p&gt;I find myself looking up how to do certain things with gdb, because I use it rarely enough not to remember, but frequently enough for these to be somewhat annoying. This post notes how to handle things like signals and paging in gdb.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Disable paging in gdb:&lt;/p&gt;

&lt;pre&gt;
set height 0
&lt;/pre&gt;

&lt;p&gt;Tell gdb not to stop execution on sigusr1 or sigusr2 - good for debugging postgresql:&lt;/p&gt;

&lt;pre&gt;
handle SIGUSR1 nostop
handle SIGUSR2 nostop
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4158907500436142452?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4158907500436142452/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/10/notes-on-gdb-use.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4158907500436142452'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4158907500436142452'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/10/notes-on-gdb-use.html' title='Notes on gdb use'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8376104043356986014</id><published>2010-10-04T09:16:00.013+08:00</published><updated>2010-10-19T08:29:00.789+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='iinet'/><category scheme='http://www.blogger.com/atom/ns#' term='txt'/><category scheme='http://www.blogger.com/atom/ns#' term='srv'/><category scheme='http://www.blogger.com/atom/ns#' term='dns'/><category scheme='http://www.blogger.com/atom/ns#' term='belkin'/><category scheme='http://www.blogger.com/atom/ns#' term='support'/><category scheme='http://www.blogger.com/atom/ns#' term='bob'/><title type='text'>BoB (Belkin F1PI243EGau) DNS is still broken</title><content type='html'>&lt;p&gt;&lt;b&gt;Update Oct 19, 2010:&lt;/b&gt;Contacting Belkin sales and customer feedback and pointing out my increasing efforts to show how poorly they've performed in public finally got a response from Belkin. Admittedly the response is to say they've put the issue though to an "overseas engineer" ... but maybe something will happen. More likely, it'll stay trapped in another layer of disinterest and poor management, this time one I can't apply direct pressure to.&lt;/p&gt;

&lt;p&gt;&lt;b&gt;UPDATE Oct 6, 2010:&lt;/b&gt;An indirect approach, by bothering a friend who works at iiNet, got this issue through the support wall and to people who can deal with it. Belkin has been notified at a higher level too. It's a real shame that a clearly demonstrable issue like this got stuck behind support people at both companies, to the point where I had to bother a friend who shouldn't have to deal with this stuff just to get the issue looked into. Sometimes tech support acts as a barrier that prevents a company from finding out about real problems, an issue I've seen not only with iiNet and Belkin but with endless other companies. Anyway, hopefully Belkin will be getting onto this now.&lt;/p&gt;

&lt;hr/&gt;

&lt;p&gt;A year ago, I reported to iiNet that AAAA lookups in the BoB (F1PI243EGau) DNS forwarder were always timing out, rather than returning SERVFAIL or correctly forwarding the query to the upstream server. This is the cause of the slow browsing issues reported for the BoB.&lt;/p&gt;

&lt;p&gt;A couple of months ago I got hold of a pre-release firmware that fixed this, and about a month ago the firmware was finally put up for public use. This fixes the AAAA issues, so browsers like Firefox and Safari on IPv6-capable operating systems like Mac OS X and Windows 7 don't take an eternity to resolve every DNS query.&lt;/p&gt;

&lt;p&gt;Unfortunately, Belkin didn't take this as a hint to properly test their DNS forwarder. They fixed AAAA lookup, but didn't fix it to return SERVFAIL when it encountered something it didn't understand, and failed to test other record types like TXT and SRV.&lt;/p&gt;

&lt;p&gt;Sure enough, TXT and SRV lookup have the same problem. This is currently causing problems with Google Talk (using Pidgin) that require the configuration of a fallback connect server to bypass TXT record lookup.&lt;/p&gt;

&lt;p&gt;Belkin support do not understand the problem. The low-level support folks at iiNet don't seem to get it either. Neither are passing the problem on to somebody with the experience and knowledge to understand the problem, and neither seem to have access to suitable hardware - or the inclination to use it if they do - to verify the issue.&lt;/p&gt;

&lt;p&gt;Here's the explanation I sent to them.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;
&lt;hr/&gt;

&lt;p&gt;The BoB (Belkin F1PI243EGau) doesn't handle DNS queries for TXT and SRV records correctly. Instead of forwarding these queries correctly or even returning SERVFAIL, it fails to respond, causing a client-side timeout.&lt;/p&gt;

&lt;p&gt;Before the latest firmware update it had the same problem with AAAA (IPv6 DNS) lookups.&lt;/p&gt;

&lt;p&gt;This issue causes problems with Google Talk (why I noticed the problem), Active Directory, SPF mail lookups, etc. It's particularly obvious when using Pidgin for Google Talk, as it relies on correct SRV record handling, where the official Google Talk client falls back to "brain dead router" mode hard-coded defaults if SRV lookups fail.&lt;/p&gt;

&lt;p&gt;I won't write a whole document on what TXT and SRV records are and why they're important here; that's what Google is for. What I will do is provide you with instructions on how to demonstrate that the BoB's dns resolver doesn't handle them correctly while other DNS resolvers do.&lt;/p&gt;

&lt;p&gt;I'm going to assume you're on Windows. If you're on Mac or Linux, replace "nslookup -a=txt" with "dig +short -t txt" ; otherwise the commands are the same.&lt;/p&gt;

&lt;p&gt;In a command prompt window (start&gt;run-&gt;cmd.exe) on a machine that has access to a BoB (F1PI243EGau) with IP 10.1.1.1, run:&lt;/p&gt;

&lt;pre&gt;
nslookup -q=srv talk.google.com 10.1.1.1
nslookup -q=txt gmail.com 10.1.1.1
&lt;/pre&gt;

&lt;p&gt;Both are queries for valid, existing records. You will find that those requests both time out:&lt;/p&gt;

&lt;pre&gt;
C:\Users\Craig&gt;nslookup -q=txt gmail.com
Server: bob.iad
Address: 10.1.1.1
DNS request timed out.
timeout was 2 seconds.
DNS request timed out.
timeout was 2 seconds.
*** Request to bob.iad timed-out

C:\Users\Craig&gt;nslookup -q=srv talk.google.com
Server: bob.iad
Address: 10.1.1.1
DNS request timed out.
timeout was 2 seconds.
DNS request timed out.
timeout was 2 seconds.
*** Request to bob.iad timed-out
&lt;/pre&gt;

&lt;p&gt;Now run the same commands again, but this time use a real DNS server directly, bypassing the BoB. Because I have no way of determining what your local DNS server IPs are, I'll give you an example that uses Google's public DNS directly, but you'll find it works with any DNS server other than the BoB:&lt;/p&gt;

&lt;pre&gt;
nslookup -q=srv talk.google.com 8.8.8.8
nslookup -q=txt gmail.com 8.8.8.8
&lt;/pre&gt;

&lt;p&gt;( 8.8.8.8 and 4.4.4.4 are Google's public DNS servers ).&lt;/p&gt;

&lt;p&gt;Note that they succeed, because we're bypassing the BoB's broken forwarding DNS resolver?&lt;/p&gt;

&lt;pre&gt;
C:\Users\Craig&gt;nslookup -q=txt gmail.com 8.8.8.8
Server: google-public-dns-a.google.com
Address: 8.8.8.8
Non-authoritative answer:
gmail.com text =
"v=spf1 redirect=_spf.google.com"

C:\Users\Craig&gt;nslookup -q=srv talk.google.com 8.8.8.8
Server:  google-public-dns-a.google.com
Address:  8.8.8.8
Non-authoritative answer:
talk.google.com canonical name = talk.l.google.com
l.google.com
        primary name server = ns3.google.com
        responsible mail addr = dns-admin.google.com
        serial  = 1429061
        refresh = 900 (15 mins)
        retry   = 900 (15 mins)
        expire  = 1800 (30 mins)
        default TTL = 60 (1 min)
&lt;/pre&gt;

&lt;p&gt;As you can see, the BoB's forwarding DNS resolver isn't correctly handling TXT and SRV record lookups. Note that this persists even if I override the ISP-configured DNS servers and use Google's instead, so it's not a problem with my ISP DNS. In any case, queries directly to my ISP DNS work fine. TXT and SRV queries fail from my Windows 7 desktop, my Linux laptop, and spare Mac OS X machine I borrowed; this is not a problem specific to one computer or operating system, it's a firmware issue.&lt;/p&gt;

&lt;hr/&gt;
For bonus points, here are the brillant replies from the Belkin support staff:

&lt;blockquote&gt;
&lt;p&gt;Dear Belkin User,&lt;/p&gt;
&lt;p&gt;Thank you for contacting Belkin Technical Support.&lt;/p&gt;
&lt;p&gt;We understand your concern and We will be happy to assist you with your queries.&lt;/p&gt;
&lt;p&gt;In order to isolate the issue we suggest you to update the firmware on the router.&lt;/p&gt;
&lt;p&gt;Please find the link below where you can update the firmware for the router F1PI243EGau.
http://en-au-support.belkin.com/app/answers/detail/a_id/2703/session/L3NpZC8xUHpIcm9iaw%3D%3D/kw/f1p1241egau/r_id/166/sno/0&lt;/p&gt;
&lt;p&gt;&lt;i&gt;(Editorial note: this link refers to the firmware for the wrong router, the F1PI241EGau, so clearly even Belkin can't keep track of their model number scheme)&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;Please do feel free to write back if you have any further queries,we will be more than happy to assist you.&lt;/p&gt;
&lt;/blockquote&gt;

&lt;p&gt;When I replied to ask if they really meant that firmware, and pointed out that my original report showed that I was already running the latest firmware for my router, the F1PI243EGau, they replied with:&lt;/p&gt;

&lt;blockquote&gt;
&lt;p&gt;Dear Belkin User,&lt;/p&gt;
&lt;p&gt;Thank you for contacting Belkin Technical Support.&lt;/p&gt;
&lt;p&gt;We understand your concern and We will be happy to assist you with your queries.&lt;/p&gt;
&lt;p&gt;&lt;i&gt;(Look like another template answer to you? Me too.)&lt;/i&gt;&lt;/p&gt;
&lt;p&gt;We would appreciate if you please get back to us with the following information to isolate the issue:&lt;/p&gt;
&lt;ol&gt;&lt;li&gt;Are you trying to do a NAT loop back?&lt;/li&gt;
&lt;li&gt;Are you trying to use DDNS? &lt;/li&gt;&lt;/ol&gt;
&lt;p&gt;Please do feel free to write back, and we will be more than happy to assist you.
Please take a moment to review our Knowledge Base at http://belkin.com/support or let us know.&lt;/p&gt;
&lt;/blockquote&gt;
&lt;p&gt;After this brillant uttering, they went silent and stopped responding to further requests. Belkin's support for their product as demonstrated here is an abject, total failure.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8376104043356986014?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8376104043356986014/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/10/bob-dns-is-still-broken.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8376104043356986014'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8376104043356986014'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/10/bob-dns-is-still-broken.html' title='BoB (Belkin F1PI243EGau) DNS is still broken'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4911777588500346766</id><published>2010-09-08T01:38:00.002+08:00</published><updated>2010-09-08T01:40:40.283+08:00</updated><title type='text'>Exception breakpoints</title><content type='html'>&lt;p&gt;Do you have a StackOverflowError thrown deep inside some (probably 3rd party) code like EclipseLink's Criteria API? Are you trying to track down exactly where it's being thrown and under what circumstances? Is it driving you insane?&lt;/p&gt;

&lt;p&gt;Use an exception breakpoint. In Netbeans: Debug -&gt; New Breakpoint. Breakpoint type "Exception". Specify the exception class being thrown, constrain the throwing class if desired, and you're set.&lt;/p&gt;

&lt;p&gt;This made my life a lot, lot, LOT easier recently. Thanks again NetBeans.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4911777588500346766?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4911777588500346766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/09/exception-breakpoints.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4911777588500346766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4911777588500346766'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/09/exception-breakpoints.html' title='Exception breakpoints'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1053450554576473103</id><published>2010-08-29T19:50:00.003+08:00</published><updated>2010-08-29T19:59:13.189+08:00</updated><title type='text'>Java EE apps as first-class citizens</title><content type='html'>&lt;p&gt;&lt;a href="http://www.java.net/blog/209941"&gt;Juliano Viana&lt;/a&gt; writes that &lt;a href="http://weblogs.java.net/blog/jjviana/archive/2010/08/16/my-top-wish-java-8-ee-applications-first-class-citizens"&gt;he hopes to see Java EE applications promoted to first-class citizens in Java EE 8.&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;His comments are focused on protecting apps running in a container from each other, and the container from the apps. The primary focus of the post is on resource leaks. He's interested in making it possible to cleanly and totally unload an app, much as you can kill a process on a regular OS to completely clean it up.&lt;/p&gt;

&lt;p&gt;This lead me to an interesting realization, though one I'm sure others have had before:&lt;/p&gt;

&lt;p&gt;&lt;strong&gt;Java EE containers are in some ways a lot like MS-DOS, Mac OS 6/7/8/9, Symbian, etc&lt;/strong&gt; ... applications share the same memory space and have access to each other's innards. There's polite agreement not to poke where they don't belong, but no strong enforcement of that. Without pointers and the resulting fun with corrupted stacks and dangling pointers leading apps to trample all over each other's memory the JVM and Java EE don't have it quite so bad  - but there are still real issues with access control, resource ownership, etc.&lt;/p&gt;

&lt;p&gt;Currently a Java EE app can delve into the innards of the container if it wants. It's not *meant* to, but it's not easily prevented and it makes the container's integrity less trustworthy. An app can break the container or cause it to misbehave. Expecting apps to be well-written is, as demonstrated by the OSes listed above, a great way to get a reputation for being unstable and generally crap. More importantly, as the referenced article notes, Java EE apps can cause the container to leak resources, or can disrupt resources the apps shares with other apps via the container, like pooled connections. That makes it hard to share one container among many different apps, let alone different users/customers.&lt;/p&gt;

&lt;p&gt;This (and garbage collection + resource use issues with big JVMs) makes admins reluctant to have many apps hosted in a single container, so you land up doing all sorts of icky inter-JVM work to make your environment managable. Shared-hosting services with Java are expensive and uncommon due to related issues.&lt;/p&gt;

&lt;p&gt;I certainly think that much stronger protection is needed to isolate the container's private core from apps, and apps from each other. &lt;strong&gt;I'm not at all convinced that trying to build that isolation entirely in software in the JVM is best, though. Modern successful OSes isolate apps using *hardware* features to do most of the grunt-work and protect them from implementation bugs&lt;/strong&gt;; apps and the kernel are isolated from each other by default and can only interact by configured exception. Protected mode, separate logical address spaces, and the security ring system in x86 provide rather strong OS/app isolation. Trying to  implement similarly capable isolation purely in software in the JVM would be bug-prone and hard to get right, kind of like the half-baked and buggy app isolation in Windows 3.x or even 9x.&lt;/p&gt;

&lt;p&gt;Perhaps a more viable future approach would be to let multiple JVMs running on a host integrate more tightly via shared memory, high performance IPC, or whatever, so the container could run in its own JVM and each app could have a private JVM, with only those resources *explicitly* shared between them accessible across JVMs. That way, apps cleanup would be as simple as killing the app's jvm and letting the OS+hardware clean up the mess. The exposed surface for problems would be restricted to that part of the container that's explicitly accessible via the shared resources.&lt;/p&gt;

&lt;p&gt;Or ... maybe the JVM will need more OS co-operation than that, so the JVM its self can use hardware features to isolate apps running within the JVM. I can't imagine OS designers wanting to let the JVM get its hooks into the kernel's memory mapping and process management, but with the advent of VMs and VM-friendly CPU extensions like VT-X and VT-IO I wonder if the JVM could use those instructions, like the kernel of a guest OS does in a VM, to isolate apps running within the JVM.&lt;/p&gt;

&lt;p&gt;Much as I'd love the JVM to be able to isolate apps properly, in my admittedly pretty ignorant opinion I don't see it happening without some kind of major re-design. I imagine a half-assed solution that works about as well as Windows 9x did is possible with the current JVM, but giving the impression apps can be isolated and cleaned up without being able to really do a comprehensive job of it is IMO in many ways worse than the current situation. Without some really powerful, low level isolation features I don't see that happening. Look at how well "shared" OSes have typically achieved app isolation for examples of just how hard the problem is.&lt;/p&gt;

&lt;p&gt;I find the idea of &lt;strong&gt;using hardware VT-X support to help the JVM isolate apps in an EE container&lt;/strong&gt; quite intriguing. I wonder if it's something anyone's done any investigation of.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1053450554576473103?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://weblogs.java.net/blog/jjviana/archive/2010/08/16/my-top-wish-java-8-ee-applications-first-class-citizens' title='Java EE apps as first-class citizens'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1053450554576473103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/java-ee-apps-as-first-class-citizens.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1053450554576473103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1053450554576473103'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/java-ee-apps-as-first-class-citizens.html' title='Java EE apps as first-class citizens'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4456921203875658766</id><published>2010-08-26T16:25:00.005+08:00</published><updated>2010-08-27T11:40:06.750+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='bugs'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='whine'/><title type='text'>Three hours in</title><content type='html'>&lt;p&gt;I'm three hours or so into my real work, after being side-tracked isolating and reporting Glassfish and Mojarra bugs. I just found &lt;a href="https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1793"&gt;another one&lt;/a&gt; in Mojarra, albeit a trivial one.&lt;/p&gt;

&lt;p&gt;(Edit) I'd written a little rant about the problem I was trying to solve, in the assumption that I'd hit another limitation/bug in JSF2. Well, this time I was just plain wrong; I'd used an incorrect test to draw an invalid conclusion and from there ran down the path - from my previous experience, admittedly - of assuming that this, too, was a tools issue.&lt;/p&gt;

&lt;p&gt;The correct answer is here: &lt;a href="http://forums.java.net/jive/thread.jspa?messageID=481123&amp;#481123"&gt;To permit null/empty/none selection in a h:selectOneMenu driven by a f:selectItems , just add a f:selectItem with no value before the f:selectItems&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4456921203875658766?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1793' title='Three hours in'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4456921203875658766/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/three-hours-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4456921203875658766'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4456921203875658766'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/three-hours-in.html' title='Three hours in'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8665191812447226354</id><published>2010-08-25T17:29:00.010+08:00</published><updated>2011-02-03T08:35:50.350+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='ejb'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='netbeans'/><title type='text'>Converting a NetBeans-generated JSF2 project to use CDI</title><content type='html'>&lt;p&gt;I started playing with Netbeans 6.9's JSF2 interface generation for database apps, and quickly noticed that (a) it uses JSF2 injection, not CDI and (b) it generates some verbose, ugly converters.&lt;/p&gt;

&lt;p&gt;I decided to switch it over, as a bit of a learning experience. Here's what landed up being necessary.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Switching the controller beans to CDI injection is as simple as changing:&lt;/p&gt;

&lt;pre&gt;
import javax.ejb.EJB;
import javax.faces.bean.ManagedBean;
import javax.faces.bean.SessionScoped;

@ManagedBean
@SessionScoped
class AdController {
   @EJB SomeFacade ejbFacade;
}
&lt;/pre&gt;

&lt;p&gt;to:&lt;/p&gt;

&lt;pre&gt;
import javax.inject.Named;
import javax.inject.Inject;
import javax.enterprise.context.SessionScoped;

class AdController implements Serializable {
   // Use a uid generator to provide a suitable id here:
   private static final long serialVersionUID = 1L;

   @Inject AdFacade ejbFacade;

}
&lt;/pre&gt;

&lt;p&gt;In other words, we've switched from JSF2 &lt;code&gt;@EJB&lt;/code&gt; injection to CDI &lt;code&gt;@Inject&lt;/code&gt;, from JSF2 &lt;code&gt;@ManagedBean&lt;/code&gt; to CDI EL name annotation &lt;code&gt;@Named&lt;/code&gt;, and from the JSF to the CDI scope annotation for &lt;code&gt;@SessionScoped&lt;/code&gt; (they're in different packages but have the same name). Because CDI enforces the requirement that session beans be serializable, we implement serializable - though in practice this was necessary for correct session bean passivation in JSF2 even without CDI.&lt;/p&gt;

&lt;p&gt;There's a catch in this conversion - the static inner converter classes in each of the controller classes.&lt;/p&gt;

&lt;p&gt;Injection doesn't work in a &lt;code&gt;@FacesConverter&lt;/code&gt;&lt;a href="#1"&gt;*&lt;/a&gt; so you can't @Inject the EJB facade you want directly into your converter. That's why the converter code generated by NetBeans uses such a roundabout method to get the ejbFacade, by looking up the controller bean in EL and getting it from there. However, CDI clears injection sites between phases. Because CDI has cleared the ejbFacade member by the time the converter runs, it'll be null by the time we need it in the converter. You're likely to land up with null pointer exceptions due to null ejbFacade members in the controller beans the converters look up up via FacesContext. (See &lt;a href="https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1785"&gt;this Mojarra bug&lt;/a&gt;)

&lt;p&gt;To work around that, we'll use the fact that the Netbeans-generated facades share a common abstract base to write an abstract base for the converters. This base will know how to look up an EJB from CDI directly, without relying on injection. Here's the code, which is derived from Dominik Dorn's &lt;a href="http://dominikdorn.com/2010/04/cdi-weld-manual-bean-lookup/"&gt;example&lt;/a&gt;:&lt;/p&gt;

&lt;pre&gt;
package com.mycompany;

import javax.faces.component.UIComponent;
import javax.faces.context.FacesContext;
import javax.faces.convert.Converter;

public abstract class AbstractFacesConverter&amp;lt;EntityType, FacadeType extends AbstractFacade&amp;lt;EntityType&amp;gt;&amp;gt; implements Converter {

    private final Class&amp;lt;FacadeType&amp;gt; facadeClass;
    private final Class&amp;lt;EntityType&amp;gt; entityClass;

    protected AbstractFacesConverter(Class&amp;lt;EntityType&amp;gt; entityClass, Class&amp;lt;FacadeType&amp;gt; facadeClass) {
        this.facadeClass = facadeClass;
        this.entityClass = entityClass;
    }

    @Override
    public Object getAsObject(FacesContext facesContext, UIComponent component, String value) {
        if (value == null || value.length() == 0) {
            return null;
        }
        FacadeType facade = (FacadeType) CDIHelper.getManagedBean(facesContext, facadeClass);
        return facade.find(getKeyFromString(value));
    }

    @Override
    public String getAsString(FacesContext facesContext, UIComponent component, Object object) {
        if (object == null) {
            return null;
        }
        if (entityClass.isInstance(object)) {
            return getKeyAsString( (EntityType)object);
        } else {
            throw new IllegalArgumentException("object " + object + " is of type " + object.getClass().getName() + "; expected type: "+AdController.class.getName());
        }
    }

    /**
     * Given the string-form of the primary key for the target object,
     * return a representation of the primary key in the proper type.
     *
     * @param value String representation of primary key
     * @return Primary key
     */
    protected abstract Object getKeyFromString(String value);

    /**
     * Given the an entity, return a string representation of the
     * primary key of that entity. The string must round-trip back
     * to the PK of the entity when passed to getKeyFromString().
     *
     * @param entity Entity to get the PK of
     * @return A string representation of the PK
     */
    protected abstract String getKeyAsString(EntityType entity);
    
}
&lt;/pre&gt;

&lt;p&gt;... and here's a converter for an entity with an Integer ID that's been implemented using the abstract base converter:&lt;/p&gt;

&lt;pre&gt;
    @FacesConverter(forClass=Ad.class)
    public static class AdControllerConverter extends AbstractFacesConverter&amp;lt;Ad,AdFacade&amp;gt; {

        public AdControllerConverter() {
            super(Ad.class, AdFacade.class);
        }

        @Override
        protected Object getKeyFromString(String value) {
            return Integer.valueOf(value);
        }

        @Override
        protected String getKeyAsString(Ad entity) {
            return entity.getId().toString();
        }

    }
&lt;/pre&gt;


&lt;p&gt;Much nicer, eh?&lt;/p&gt;

&lt;p&gt;Let's try running the code. It should work initially, but if you're on Glassfish 3.0.1 it'll fail with an exception like:&lt;/p&gt;

&lt;pre&gt;
java.lang.IllegalStateException: Unable to convert ejbRef for ejb AdFacade to a business object of type class com.mycompany.AbstractFacade
&lt;/pre&gt;

&lt;p&gt;... as soon as you try to do anything much. The cause is &lt;a href="https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1785"&gt;this Glassfish/Weld integration bug&lt;/a&gt;. As a workaround for the bug, you can override all the methods implemented in the AbstractFacade in each concrete facade EJB with trivial wrapper methods. NetBeans' "Insert Code" -&gt; "Override Method" context menu option is very useful for this.&lt;/p&gt;

&lt;p&gt;Once you've wrapped your inherited EJB methods, you should finally be back where you started, with an app that's now CDI-enabled and does exactly what the JSF2-only one did. Yay?&lt;/p&gt;

&lt;hr/&gt;

&lt;p&gt;&lt;a name="1"&gt;*&lt;/a&gt; The &lt;a href="http://seamframework.org/Seam3/FacesModuleOverview"&gt;SeamFaces&lt;/a&gt; project - a part of Seam 3 - is intended to fix that, but isn't ready yet. There are also apparently plans to fix this limitation in JSF 2.1. &lt;a href="seamframework.org/Seam3/Solder"&gt;Seam Solder&lt;/a&gt; offers tools for &lt;code&gt;BeanManager&lt;/code&gt; lookup, etc ... but doesn't work on Glassfish 3.0.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8665191812447226354?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8665191812447226354/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/cdi-ified-netbeans-jsf2-generated.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8665191812447226354'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8665191812447226354'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/cdi-ified-netbeans-jsf2-generated.html' title='Converting a NetBeans-generated JSF2 project to use CDI'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-1919276865108362427</id><published>2010-08-24T15:17:00.016+08:00</published><updated>2011-07-15T09:36:41.566+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='ejb'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><title type='text'>Untangling Java EE 6 - a broad conceptual overview</title><content type='html'>&lt;h2&gt;WARNING&lt;/h2&gt;
&lt;p&gt;&lt;span style="color: red"&gt;This document was written by someone who's only learning Java EE 6.&lt;/span&gt; On one hand, I remember the &lt;a href="http://blog.ringerc.id.au/2010/08/im-not-smart-enough-to-use-java-ee.html"&gt;frustrations&lt;/a&gt; clearly and still encounter issues of understanding (and bugs) regularly, so I'm in a good position to think about how a newbie needs to have things explained. On the other hand, &lt;b&gt;a bunch of this is probably wrong&lt;/b&gt; because my own understanding is still developing. Beware.&lt;/p&gt;

&lt;p&gt;This is also a work in progress. It's been published for comment and review, and isn't a fully edited and checked final work.&lt;/p&gt;

&lt;p&gt;If you're coming from Spring, rather than starting with Java EE cold, you should start &lt;a href="http://ocpsoft.com/java/spring-to-java-ee-a-migration-guide-cdi-jsf-jpa-jta-ejb/"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;h2&gt;Java EE 6&lt;/h2&gt;

&lt;p&gt;Java EE 6 is not a product you can download and install. It is a
specification that describes how implementing software should behave. It
refers to many sub-specifications for particular features, each of which has one
or more implementations in software. Some of these implementations can be used
outside a full Java EE 6 environment as standalone products.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;There are a few different application servers availible that provide full
Java EE 6 environments by collecting and integrating the implementations of the
various Java EE 6 components into a product. These products have their own
features, administration tools, etc as well as support for the Java EE 6
spec.&lt;/p&gt;

&lt;p&gt;Unsurprisingy, when you are trying to get a handle on how it all works, it's
very confusing. The fact that most Java EE 6 documentation and tutorials refer
to Java EE 5 with the assumption that you're an existing Java EE 5 user
upgrading to Java EE 6 doesn't help.&lt;/p&gt;

&lt;p&gt;Here I'll try  to provide an overview of how it all fits together, helping
you decode the names and figure out what's what.&lt;/p&gt;


&lt;h2&gt;Java EE 6 isn't a downloadable, installable product&lt;/h2&gt;

&lt;p&gt;The first thing to understand is that Java EE 6 isn't a piece of software.
It's a client interface specification (API) describing what services an
environment (the "container" or application server) provides to software
running within that environment, and how those services should behave.  It's
also a server specification that partly specifies now those services should
work and what they should do.&lt;/p&gt;

&lt;p&gt;Consequently, you can't "install" Java EE 6, you can only install an
implementation of it like an application server.&lt;/p&gt;

&lt;p&gt;There is one piece of concrete code for Java EE 6: the API JARs. The Java EE 6 api and Java EE 6 Web Profile API jars contain Java interfaces that let you
compile code to use Java EE 6 services and features. They do not contain implementations of those features, and do not require a particular implementation; that's the job of the particular Java EE 6 container/app server you use.&lt;/p&gt;




&lt;h2&gt;The parts of the Java EE 6 spec&lt;/h2&gt;

&lt;p&gt;As already noted, the Java EE 6 spec is composed of many parts.Some of them
will be familiar, like JavaMail and JDBC, while others like EJB 3.1, CDI, JNDI
and JAX-RS will be unfamiliar to you. Java SE is availible in the Java EE
environment, so most of what you are used to in Java SE should remain
unchanged, but the &lt;i&gt;way you use it&lt;/i&gt; is completely different.&lt;/p&gt;

&lt;p&gt;There is a list of the technologies used in Java EE 6 here:&lt;br/&gt;
&lt;a href="http://www.oracle.com/technetwork/java/javaee/tech/index.html"&gt;http://www.oracle.com/technetwork/java/javaee/tech/index.html&lt;/a&gt;&lt;br/&gt;
Most of those technologies can be used outside a full Java EE 6 environment, and were
developed as individual specifications under the Java Community Process (JCP),
usually alongside a reference implementation of the specification being
developed.&lt;/p&gt;

&lt;p&gt;Application servers (containers) that support the Java EE 6 spec promise
to provide implementations of all the various sub-specs in Java EE 6 for
an application's use.&lt;/p&gt;



&lt;h2&gt;Application servers and containers&lt;/h2&gt;

&lt;p&gt;Application servers like Glassfish and JBoss AS implement the Java EE 6
spec, providing applications with a managed container they can run in to use
all the Java EE 6 services. You can think of the container as a lot like the
operating system and JVM a Java SE applicatoin runs within - it's outside the
application, but provides services and facilities that enable the application
to work.&lt;/p&gt;

&lt;p&gt;The app server takes responsibility for making the spec's requirements
happen, so user code just has to use the Java EE 6 interfaces.&lt;/p&gt;

&lt;p&gt;There are also containers like Tomcat and Jetty that do not implement the
full Java EE 6 spec. It is possible to use most of the technologies used by
Java EE 6 within such containers by installing implementations of them in the
container or bundling them with your application. This is why you will see
references to using Tomcat or Jetty with technologies like JSF2 that are also
part of Java EE 6.

&lt;p&gt;The vendor of a full Java EE 6 applicaton server like Glassfish or JBoss AS
application server vendor doesn't implement all of Java EE 6 directly either.
Most of the Java EE 6 implementation provided by an app server is in separate
products bundled with the server to provide an implementation of each
individual Java EE 6 sub-spec. The server vendor chooses the implementation to
use for each sub-spec, and provides the code to load them and integrate them
into the full environment. Some specs (like EJB 3.1) &lt;i&gt;will&lt;/i&gt; be implemented
directly by the server vendor and won't be a separate product or package,
though they'll generally still be a module within the application server.&lt;/p&gt;

&lt;p&gt;For example, Glassfish 3 (an application server) supports java EE 6 (the
spec), including support for CDI (a sub-spec) using JBoss Weld (an
implementation of the CDI spec). Glassfish also supports EJB 3.1 (another
sub-spec) as a core part of the Glassfish server, rather than via 3rd party
code.&lt;/p&gt;

&lt;p&gt;You will have noticed that Glassfish uses a component, JBoss Weld, from a
competing application server vendor. That's commonplace, so you shouldn't
assume that because a component was developed by Oracle, Apache, JBoss, etc
it's only used in their application server.&lt;/p&gt;

&lt;p&gt;A list of servers, specs, and implementations of those specs can be found
at the end of this article.&lt;/p&gt;


&lt;h2&gt;Using Java EE 6 in applications&lt;/h2&gt;

&lt;p&gt;Applications written for Java EE 6 are compiled and bundled into a package
(an ejb jar, a war, or an ear file: see later) that is deployed to the
application server. Deployment involves loading the application archive after
copying it to the server if required, scanning it for deployment descriptors
like web.xml, and setting it up in the container's environment so it can be
accessed. Depending on the application, that might involve enabling web service listeners, adding servlets to the container's HTTP namespace, etc.&lt;/p&gt;

&lt;p&gt;Applications do not have to use all the features of Java EE 6, and can
choose to only "see" features they use. Instead of having the full Java EE 6
API JARs on the compile-time classpath, they can include only the API JARs for
the sub-specs they want to use. For example, an application might choose to
use the Servlet API and the JPA 2.0 API, but not bother with the EJB 3.1 API 
or JavaServer Faces. To do that, it omits the full Java EE 6 API JARs from the
classpath, and instead includes the API JARs for the sub-specs of interest. You
will see this a lot in example code - for example, JavaServer Faces (JSF)
projects often use the JSF API and Servlet API jars rather than the Java EE 6
API jars.&lt;/p&gt;

&lt;p&gt;Even if you include the full JAva EE 6 API JARs, many features won't affect
you (or activate at all) unless you use them by putting their annotations in
your code, configuring them in web.xml, etc.&lt;/p&gt;

&lt;p&gt;Because you don't have to use all of Java EE 6, it's quite possible to build
an application that'll deploy and run in regular containers as well as full
Java EE 6 application servers. Such an application generally requires
additional software to be installed in the non-EE container to support the
features it needs, or has to bundle the implementations with the
application.&lt;/p&gt;

&lt;h2&gt;Java EE 6 can be extended&lt;/h2&gt;

&lt;p&gt;Just as you don't have to use all of Java EE 6 in applications, it's common to add additional components to extend the capabilities of the application server. Some of these components are regular class libraries, but some of them are container components that use servlet filters or other EE features to extend the capabilities of the container its self.&lt;/p&gt;

&lt;p&gt;For example, instead of using the Java EE 6 standard web interface toolkit, it's fairly common to use one of the innumerable other toolkits like Wicket, Tapestry, Struts, etc instead, while still running within a Java EE 6 application server container.&lt;/p&gt;

&lt;p&gt;It's also common to include extensions to Java EE 6 functionality. For example, there are JSF 2.0 extensions like RichFaces and PrimeFaces that add new features and new components to the existing JSF 2.0 implementation.&lt;/p&gt;

&lt;p&gt;One key extension is &lt;a href="http://seamframework.org/Seam3/FacesModuleOverview"&gt;SeamFaces&lt;/a&gt;, which seeks to address some of the more painful limitations of JSF2, like the inability to inject resources into a &lt;code&gt;@FacesConverter&lt;/code&gt;. SeamFaces is a work in progress and wasn't at release quality at time of writing; see the project page for details on its status.&lt;/p&gt;

&lt;h2&gt;Pitfalls&lt;/h2&gt;

&lt;p&gt;There are some holes in the current Java EE 6 spec, and some areas of confusion where the required behaviour isn't particularly clear. These will be discussed in detail in separate documents.&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@FacesConverter&lt;/code&gt; can't be a managed bean and isn't subject to injection. See SeamFaces (above).&lt;/li&gt;
&lt;li&gt;Multiple dependency injection implementations exist in the core spec.
&lt;ul&gt;&lt;li&gt;JSF2 provides basic dependency injection support, liftime and scoping, EL naming, etc. These live in the &lt;code&gt;javax.faces.bean&lt;/code&gt; package.&lt;/li&gt;
&lt;li&gt;The CDI spec provides a more complete dependency injection (@Inject), lifetime and scoping, and EL naming system using different facilities that *mostly* interoperate with JSF2's. The annotations live in the &lt;code&gt;javax.enterprise.context&lt;/code&gt; and &lt;code&gt;javax.enterprise.inject&lt;/code&gt; packages. &lt;/li&gt;
&lt;li&gt;EJB 3 has its own injection via @javax.ejb.EJB&lt;/li&gt;
&lt;/ul&gt;

The JSF2 features are not clearly deprecated and it's not always clear how they will interact with CDI or old-style EJB interaction. A separate document will discuss CDI and JSF2 injection and scoping.&lt;/li&gt;
&lt;/ul&gt;

&lt;p&gt;There are also a few things that, while not JSF issues as such, are likely to bite you if you don't know about them, and aren't particularly well documented:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;&lt;code&gt;@PersistenceContext(type=PersistenceContextType.EXTENDED)&lt;/code&gt;, ie an extended persistence context, can only be used within a @Stateful session bean. In other contexts, like a POJO, you'll get odd errors from within the runtime, like (in Glassfish 3) a NullPointerException from &lt;code&gt;com.sun.enterprise.container.common.impl.EntityManagerWrapper&lt;/code&gt;.&lt;/li&gt;
&lt;/ul&gt;


&lt;h2&gt;Thinking Java EE 6&lt;/h2&gt;

&lt;p&gt;There are some really big differences between how a Java EE 6 application is constructed and how a Java SE application is constructed. Many of these stem from the container-managed nature of Java EE, where you provide a set of interacting components that run in the container, rather than a whole "program" with dedicated flow control from start to finish. Adapting to Java EE is a lot like adapting to event-driven programming for someone used to the imperative programming model - it's brain-bending, but worth it. You have to relax the idea that you dedicate the flow of execution through your code, and instead get used to providing a set of services that interact via events, messages, and the actions of client code.&lt;/p&gt;

&lt;p&gt;Even object lifetimes and object instantiation are largely out of your hands. Most of your objects will be container-managed, with lifetime and scope controlled by the container and linked to the user request or session. The objects they interact with will be injected, rather than obtained from a factory or directly created. It's the container that's responsible for selecting or creating an instance of an object to inject, not your code.&lt;/p&gt;

&lt;p&gt;You will hear terms like "dependency injection" and "inversion of control" thrown about, but they won't mean much to you until you start using Java EE in practice. Once you wrap your brain around it then, just like event-driven programming, it becomes an obvious and sensible way to do things and lets you just forget about problems you used to obsess about. Mostly, anyway.&lt;/p&gt;

&lt;h2&gt;Where to go from here&lt;/h2&gt;

&lt;p&gt;Start with the excellent &lt;a href="http://download.oracle.com/javaee/6/tutorial/doc/"&gt;Java EE 6 tutorial&lt;/a&gt;. Don't forget about the &lt;a href="http://download.oracle.com/javaee/6/api/"&gt;Java EE 6 API documentation&lt;/a&gt; and the other resources availible on Oracle's &lt;a href="http://www.oracle.com/technetwork/java/javaee/documentation/tutorials-137605.html"&gt;Java EE 6 page&lt;/a&gt; either.&lt;/p&gt;

&lt;p&gt;It can be helpful to grab the spec for a given Java EE 6 technology from the JSRs linked from &lt;a href="http://www.oracle.com/technetwork/java/javaee/tech/index.html"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;There's a good tutorial on JavaServer Faces &lt;a href="http://www.coreservlets.com/JSF-Tutorial/jsf2/"&gt;here&lt;/a&gt;. Avoid the JSF specification, as unlike most of the surprisingly well written JSR specs it's an unreadable monstrosity that will only confuse you.&lt;/p&gt;

&lt;h2&gt;Names and acronyms galore&lt;/h2&gt;

&lt;p&gt;One of the big challenges of understanding Java EE 6 is figuring out what
all the names, acronyms and code annotations mean and how the inter-relate.&lt;/p&gt;

&lt;p&gt;This short guide will attempt to untangle things for you. (It's currently very incomplete, though, and should probably move to a separate post.)&lt;/p&gt;

&lt;pre&gt;
Containers
- full Java EE 6 app servers
-- Glassfish 3
--- Uses Mojarra (JSF2 and JSP), Weld (CDI)
-- JBoss AS 6 (prerelease)
--- Uses Mojarra (JSF2 and JSP), Weld (CDI)
- Java EE 5 containers
-- Glassfish 2
-- JBoss AS 5
-- Geronimo (built on Tomcat / Jetty)
-- WebSphere
-- WebLogic
-- Etc etc. See wikipedia "comparison of application servers"
- non-EE applicatoin containers / servlet engines
-- Tomcat
-- Jetty

Technologies used in Java EE 6 with widespread competing implementations
- CDI
-- JBoss Weld (RI)
-- Apache OpenWebBeans
- JSF2
-- Mojarra (RI)
-- Apache MyFaces
- JPA2
-- Apache OpenJPA
-- Oracle TopLink Essentials
-- JBoss Hibernate 
-- EclipseLink

Commonly used add-ons
- JSF2 extensions and component libraries
-- RichFaces
-- IceFaces
-- PrimeFaces
-- PrettyFaces
- Non-JPA persistence
-- Direct use of JDBC
-- iBatis / MyBatis

Other names to place:
- Apache Felix (OSGi)
- JBoss Seam 2 and 3
- Spring

Other references
- Wikipedia "Java EE version history"
&lt;/pre&gt;

&lt;p&gt;A separate post will be dedicated to associating commonly used EE annotations with the specifying JSRs and implementations of those JSRs. Things like @ManagedBean, @Named, @SessionScoped, @RequestScoped, @ApplicationScoped (both CDI and JSF versions), @Local, @Remote, @Stateless, @Stateful, @Singleton, @Inject, @Resource, @EJB, @PersistenceContext, etc. Particular attention needs to be paid to areas of overlapping functionality and the differences between the options: JSF scoped beans vs CDI scoped beans vs EJBs in lifetime management, for example.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1919276865108362427?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1919276865108362427/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/java-ee-6-is-not-product-you-can.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1919276865108362427'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1919276865108362427'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/java-ee-6-is-not-product-you-can.html' title='Untangling Java EE 6 - a broad conceptual overview'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-8046589288434976524</id><published>2010-08-22T14:14:00.007+08:00</published><updated>2010-08-25T18:05:49.883+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='whine'/><category scheme='http://www.blogger.com/atom/ns#' term='seam'/><category scheme='http://www.blogger.com/atom/ns#' term='cdi'/><title type='text'>Java EE 6 doesn't "just work" in  even trivial real-world uses</title><content type='html'>&lt;p&gt;Lincoln Baxter III writes that with Java EE 6, &lt;a href="http://ocpsoft.com/java/why-doesnt-jpa-jma-jta-ejb-jsf-cdi-work-jee-is-too-complicated/"&gt;It all “Just works”&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Personally, I have not found that to be the case in even simple, &lt;a href="http://soapyfrogs.blogspot.com/2010/08/cdi-ified-netbeans-jsf2-generated.html"&gt;toy applications&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;I suspect it'll be true in a few years, probably after a much needed "Java EE 6.2" and some major bug-fixing. Right now it all "just works" ... except when it doesn't, which is frequently, due to spec oversights, bugs, and a general lack of real-world use and testing.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Using CDI "just works" until you realize that &lt;code&gt;@Inject&lt;/code&gt; isn't supported in obvious places like a &lt;code&gt;@FacesConverter&lt;/code&gt; due to limitations in JSF2/CDI integration. OK, that's easily worked around by looking up a managed bean from the &lt;code&gt;FacesContext&lt;/code&gt; and getting the desired EJB from the &lt;code&gt;@Inject&lt;/code&gt; site on that managed bean. Except that the ejb injection site is null during that phase of processing, having been helpfully cleared by CDI, so you get a null pointer exception that looks downright inexplicable until you figure out that CDI is clearing the injection site behind your back. (See: &lt;a href="http://forums.java.net/jive/thread.jspa?messageID=480669"&gt;this report&lt;/a&gt; and &lt;a href="https://javaserverfaces.dev.java.net/issues/show_bug.cgi?id=1785"&gt;this Mojarra bug&lt;/a&gt;). In that case I eventually found out how to look up the desired EJB via the &lt;code&gt;BeanManager&lt;/code&gt; (See: &lt;a href="http://dominikdorn.com/2010/04/cdi-weld-manual-bean-lookup/"&gt;http://dominikdorn.com/2010/04/cdi-weld-manual-bean-lookup/&lt;/a&gt;) but it certainly doesn't "just work" even between ostensibly co-operating, integrated technologies.&lt;/p&gt;

&lt;p&gt;I hit that roadblock *after* getting stuck on a Glassfish/CDI integration bug (&lt;a href="https://glassfish.dev.java.net/issues/show_bug.cgi?id=13040"&gt;https://glassfish.dev.java.net/issues/show_bug.cgi?id=13040&lt;/a&gt;) that means that those nice new no-interface views of local EJBs don't actually work if you inherit methods from a superclass and are using Glassfish (even 3.1 as of build 15).&lt;/p&gt;

&lt;p&gt;To add to the confusion, often using the JSF2 injection support to do something works, but CDI doesn't, or vice versa. And where the JSF2 or CDI injection support works on Glassfish it'll often fail on the (admittedly alpha) JBoss AS 6 or vice versa.&lt;/p&gt;

&lt;p&gt;This is in my first JSF2 project on a Glassfish 3.0.1 container, using all the new "just works" stuff, and I must say that it sure as hell doesn't "just work" some of the time.&lt;/p&gt;

&lt;p&gt;Another factor that doesn't help is all the legacy surrounding Java EE / J2EE. It's hard for someone trying to learn Java EE 6 to separate it from all the JE22 4 and Java EE 5 stuff. Even within Java EE 6 there's "new legacy", things that are effectively deprecated even though they've barely been released. Consider JSF2 scope annotations (replaced by CDI), JSF2 @ManagedBean (replaced by the Managed Beans specification, then promptly replaced again by CDI, all within the span of the Java EE 6 spec) and JSF2 injection support (replaced by CDI). It takes a great deal of sorting out before you can have much idea what the real, practical difference is between:&lt;/p&gt;

&lt;pre&gt;
@javax.faces.bean.ManagedBean
@javax.faces.bean.SessionScoped
&lt;/pre&gt;

&lt;p&gt;and:&lt;/p&gt;

&lt;pre&gt;
@javax.inject.Named
@javax.enterprise.context.SessionScoped
&lt;/pre&gt;

&lt;p&gt;let alone &lt;code&gt;@javax.annotation.ManagedBean&lt;/code&gt; (hooray pointless duplication).&lt;/p&gt;

&lt;p&gt;Then there's un-muddling the chaos of @Inject, @EJB and @Resource (which I'm still struggling with, since @EJB seems to be used by CDI in a *completely* *different* context), the difference between injecting an EntityManager via @PersistenceContext vs @Inject, etc.&lt;/p&gt;

&lt;p&gt;No, it really, really doesn't "just work". It's a nightmare of overlapping, muddled conflicting specs and half-integrated half-co-operating implementations with weird quirks and holes in their support. It really needs an EJB 6.2 release that clearly deprecates all the old stuff and documents how it all interacts.&lt;/p&gt;

&lt;p&gt;I've finally got to the point, in my efforts to learn about Java EE 6, where I'm starting to see the bigger picture of how it's *supposed* to work, and supposed to fit together. I can see now how it's meant to be simple to use, and if you don't hit any of the exciting bugs it even can be simple to use and nice to use.&lt;/p&gt;

&lt;p&gt;The biggest single improvement that could be made to Java EE 6 is the heavy use of the @Deprecated annotation on 80% of the existing public interfaces, and the option to deploy an app in a CDI-only, no-JSF2-injection, legacy-free environment where attempts to use legacy container services are detected as errors (in development mode) or are ignored/fail (in production mode).&lt;/p&gt;

&lt;p&gt;It'd also help to have some &lt;a href="http://ocpsoft.com/opensource/improving-community-the-power-of-good-centralized-documentation/"&gt;centralized documentation&lt;/a&gt; that's not focused on one particular implementation, framework, etc, and clearly takes account of spec versions so as to disambiguate discussion about J2EE4, Java EE 5, Java EE 6, and Java EE 6.1.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8046589288434976524?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://ocpsoft.com/java/why-doesnt-jpa-jma-jta-ejb-jsf-cdi-work-jee-is-too-complicated/' title='Java EE 6 doesn&apos;t &quot;just work&quot; in  even trivial real-world uses'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8046589288434976524/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/java-ee-6-doesnt-just-work-in-even.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8046589288434976524'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8046589288434976524'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/java-ee-6-doesnt-just-work-in-even.html' title='Java EE 6 doesn&apos;t &quot;just work&quot; in  even trivial real-world uses'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-4297349032777655702</id><published>2010-08-18T17:05:00.004+08:00</published><updated>2010-09-03T09:54:54.394+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='whine'/><category scheme='http://www.blogger.com/atom/ns#' term='glassfish'/><category scheme='http://www.blogger.com/atom/ns#' term='cdi'/><title type='text'>Simple Java EE 6 project fails in different ways on Glassfish and JBoss AS 6</title><content type='html'>(Update: this issue was caused by a Glassfish bug. See &lt;a href="https://glassfish.dev.java.net/issues/show_bug.cgi?id=13040"&gt;Bug 13040 in the Glassfish tracker&lt;/a&gt;.)

&lt;p&gt;I'm coming to really like Java EE 6 ... or at least, I would if anything worked properly. There seem to be an awful lot of quirks and bugs in even fairly basic functionality, though.&lt;/p&gt;

&lt;p&gt;Take this simple project, where a @Stateless EJB that inherits from an abstract base is injected into a regular JSF2 backing bean. It's done two different ways - once using the new CDI/Weld injection facilities, and once with old-style JSF2 injection and EJB container management.&lt;/p&gt;

&lt;p&gt;One works on glassfish, the other on JBoss. Neither works on both.&lt;/p&gt;

&lt;p&gt;The glassfish failure is caused by a problem resolving methods inherited from superclasses in the Weld-generated local no-interface EJB proxies. The JBoss issue appears to be caused by issues with its local no-interface EJB support, too, though I'm not sure exactly what yet.&lt;/p&gt;

&lt;p&gt;The point: Local no-interface EJBs were supposed to save time and pain for developers, yet all I'm finding so far is bugs, or at least quirks and inconsistent behaviour.&lt;/p&gt;

&lt;p&gt;Sources to the sample can be found &lt;a href="http://www.postnewspapers.com.au/~craig/public_files_keep/ErrorDemo.zip"&gt;here&lt;/a&gt; and a deployable war is &lt;a href="http://www.postnewspapers.com.au/~craig/public_files_keep/ErrorDemo.war"&gt;here&lt;/a&gt;.&lt;/p&gt;

&lt;p&gt;Posted on &lt;a href="http://stackoverflow.com/questions/3509255/unable-to-convert-ejbref-for-ejb-on-cdi-weld-injection-of-stateless-ejb-into"&gt;StackOverflow&lt;/a&gt; and &lt;a href="http://forums.java.net/jive/thread.jspa?threadID=152567"&gt;The Glassfish forums&lt;/a&gt;&lt;/p&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4297349032777655702?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4297349032777655702/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/simple-java-ee-6-project-fails-in.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4297349032777655702'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4297349032777655702'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/simple-java-ee-6-project-fails-in.html' title='Simple Java EE 6 project fails in different ways on Glassfish and JBoss AS 6'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1484159487430734076</id><published>2010-08-16T10:58:00.002+08:00</published><updated>2010-08-25T18:09:15.524+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Linux (ubuntu 10.04) ppp maxfail 0 and persist not working? (WORKAROUND)</title><content type='html'>&lt;p&gt;I've been wondering for a while why, after an upgrade to Ubuntu 10.04, the ppp daemon on my router/server had stopped automatically reconnecting after it lost ADSL line sync or otherwise had its connection dropped.&lt;/p&gt;

&lt;p&gt;The culprit turns out to be a helper script 
(&lt;code&gt;/etc/init/network-interface.conf&lt;/code&gt;) in upstart that tries to handle hotplugging of network interfaces, automatically bringing them up when plugged in and down when unplugged.&lt;/p&gt;

&lt;p&gt;Unfortunately, it views ppp interfaces as hot-plugged, and helpfully calls &lt;code&gt;ifdown&lt;/code&gt; on them when they dissappear after a connection is lost. This kills pppd, preventing it from creating a new ppp interface and trying to connect again.&lt;/p&gt;

&lt;p&gt;Unlike most interfaces, ppp interfaces are created and destroyed as a &lt;i&gt;consequence&lt;/i&gt; of &lt;code&gt;ifup&lt;/code&gt; and &lt;code&gt;ifdown&lt;/code&gt; calls. Well, really pppd invocation, but that's usually done via ifup/ifdown these days. Calling ifdown when a ppp interface vanishes isn't always wrong, but it's wrong if the pppd is set to "maxfail 0 persist".&lt;/p&gt;

&lt;p&gt;As the script isn't smart enough to know that, and I don't hotplug interfaces on my router anyway, I've opted to simply disable the script in question by renaming it to &lt;code&gt;/etc/init/network-interface.conf.disabled&lt;/code&gt;. That'll be broken whenever the upstart package is updated, though, so a better solution is required.&lt;/p&gt;

&lt;p&gt;Unconditionally ignoring all ppp interfaces in the script isn't necessarily right, as it would be nice to ifdown them cleanly when the pppd has exited and is thus no long retrying. It's hard to do that reliably as the interface is destroyed before pppd actually terminates, and it's hard to query pppd to find out if it plans to quit or retry. Arguably the retry logic should be moved out of pppd and into upstart or network-manager, but with the state of those tools at this point that's a recipe for pain and suffering.&lt;/p&gt;

&lt;p&gt;In the mean time, you can work around it by disabling the problematic init script.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1484159487430734076?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1484159487430734076/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/linux-ubuntu-1004-ppp-maxfail-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1484159487430734076'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1484159487430734076'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/linux-ubuntu-1004-ppp-maxfail-and.html' title='Linux (ubuntu 10.04) ppp maxfail 0 and persist not working? (WORKAROUND)'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8994123659021245286</id><published>2010-08-15T21:40:00.002+08:00</published><updated>2010-08-16T10:47:45.301+08:00</updated><title type='text'>simple script to extract Stanza epub ebooks from an iTunes backup</title><content type='html'>&lt;p&gt;iTunes and the iPhone don't like to let go of data they have their claws into. Sometimes, however, you might need that data outside the Apple Garden, in which case you're going to have to get your hands dirty diving in iTunes backups, as they're the easiest way to regain control of your files.&lt;/p&gt;

&lt;p&gt;(Tip: if you need to fight your software to access your own files, your platform is hostile. I don't use the iPhone myself and loathe the way Apple does things, but sometimes I have to work with it for other people. Hence this post.)

&lt;p&gt;I needed to recover some Stanza ebooks from an iPhone. It's hard enough to get them *on* to the phone, and getting them off is nigh on impossible, as Apple continuously changes things to make it hard to access the phone via anything but iTunes. In this case, though, the latest change (to the backup format) made it easier to work with, not harder, so the extraction wasn't too hard.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Thankfully, the newer iTunes backup format isn't too hard to work with. It saves two backup files for every file on the phone - one with a &lt;code&gt;.mdinfo&lt;/code&gt; extension that's an Apple binary p-list file containing the file's path and metadata, and a second with a &lt;code&gt;.mddata&lt;/code&gt; extension that's the actual file data. Other than file extension both have the same name, so they're easy to associate.&lt;/p&gt;

&lt;p&gt;The plist format is unpleasant to work with and I haven't made any effort to parse it properly. &lt;i&gt;If anyone has a decent plist parser that doesn't require distribution of Apple shared libraries, please let me know.&lt;/i&gt; It's relatively easy to hack together a "dumb" processor that looks for and extracts strings within the plist-format &lt;code&gt;.mdinfo&lt;/code&gt; files to obtain path information, as I've done here, but it's extremely fragile and likely to break at minor point revisions of the backup format.&lt;/p&gt;

&lt;p&gt;Take the following script, which looks for &lt;code&gt;.mdinfo&lt;/code&gt; files containing the string &lt;code&gt;.epub&lt;/code&gt;, extracts the filename, and copies the associated &lt;code&gt;.mddata&lt;/code&gt; file to the "ebooks" folder in the user's home directory.&lt;/p&gt;

&lt;p&gt;Run this script after cd'ing to your iTunes backup folder. On Windows that's in %APPDATA%\Apple\MobiSync (I think, I'm not at the Windows box right now). Within there are folders for each backup. Check the dates to find the most recent, cd into it, and run this script. Your ebooks should appear in a &lt;code&gt;ebooks&lt;/code&gt; folder in your home directory.&lt;/p&gt;

&lt;p&gt;This is the dirtiest possible hack for the .mdinfo reading and filename extraction. I need to tidy it up into something a bit less gross, but hey, it's enough to play with.&lt;/p&gt;

&lt;pre&gt;
#!/usr/bin/env python
import os
import shutil
import errno

outfolder=os.path.expanduser(r"~/ebooks")

def getfn(f):
    info = open(f,"r").read()
    if "epub" in info:
        return info.split("^P")[2][11:-6]

for f in os.listdir("."):
    try:
        os.mkdir(outfolder)
    except OSError,e:
        if e.errno != errno.EEXIST:
            raise e
    if f.endswith(".mdinfo"):
        fn = getfn(f)
        if fn is not None:
            datafile = os.path.basename(f)
            shutil.copyfile(datafile, os.path.join(outfolder, fn))
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8994123659021245286?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8994123659021245286/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/simple-script-to-extract-stanza-epub.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8994123659021245286'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8994123659021245286'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/simple-script-to-extract-stanza-epub.html' title='simple script to extract Stanza epub ebooks from an iTunes backup'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-7490735032769237860</id><published>2010-08-09T11:27:00.009+08:00</published><updated>2010-08-25T18:19:14.889+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='whine'/><title type='text'>I'm not smart enough to use Java EE</title><content type='html'>&lt;p&gt;I don't seem to be smart enough to use Java EE. (Update: I seem to be managing anyway, and am trying to help others avoid the same suffering I went through by providing &lt;a href="http://soapyfrogs.blogspot.com/2010/08/java-ee-6-is-not-product-you-can.html"&gt;some overview level documentation on how it all fits together&lt;/a&gt; and what all the damn acronyms mean.)&lt;/p&gt;

&lt;p&gt;You know what helps, though? &lt;i&gt;Reading &lt;a href="http://download-llnw.oracle.com/javaee/6/tutorial/doc/p1.html"&gt;the manual!&lt;/a&gt;&lt;/i&gt;, which is actually very good. Before sitting down for some quality reading time, I was totally lost. This is not something you can dive head-first into.&lt;/p&gt;

&lt;p&gt;Seam. Struts. Tapestry. Weave. Weld. JSF. JSF2. JPA. JPA2. EJB2. EJB3. CDI. Hibernate, EclipseLink, etc. J2EE. JARs in OSGi bundles in WARs in EARs. Glassfish. JBoss. Tomcat. Jetty. @Inject. @EJB. @Resource. @PersistenceContext. @javax.annotation.ManagedBean. @javax.faces.bean.ManagedBean. @Named. @Stateless. @Stateful. Spring. Servlet filters. @javax.faces.bean.RequestScoped, javax.enterprise.context.RequestScoped, @javax.faces.bean.SessionScoped, javax.enterprise.context.SessionsCoped, @javax.faces.bean.ApplicationScoped, javax.enterprise.context.ApplicationScoped, javax.enterprise.context.ConversationScoped, @javax.faces.bean.ViewScoped. @Singleton. DAOs and data access layers. JTA. JNDI. JMS. RMI. Deployment descriptors (web.xml, etc). Vendor deployment descriptors (sun-web.xml, sun-resources.xml, etc). Various config files, some of which have an effect even when empty (faces-config.xml, beans.xml). Maven plugins, oh god the maven plugins. RichFaces, MyFaces, IceFaces, PrimeFaces, SeamFaces.&lt;/p&gt;

&lt;p&gt;Part of the problem is that there are several generation of "technology" that are mashed together in an overlapping mess. For example, J2EE6 uses CDI with Weld, but everyone already does this with Spring, which also does all sorts of other things so you might want to still use it even though you're doing DI with Weld in a J2EE container. Your objects might be being managed by some combination of JSF2 @ManagedBean lifecycle management, @EJB lifecycle management, Weld, or (if you're using it) Spring.&lt;/p&gt;

&lt;p&gt;Or does JSF2 actually use CDI to manage its beans via Weld? Who knows! Every framework uses every other framework or has semi-transparent integration hooks for them. Every framework claims to make everything simple, while adding yet another layer of nightmarish complexity. Specifications inevitably have a reference implementation from Sun or JBoss, and an Apache implementation, so you have to figure out what the relationship between the implementation names and the spec name is for each, what they actually do, which implementation you might want to choose and why, etc.&lt;/p&gt;

&lt;p&gt;When trying to learn about this stuff, it's hard to find materials that don't already assume you know the older technologies and just want to update to the newer ones. Hell, it's hard to figure out what the older ones and newer ones even are, or why you should want to use one over the other.&lt;/p&gt;

&lt;p&gt;The J2EE platform releases are supposed to help with that, but seem to add complexity more than remove it, due to the need for backward compat, support for older frameworks and code that uses them, etc.&lt;/p&gt;

&lt;p&gt;There's so much transparent management of application state, object lifetimes, request handling, etc that it becomes quite hard to figure out what your app is actually doing, how and when to do something, or how to alter some part of the automatic behaviour when you need to. As for debugging...&lt;/p&gt;

&lt;p&gt;Java EE makes me understand how "non computer people" feel when listening to programmers talk.&lt;/p&gt;

&lt;p&gt;On the other hand, it makes me feel better when really, really smart people &lt;a href="http://www.joelonsoftware.com/articles/fog0000000018.html"&gt;point out that all this architecture makes it hard to see what you're actually trying to do&lt;/a&gt;.&lt;/p&gt;

&lt;blockquote&gt;
&lt;i&gt;&lt;a href="http://www.joelonsoftware.com/articles/LeakyAbstractions.html"&gt;... and all this means that paradoxically, even as we have higher and higher level programming tools with better and better abstractions, becoming a proficient programmer is getting harder and harder.&lt;/a&gt;&lt;/i&gt;
&lt;/blockquote&gt;

&lt;p&gt;.... and really, reading the manual makes it all much more approachable ;-)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-7490735032769237860?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7490735032769237860/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/im-not-smart-enough-to-use-java-ee.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7490735032769237860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7490735032769237860'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/im-not-smart-enough-to-use-java-ee.html' title='I&apos;m not smart enough to use Java EE'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-7314261125263469957</id><published>2010-08-04T15:27:00.001+08:00</published><updated>2010-08-04T15:27:31.215+08:00</updated><title type='text'>Great service providers I use</title><content type='html'>&lt;p&gt;A few of the service providers I use deserve some credit for great work. They are:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt; &lt;a href="http://linode.com/"&gt;Linode.com&lt;/a&gt;, a great VPS host with excellent prices, good service, and really, really good tools for VPS management and control. You'll never need to call support because you broke SSH or installed a busted kernel ever again, since you have a web-based virtual serial console and full bootloader control over the web interface. Check them out, they're excellent.&lt;/li&gt;
&lt;li&gt; &lt;a href="http://simplecdn.com/"&gt;SimpleCDN.com&lt;/a&gt;, whose mirroring CDN service "Mirror Buckets" is truly incredible. I put about fifty bucks into it six months ago, and still haven't run out. It's about two thousand times cheaper than my old host's traffic charges - though admittedly that was an Australian VPS host with typically Australian bandwidth-prices-of-doom.&lt;/li&gt;
&lt;li&gt; &lt;a href="http://internode.on.net/"&gt;Internode&lt;/a&gt;, who are the only Australian ISP at time of writing who're offering native IPv6 to regular ADSL customers. They're otherwise pretty decent within the limits of a cut-price cut-throat industry. Imperfect, but they're in telecommunications, and compared to the telco/ISP average they don't even rank on the suck chart.&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/427258967255390991-7314261125263469957?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7314261125263469957/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/great-service-providers-i-use.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7314261125263469957'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7314261125263469957'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/great-service-providers-i-use.html' title='Great service providers I use'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-5479891855751970813</id><published>2010-08-02T12:31:00.001+08:00</published><updated>2010-08-25T18:20:08.321+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='datarecovery'/><title type='text'>All hail GNU Parted</title><content type='html'>&lt;p&gt;A user bought in a dead laptop today. It was booting into Dell MediaDirect (an XP Embedded OS stored on the same disk as the main Windows XP install) instead of Windows XP, then bluescreening early in MediaDirect boot.&lt;/p&gt;

&lt;p&gt;The user had accidentally hit the hardware button that boots the machine into MediaDirect instead of XP.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;The machine booted fine under an Ubuntu live CD, and examination of the partition table quickly revealed that it was completely mangled. The primary partition that should've mapped Windows XP instead pointed to the same 2GB near the end of the disk that was mapped by the extended partition used by MediaDirect. So the partition table had two identical overlapping partitions, neither of which pointed to the real OS install.&lt;/p&gt;

&lt;p&gt;The fix was trivial, thanks to GNU Parted.&lt;/p&gt;

&lt;pre&gt;
$ sudo parted /dev/sda
rm
1
unit
%
rescue
0
100
&lt;/pre&gt;

&lt;p&gt;In other words: Remove the primary partition (/dev/sda1) that should've been Windows XP, but instead pointed to MediaDirect (which was also on /dev/sda5). Then set units to percentage, and tell parted to scan the whole disk for lost file systems.&lt;/p&gt;

&lt;p&gt;It'll find the original NTFS file system (for XP) and offer to add a partition for it. Easy.&lt;/p&gt;

&lt;p&gt;You need to make sure the partition is flagged 'active' as XP's bootloader is a little dim. As I don't know parted well, I did this with:&lt;/p&gt;

&lt;pre&gt;
$ sudo fdisk /dev/sda
a
1
w
&lt;/pre&gt;

&lt;p&gt;... then rebooted into a working XP for the user.&lt;/p&gt;

&lt;p&gt;The original cause is unknown, but perhaps this'll help others who've had MediaDirect mysteriously eat their partition table.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5479891855751970813?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5479891855751970813/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/all-hail-gnu-parted.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5479891855751970813'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5479891855751970813'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/all-hail-gnu-parted.html' title='All hail GNU Parted'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-7441079081482110393</id><published>2010-08-01T13:03:00.005+08:00</published><updated>2010-08-25T18:07:17.488+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='whine'/><title type='text'>The Java needs language-level property support (and a reset)</title><content type='html'>&lt;p&gt;I find myself increasingly bewildered by some of the design choices in the Java world. The core language's use of accessor methods is a notable example.&lt;/p&gt;

&lt;p&gt;Not that properties are the only &lt;a href="http://www.clintonbegin.com/2008/02/clintons-java-5-rant.html"&gt;bewildering Java design choice&lt;/a&gt;.&lt;/p&gt;
&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Since the very early Java days, this has been discouraged:&lt;/p&gt;

&lt;pre&gt;
public class Something { 
  public int prop;
}

public class SomethingUser {
  public void outputThing(Something thing) {
    System.out.println("Thing " + thing + " has value " + thing.prop);
  }
}
&lt;/pre&gt;

&lt;p&gt;in favour of an accessor-driven approach:&lt;/p&gt;

&lt;pre&gt;

public class Something { 
  private int prop;
  
  public int getProp() {
    return prop;
  }

  public void setProp(int prop) {
     this.prop = prop;
  }
}

public class SomethingUser {
  public void outputThing(Something thing) {
    System.out.println("Thing " + thing + " has value " + thing.getProp());
  }
}
&lt;/pre&gt;

&lt;p&gt;... which is verbose and annoying. On the other hand there are good arguments for why you want accessors - essentially they're a large part of the difference between using "structs and procedures" and using objects and messaging (OOP). Both can be good models, but if you're going to try for an OOP approach you need ways to trigger side effects when values change, or to return generated values as the internal representation of an object changes, both of which require accessors.&lt;/p&gt;

&lt;p&gt;One might wonder, though, if something this important should be something the language can do for you in simple cases, so you only have to hand-write the accessors that do non-default things. Like the following (wishful thinking) pseudo-Java:&lt;/p&gt;

&lt;pre&gt;
public class Something { 
  public property int prop;
}

public class SomethingUser {
  public void outputThing(Something thing) {
    System.out.println("Thing " + thing + " has value " + thing.getProp());
  }
}
&lt;/pre&gt;

&lt;p&gt;This wishful thinking becomes more significant when the need to introduce property change listeners is considered. Java has a system for listening for changes to properties of objects and triggering events when these properties change. It's entirely at the library level, though, with no language support. A property change enabled version of our first example might look like this:&lt;/p&gt;


&lt;pre&gt;
public class Something { 
  private static PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);
  private int prop;
  
  public int getProp() {
    return prop;
  }

  public void setProp(int prop) {
     changeSupport.firePropertyChange("prop", this.prop, this.prop = prop);
  }

  public void addPropertyChangeListener(PropertyChangeListener listener) {
    changeSupport.addPropertyChangeListener(listener);
  }

  public void addPropertyChangeListener(String propName, PropertyChangeListener listener) {
    changeSupport.addPropertyChangeListener(propName, listener);
  }

  public void removePropertyChangeListener(PropertyChangeListener listener) {
    changeSupport.removePropertyChangeListener(listener);
  }

  public void removePropertyChangeListener(String propName, PropertyChangeListener listener) {
    changeSupport.removePropertyChangeListener(propName, listener);
  }
}

public class SomethingUser {
  public SomethingUser(Something thing) {
    thing.addPropertyChangeListener("prop", new PropertyChangeListener() {
      public propertyChange(PropertyChangeEvent evt) {
        outputThing((Thing)evt.getSource());
      }
    });
  }

  public void outputThing(Something thing) {
    System.out.println("Thing " + thing + " has value " + thing.getProp());
  }
}
&lt;/pre&gt;


&lt;p&gt;Getting verbose, isn't it? Given how important the property change system is in Java, one might wonder why there's no language integration. Imagine a "bean" modifier that inserted property change support (we'd do this with a mixin class, but Java doesn't have multiple inheritance or mixins, so we need language support) and enabled property change notification on declared properties:&lt;/p&gt;

&lt;pre&gt;
public bean class Something { 
  public property int prop;
}

public class SomethingUser {
  public SomethingUser(Something thing) {
    thing.addPropertyChangeListener("prop", new PropertyChangeListener() {
      public propertyChange(PropertyChangeEvent evt) {
        outputThing((Thing)evt.getSource());
      }
    });
  }

  public void outputThing(Something thing) {
    System.out.println("Thing " + thing + " has value " + thing.getProp());
  }
}
&lt;/pre&gt;

&lt;p&gt;The property client side is still gruesome, but writing those property-change enabled beans is suddenly much, much less painful. You can remove the "property" modifier and implement the accessors yourself if you need to do anything interesting, but the 99% of boilerplate crap you have to write usually just goes away.&lt;/p&gt;

&lt;p&gt;Rather better thought out proposals have been made by many others, such as &lt;a href="http://hookom.blogspot.com/2007/01/properties-as-extensions-of-member.html"&gt;this one&lt;/a&gt;, though like most of them the linked proposal fails to consider bean binding and change notification.&lt;/p&gt;

&lt;p&gt;Unfortunately, rather than solving real-world pain points for Java developers, the Java language appears to be stuck in arguments over whether closures should be supported, endless nattering about reified vs erasing generics, etc. These are all useful, but there are so many &lt;i&gt;pain points&lt;/i&gt; in the library that could be dealt with by a few language extensions.&lt;/p&gt;

&lt;p&gt;Even a decent, standard, core way of doing mixins would help a lot. As things stand you have numerous aspect-oriented programming frameworks that half-solve the problem, but also tend to conflict with each other, add complexity, require annotation processors or other code generation, and generally be a pain to use.&lt;/p&gt;

&lt;p&gt;You know Java is in a bad way when you start to miss coding C++.&lt;/p&gt;

&lt;p&gt;(Yes, I know I can do this with Groovy. Sort-of. Of course, Groovy doesn't play well with some other Java features like anonymous inner classes, requires a whole new set of tools and tool support enhancements, its own dev env and compiler, etc. Why not fix the core language?)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-7441079081482110393?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7441079081482110393/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/08/java-and-properties.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7441079081482110393'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7441079081482110393'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/08/java-and-properties.html' title='The Java needs language-level property support (and a reset)'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1265595943291678540</id><published>2010-07-30T16:04:00.003+08:00</published><updated>2010-08-25T18:07:41.560+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='jsf2'/><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><title type='text'>Sanity in the J2EE / JSF world</title><content type='html'>&lt;p&gt;I was very impressed with &lt;a href="http://www.coreservlets.com/JSF-Tutorial/jsf2/"&gt;this tutorial&lt;/a&gt; from CoreServlets.com, which explains JSF and JSF2 development without assuming that you know all the Java server-side acronyms and mess already.&lt;/p&gt;

&lt;p&gt;It also has a rational, calm discussion of web framework selection, covering Google Web Toolkit (GWT), Wicket/Tapestry, JSF2, etc that focuses on what each is good for, rather than trying to name some One True Framework. In my reading on the J2EE world thus far, this is unique.&lt;/p&gt;

&lt;p&gt;Another article that anyone looking to get into JSF2 should read is this &lt;a href="http://java.dzone.com/articles/how-do-10-common-tasks-jsf-20?utm_source=feedburner&amp;utm_medium=feed&amp;utm_campaign=Feed%3A+javalobby%2Ffrontpage+%28Javalobby+%2F+Java+Zone%29&amp;utm_content=Google+Reader"&gt;DZone article summarizing how to handle some common tasks in JSF2&lt;/a&gt;. It won't make much sense until you've read some basics about JSF, like the tutorial linked to at the start of this post, but it's a great overview and refresher once you have.&lt;/p&gt;

&lt;p&gt;Of course, as &lt;a href="http://www.clintonbegin.com/2008/05/re-java-haters-gtfo.html"&gt;others have said very well indeed&lt;/a&gt;, getting into the Java Enterprise programming world of JSF2 etc may be a mistake.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1265595943291678540?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.coreservlets.com/JSF-Tutorial/jsf2/' title='Sanity in the J2EE / JSF world'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1265595943291678540/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/07/sanity-in-j2ee-jsf-world.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1265595943291678540'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1265595943291678540'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/07/sanity-in-j2ee-jsf-world.html' title='Sanity in the J2EE / JSF world'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-5925348086176543126</id><published>2010-07-25T12:57:00.001+08:00</published><updated>2010-07-25T12:58:30.247+08:00</updated><title type='text'>A message for SCO OpenServer users</title><content type='html'>&lt;p&gt;I've seen a few queries on the PostgreSQL list from people who want to run PostgreSQL on SCO OpenServer, or upgrade ancient versions of Pg to modern ones on SCO OpenServer boxes. Some want to solve issues with connection count limits on their elderly SCO installs, so they can increase client counts above 100 or so. Sometimes people even want to upgrade to a new (ie post-1995, not truly new) SCO OpenServer release and want to know how Pg will cope.&lt;/p&gt;

&lt;p&gt;I have a message for those folks, and the management behind them who're usually the ones pushing to stay on SCO.&lt;/p&gt;


&lt;p&gt;Your boss may not realize that SCO basically dropped OpenServer as a product line in favour of UnixWare in the late 90s. Since then there was no significant work done on OpenServer. There's been no work done on it at all (as far as I can tell) since Caldera bought the SCO name and OpenServer product from the original Santa Cruz Operation, fired all the software engineers, hired some lawyers and sued world+dog. The Santa Cruz Operation renamed themselves Tarantella after their primary profitable product and went on with life, but "SCO" as a company is history.&lt;/p&gt;

&lt;p&gt;OpenServer is dead, dead, dead. Any money put into products targeting openserver is a sunk cost, and you can't change that, but you should really avoid sinking more money into that mess. If your management is still sticking to OpenServer, they should probably read about &lt;a href="http://en.wikipedia.org/wiki/Escalation_of_commitment"&gt;escalation of commitment&lt;/a&gt;, a decision making tendency that's very dangerous and very easy to fall into if you don't think about it carefully.&lt;/p&gt;

&lt;p&gt;Upgrading from 5.0.5 / 5.0.7 to 6.0 is like upgrading from Windows 95 to Windows ME in 2010. Or Mac OS 7.1 to Mac OS 9.2. You're upgrading from the corpse of an operating system to one that's still twitching feebly. This is not going to be a good way to invest time and money.&lt;/p&gt;

&lt;p&gt;In case you think I'm just a Linux zealot flag-waving, I have a SCO OpenServer 5.0.5 box in the back room, running business critical applications. The apps are actually for Microsoft Xenix (yes, 1983 binaries) running in the Xenix persionality on OpenServer. I considered a port to OpenServer 6.0, but realized it was just slightly delaying the inevitable move to something modern.&lt;/p&gt;

&lt;p&gt;So ... I keep it running - in VMWare*, since 5.0.5 runs about ten times faster as a VMWare guest on a Linux host than it does natively on the same hardware. It's faster because SCO doesn't use much RAM for disk cache, doesn't readahead, and is generally just sloooooow in its disk access and memory use strategies. The Linux guest in a vmware setup can cache the whole SCO OS and apps disk in RAM, making the whole setup much faster. It seems more stable under VMWare than running natively on modern hardware, too.&lt;/p&gt;

&lt;p&gt;I'd recommend you do much what I've done. Move your SCO instances to VMs running under Linux. Provide modern PostgreSQL on the Linux host, and just compile libpq for the SCO guest. Then start work on migrating your app to run natively on Linux/BSD/Solaris/whatever.&lt;/p&gt;

&lt;p&gt;* SCO OpenServer doesn't seem to run under KVM or qemu due to bugs and limitations in their SCSI emulation. VMWare Server is free, so just use that until you're free of SCO.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5925348086176543126?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5925348086176543126/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/07/message-for-sco-openserver-users.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5925348086176543126'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5925348086176543126'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/07/message-for-sco-openserver-users.html' title='A message for SCO OpenServer users'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8129917193882373227</id><published>2010-07-22T12:04:00.013+08:00</published><updated>2010-08-25T18:08:17.858+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javaee6'/><category scheme='http://www.blogger.com/atom/ns#' term='whine'/><title type='text'>Java "Enterprise" tools: every problem half-solved with twice the required complexity</title><content type='html'>&lt;p&gt;The more I work with Java, the more I feel like it's a great core language surrounded by endless onion-like layers of increasingly awful frameworks and tools.&lt;/p&gt;

&lt;p&gt;The Netbeans platform. JPA2. Hibernate / OpenJPA / EclipseLink / Swing. EJB3. OSGi. With tools like those, you'd think that a simple task like writing  a business database application would be easy, right?&lt;/p&gt;

&lt;p&gt;Ha! You spend so much time working around problems created by these tools' solutions to other problems that you're lucky to get any actual work done on your app.&lt;/p&gt;

&lt;blockquote&gt;
&lt;i&gt;Java is like a variant of the game of Tetris in which none of the pieces can fill gaps created by the other pieces, so all you can do is pile them up endlessly. -- Steve Yegge (2007, Codes Worst Enemy)&lt;/i&gt;
&lt;/blockquote&gt;

&lt;p&gt;Examples demonstrating the use of JPA, Hibernate, etc are full of assumptions and limitations that render them useful only for utterly trivial toy projects. For tools that're touted as making it easy to build "scalable" apps, I find this interesting. One &lt;a href="http://java.sys-con.com/node/46977"&gt;Hibernate+JSF tutorial&lt;/a&gt;, for example, writes:&lt;/p&gt;

&lt;blockquote&gt;
&lt;i&gt;One of JCatalog's assumptions is that no more than 500 products are in the catalog. All product information can fit into the user session.&lt;/i&gt;
&lt;/blockquote&gt; 

&lt;p&gt;We're building an Enterprise Application with n-teir Enterprise Architecture ... that's limited to many fewer items tracked than there are lines of code in the project. Does this seem off to you?  If it's so easy to build scalable data access with Hibernate, why isn't it done in demoes and tutorials? My own experience suggests that's because it's not actually at all easy, it's painful and complex if possible at all.&lt;/p&gt;

&lt;p&gt;&lt;a href="http://www.clintonbegin.com/2008/05/re-java-haters-gtfo.html"&gt;This post&lt;/a&gt; expresses much of what I'm trying to say below, and expresses it much, much better than I can.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;
&lt;hr/&gt;

&lt;p&gt;I should've just stuck to JDBC and core Java + Swing. Not that Swing is exactly nice, as its event-driven single-threaded design combined with rather less than event-driven models requires some complex thread-juggling to maintain a reliable, responsive app. Maybe just learned Ruby+Rails, PHP, or done it as a simple Python webapp. I'd be done in no time, with none of the stupidity involved in fighting the JPA to make it do what you need. My productivity in terms of real, usable application code produced has gone down to near-zero with these tools. There are endless demos on how great these tools all are (most of which demonstrate tiny toy apps with hopeless limitations) but there's no information out there on how to solve the problems with their real-world use and get a usable app going with them. See, eg:&lt;/p&gt;

&lt;p&gt;&lt;a href="http://platform.netbeans.org/tutorials/nbm-crud.html"&gt;Creating a CRUD netbeans application&lt;/a&gt;, which produces a completely useless toy app that fails to solve any of the interesting problems with desktop DB apps, like handling DB disconnects, 'net connection loss, latency, limited bandwidth, etc while maintaining a responsive and well behaved UI. I know, let's sweep those problems under the rug and pretend they don't exist! For just how complex &lt;i&gt;some&lt;/i&gt; of these problems are, see &lt;a href="http://blog.schauderhaft.de/2008/09/28/hibernate-sessions-in-two-tier-rich-client-applications/"&gt;this excellent article&lt;/a&gt; on using JPA with Swing.&lt;/p&gt;

&lt;p&gt;For others considering combining these technologies: JPA is designed for the server-side world, where it lives in an application server with reliable low-latency access to the database and a pool of identical connections available to it. It claims to let you write database-transparent code that works with database entities as normal Java objects, largely taking care of the persistence of these objects for you. It offers transaction management features and is theoretically very nice to use.&lt;/p&gt;

&lt;p&gt;On the server. Well, sometimes it's fine on the server, sometimes a &lt;a href="http://code.google.com/webtoolkit/articles/using_gwt_with_hibernate.html"&gt;horrifying train wreck of multi-layered duplicate code and copying&lt;/a&gt; that's in true Enterprise Application style.&lt;/p&gt;

&lt;p&gt;Don't expect easy use of native database types, easy type mapping and translation (think: emulating boolean in crap databases that don't support it), though.&lt;/p&gt;

&lt;p&gt;As for desktop and simple 2-teir apps: People will say JPA can work happily in desktop apps, but I suspect these people haven't written &lt;i&gt;real&lt;/i&gt; desktop apps with it, or they're into over-abstraction, pain, and "Enterprise-y" design. You know, where you spend twice as long building the tools to build the tools to build the frameworks to control the enterprise architecture to model the modules that eventually contain some actual useful application-domain code as you do writing the code that &lt;i&gt;does the work your application is for&lt;/i&gt;. Either that, or they think that having their app appear to crash/hang for long periods when the user's wifi drops, they go out of 3G cellular range, etc is OK. They also have no problem with transferring 1000x as much data is needed or making 100x as many database round trips as required so the app is incredibly, glacially slow on high latency connections.&lt;/p&gt;

&lt;p&gt;Why is JPA so dreadful for desktop apps? For just one reason, see my &lt;a href="http://soapyfrogs.blogspot.com/2010/07/jpa-and-hibernateeclipselinkopenjpaetc.html"&gt;previous post&lt;/a&gt; on issues with the interaction of JPA lazy fetching with the need to keep the EDT from blocking in Swing.&lt;/p&gt;

&lt;p&gt;What it all comes down to is that your app's code really all does have to be aware of the database. It has to be aware that database access can be slow, can time out, and requires user feedback. It has to figure out what it'll need to know in advance and fetch that information in efficient batches. It has to selectively retrieve only what information it needs, and do so without having to call back to the database continuously to get information it missed. This is unavoidably hard to do, and trying to hide it from the app like JPA does makes it much harder, not easier, to get right.&lt;/p&gt;

&lt;p&gt;Overall, my personal experience with JPA/Hibernate and friends like EclipseLink and TopLink Essentials has been that they're absolute productivity killers. They seem great at first brush, but the more you work with them the more you realise they're a clumsy solution and create more problems than they solve.&lt;/p&gt;

&lt;p&gt;Hibernate and co can make deleting an object from a database into a complicated operation where you stare at inter-entity cascades until you're half blind.&lt;/p&gt;

&lt;p&gt;Even the experts can't get it right. The NetBeans IDE generates rather broken JPA mappings by default, which can't even be used to remove an entity until they're fixed. Lest you think it's my UI code, I'm using an IDE-generated JSF2 interface too, and haven't typed a line of code in this project. Pity it doesn't work.&lt;/p&gt;

&lt;p&gt;So: If you're going to use the NetBeans Platform or any Swing app, think very carefully about whether it's a good idea to use JPA, and understand the issues nobody likes to talk about. For that matter, think carefully about it if you're building a webapp too - you might well be better off with something like iBatis/MyBatis.&lt;/p&gt;

&lt;p&gt;With Enterprise Java in general you'll get very used to this feeling:&lt;/p&gt;

&lt;blockquote&gt;
&lt;i&gt;Whenever I write code in Java I feel like I'm filling out endless forms in triplicate. -- Joe Marshall (aka jrm)&lt;/i&gt;
&lt;/blockquote&gt;

&lt;p&gt;Quotes from &lt;a href="http://harmful.cat-v.org/software/java"&gt;here&lt;/a&gt;. Some of the later ones ring really, really true of "modern" Java development, where frameworks are stacked on frameworks to the point where you can't even see your own code anymore.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8129917193882373227?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8129917193882373227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/07/java-every-problem-half-solved-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8129917193882373227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8129917193882373227'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/07/java-every-problem-half-solved-with.html' title='Java &quot;Enterprise&quot; tools: every problem half-solved with twice the required complexity'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-6837083170736949584</id><published>2010-07-22T10:59:00.003+08:00</published><updated>2010-08-25T18:08:38.463+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='jpa'/><category scheme='http://www.blogger.com/atom/ns#' term='whine'/><title type='text'>JPA (and Hibernate/EclipseLink/OpenJPA/etc) in desktop apps is awful</title><content type='html'>&lt;p&gt;Using the JPA API in desktop apps seems like a great idea. It provides a standard interface to Java ORM systems, letting you use annotations to map database entities to beans in a quick and convenient manner. Implementations may be plugged in as required, so you can switch from Hibernate to EclipseLink under the hood with little pain and fuss.&lt;/p&gt;

&lt;p&gt;You'll find lots of examples and tutorials on the 'net that demonstrate how to use JPA to build simple "CRUD" apps with various frameworks, like the NetBeans platform, the (now defunct) Swing Application Framework, etc. They make it look like a quick, convenient and easy way to develop database access apps in Java.&lt;/p&gt;

&lt;p&gt;There's just one wee problem... they don't work in the real world. With desktop apps, that is; I'm sure Hibernate and friends are great on the server in a 3-tier Enterprise Application with a suitable army of coders.&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;JPA 2.0 relies on lazy initialization of beans to be efficient. You don't want to download all the data you have on hand about 10,000 customers in your database just to display their names in a tree view. You want to retrieve just their names, lazily fetching most other attributes. Later, when the user goes to (say) edit the customer, you can just fetch the lazy attributes on first use. That's how JPA works, and it seems like a great approach that saves a lot of hassle.&lt;/p&gt;

&lt;p&gt;Theoretically, anyway. JPA lazy fetching &lt;i&gt;only works within the context of an open EntityManager session&lt;/i&gt;. Entity beans retrieved by an EntityManager typically have a much longer lifetime than that of the EntityManager, ie they become detatched from the database session. Unfortunately, detatched entities will fail to access lazily loaded properties, throwing an exception if access to a lazy property that's not yet loaded is attempted.&lt;/p&gt;

&lt;p&gt;It might seem like the sensible thing to do is just keep the EntityManager around, so the entity beans remain attached to the session and can lazily load properties whenever required. Unfortunately this isn't very practical. For one thing, you'll often be using entity beans with APIs (like Swing models) that don't know they're entity beans and have no awareness of their connection to the database. Making this possible is half the point of JPA, but gives you no way to link the lifespan of the EntityManager to that of the beans it manages, except with the ugly hack of inserting a reference to the entity manager into each managed bean. Even then you can't guarantee to close the EntityManager when the last bean that uses it is being disposed of, because of the unreliability of on-finalize actions in Java. Letting the entity manager be quietly gc'd is unacceptable for the same reason - there's no guarantee it'll properly release its non-memory resources like any connection it's checked out of the pool, so if you don't explicitly close the entity manager you can leak connections from your pool.&lt;/p&gt;

&lt;p&gt;Worse than that, though, is that even if you keep the entity manager around to handle lazy property loading and you've somehow solved the lifespan/scope issues, lazy loading can cause a thread to block waiting for database/network access whenever you access a bean property that happens to be mapped as lazy. You have no control over what thread the database access is done in, and no way to let a thread do other things while lazy properties are loaded. There's no way to respond to a call to"getCustomerName()" with "Er, try again later, I'm finding that out for you now". This is &lt;i&gt;awful&lt;/i&gt; for GUI applications, because it means that the UI thread will block while the database is accessed without the ability to even display a busy cursor. What if the user's wifi drops, or they're on cellular? Your app just "crashed" as far as they're concerned.&lt;/p&gt;

&lt;p&gt;Working around the blocking issue removes most of the benefits of JPA use. You have to do all your database work in a separate database worker thread (or set of threads). Remember Swing 101: don't block the EDT. Because you can't control blocking when lazy loading is used, you have to detach all entities before letting them escape the context of the database worker thread. This means that you either can't use lazy properties (so you waste a LOT of network bandwidth and loading time retrieving things you don't need) or your non-database components need to be aware of how to handle detatched entities with lazy properties.&lt;p&gt;

&lt;p&gt;Because there's no way to reliably tell what lazy properties of an object might be required when you're retrieving it, you'll find yourself reloading entities frequently as they're passed around the app. Each time there's a database access delay, and at each point you have to be able to handle the user going off and doing other things in the UI, or have to block the UI with appropriate feedback. Every GUI component starts to sprout code to check entities its passed to see if they have the required properties loaded and if not, return control to the EDT while reloading the entity with the required properties preloaded. &lt;b&gt;For lazy loading to be useful in an environment where you can't block on database access, you need to know what properties of a bean will be used by what components in advance&lt;/b&gt;.&lt;/p&gt;

&lt;p&gt;At this point, you may start to wonder if working directly with JDBC was such a bad thing after all. At least with JDBC it's easy to retrieve arbitrary subsets of your data, rather than fighting a framework that wants to have a fixed set of lazy/eager attributes and requires messing with "left join fetch" HQL hacks to selectively eagerly fetch.&lt;/p&gt;

&lt;p&gt;Update: Someone else who's written about this has some pretty similar issues and no better answers. See &lt;a href="http://blog.schauderhaft.de/2008/09/28/hibernate-sessions-in-two-tier-rich-client-applications/"&gt;http://blog.schauderhaft.de/2008/09/28/hibernate-sessions-in-two-tier-rich-client-applications/&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-6837083170736949584?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/6837083170736949584/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/07/jpa-and-hibernateeclipselinkopenjpaetc.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/6837083170736949584'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/6837083170736949584'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/07/jpa-and-hibernateeclipselinkopenjpaetc.html' title='JPA (and Hibernate/EclipseLink/OpenJPA/etc) in desktop apps is awful'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1759010912270577121</id><published>2010-07-20T12:10:00.001+08:00</published><updated>2010-07-20T12:11:40.577+08:00</updated><title type='text'>I'm on the verge of recommending that people by Mac laptops</title><content type='html'>&lt;p&gt;.... solely because the amount of work required for Joe Average to take your average Windows laptop and strip the crapware out is too high. &lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;I use Windows machines reasonably happily some of the time, and generally prefer them to Mac OS X for my own personal use (though I'll be on Linux whenever possible). However, to take your average machine from the state it arrives in to an acceptable state of usable, reliable service often requires:&lt;/p&gt;

&lt;ul&gt;
&lt;li&gt;A clean-install of the OS. This increasingly requires finding installation media, because OEMs don't ship Windows install DVDs anymore. Grr. If you can't do this, it's much harder to get rid of the mangled drivers and crapware apps from the machine.&lt;/li&gt;
&lt;li&gt;Finding and installing *clean* drivers, not the butchered crap many OEMs ship. Frequently one can identify the chipset a part is based on and install the appropriate unmodified driver from Intel, RealTek, nVidia, etc, but sometimes this takes some wrangling. Video card and wireless drivers are notable offenders in this area.&lt;/li&gt;
&lt;li&gt;Figuring out if any of the garbage the OEM shipped on the machine does anything useful, and installing it from the OEM's updates site (if you can find it). Vendors love to ignore standards on things like power management, building their own wacky interfaces that require their own weird drivers or control apps instead of using the standard platform interfaces like ACPI. Even if they do use the platform interfaces they often mis-implement them, then hack around the damage using replacement drivers in Windows. Grr.&lt;/li&gt;
&lt;li&gt;Deciding on and installing antivirus software. Most users need it, despite the many downsides. One has to find an antivirus package that &lt;i&gt;doesn't&lt;/i&gt; try to replace the perfectly good, reliable Windows firewall with its own crap, doesn't install buggy plugins into the browser, etc and just sticks to plain OS-level AV. Add the requirement that it not slow the machine to a glacial crawl and this can be challenging. I hate AV.&lt;/li&gt;
&lt;li&gt;Educating the user. No, don't install multiple AV packages at once. Which updates are safe to install when prompted (and in fact vital) and some clues about how to tell the difference between them and scam popups from websites. Not that it'll do any good. Etc.&lt;/li&gt;
&lt;/ul&gt; 

&lt;p&gt;Windows, and the PC platform, is ugly. But so is Mac OS X and Apple's hardware, honestly. Microsoft's stuff is more backward compatible, has much safer and more reliable updates, and in my experience more stable on good hardware. Apple's stuff is shinier and can be nicer to use, has fewer (but far from no) UI warts, and currently has a lower (but non-zero) rate of malware, malicious apps, etc. OTOH, Apple's stuff is buggy, and much more likely to break with a system update. But Microsoft systems can't be used safely by a normal user without AV software, which is a nightmare.&lt;/p&gt;

&lt;p&gt;They both suck. All computers suck. Argh!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1759010912270577121?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1759010912270577121/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/07/im-on-verge-of-recommending-that-people.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1759010912270577121'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1759010912270577121'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/07/im-on-verge-of-recommending-that-people.html' title='I&apos;m on the verge of recommending that people by Mac laptops'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4621775152803137255</id><published>2010-07-10T16:49:00.004+08:00</published><updated>2012-01-19T12:14:29.403+08:00</updated><title type='text'>How to make SCO OpenServer 5.0.5 printing work</title><content type='html'>SCO OpenServer's printing is more than a little bit broken. &lt;code&gt;lpsched&lt;/code&gt; tends to stop responding, and &lt;code&gt;lpd&lt;/code&gt; seems to decide it doesn't feel like processing any jobs after a while. Killing &lt;code&gt;lpsched&lt;/code&gt; and &lt;code&gt;lpd&lt;/code&gt; then re-starting &lt;code&gt;lpsched&lt;/code&gt; seems to solve the issue ... for a while.&lt;br /&gt;
I eventually got sick of it, and replaced the SCO print system entirely. I would've liked to use CUPS, but getting even the CUPS client to build on SCO was going to be an exciting effort in porting. The fact that one of the SCO patches broke the C compiler didn't help any, either.&lt;br /&gt;
In the end I wrote a simple Python script to relay print data to a Python server running on a modern Linux box, where the print data is handed to CUPS for printing. Since replacing the print system on the SCO box I've not had to intervene to fix SCO printing even once. &lt;br /&gt;
On the chance that others are stuck running this elderly operating system for their own scary legacy apps, I'm publishing the scripts here as a base for others to adapt to their own uses.&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
This print client program should be called instead of "lp" on your SCO box. It requires Python 1.5 from Skunkware to be installed. You should probably leave the original &lt;code&gt;lp&lt;/code&gt; in place, instead adapting your PATH or your client apps to call this by preference.&lt;br /&gt;
The command line processing in this client program is incredibly simplistic. It expects to be called with a print spool file name, and optionally -dPRINTERNAME to specify a queue. It doesn't understand any other arguments. If you need more functionality, you'll have to extend the protocol and client.&lt;br /&gt;

&lt;p&gt;&lt;a href="https://github.com/ringerc/scrapcode/blob/master/scripts/sco_openserver505_replacement_lp/printserver.py"&gt;https://github.com/ringerc/scrapcode/blob/master/scripts/sco_openserver505_replacement_lp/printserver.py&lt;/a&gt;&lt;/p&gt;

&lt;p&gt;&lt;a href="https://github.com/ringerc/scrapcode/blob/master/scripts/sco_openserver505_replacement_lp/lp.py"&gt;https://github.com/ringerc/scrapcode/blob/master/scripts/sco_openserver505_replacement_lp/lp.py&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4621775152803137255?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4621775152803137255/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/07/how-to-make-sco-openserver-505-printing.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4621775152803137255'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4621775152803137255'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/07/how-to-make-sco-openserver-505-printing.html' title='How to make SCO OpenServer 5.0.5 printing work'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-5125302628660925968</id><published>2010-06-29T15:57:00.007+08:00</published><updated>2010-10-04T09:15:28.661+08:00</updated><title type='text'>Brain-dead Bob</title><content type='html'>&lt;a href="http://www.iinet.net.au/"&gt;iiNet&lt;/a&gt;'s &lt;a href="http://www.iinet.net.au/bob/"&gt;BoB&lt;/a&gt; (actually a Belkin F1PI243EGau) isn't very bright. For a highly capable device, "he" has some bad habits that just aren't being trained out.&lt;br /&gt;&lt;br/&gt;

&lt;b&gt;UPDATE September 2010:&lt;/b&gt; Belkin/iiNet have &lt;i&gt;finally&lt;/i&gt; released a firmware with a fix for the broken IPv6 lookup / DNS delay issue. See &lt;a href="http://en-au-support.belkin.com/app/answers/detail/a_id/2498/session/L3NpZC9RNUJDV1Rhaw%3D%3D/kw/BoB%20firmware/r_id/166/sno/1"&gt;Firmware 1.2.37.86 in support answer id 2498&lt;/a&gt;. It's taken a year too long, but they got there in the end, and you don't have to ask for the Super Top Secret firmware from support anymore.&lt;br/&gt;&lt;br/&gt;

&lt;b&gt;UPDATE2 October 2010:&lt;/b&gt; Belkin's firmware update fixed AAAA records, but SRV and TXT records are still broken. Morons. Same thing: TXT and SRV queries just time out. This is less than impressive QA, especially after being informed of the AAAA issue. One would think that Belkin would have a test suite to verify basic DNS support in their products that's run as part of the release process...&lt;br/&gt;&lt;br/&gt;

&lt;a name='more'&gt;&lt;/a&gt;

Original post continues:&lt;br/&gt;
&lt;hr/&gt;

&lt;br /&gt;
Take DNS lookups. The built-in DNS forwarder doesn't understand AAAA lookups, the IPv6 address record query. This is important even if you don't use IPv6 because most modern browsers and operating systems issue IPv6 queries as well as, or before, IPv4 "A" queries when looking up hosts. They expect the IPv6 query to return promptly &lt;i&gt;or fail promptly&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
The BoB simply ignores such queries, causing the client DNS resolver to time out. This causes painfully long delays before the resolver realizes that the upstream DNS is never going to reply, and tries again with a regular "A" record lookup.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;(Update 2010-07: there is a beta firmware ETH-WAN_v1.2.36.79 that 
fixes the AAAA record DNS issue described here, though A6 lookup is still busted. It's only available by calling 
iiNet support and asking for it, you won't find it on the website.)&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
I've observed this issue with Mac OS X and with modern Linux. I don't know if it affects Windows Vista and Windows 7 yet, but expect it does, though Windows might be smart enough to disable IPv6 lookup attempts after a few failed tries, figuring (correctly in this case) that the DNS server is a wee bit dim.&lt;br /&gt;
&lt;br /&gt;
I reported this to iiNet months ago for another user, and it never got fixed. So I'm writing here to help people who have incredibly slow, glacial, painful DNS lookups - and thus web browsing, email, games, etc - on their iiNet BoB (Belkin F1PI243EGau) routers.&lt;br /&gt;
&lt;br /&gt;&lt;div style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;
$ # Lookup via BoB DNS forwarder, the DHCP-issued default: &lt;/div&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$ time dig +short -t AAAA @10.1.1.1 google.com&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;;; connection timed out; no servers could be reached&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;real&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m15.009s&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;user&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m0.004s&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;sys&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m0.000s&lt;/span&gt;&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&amp;nbsp;&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$ # lookup via iinet DNS direct for same address immediately&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$ # returns no result, just like it should.&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;$ time dig +short -t AAAA @203.0.178.191 google.com&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;real&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m0.028s&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;user&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m0.004s&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;sys&amp;nbsp;&amp;nbsp;&amp;nbsp; 0m0.004s&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Oh, it also defaults to sending a very "special" DNS 
search path over DHCP: "iiNet BoB". Because that's a valid domain name, 
and there's NO way that'll cause lots of bogus lookups for unqualified 
names...&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5125302628660925968?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.iinet.net.au/meet-bob/' title='Brain-dead Bob'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5125302628660925968/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/06/brain-dead-bob.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5125302628660925968'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5125302628660925968'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/06/brain-dead-bob.html' title='Brain-dead Bob'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-820666034707485477</id><published>2010-05-26T22:57:00.000+08:00</published><updated>2010-05-26T22:57:12.159+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='tech'/><category scheme='http://www.blogger.com/atom/ns#' term='desktop'/><title type='text'>Core 2 Duo CPU whine - workaround for Linux</title><content type='html'>Do you use Linux on a laptop and fear you're going to be driven &lt;i&gt;totally insane&lt;/i&gt; by the high-pitched whining noise (squeal/screech) coming from your Core 2 Duo CPU's badly designed cpu packaging and voltage regulation? Like proximity to your laptop gives you tinea?&lt;br /&gt;
&lt;br /&gt;
Me too. A workaround that'll cost you a little battery life (but probably not more than a few tens of minutes out of four-plus hour runtimes) is to add &lt;code&gt;processor.max_cstate=3&lt;/code&gt; to your grub command line. This turns off the problematic C4 power state of deep CPU sleep at reduced voltage, but otherwise leaves power management unaffected.&lt;br /&gt;
&lt;br /&gt;
( For Ubuntu users, &lt;code&gt;dpkg-reconfigure grub2&lt;/code&gt; will offer to let you edit the kernel command line. )&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-820666034707485477?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/820666034707485477/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/05/core-2-duo-cpu-whine-workaround-for.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/820666034707485477'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/820666034707485477'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/05/core-2-duo-cpu-whine-workaround-for.html' title='Core 2 Duo CPU whine - workaround for Linux'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-5998555158674363840</id><published>2010-05-20T23:16:00.004+08:00</published><updated>2011-03-07T23:24:10.534+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='x509'/><category scheme='http://www.blogger.com/atom/ns#' term='java'/><category scheme='http://www.blogger.com/atom/ns#' term='crypto'/><category scheme='http://www.blogger.com/atom/ns#' term='client certificate'/><category scheme='http://www.blogger.com/atom/ns#' term='postgresql'/><title type='text'>Using PKCS#12 client certificates with PgJDBC</title><content type='html'>&lt;p&gt;&lt;i&gt;(This post is a reference copy of a &lt;a href="http://postgresql.1045698.n5.nabble.com/Documentation-improvements-for-PgJDBC-td2168692.html"&gt;mailing list post I made&lt;/a&gt; explaining how to use client certificates with PgJDBC if you wanted to be able to accept user-provided PKCS#12 files.)&lt;/i&gt;&lt;/p&gt;

&lt;a name='more'&gt;&lt;/a&gt;

&lt;p&gt;Applications should use the core &lt;code&gt;SunPKIX&lt;/code&gt; trust and 
certificate managers and the default &lt;code&gt;SSLSocketFactory&lt;/code&gt; if at all 
possible, because they're flexible enough to handle pluggable 
authentication methods like hardware tokens with no app code changes. If 
you override the &lt;code&gt;SSLSocketFactory&lt;/code&gt; and provide your own trust and key 
managers you lose a lot of the power of the system.&lt;/p&gt;

&lt;p&gt;If, like mine, your app needs to offer a user a way to configure 
additional keys and trusted certs, just create your own &lt;code&gt;JKS&lt;/code&gt; or &lt;code&gt;JECKS&lt;/code&gt;
KeyStores for the trust- and key- stores during startup (if they don't 
already exist), and set the &lt;code&gt;javax.net.ssl.&lt;/code&gt; properties to point to them. 
The default Sun providers will then load those app-specific stores 
instead of the global cert- and key- stores.&lt;/p&gt;

&lt;p&gt;If the user attempts a connection to a service that requires a client 
certificate, or a service whose certificate isn't trusted, you  can 
catch the resulting exception, examine it to determine the root cause, 
and prompt the user to supply a client certificate or to install a 
trusted root cert as appropriate. Apache Commons Lang is useful here for 
inspection of deep exception chains.&lt;/p&gt;

&lt;p&gt;If you'd prefer to load a user PKCS#12 file as the keystore its self - rather than importing it into a &lt;code&gt;JECKS&lt;/code&gt; keystore - 
just specify its path in your app's &lt;code&gt;javax.net.ssl.&lt;/code&gt; system properties via 
&lt;code&gt;System.setProperty(...)&lt;/code&gt; before any SSL code is invoked. The default 
&lt;code&gt;X509KeyStore&lt;/code&gt; will be happy to load and use it. Make sure to set 
&lt;code&gt;javax.net.ssl.keyStoreType&lt;/code&gt; to &lt;code&gt;pkcs12&lt;/code&gt;.&lt;/p&gt; 

&lt;p&gt;If you really do need to replace the &lt;code&gt;X509TrustManager&lt;/code&gt; for some reason, 
use the certificate verification built-in to the JVM rather than 
re-implementing it. Create a &lt;code&gt;Set&amp;lt;TrustAnchor&amp;gt;&lt;/code&gt; from your list of trusted 
&lt;code&gt;X509Certificate&lt;/code&gt; instances from your trust store, and use it with 
&lt;code&gt;CertPathValidator&lt;/code&gt; to validate a &lt;code&gt;CertPath&lt;/code&gt; created from the 
&lt;code&gt;X509Certificate[]&lt;/code&gt; provided to the &lt;code&gt;TrustManager&lt;/code&gt;. This does proper PKIX 
validation a few lines of code.&lt;/p&gt;

&lt;p&gt;Even better is to load the default "SunPKIX" &lt;code&gt;X509TrustManager&lt;/code&gt; and 
&lt;code&gt;X509KeyManager&lt;/code&gt; and put them before your own in the appropriate array 
when creating the SSL context in your &lt;code&gt;SSLSocketFactory&lt;/code&gt;. The SSL context 
will try them in order. That way the user can still use all the 
pluggable features, but you can (eg) fall back to your &lt;code&gt;X509TrustManager&lt;/code&gt; 
if the cert isn't trusted so you can display a "add to trust store?" prompt.&lt;/p&gt;

&lt;p&gt;About the only downside with this that I've found is that there doesn't 
appear to be any way to force the SunPKIX code to re-load a &lt;code&gt;KeyStore&lt;/code&gt; 
(the trust store or the key store) after SSL has been inited, so if the 
user adds a trusted cert or key they seem to need to re-start the app. 
You can get around it by using non-public &lt;code&gt;com.sun&lt;/code&gt; api, but that's never 
a good idea.&lt;/p&gt;

&lt;p&gt;If you're having problems in this area, you should read the JSSE 
reference guide, as it'll help you understand how it all works: &lt;/p&gt;

&lt;p&gt;&lt;a href="http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html"&gt;http://java.sun.com/javase/6/docs/technotes/guides/security/jsse/JSSERefGuide.html&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5998555158674363840?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5998555158674363840/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/05/using-pkcs12-client-certificates-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5998555158674363840'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5998555158674363840'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/05/using-pkcs12-client-certificates-with.html' title='Using PKCS#12 client certificates with PgJDBC'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1134972609803054283</id><published>2010-05-05T12:31:00.003+08:00</published><updated>2010-05-05T12:56:26.441+08:00</updated><title type='text'>Using IET (ISCSI Enterprise Target) for Windows Server Backup</title><content type='html'>Windows Server Backup in win2k8 server is fantastic - it's a consistent, snapshot-based automatic backup system capable of full disaster recovery / bare metal restore. I'm not a huge fan of much of the way the Windows servers work, but the backup setup is fantastic. With one wee flaw...&lt;br /&gt;
&lt;br /&gt;
Manual backups may be made to a network share, or to a local volume then copied to a network share. Fuss free, but only with operator intervention.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, automatic scheduled backups require direct access to a drive, they won't work on a mounted NTFS volume or on a network share. This doesn't do me much good for disaster recovery, as even a USB2 or FireWire drive nearby has a good chance of being destroyed by anything that takes out my server. It rained (and hailed) in my server room last month, so I'm taking disaster recovery even more seriously, and a nearby HDD just isn't good enough.&lt;br /&gt;
&lt;br /&gt;
I could run a FireWire 800 drive over cat5e to the near-site backup location, but that's surprisingly expensive to do, especially as I want redundant storage to protect against pesky HDD failures. I have a perfectly good Ethernet-connected Linux server with a 10TB RAID array running Bacula to back up everything else on thge network, and I'd prefer to just use it for Windows Server Backup too.&lt;br /&gt;
&lt;br /&gt;
The solution: Win2k8 has a built-in iSCSI initiator. Simply turn the backup server into an iSCSI target, then use Windows 2008's built-in iSCSI initiator to connect to it so Windows Server Backup sees it as a local disk and can write backups to it. This turns out to be astonishingly easy, at least on an Ubuntu system.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: large;"&gt;Security notice&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
The following configuration does NOT authenticate the windows server to the iSCSI target via iSCSI mutual authentication, so it may be possible to trick the server into backing up onto a different server and "steal" the backup. It also passes the actual backup over the network in the clear, as it doesn't use IPSec. You may wish to address those limitations in your implementation.&lt;br /&gt;
&lt;br /&gt;
It would be a very good idea to enable mutual authentication, but by time of writing I was 
unable to get it working. The win2k8 iSCSI initiator complained about 
secret length, even though the provided secret appeared to match its 
criteria and had been entered in the main part of the control panel 
where the mutual authentication secret is expected. Similarly, IPSec 
wouldn't be a bad idea to prevent your backups passing over the network 
in the clear. &lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: large;"&gt;Configuring the iSCSI target &lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
First, install the ISCSI Enterprise Target software (IET):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;apt-get install iscsitarget

&lt;/pre&gt;
Now provision a volume to export as a target. This may be a local raw disk or partition, a logical volume provided by LVM, or even a great honking file on one of your mounted file systems. I'm using LVM, so I'll just allocate a logical volume:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;lvm lvcreate -n winimagebackup -L 300G backupvg 

&lt;/pre&gt;
There is no need to format the volume; Windows does that. Just export it via iSCSI by adding a suitable target entry to &lt;code&gt;/etc/ietd.conf&lt;/code&gt; (it might be &lt;code&gt;/etc/iet/ietd.conf&lt;/code&gt; on your system):&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;&amp;nbsp;Target iqn.2010-01.localnet.backup:winimagebackup
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Lun 0 Path=/dev/backup/winimagebackup_iscsi,Type=blockio
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; Alias winimagebackup
&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp;&amp;nbsp; IncomingUser iqn.1991-05.com.microsoft:winhostname xxxx&lt;/pre&gt;
&lt;br /&gt;
See the comments in the default &lt;code&gt;ietd.conf&lt;/code&gt; and the &lt;code&gt;ietd.conf&lt;/code&gt; man page for details on this. In brief:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt; Change "localnet.backup" to the reversed host and domain name of your target server's name (mine is called "backup.localnet").&amp;nbsp;&lt;/li&gt;
&lt;li&gt;Change "IncomingUser" to the user name you want the Win2k8 server to have to give to be permitted to connect, and "xxxx" to the password you wish to require. By default a 2k8 box will give the above user name, with "winhostname" replaced with the win2k8 box's hostname.&lt;/li&gt;
&lt;li&gt;Set the path after "Path" to the location of your storage.&lt;/li&gt;
&lt;li&gt;If you're using a file, you may need to specify "fileio" instead of "blockio" as the Type.&lt;/li&gt;
&lt;/ul&gt;
Restart ietd, and you're ready to connect the 2k8 box.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: large;"&gt;Connecting 2k8 to the iSCSI Target&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
Connecting to the target from win2k8 is similarly trivial. In the iSCSI Target control panel, in the "discovery" tab enter the dns name or ip of the target.
 Do not configure authentication (unless you've deviated from the &lt;code&gt;ietd.conf&lt;/code&gt;above), just accept the dialog.&lt;br /&gt;
&lt;br /&gt;
The server should appear in "target portals" and no error 
should be displayed. If successful, go to the "targets" tab, where you 
should see a target named "winimagebackup". Click "Log on..." to connect
 to it. Check the option to restore the connection at boot-time. Under 
Advanced, Configure CHAP authentication, using the password given in 
ietd.conf for IncomingUser under the target winimagebackup. Do not enable mutual authentication*. Accept 
the dialog, and the status of the volume should change to "connected".&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: large;"&gt;Configuring Windows Server Backup&lt;/span&gt; &lt;br /&gt;
&lt;br /&gt;
You're now ready to use Windows Server Backup with the volume. You do not need to format it under the disk mmc snapin before use. Just fire up Windows Server Backup and click "Backup Schedule", then follow the prompts, picking the iSCSI target as the backup storage when prompted.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1134972609803054283?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1134972609803054283/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/05/using-iet-iscsi-enterprise-target-for.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1134972609803054283'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1134972609803054283'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/05/using-iet-iscsi-enterprise-target-for.html' title='Using IET (ISCSI Enterprise Target) for Windows Server Backup'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-847384990985498400</id><published>2010-05-03T13:23:00.001+08:00</published><updated>2010-08-25T18:09:21.298+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Ubuntu Lucid and LTSP thin clients - wow</title><content type='html'>For a while I've been suspecting that most people who do any Linux-based GUI develpoment neglect remote X11 and thin client considerations. Performance has been decreasing slowly and painfully, and more and more workaround have been needed to get systems to behave.&lt;br /&gt;
&lt;br /&gt;
Well, no longer. The new Ubuntu release, lucid, with the latest gtk etc absolutely screams along. I'm seriously astonished that remote X11 can be this fast, given the round-trip-happy nature of the toolkits and the protocol's flaws. It's a real pleasure to use.&lt;br /&gt;
 &lt;br /&gt;
 To everyone involved in gtk development - &lt;b&gt;thank you!&lt;/b&gt;. Doubly so to those who looked into my bug reports on specific areas where gtk used excessive network round trips, particularly the Evolution compose window bug.&lt;br /&gt;
&lt;br /&gt;
By the way, for those of you who deploy LTSP, another thing that'll make a huge difference to performance is to make sure your LTSP clients are on a private VLAN and then enable direct X11 communication between client and server with &lt;code&gt;LDM_DIRECTX = Y&lt;/code&gt; in &lt;code&gt;lts.conf&lt;/code&gt;. With this option you still use ssh to login and establish a session (so there's no godawful XDMCP to fight), but only the ssh login is tunneled and encrypted. During session setup, DISPLAY is redirected to point directly to the client's listening X server. This offers a huge performance boost, especially to slower/older clients without onboard hardware crypto engines. (Oddly, the 600MHz Via C3 boxes outperform the Intel Core 2 boxes when all X11 comms are encrypted).&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-847384990985498400?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/847384990985498400/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/05/ubuntu-lucid-and-ltsp-thin-clients-wow.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/847384990985498400'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/847384990985498400'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/05/ubuntu-lucid-and-ltsp-thin-clients-wow.html' title='Ubuntu Lucid and LTSP thin clients - wow'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4856915352482649902</id><published>2010-04-30T11:09:00.001+08:00</published><updated>2010-08-25T18:09:26.314+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Splash screens are a plague upon Linux systems</title><content type='html'>How many splash screen tools can you remember appearing in at least one released version of at least one distro?&lt;br /&gt;
&lt;br /&gt;
Too many, I bet, and all of them buggy.&lt;br /&gt;
&lt;br /&gt;
In recent Ubuntu alone both usplash and plymouth have come to plague users. These tools purport to make bootup "friendly", but in fact:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Cover up important messages/warnings&lt;/li&gt;
&lt;li&gt;Make recovering from issues during boot well-nigh impossible&lt;/li&gt;
&lt;li&gt;Make boot-time fsck fragile and hard to interact with&lt;/li&gt;
&lt;li&gt;Interact poorly with graphics drivers (xorg or kms) &lt;/li&gt;
&lt;li&gt;Are much, much too hard to disable&lt;/li&gt;
&lt;/ul&gt;
Unfortunately, they don't even have a standard mechanism for disabling them. "nosplash"&amp;nbsp; on the kernel command line used to work most of the time, but Plymouth displays a splash screen if the sequence "splash" appears anywhere in the kernel command line - including mid-word. It'll merrily accept "nosplash" as a request for a splash screen. With plymouth you must instead omit "splash" from the kernel command line entirely - and woe betide you if something else you need happens to include those characters!&lt;br /&gt;
&amp;nbsp; &lt;br /&gt;
Even better, Plymouth can't be uninstalled without ripping the guts out of an Ubuntu lucid system completely. It's wired deep into the package dependency system.&lt;br /&gt;
&lt;br /&gt;
Argh. I'm coming to dread distro upgrades because I have to learn how to get rid of the blasted splash screen all over again. If only they'd stay uninstalled...&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4856915352482649902?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4856915352482649902/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/splash-screens-are-plague-upon-linux.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4856915352482649902'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4856915352482649902'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/splash-screens-are-plague-upon-linux.html' title='Splash screens are a plague upon Linux systems'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-7231690706265278774</id><published>2010-04-24T14:37:00.000+08:00</published><updated>2010-04-24T14:37:46.278+08:00</updated><title type='text'>Intel, where's my EFI Network/USB/FireWire Target Disk Mode?</title><content type='html'>I'm torn.&lt;br /&gt;
&lt;br /&gt;
I really don't like working with Mac OS X (or Windows, for that matter - I'm a Linux user by preference) .... but I &lt;i&gt;love&lt;/i&gt; some features of Apple's hardware. The build quality and disk bays of the Mac Pro, for example. But above all else, what I love and envy about macs is ... &lt;i&gt;&lt;b&gt;Target Disk Mode&lt;/b&gt;&lt;/i&gt;. It's a service tech and sysadmin's dream.&lt;br /&gt;
&lt;br /&gt;
Intel likes to make a lot of fuss about all its fancy in-chipset managment features, yet it seems to lack that one most crucial and handy feature - a Target Disk Mode equivalent. C'mon Intel, you can do better than this! You can not only implement FireWire target disk, but Ethernet-based iSCSI Target Disk for true sysadmin heaven. For bonus points, add TPM support so only authorized service techs for the company can get in, and use the built-in network management features to let admins remote-reboot a machine into target mode.&lt;br /&gt;
&lt;br /&gt;
Sadly, I suspect the reason we're not all using this is that the "good" (cough, cough) old PC BIOS is still malingering, and failing to decently give way to EFI/OpenFirmware/whatever like it should.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-7231690706265278774?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7231690706265278774/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/intel-wheres-my-efi-networkusbfirewire.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7231690706265278774'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7231690706265278774'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/intel-wheres-my-efi-networkusbfirewire.html' title='Intel, where&apos;s my EFI Network/USB/FireWire Target Disk Mode?'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-5357222976698658916</id><published>2010-04-23T15:40:00.003+08:00</published><updated>2010-08-25T18:09:32.118+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><title type='text'>Use Linux software RAID? Schedule periodic RAID scrubbing or lose your data</title><content type='html'>This post is bought to you by the fun of unnecessary wasted time and
work rebuilding a server after a double-disk RAID array failure. RAID
scrubbing is essential - and is supported by Linux's software
RAID, &lt;i&gt;but not used without explicit user action&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
Linux's `md' software RAID isn't unique in this, but as its use is so wide spread it's worth singling out. No matter what RAID card/driver/system you use, you &lt;i&gt;need&lt;/i&gt; to do raid scrubbing. Sometimes RAID vendors call this a "patrol read", "verify" or "consistency check", but it's all generally the same thing.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Why you should scrub your RAID arrays regularly&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
I
had a disk fail in the backup server (some time ago now). No hassle - replace it,
trigger a rebuild, and off I go. Unfortunately, during the rebuild
another disk was flagged as faulty, rendering the array useless as it
had a half-rebuilt spare and a second failed drive.&lt;br /&gt;
&lt;br /&gt;
You'd think
the chances of this were pretty low, but the trouble is that the second
failed drive will have developed just a couple of defective sectors (a
SMART check confirms this) that weren't detected because those sectors
weren't being read. Until the drive was sequentially read during the
rebuild, that is.&lt;br /&gt;
&lt;br /&gt;
To reduce the chance of this, you can
periodically verify your arrays and if bad sectors are discovered,
attempt to force them to be remapped (by rewriting them from redundant
data) or failing that fail the drive. This will also detect any areas where different disks disagree on what the correct data is, helping you to catch corruption caused by failing hardware early.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, Linux's software
RAID doesn't do this automatically.&lt;br /&gt;
&lt;br /&gt;
A simple shell script like this, dropped in /etc/cron.weekly and flagged executable, will save you a LOT of hassle:&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;#!/bin/bash
for f in /sys/block/md? ; do 
    echo check &amp;gt; $f/md/sync_action
done&lt;/pre&gt;
&lt;br /&gt;
Make sure to TEST YOUR EMAIL NOTIFICATION from mdadm, too. If a drive fails and you never get notified, you're very likely to lose that data the moment anything else goes wrong. &lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Use S.M.A.R.T too&lt;/b&gt; &lt;br /&gt;
&lt;br /&gt;
For extra protection from failure, install smartmontools and configure smartd to run regular "long" tests of all your RAID member disks, so you're more likely to discover failing disks early.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, many consumer-oriented disk firmwares lie to the host and try to cover up bad sectors and read errors - probably to reduce warranty costs. Manufacturer's disk tools tend to do the same thing. Some even seem to lie during S.M.A.R.T self-testing, re-allocating sectors as they find bad ones and then claiming that everything is fine. In fact, I've actually had a consumer SATA drive that &lt;i&gt;can't even read sector 0&lt;/i&gt; return PASSED when queried for a SMART general health check, though at least it failed an explicitly requested self-test.&lt;br /&gt;
&lt;br /&gt;
My point is that SMART testing alone isn't sufficient to ensure your disks are trustworthy, you really &lt;i&gt;need&lt;/i&gt; to use a redundant array with some kind of parity or data duplication and do proper RAID scrubbing. And, of course, &lt;b&gt;good backups.&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
If you use "RAID friendly" disks (usually the same physical drive with honest firmware and a much bigger price tag) you shouldn't have as many issues with SMART self-tests.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5357222976698658916?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5357222976698658916/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/use-linux-software-raid-cron-raid.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5357222976698658916'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5357222976698658916'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/use-linux-software-raid-cron-raid.html' title='Use Linux software RAID? Schedule periodic RAID scrubbing or lose your data'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1735914760749820</id><published>2010-04-23T14:49:00.004+08:00</published><updated>2010-04-23T16:03:18.739+08:00</updated><title type='text'>Battery backed cache for Linux software raid (md / mdadm)?</title><content type='html'>&lt;a href="http://www.linuxfoundation.org/collaborate/workgroups/linux-raid"&gt;Linux's software RAID implementation&lt;/a&gt; is absolutely wonderful. Sufficiently so that I no longer use hardware RAID controllers unless I need write caching for database workloads, in which case a battery backed cache is a necessity. I'm extremely thankful to &lt;a href="http://neil.brown.name/blog/mdadm"&gt;those involved&lt;/a&gt; in its creation and maintenance.&lt;br /&gt;
&lt;br /&gt;
Alas, when I do need write-through mode (write caching), I can't use mdadm software RAID. There's actually no technical reason hardware already on the market (like "RAM Drives") can't be used as write cache, it's just that the Linux `md' layer doesn't know how to do it.&lt;br /&gt;
&lt;br /&gt;
I say "it's just that" in the same way that I "just" don't know how to fly a high performance fighter jet with my eyes closed. Being possible doesn't make it easy to implement or practical to implement in a safe, reliable and robust way.&lt;br /&gt;
&lt;br /&gt;
This would be a really interesting project to tackle to bring software RAID truly on par with hardware RAID, but I can't help wondering if there's a reason nobody's already done it.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Are you wondering what write caching is, why write caching is so important for databases, or how on earth you can safely write-cache with software RAID? Read on...&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;WHAT IS WRITE CACHING?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Write caching (often called "write back mode") refers to a storage device that&amp;nbsp; copies data
written to it by the host OS into some kind of fast non-disk storage and then tells the operating
system that data has hit the disk before it truly has. Not only does
this provide incredibly fast fsync()s, but it also lets the storage
device intelligently bunch up many small writes into fewer bigger
writes to nearby disk regions, massively speeding up random write
speeds. If your workload is mostly random writes with frequent fsync()s then you can expect speedups of tens or hundreds of times when enabling write caching.&lt;br /&gt;
&lt;br /&gt;
For write caching to be safe, the
fast storage used for cache has to be persistent even in the face of system power
loss, sudden OS reset, removal of the RAID card/hard drive from the
computer, etc. If it is not, any sort of failure will leave your data in a horrifying half-written half-lost state made even worse by the out-of-order writes done to improve random write speeds. It doesn't bear thinking about.&lt;br /&gt;
&lt;br /&gt;
So - all it takes to make write caching perfectly safe is robust, persistent storage for the cache, and with such storage you can achieve orders of magnitude improvements in performance. Sound interesting?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;WHY USE WRITE CACHING FOR DATABASES?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Database workloads benefit from write caching because they tend to require strict guarantees about what has hit disk. They also tend to do a lot of writes across random and widely scattered parts of the storage. Because of their need to ensure ordering of their writes, they tend to force those random writes to disk in order, preventing the RAID controller from accumulating them and combining them into bigger more ordered writes.&lt;br /&gt;
&lt;br /&gt;
Given how bad rotational storage is at random writes, this is pretty much a worst-case workload for storage performance.&lt;br /&gt;
&lt;br /&gt;
Even if your particular workload doesn't care about a few lost transactions so it doesn't require the database to promise that data has hit disk before returning from COMMIT, the database its self tends to require strictly ordered writes to avoid severe corruption of its storage in the case of unexpected power loss or reboot. This is particularly true for database systems like PostgreSQL that do write-ahead logging, a crash-integrity strategy somewhat like file system data journaling.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;WHY DO YOU NEED A HARDWARE RAID CONTROLLER FOR WRITE CACHING?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Pure software RAID working only hard disks cannot provide write caching. It can cache writes in RAM, but if the OS suddenly crashes, the user hard-resets the machine, or the power goes, your data is in a truly messy half-written, half-lost state. The software raid system simply has no suitable storage for the write cache.&lt;br /&gt;
&lt;br /&gt;
Hardware RAID controllers generally use ordinary DDR/DDR2 DIMMs (standard PC memory) plugged into the controller as their cache. Because this memory is erased when it loses power, they also include a small battery on the RAID card that maintains power to the memory for many hours, giving you time to get power back to the system before the cache contents are lost and your data is corrupted. When power is restored, the RAID controller resumes where it left off, writing any data it finds in the battery-backed cache memory.&lt;br /&gt;
&lt;br /&gt;
There is nothing that inherently prevents software RAID from using the same strategy. All it needs is somewhere persistent to put the cache. There's an obvious way to provide such cache. Exactly the same option used by hardware RAID controllers can be used for software RAID - a DIMM or two on a PCI Express card or even a SATA "RAM drive" would do, so long as it had a battery to keep that memory alive across power outages.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
So, in fact, &lt;i&gt;you don't need a hardware RAID controller for write caching at all, you just need somewhere to put the cache.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;IF SOFTWARE RAID CAN DO WRITE CACHING WITH SUITABLE CACHE MODULES, WHERE ARE THOSE MODULES?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Given the huge costs of true hardware RAID controllers, especially
with the large premium charged by most vendors for an add-on battery
backup unit, you'd expect that there'd be plenty of options out there, and that software RAID implementations would take advantage of them.&lt;br /&gt;
&lt;br /&gt;
Yet, oddly, the only software RAID implementations I can find that support write-through (caching) mode with a battery-backed cache are vendor-specific "host raid" implementations embedded in drivers for "fakeraid" cards. These cards pretend to be hardware RAID, but really do the work in a driver in the operating system. The hardware is just a plain old SATA or SAS controller with an on-board BIOS Option ROM that understands enough of the RAID layout that it can read the boot loader and get the OS up and running to the point where the drivers load. It's easy for the vendors of these cards to add BBU / write cache support, since the software RAID is tightly bound to particular hardware that they can just add a RAM slot and a battery to. Unfortunately, they tend to add a rather impressive price tag as well.&lt;br /&gt;
&lt;br /&gt;
I find myself wondering where the "write-through mode cards" for the built-in software RAID features in Linux, Mac OS X and Windows are. The success of fakeraid cards with BBU shows there's a market, so where are the devices?&lt;br /&gt;
&lt;br /&gt;
Well, they're &lt;a href="http://techreport.com/articles.x/16255"&gt;right&lt;/a&gt; &lt;a href="http://techreport.com/articles.x/9312"&gt;here &lt;/a&gt;. "RAM drives" are pretty much perfect for the job. They're fast, battery backed storage that's just what's needed. It'd be nice if they offered direct PCIe access to the memory via an onboard ACHI-compatible host interface (or custom driver) rather than having a real SATA interface, but that's far from vital. &lt;br /&gt;
&lt;b&gt; &lt;/b&gt;&lt;br /&gt;
Unfortunately, the RAID implementations in Windows, Mac OS X and Linux don't know how to use persistent cache like this to enable write-through mode! It's out there, cheap, and ready for the taking, but the software just doesn't support it. md/mdadm support storing a write-intent bitmap for faster crash recovery, but it's just not close to the same.&lt;br /&gt;
&lt;br /&gt;
&lt;i&gt;Wouldn't it be awfully handy if md/mdadm supported an external cache module? Software RAID at hardware-RAID speeds.&lt;/i&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;An aside: WHY NOT USE AN SSD FOR WRITE CACHE STORAGE?&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
"Why not use an SSD?" you ask? Good question. Their random write performance is awful, but if you use them as a cyclic write buffer they're wonderful, and they'd be a near-perfect choice as they don't require backup batteries. Unfortunately, they're generally incredibly bad at doing lots of small writes, even if those writes are sequential, because of their erase blocks. Only high-end SSDs with internal cache and a big capacitor to let them finish writing after power-loss would be suitable, and they're way more expensive than a board with some DIMMS and a battery would be. You're better off with hardware RAID cards or a dedicated cache module for software RAID.&lt;br /&gt;
&lt;br /&gt;
Before you say "Intel's X-25 series address the issues with small writes" ... try getting one of them, doing a bunch of writes, and yanking the power. See if what you see when you restore the power makes any sense at all. Those drives apparently don't have the juice to finish writing their internal cache out when they lose power - they're very much like software RAID in write-through mode without a BBU - ie incredibly unsafe. I've heard horror stories about them on the PostgreSQL mailing list, and wouldn't use them except on a machine with a really good UPS. All Intel would need to add would be a great honking capacitor or an itsy little backup battery to give it enough juice to do an emergency write-out, but presumably for cost reasons the devices don't offer anything like that.&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;RELATED LINKS&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
very experimental write-back cache support patches that only use unsafe, non-BBU main memory as cache: http://lwn.net/Articles/229976/&lt;br /&gt;
&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1735914760749820?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1735914760749820/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/battery-backed-cache-for-linux-software.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1735914760749820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1735914760749820'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/battery-backed-cache-for-linux-software.html' title='Battery backed cache for Linux software raid (md / mdadm)?'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-7919165495150346517</id><published>2010-04-16T15:03:00.001+08:00</published><updated>2010-04-16T15:03:52.610+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='tech'/><category scheme='http://www.blogger.com/atom/ns#' term='backup'/><title type='text'>LVM vs VSS - it's no contest</title><content type='html'>&lt;br /&gt;
Richard Jones &lt;a href="http://dcsblog.burtongroup.com/data_center_strategies/2009/02/linux-wheres-your-vss.html"&gt;writes&lt;/a&gt; about Microsoft's Virtual Shadow Copy Service (VSS, not to be confused with Visual Source Safe), and laments the lack of any equivalent or good alternative on Linux servers.&lt;br /&gt;
&lt;br /&gt;
I couldn't agree more, but there's more to it than what he focuses on. Application consistency is in fact the least of the problems.&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: large;"&gt;APP CONSISTENCY&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
App consistency and pausing is missing in Linux, but is in principle not hard to introduce. It is something where D-BUS can be very useful, as it provides another piece of the puzzle in the form of inter-application and application&amp;lt;-&amp;gt;system signaling. The trouble is that, like with most things in the Linux-related world, some kind of agreement needs to be reached by interested parties.&lt;br /&gt;
&lt;br /&gt;
All it really takes for app consistency is two d-bus events:&lt;br /&gt;
&lt;ol&gt;
&lt;li&gt;"Prepare for snapshot - pause activity, fsync, etc."&lt;/li&gt;
&lt;li&gt;"Snapshot taken/failed/cancelled, safe to resume"&lt;/li&gt;
&lt;/ol&gt;
... though there needs to be some aliveness-checking and time-limiting in place so that a broken application can't cause indefinite outages in other apps by not responding to a snapshot message.&lt;br /&gt;
&lt;br /&gt;
Off the top of my head, products that'd benefit from this include Cyrus IMAPd, PostgreSQL, MySQL, any other database you care to name...&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: large;"&gt;... IS ONLY A SMALL PART OF THE PUZZLE&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
As compared to VSS, using dm-snapshot (LVM snapshots) on Linux suffers from a number of significant deficiencies and problems:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;Snapshots require you to use LVM. &lt;a href="http://lwn.net/Articles/283161/"&gt;LVM doesn't support write barriers&lt;/a&gt;, so file systems must use much slower full flushes instead.&lt;/li&gt;
&lt;li&gt;Snapshots are block-level not file-system-level, so the file system isn't aware of the snapshot being taken.&lt;/li&gt;
&lt;li&gt;Because snapshots are block-level and not filesystem-aware, the snapshot must track even low-level file system activity like file defragmentation, erasing free space, etc. This means they grow fast and have a higher impact on system I/O load.&lt;/li&gt;
&lt;li&gt;Accessing a snapshot requires mounting it manually to another location and faffing around with the changed paths. There's no way for an app to simply request that it sees a consistent point-in-time view of the FS instead of the current state, as in VSS. This is clumsy and painful especially for backups and the like.&lt;/li&gt;
&lt;li&gt;The file system being mounted has to be able to cope with being mounted read-only in crashed state - it can't do journal replay/recovery etc. LVM doesn't even give the *filesystem* a chance to make its self consistent before the snapshot. Some file systems, like XFS, offer manual pause/resume commands that may be issued before and after a snapshot is taken to work around this.&lt;/li&gt;
&lt;li&gt;Snapshots don't age out particularly gracefully. You have to guess how much space you'll need to store changes, as LVM isn't smart enough to just use free space in the VG until it runs out. The snapshot's change tracking space is reserved and can't be used for anything else (even another snapshot) until the snapshot is freed. If you over-estimate you can have fewer snapshots and need to keep more free space around. If you under-estimate your snapshot may die before you finish using it. Argh!&lt;/li&gt;
&lt;/ul&gt;
So: even with the ability to pause app activity, there's unfortunately a lot more to be done at the LVM level before anything even half as good as VSS is possible. LVM has been progressing little, if at all, for quite a few years now and some of these problems (such as the namespace issues and lack of FS integration) aren't even practical to solve with a block-driver level approach like LVM's.&lt;br /&gt;
&lt;br /&gt;
At this point, one can only hope that BTRFS can do a better job, so that we can switch to runnning btrfs on mdadm raid volumes and breathe a sigh of relief.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-7919165495150346517?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://dcsblog.burtongroup.com/data_center_strategies/2009/02/linux-wheres-your-vss.html' title='LVM vs VSS - it&apos;s no contest'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/7919165495150346517/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/richard-jones-writes-about-microsofts.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7919165495150346517'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/7919165495150346517'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/richard-jones-writes-about-microsofts.html' title='LVM vs VSS - it&apos;s no contest'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4344036994905453458</id><published>2010-04-16T15:03:00.000+08:00</published><updated>2010-04-16T15:03:12.451+08:00</updated><title type='text'>The Australian Maginot Line - because it worked great last time</title><content type='html'>Peter Thrush of ICANN recently &lt;a href="http://www.misaustralia.com/viewer.aspx?EDP://1246229275673&amp;amp;section=news" id="link_75"&gt;commented&lt;/a&gt; that the Australian Internet Filter proposal is akin to the Maginot Line of WWII French fame. We all know how well that worked.&lt;br /&gt;
&lt;br /&gt;
This
is a surprisingly good analogy. The Maginot line presumed that the
attacker would do what was expected of them, and wouldn't take the
defenses into consideration when planning what they were doing. In much
the same way, the Australian internet filter presumes that if it blocks
what people do now, they won't change their behavior to circumvent the
blocking with trivially available tools and techniques like encryption,
tunneling, outside proxies, etc.&lt;br /&gt;
&lt;br /&gt;
We &lt;i&gt;already&lt;/i&gt; know that's
an invalid assumption - not only is it rather contrary to general human
nature, but it's being seen over and over in China with the Great
Firewall. This despite the fact that China's Great Firewall is much
more restrictive than Australia's is ever likely to be even under the
most moralistic, conservative, idiotic government. Let's not forget,
also, that in China it can be unhealthy to circumvent blocks that
prevent you from accessing or posting information that's not meant to
get around ... something I don't see becoming the case here.&lt;br /&gt;
&lt;br /&gt;
So
- in much more hostile circumstances, people still just waltz through
the Great Firewall. Heck, I've done it myself - I had a workmate in
China who needed unfiltered access, and it was the work of a few
seconds to help him set up an encrypted SSH tunnel to a proxy on work's
servers from which he could get to whatever websites he liked and do so
undetectably. It's not even possible to tell that the encrypted data is
web browsing data rather than something else.&lt;br /&gt;
&lt;br /&gt;
Once again, it's
clear that the only way the internet filter can work is if it's a
whitelist. If a site isn't approved, you can't access it. If a protocol
can't be inspected and content-filtered, it's blocked. No encryption of
any sort may be used. Even that's imperfect due to cracking of
whitelisted sites and use of them for proxies, etc.&lt;br /&gt;
&lt;br /&gt;
It's a dumb idea. Why are we still wasting time and taxpayer money on such blithering idiocy?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4344036994905453458?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4344036994905453458/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/australian-maginot-line-because-it.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4344036994905453458'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4344036994905453458'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/australian-maginot-line-because-it.html' title='The Australian Maginot Line - because it worked great last time'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4000019324938870227</id><published>2010-04-16T15:01:00.001+08:00</published><updated>2010-04-16T15:01:35.250+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='notetoself'/><category scheme='http://www.blogger.com/atom/ns#' term='tech'/><category scheme='http://www.blogger.com/atom/ns#' term='music'/><title type='text'>Scripts to add replaygain tags to a flac collection</title><content type='html'>If, like me, you like to be able to play your music without
constantly having to lunge for the volume control, and you store the
master copy of your (ripped) CD collection in &lt;acronym title="Free Lossless Audio Codec"&gt;FLAC&lt;/acronym&gt; format you might be interested in a quick way to (re)calculate all the &lt;a href="http://replaygain.hydrogenaudio.org/" id="link_50"&gt;ReplyGain&lt;/a&gt;
tags on your music. I'll also go into how to automatically create
smaller MP3 copies of the files with the replaygain bias "burned in" so
stupid players still get the volume right.&lt;br /&gt;
Finally, there's another little script here that can be used to fix
up issues like inconsistent artist naming. It only does "The X" -&amp;gt;
"X, The" at the moment, but it's easy enough to extend.&lt;br /&gt;
Read on if you're happy with shell scripting (and a Linux or advanced Mac OS X user). &lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
&lt;a href="http://www.blogger.com/post-edit.g?blogID=427258967255390991&amp;amp;postID=4000019324938870227" name="cutid1"&gt;&lt;/a&gt;

The following shell snippet will scan the target directory, in this case &lt;code&gt;$HOME/flac&lt;/code&gt;, and calculate ReplayGain for all &lt;code&gt;.flac&lt;/code&gt;
files within it. It assumes that each directory containing any flac
files may be treated as an album for purposes of ReplayGain tagging.
None of the other track metadata is touched and the audio stream is not
altered in any way.&lt;br /&gt;
You'll need the flac tools installed since &lt;code&gt;metaflac&lt;/code&gt; is what we'll use to calculate and apply replaygain tags. On an Ubuntu system the flac tools are in the (surprise!) &lt;code&gt;flac&lt;/code&gt; package.&lt;br /&gt;
Copy this to &lt;code&gt;gain.sh&lt;/code&gt;:&lt;br /&gt;
&lt;pre&gt;#!/bin/bash
if test $# -lt 1 ; then
        echo "Usage: $0 target-dir [target-dir [target-dir [ ... ] ] ]"
        exit 1
fi
find "$@" -type d -execdir \
 bash -c 'echo Scanning `pwd`/{}; if ls "{}"/*.flac &amp;gt;&amp;amp;/dev/null ; then metaflac --add-replay-gain "{}"/*.flac; fi' \
 \;&lt;/pre&gt;
Then invoke as &lt;code&gt;sh gain.sh $HOME/flac&lt;/code&gt; (assuming &lt;code&gt;$HOME/flac&lt;/code&gt; is the directory you want to recursively retag). You may specify more than one target directory to scan.&lt;br /&gt;
Once you've tagged your flac files with track and album level
ReplayGain information you can play music from different publishers, in
different styles, and of different ages without some of it being almost
inaudible and other tracks mind blowingly loud. Your player must
support ReplayGain, of course.&lt;br /&gt;
Rhythmbox (if you use it) doesn't apply ReplayGain by default, but does support it. To enable:&lt;br /&gt;
&lt;pre&gt;gconftool-2 --type bool --set /apps/rhythmbox/use_replaygain 1&lt;/pre&gt;
No idea about iTunes and friends - can they even play flac?&lt;br /&gt;
Handily, you can also bulk convert your flac formatted collection to
other formats (say, mid-quality MP3 or vorbis) for something like a
portable player or a laptop with a small disk and in the process "burn
in" the volume bias indicated by the ReplayGain tagging. That way
players that don't understand per-track preamp, replaygain, etc will
still produce reasonable volume levels without you having to re-encode
your tracks from a lossy source and get the usual horrid results.&lt;br /&gt;
The &lt;code&gt;lame&lt;/code&gt; mp3 encoder automatically calculates
replaygain, but it doesn't handle per-album gain. You can instruct it
not to apply replaygain and instead apply the preamp recorded in the
flac metadata with the undocumented flac decoder option &lt;code&gt;--apply-replaygain-which-is-not-lossless&lt;/code&gt;. There may be better ways; ideally you could specify an encoding preamp to the mp3 encoder.&lt;br /&gt;
This script will scan the directory &lt;code&gt;flac&lt;/code&gt; in the current directory, and write mp3 files to the &lt;code&gt;mp3&lt;/code&gt;
directory under the current directory. Output will be medium/high
quality VBR MP3 with gain burned in to the audio stream and proper ID3
tags included. You can run one instance of the script per CPU core for
best performance, as the instances will communicate and make sure not
to step on each others' toes or duplicate work. You will need the &lt;code&gt;lame&lt;/code&gt;
MP3 encoder installed to use this script. The script will skip files
that already exist in the target directory, so you can re-run it after
ripping your latest CD acquisitions and it'll only encode the new files.&lt;br /&gt;
Of course, only a &lt;b&gt;crazy person&lt;/b&gt; would run this without first making backups of their music collection. Then again, you'd have backups already, right? Right? &lt;i&gt;Sigh.&lt;/i&gt;&lt;br /&gt;
Save to &lt;code&gt;flac2mp3.sh&lt;/code&gt; and &lt;code&gt;chmod a+x flac2mp3.sh&lt;/code&gt;:&lt;br /&gt;
&lt;pre&gt;#!/bin/bash

if test "$#" -lt 1 ; then
        echo "Usage: $0 target-directory"
        exit 1
fi

function gettag() {
        awk -F = /^${1}=/' { print $2; }' /tmp/tags-$$
}

function rmlock() {
        trap '' EXIT
        rm -f "${lockfn}"
        exit 2
}

if test "$1" = "convert" ; then
        # Called by self to convert a track. $2 is source track name,
        # $3 is source dir prefix, and $4 is target dir prefix.
        #
        # The track metadata is extracted, then the track is decoded with replaygain
        # bias applied during decoding. High quality VBR MP3 encoding is then done
        # on the adjusted audio stream. The end result is a decent quality MP3
        # that will play at a sane volume even on players that don't understand
        # ReplayGain MP3 headers/tags.
        ifn="$2"
        ofn="${2/.flac/.mp3}"
        ofn="${ofn/$3/$4}"
        if ! test -a "${ofn}" || test "${ifn}" -nt "${ofn}" ; then
                # The file is either absent in the target directory or is older
                # then the source file.
                #
                # Check to see if another instance of the script is currently encoding
                # this file, and if not, start encoding it ourselves. There's a race here,
                # in that another instance may start work on the file between when we check
                # and when we start work, but we don't actually care as the worst outcome
                # is a little wasted CPU time.
                #
                # We'll use a lock file in the target directory to indicate activity.
                #
                odir="`dirname "${ofn}"`"
                lockfn="${odir}/.`basename "${ofn}"`.lock"
                if test -e "${lockfn}" 2&amp;gt;/dev/null ; then
                        # Appears locked by another instance. Is it still alive?        
                        if kill -0 $(cat "${lockfn}"); then
                                echo "Skipping ${ifn} - locked by another instance"
                                exit 1
                        else
                                echo "Clearing stale lock file for ${ofn}"
                                rm "${lockfn}"
                        fi
                fi
                # Notify any concurrent instances that we're working on this file
                trap rmlock EXIT SIGTERM SIGINT SIGQUIT SIGHUP
                mkdir -p "${odir}"
                touch "${lockfn}"
                # then process it
                echo "$$" &amp;gt; "${lockfn}"
                metaflac --export-tags-to=/tmp/tags-$$ "${ifn}"
                echo -n "Converting: ${ifn} ..."
                flac -d "$2" -c -s --apply-replaygain-which-is-not-lossless 2&amp;gt;/dev/null \
                        | nice lame -S -h -V 3 --noreplaygain \
                                --tt "$(gettag TITLE)" --ta "$(gettag ARTIST)" --tl "$(gettag ALBUM)" \
                                --ty "$(gettag DATE)" --tn "$(gettag TRACKNUMBER)" --tg "$(gettag GENRE)" \
                                --ignore-tag-errors \
                                - "/tmp/working-$$.mp3"
                if [ $? ] ; then
                        mv /tmp/working-$$.mp3 "${ofn}"
                fi
                echo " done"
        else
                echo "Skipping ${ifn} - destination exists and is up to date"
        fi
        exit 0
fi

# For each flac file in the source tree test to see if an MP3 must
# be created and if so, make one.
find "$1" -name \*.flac \
        -exec "$0" convert "{}" flac mp3 \;
&lt;/pre&gt;
Here's another script that goes through the collection and changes
FLAC metadata to rename, eg, "The Cure" to "Cure, The". It's easily
adapted to do other things too.&lt;br /&gt;
&lt;pre&gt;#!/bin/bash
#
# Fix some common metadata issues on my flac collection, including rewriting
# artist names from "The X" to "X, The".
#
# Typical usage:
#    find flac -type f -name \*.flac -exec bin/cleanup_flac "{}" \;

if test $# -lt 1 ; then
        echo "Usage: $0 target-file.flac"
        exit 1
fi

function gettag() {
        awk -F = /^${1}=/' { print $2; }' /tmp/tags-$$
}

metaflac --export-tags-to=/tmp/tags-$$ "$1"
artist="$(gettag ARTIST)"

# Convert "The X" -&amp;gt; "X, The"
echo -n "Testing \"$1\": "
if test "${artist:0:4}" = "The "; then
        artist="${artist:4}, The"
        metaflac --remove-tag="ARTIST" --set-tag="ARTIST=${artist}" "$1"
        echo "artist changed to ${artist}"
else
        echo "ok"
fi&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4000019324938870227?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4000019324938870227/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/if-like-me-you-like-to-be-able-to-play.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4000019324938870227'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4000019324938870227'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/if-like-me-you-like-to-be-able-to-play.html' title='Scripts to add replaygain tags to a flac collection'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-4038680282092333726</id><published>2010-04-16T14:57:00.003+08:00</published><updated>2010-04-16T14:58:14.884+08:00</updated><title type='text'>The Great Australian Firewall just won't work</title><content type='html'>The proposed Australian Internet censorship rules will not work.
Like most such blacklist-based schemes without active human monitoring
it can be trivially bypassed by anybody capable of following simple
instructions. As such, all the people it's designed to stop (like
kiddie porn scum) will ignore its supposed blocking effects completely.
Meanwhile we'll all have to live with the performance degradation,
reliability problems, and so on.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;a name='more'&gt;&lt;/a&gt;&lt;br /&gt;
It can't work because:&lt;br /&gt;
&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Blacklists are not useful if you don't know what to block.&lt;/b&gt;
Private websites (content unknown until you have the password),
websites that switch content dynamically based on source IP or
authentication details, etc all defeat blacklists unless an insider
reports the site as a problem.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Blacklists are always out of date&lt;/b&gt;. A content provider can
hop domains and IP addresses much faster than a blacklist has any hope
of keeping up with, and can easily inform interested parties of the
changes over IRC, email, etc.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;&lt;acronym title="Secure HTTP, an encrypted version of the ordinary protocol used for web browsing"&gt;HTTPS&lt;/acronym&gt; prevents content inspection.&lt;/b&gt;
A filter cannot content-filter encrypted communication like HTTPs, so
all they can go by is the IP address and reverse DNS. If the blacklist
doesn't cover that IP address, the communication must be permitted.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Encrypted tunnels and &lt;acronym title="Virtual Private Network, the generic term for any technology that joins two private networks together via an encrypted channel over a public network like the Internet"&gt;VPNs&lt;/acronym&gt; can completely bypass the filtering.&lt;/b&gt;
You can't block all encrypted traffic through the filter. You'd prevent
secure online payment, remote access to business networks by
teleworkers, system administrators logging in to maintain their
networks, etc. To bypass the filter all somebody needs to do is have a
host outside Australia that runs a proxy server and accepts SSH
connections, terminates &lt;acronym title="Point to Point Tunneling Protocol"&gt;PPTP&lt;/acronym&gt; &lt;acronym title="Virtual Private Network, the generic term for any technology that joins two privatenetworks together via an encrypted channel over a public network like the Internet"&gt;VPN&lt;/acronym&gt;s, accepts &lt;acronym title="IPSec is the secure Internet Protocol, an encrypted and authenticating version of IP"&gt;IPSec&lt;/acronym&gt;
connections, or any number of other encryption options. The filter
cannot see the content of the data stream as it's encrypted, but the
user has free and unimpeded web browsing through the proxy. You can get
a web host with SSH access for around $50/month, and groups of people
could easily get together to set up proxy hubs. In fact, they're
certain to, and few of them will be criminals - most will just want
their web browsing to work.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Peer-to-peer file transfer clients support encrypted communication.&lt;/b&gt;
Even if there were effective methods to fingerprint files (such as
images) transferred via BitTorrent and such, those methods are easily
foiled. A client may encrypt the data so that only the sender and
recipient know what it is, rendering a filter between the two unable to
tell the difference between "My Gradmother's 100th Birthday Videos" and
kiddie porn. Even if the P2P client didn't support encryption,
transferring an encrypted zip file with the password or decryption key
in a text file would work just as well.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Files can be transferred out-of-order and cannot be fingerprinted unless complete.&lt;/b&gt;
This means that you either need to block out-of-order transfers (which
will stop all sorts of legit things that programs you use every day,
like Adobe Reader, rely on) or have the filtering system completely
download the file before passing any of it on to the recipient. The
former isn't acceptable if you claim the impact of the filter will be
minimal on legit users. The latter is a trivial denial of service -
request 1kb of every multi-megabyte PDF you can find, forcing the
filter to download all of them without costing you any significant
local resources. Yet this is a perfectly legitimate thing to do, and
the filter should not prevent it. A store and forward system like this
would also massively slow all web browsing, and require mind boggling
amounts of fast storage to successfully operate.&lt;/li&gt;
&lt;/ul&gt;
None of the above is difficult. You might not know the technical
terms involved, but actually using it is absolutely trivial - along the
lines of "visit the website https://10.101.122.144/", "Go to
https://safeandhappy.com/secretlogin.php"&lt;wbr&gt;&lt;/wbr&gt;, or "Click on &lt;i&gt;connect to VPN&lt;/i&gt; and enter the address 10.101.122.144 with username fred and password fred".&lt;br /&gt;
So, the filtering won't stop any criminals except the very, very
stupid. For example, it's been proved time and time again that child
porn traffickers, who're one of the groups being bought up repeatedly
in arguments in favour of this filter, are quite sophisticated in their
use of technology to hide their activities. They use encryption and
private secure web sites heavily, for one thing. As a result, they may
not even &lt;i&gt;notice&lt;/i&gt; the introduction of filtering. The only ones
who will get caught or blocked are the occasional casual sickos
searching the Internet. While disgusting, they are not the dangerous
ones, because they're not the ones abusing children to supply their
habit.&lt;br /&gt;
&lt;a href="http://www.blogger.com/post-edit.g?blogID=427258967255390991&amp;amp;postID=4038680282092333726" name="cutid1"&gt;&lt;/a&gt;

The same applies to basically any illegal activity. Stop people
obtaining "terrorist training materials"? Yes, you'll stop legitimate
researchers afraid to bypass the filters, the incredibly stupid, and
nobody else.&lt;br /&gt;
As if its total ineffectiveness wasn't bad enough, such filtering
schemes introduce a huge and complex system that sits between Australia
and the rest of the 'net. There will be an inevitable reliability
impact due to outages of the system. Additionally, it'll block content
that should not be blocked - even the best systems have false positive
rates as high as a couple of percent, or 1 in 50 files/pages. I don't
even want to imagine the effect that'll have on automatic software
updates, XML-RPC, and other non-web-browsing communication that often
happens over HTTP. If they try to filter traffic on ports other than
80, it's going to be vastly worse in terms of the amount of software it
just breaks.&lt;br /&gt;
The filter is clearly nonsensical from a practical and technical
perspective. That's without even getting into the argument about
whether it is right.&lt;br /&gt;
If there was a system that could effectively catch (blocking is
pointless; they'll just use other methods) people trafficking child
porn and the like, I'd be all for it - even if it had some significant
side effects. There isn't, though, and no technical advance can create
one so long as you consider encrypted communication without a
government decryption backdoor to be allowable.&lt;br /&gt;
&lt;h2&gt;

Whitelists&lt;/h2&gt;
Fundamentally, the only sort of filter that actually works is one
that disallows all communication of any sort not known to be "safe". No
encrypted communication may be permitted except to sites known to be
entirely legal, so most secure logins and online payment is out. All
websites must be checked in depth before being added to the whitelist
... and must be regularly re-checked to make sure they're still
compliant. No user-generated content websites can be allowed unless
they check all submissions before posting.&lt;br /&gt;
Even then, it's necessary to have a huge team of humans working for
the filtering provider check random samplings of URLs being requested
and view random samplings of images, etc. An apparently legit site can
trivially hide a back door into a secure private area where less nice
things go on. The site operator might not even know the back door is
there, since servers are regularly broken into and defaced or used to
host malware by malicious 3rd parties.&lt;br /&gt;
It's also necessary to fingerprint files and compare them to a list
of known prohibited files as another step toward detecting this sort of
thing. This requires store-and-forward proxying (since you can't block
a file you've already passed on to the recipient), which as noted above
is insanely expensive in storage and processing power.&lt;br /&gt;
Despite all that work, malicious parties can still get a lot done
between breaking in to a server and the break-in being detected. File
fingerprinting is of limited use as it's not particularly reliable and
any system without absurdly high false positive rates is easily tricked
by small changes in the file.&lt;br /&gt;
It's also possible to hide information in images and certain other file types in such a way as that it's actually &lt;i&gt;undetectable&lt;/i&gt;
but can still be extracted if you know it is there. This is called
steganography. The amount of information that can be hidden in a given
image is fairly small, but over time quite a bit can be transferred.
This technique can be used to undetectably transfer whatever you want
in blog post after blog post of picures of cats.&lt;br /&gt;
The result of a whitelist scheme (which is &lt;b&gt;not&lt;/b&gt; proposed, but
is the only thing that could work) would be that only a tiny percentage
of the Web would be accessible. There could would be no email hosted
outside Australia. No peer to peer file transfers. No access to
Facebook, LiveJournal, etc as untrusted user content can be put up
there. No instant messaging until/unless the companies involved worked
with the filter providers to enable filtering of messages and file
transfers done through their programs. No way to administrate servers
located outside Australia (no SSH). No way for remote workers to log in
to company networks internationally. The list is almost endless,
actually ... &lt;b&gt;and even with all these consequences it'd just make life a bit more difficult for the people you're trying to stop.&lt;/b&gt;&lt;br /&gt;
Note that you &lt;i&gt;can&lt;/i&gt; allow encryption if you force all
encryption users to disclose their decryption keys to the government.
However, the computational overhead of testing and decrypting each data
stream would be astounding. Furthermore, in some cases you can't tell
if you have the right key or not until the whole data stream has been
transferred, meaning that the filter must store the data stream without
forwarding it to the eventual recipient until it's all been received.
The storage performance required would be incredible, and the
compuational requirements of store-and-forward gatewaying all
Australian web browsing etc is incomprehensible.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-4038680282092333726?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/4038680282092333726/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/proposed-australian-internet-censorship.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4038680282092333726'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/4038680282092333726'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/proposed-australian-internet-censorship.html' title='The Great Australian Firewall just won&apos;t work'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-3148827577602994773</id><published>2010-04-16T14:56:00.001+08:00</published><updated>2010-04-16T14:56:03.146+08:00</updated><title type='text'>Reasons why Microsoft Access absolutely stinks as a DB UI RAD tool</title><content type='html'>When used as a UI builder &amp;amp; RAD tool for accessing a real
database (in this case PostgreSQL), Microsoft Access absolutely stinks.
It has some severe problems that require cumbersome workarounds. In
fact, if you're vaguely competent with a programming language it's
almost certainly better to just write the application in your preferred
language and be done with it. Java + Swing + Hibernate, for example,
would do the job very well, as would C++ &amp;amp; Qt .&lt;br /&gt;

I was forced to use Access in this most recent project, and thought
I'd post a warning for others who might be considering it but have
alternatives available.&lt;br /&gt;


&lt;a href="" name="cutid1"&gt;&lt;/a&gt;

Note that &lt;b&gt;none of this applies if you use MS SQL Server as the backend&lt;/b&gt;
since Access doesn't use ODBC linked tables for MS SQL Server, but
rather its own custom interface that's much smarter and more capable.&lt;br /&gt;


Problems using Access with ODBC linked tables include:&lt;br /&gt;


&lt;ul&gt;
&lt;li style="padding-top: 0.5em;"&gt;&lt;b&gt;It doesn't understand server-side generated synthetic primary keys.&lt;/b&gt;
It can't just insert a record with a DEFAULT key and then retrieve the
generated key through the ODBC driver. To use a PostgreSQL SERIAL
column (a sequence) you must write an event procedure in VB that fires
on the Form_BeforeInsert event. This procedure must issue a passthrough
query to invoke &lt;code&gt;nextval('seqname')&lt;/code&gt;, then store the value
in the primary key field of the form. Hooray for a tool that does
everything for you transparently, simply, and automatically.&lt;/li&gt;
&lt;li style="padding-top: 0.5em;"&gt;When you write your passthrough query,
you will notice when examining the server query logs that it fires
twice. That's because, if the ReturnsRecords property is set on a
query, Access appears to issue the query twice. Presumably it's using
the first instance to enumerate the returned fields for type-checking
purposes, as it does not do this when the ReturnsRecords property is
false. In any case, this means that &lt;b&gt;you can't get the return value of a stored procedure with side effects without invoking the procedure twice.&lt;/b&gt; So, for &lt;code&gt;nextval&lt;/code&gt;, I've had to first call &lt;code&gt;nextval('seqname')&lt;/code&gt; and discard the return value, then call &lt;code&gt;currval('seqname')&lt;/code&gt;
to get the return value. That's two round trips to the database that
were completely unnecessary, and a total of four for a single INSERT.&lt;/li&gt;
&lt;li style="padding-top: 0.5em;"&gt;&lt;b&gt;Access has no concept of data dependencies between fields.&lt;/b&gt;
If one field is (say) a combo box populated by a query that depends on
the value of another field, you must use an event procedure in VB to
trigger a refresh of the dependent field when the field it depends on
is changed. This would seem to be one of the more obvious and basic
things a RAD database tool would do for you, and I was stunned that it
does not.&lt;/li&gt;
&lt;li style="padding-top: 0.5em;"&gt;&lt;b&gt;Access loves to retrieve huge amounts of unnecessary data.&lt;/b&gt; If you open a form that's bound to a linked table, it'll issue a query like &lt;code&gt;SELECT pkey FROM table;&lt;/code&gt;,
which will result in a sequential scan on the whole table for databases
that can't return the data from the index (like Pg¹). Even for
databases that can, they must still read and scan the whole index.
That's also potentially a lot of network I/O. You can work around this
with EXTREMELY careful use of filter rules, but you have to be quite
paranoid to make sure it never sees the form/table without a filter
rule.&lt;/li&gt;
&lt;li style="padding-top: 0.5em;"&gt;&lt;b&gt;Access cannot natively perform non-trivial queries server-side&lt;/b&gt;
- you have to write them yourself with Visual Basic and use unbound
forms, which isn't too dissimilar to how you'd be working with (say)
Hibernate, just uglier. With a bound form and a linked table, Access
will use a WHERE clause on the primary key when fetching a record, but
that's about it. It can perform simple inner joins server side too,
apparently, but I've hidden everything I can behind server-side
updatable views so I haven't had to struggle with this aspect yet.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;Access cannot handle NULL boolean values.&lt;/b&gt;. Use a smallint with a constraint on it instead.&lt;/li&gt;
&lt;li style="padding-top: 0.5em;"&gt;&lt;b&gt;Access needs some custom operators and casts defined to work with PostgreSQL.&lt;/b&gt;.
It assumes that certain types may be cast to other types, compared to
them, etc in ways that aren't allowed by default. Thankfully PostgreSQL
supports user-defined operators and casts, so you can just add them. &lt;a href="http://www.postgresonline.com/journal/index.php?/archives/24-Using-MS-Access-with-PostgreSQL.html" id="link_50"&gt;Here's one page with a useful set of operators and casts.&lt;/a&gt;.&lt;/li&gt;
&lt;li style="padding-top: 0.5em;"&gt;&lt;b&gt;Access likes to test for nullity with the = operator.&lt;/b&gt;. This is just wrong - the result of &lt;code&gt;NULL = NULL&lt;/code&gt; is &lt;code&gt;NULL&lt;/code&gt;, not &lt;code&gt;TRUE&lt;/code&gt;, since the two unknown values may or may not be equal (the result is also unknown). PostgreSQL has a hack parameter, &lt;code&gt;transform_null_equals&lt;/code&gt;,
that may be set to enable the incorrect behaviour that MS Access
expects. Access MAY have been fixed in version 2007; I haven't had
cause to test this particular quirk yet.&lt;/li&gt;
&lt;/ul&gt;
You can work around a lot with careful use of filters, lots of
Visual Basic code, plenty of views, updateable views, and stored
procedures, and by writing your stored procedures to have EITHER side
effects OR a return value (never both). It's not much fun, though, and
it's still far from efficient.&lt;br /&gt;


On the upside, with row versioning enabled in the PostgreSQL ODBC
driver, Access uses the MVCC attributes of the tuples (xmin, etc) to do
optimistic locking. This allows it to avoid long-running transactions
and long-held locks during user interaction without having to mess with
the schema to add a field for oplocking use. It'll even play well with
the oplocking used by things like Hibernate, and will work properly
with apps that use ordinary transactional locking (SELECT ... FOR
UPDATE and so on) without the need for any sort of support triggers.
It's very nice.&lt;br /&gt;


To use Access with PostgreSQL, you REALLY need to set these options in the ODBC DSN for the connection:&lt;br /&gt;


&lt;ul&gt;
&lt;li&gt;&lt;b&gt;Row Versioning&lt;/b&gt; causes the driver to use only the primary key
in WHERE clauses, and to use xmin &amp;amp; affected record count during
updates to avoid conflicts.&lt;/li&gt;
&lt;li&gt;&lt;b&gt;True as -1&lt;/b&gt;&lt;/li&gt;
&lt;li&gt;&lt;b&gt;(unchecked) Bool as Char&lt;/b&gt;&lt;/li&gt;
&lt;/ul&gt;
&lt;div style="color: grey; font-size: smaller;"&gt;
¹ This isn't as stupid as it sounds. PostgreSQL uses an &lt;acronym title="Multi-Version Concurrency Control"&gt;MVCC&lt;/acronym&gt;
approach that allows for very efficient, highly concurrent inserts and
selects. The price is that it must store tuple visibility information
to know which transactions should see which tuples, since the table
contains the data at many different points in time all at once.
Visibility information isn't in the indexes (because it'd generate lots
more write activity on the indexes, slowing down I/O) so once an index
indicates a match on a tuple, it must be read from the heap to check
visibility even if the index contains the actual data point we want.
It's a performance trade-off that is not particularly great when you're
doing a `count(*)' or `select pkey from table' but is wonderful on
highly concurrent systems.&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-3148827577602994773?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/3148827577602994773/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/reasons-why-microsoft-access-absolutely.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3148827577602994773'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3148827577602994773'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/reasons-why-microsoft-access-absolutely.html' title='Reasons why Microsoft Access absolutely stinks as a DB UI RAD tool'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-2156764846833807230</id><published>2010-04-16T14:29:00.002+08:00</published><updated>2010-04-16T14:29:57.648+08:00</updated><title type='text'>Nice work, NetworkManager</title><content type='html'>I'm yet to encounter a cellular modem that NetworkManager 0.7 (in
Ubuntu 8.10 and 9.04-beta) doesn't automatically recognize without any
user configuration, driver installation, or anything. Just plug it in
(if not built in) and start using it.&lt;br /&gt;&lt;br /&gt;Very nice work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-2156764846833807230?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/2156764846833807230/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/nice-work-networkmanager.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2156764846833807230'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2156764846833807230'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/nice-work-networkmanager.html' title='Nice work, NetworkManager'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-2477982219592189910</id><published>2010-04-16T14:27:00.003+08:00</published><updated>2010-04-16T14:28:57.557+08:00</updated><title type='text'>Dongles are EVIL</title><content type='html'>&lt;span style="font-size: small;"&gt;&lt;/span&gt;&lt;br /&gt;
&lt;div style="font-family: inherit;"&gt;
&lt;span style="font-size: small;"&gt;The device you see on the right is actually the devil. Or, at least, it's close enough if you are a system administrator.&lt;/span&gt;&lt;/div&gt;
&lt;div style="font-family: inherit;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style="font-family: inherit;"&gt;
&lt;span style="font-size: small;"&gt;It is a single piece of hardware that controls your access to
business-critical programs. Lost the dongle? Whoops, no classified ads
in the newspaper this week. Dongle broke? Ditto. Dongle fried by a
computer malfunction or power fault? Ditto. Computer stolen? Ditto.&lt;/span&gt;&lt;/div&gt;
&lt;div style="font-family: inherit;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style="font-family: inherit;"&gt;
&lt;span style="font-size: small;"&gt;What's even more fun is that as computers move on and older
interfaces become obsolete, it becomes hard to even find a computer you
can plug the dongle in to. Most machines don't have parallel ports
anymore, so parallel dongles like this one are a big problem. At least
that can be worked around using USB adapters.&lt;/span&gt;&lt;br /&gt;
&lt;/div&gt;
&lt;div style="font-family: inherit;"&gt;
&lt;span style="font-size: small;"&gt;Of course, then you run into exciting issues like XP being unable to
allow 16-bit code access to the parallel port. The program would work
fine on XP, but for the stupid bloody dongle. So you're forced to
maintain legacy hardware or waste time on complex
emulation/virtualisation options just to get the program working, when
it'd be just fine but for this dongle.&lt;/span&gt;&lt;/div&gt;
&lt;div style="font-family: inherit;"&gt;
&lt;br /&gt;&lt;/div&gt;
&lt;div style="font-family: inherit;"&gt;
&lt;span style="font-size: small;"&gt;So, if you are ever offered software for any reason that requires a dongle, &lt;b&gt;just say no&lt;/b&gt;.&lt;/span&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;span style="font-size: small;"&gt;Bought to you by the exciting battle to get an old and alas mission-critical win16 app to work under WinXP or even WINE.&lt;/span&gt;&lt;/div&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-2477982219592189910?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/2477982219592189910/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/dongles-are-evil.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2477982219592189910'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2477982219592189910'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/dongles-are-evil.html' title='Dongles are EVIL'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-5418183583603454528</id><published>2010-04-16T14:25:00.008+08:00</published><updated>2010-06-05T16:00:33.133+08:00</updated><title type='text'>Getting GNOME Evolution to offer a client certificate for IMAP SSL/TLS</title><content type='html'>GNOME Evolution isn't noted for its client certificate support. Entries in the bug tracker about it have &lt;a href="http://bugzilla.gnome.org/show_bug.cgi?id=270893"&gt;rotted for years&lt;/a&gt;, and it has absolutely no acknowledged support whatsoever. Most other mail clients have had client cert support for years if not decades.&lt;br /&gt;
&lt;br /&gt;
Unfortunately, Evolution is quite attractive in other ways - calendar integration, LDAP address books, etc. Unlike Thunderbird (especially when large images are involved) it also has acceptable performance over remote X11 connections.&lt;br /&gt;
&lt;br /&gt;
So - I'd rather like to be able to use Evolution, but it's client support ... isn't.&lt;br /&gt;
&lt;br /&gt;
It turns out, though, that Evolution uses the Network Security Services library from Netscape/Mozilla . It's used, among other things, for IMAP SSL/TLS support. This library does support client certificates; after all, Thunderbird and Firefox support client certificates and they do their crypto through NSS.&lt;br /&gt;
&lt;br /&gt;
Is it not then possible to introduce a client certificate at the libnss level, so Evolution doesn't even know it's doing client certificate negotiation during its hand-off to NSS for SSL/TLS setup?&lt;br /&gt;
&lt;br /&gt;
Why, yes, it is, and it takes one line of code in &lt;span style="font-family: &amp;quot;Courier New&amp;quot;,Courier,monospace;"&gt;camel-tcp-stream-ssl.c&lt;/span&gt; to do it.&lt;br /&gt;
&lt;br /&gt;
&lt;pre&gt;camel-tcp-stream-ssl.c:
- /*SSL_GetClientAuthDataHook (sslSocket, ssl_get_client_auth, (void *) certNickname);*/
+ SSL_GetClientAuthDataHook (ssl_fd, (SSLGetClientAuthData)&amp;amp;NSS_GetClientAuthData, NULL );
&lt;/pre&gt;
&lt;br /&gt;
Because Evolution its self still has no idea about client certificates, if the server demands one and you don't have one installed you'll still get a useless error message instead of an appropraite prompt to install a client certificate. Just like Thunderbird and most other client-cert supporting apps. However, if you install a client cert by importing it into the Certificates section of the preferences, evolution (or more accurately libnss) will present it and use it when the server asks for it. 
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Update late 2009:&amp;nbsp;&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
Committed in stable (gnome 2.28.1+)
&lt;a href="http://git.gnome.org/cgit/evolution-data-server/commit/?h=gnome-2-28&amp;amp;id=87238717ceb0a158a00c76fc07c6e27c769c2cf0"&gt;http://git.gnome.org/cgit/evolution-data-server/commit/?h=gnome-2-28&amp;amp;id=87238717ceb0a158a00c76fc07c6e27c769c2cf0&lt;/a&gt;&lt;br /&gt;
Committed in master (gnome 2.29.1+)
&lt;a href="http://git.gnome.org/cgit/evolution-data-server/commit/?id=429a106d101bf205ba0c8ee8f94a818327c2d736"&gt;http://git.gnome.org/cgit/evolution-data-server/commit/?id=429a106d101bf205ba0c8ee8f94a818327c2d736&lt;/a&gt;&lt;br /&gt;
&lt;br /&gt;
&lt;b&gt;Update mid 2010:&lt;/b&gt;&lt;br /&gt;
&lt;br /&gt;
This code has now hit shipping Evolution versions in up-to-date distros like Ubuntu 10.04 and Fedora 13. I've tested it in Ubuntu 10.04 and verified that client cert support works now. Hooray!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-5418183583603454528?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/5418183583603454528/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/getting-gnome-evolution-to-offer-client.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5418183583603454528'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/5418183583603454528'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/getting-gnome-evolution-to-offer-client.html' title='Getting GNOME Evolution to offer a client certificate for IMAP SSL/TLS'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-2607578573713472216</id><published>2010-04-16T14:23:00.000+08:00</published><updated>2010-04-16T14:23:24.524+08:00</updated><title type='text'>Getting central certificate management working on modern Linux</title><content type='html'>Modern Linux (GNOME, anyway) systems actually have a central certificate store. It's
a bit lacking in management UI so far, but it works, and you can use it
instead of loading your PKCS#12 certificates into every app you use
manually.&lt;br /&gt;
&lt;br /&gt;


First, import your certificate into the GNOME keyring with:&lt;br /&gt;


&lt;pre&gt;gnome-keyring import /path/to/certificate.p12&lt;/pre&gt;
&lt;br /&gt;

Install the &lt;code&gt;libnss3-tools&lt;/code&gt; package (containing &lt;code&gt;modutil&lt;/code&gt;).&lt;br /&gt;
&lt;br /&gt;


Now exit every application you can, particularly your browser and mail client. Kill evolution-data-server too.&lt;br /&gt;
&lt;br /&gt;


Find all instances of the nss security module database on your
homedir, and for each one (a) test to make sure it's not open and (b)
install the gnome-keyring PKCS#11 provider in it. The following shell
script snippet will do this for you. Just copy and paste it onto your
command line:&lt;br /&gt;
&lt;br /&gt;


&lt;pre&gt;for f in $(find . -maxdepth 5  -name secmod.db -type f  2&amp;gt;/dev/null ); do
  echo "Testing: `basename $f`"
  if fuser `dirname $f`/cert8.db &amp;gt;&amp;amp;/dev/null; then
    echo -n "In use by: "; fuser `dirname $f`/cert8.db; echo " - Skipping"
  else
    modutil -force -dbdir `dirname $f` -add GnomeKeyring \
            -libfile /usr/lib/gnome-keyring/gnome-keyring-pkcs11.so
  fi
done&lt;/pre&gt;
&lt;br /&gt;

Now all your NSS-based apps should know about gnome-keyring and use the gnome-keyring certificate store.&lt;br /&gt;
&lt;br /&gt;


If you use Evolution and want client certificate support, patch evolution-data-server as per &lt;a href="http://bugzilla.gnome.org/show_bug.cgi?id=270893" id="link_9"&gt;GNOME bug 270893&lt;/a&gt; to enable that too. It'll use gnome-keyring automatically.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-2607578573713472216?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/2607578573713472216/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/getting-central-certificate-management.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2607578573713472216'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/2607578573713472216'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/getting-central-certificate-management.html' title='Getting central certificate management working on modern Linux'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8922905749871933240</id><published>2010-04-16T14:20:00.000+08:00</published><updated>2010-04-16T14:20:23.535+08:00</updated><title type='text'>i1Pro and the Australia Tax</title><content type='html'>The X-Rite i1Pro is an important instrument for anyone doing serious computer colour work, particularly for print and pre-press. It's also incredibly pricey, especially if you happen to live in Australia.&lt;br /&gt;
&lt;br /&gt;
There's this oddity known locally as the "Australia Tax". The Australian Tax Office may not know about it, but it appears that local distributors for international businesses are convinced that it exists, and that it's &lt;i&gt;high&lt;/i&gt;. That's the only explanation I can find for some of the&lt;a href="http://www.zdnet.com.au/adobe-explains-aussie-cs5-pricing-339302455.htm"&gt; jaw-dropping price differences between US and Australian versions of the same products&lt;/a&gt; - most of which are made in China anyway.&lt;br /&gt;
&lt;br /&gt;
I got the X-Rite i1Pro instrument cheap (ish) - at AU$1500 ex GST and shipping
compared to the AU$1800 quoted price. X-Rite force you to buy through
exclusive local dealerships that add a huge markup, so while the US
price is US$995 for the same instrument (AU$1200 @ current rates) you
can't just order from the US. They won't ship it to you. You can use a
US remailing service but X-Rite won't register it and won't support it
outside the US - and neither will the AU distributor. You can't get it
recalibrated etc without a painful amount of effort.&lt;br /&gt;&lt;br /&gt;The AU
dealership tries to claim it "adds value" ... but they don't do local
advanced tech support, don't have any techs or offices outside metro
Sydney, ship the instruments off to the US (3-4 week round trip) for
calibration, and don't even keep spares in stock. So what value,
exactly, do they add, other than to the price tag?&lt;br /&gt;
&lt;br /&gt;
X-Rite and their distributors are raking it in with this arrangement. Unfortunately, X-Rite have been buying out all their competitors (like GretagMacbeth)
so they're the only game in town. Like Quark, they'll suffer for their
customer-hostile attitude and parallel import restrictions eventually,
but right now they're in the "raking in the dough" phase.&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
(Of course, literally three days after I bought the i1Pro, Graham Gill, who develops &lt;a href="http://argyllcms.com/" id="link_27"&gt;Argyll CMS&lt;/a&gt;,
announced support for the much cheaper ColorMunki spectrophotometer ...
but hey, the i1Pro is a much better instrument so no harm done.)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8922905749871933240?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8922905749871933240/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/i1pro-and-australia-tax.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8922905749871933240'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8922905749871933240'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/i1pro-and-australia-tax.html' title='i1Pro and the Australia Tax'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-3536845461234397838</id><published>2010-04-16T14:08:00.002+08:00</published><updated>2010-07-25T12:23:17.410+08:00</updated><title type='text'>Client certificate WTF</title><content type='html'>Why does almost nobody bother to support X.509 client certificates
properly? They're a weak, poorly implemented afterthought in many
systems if they're supported at all.&lt;br /&gt;

(Note: Android info here may be dated, as I last tested with 2.0, and applies only to the stock Android distribution not apps or patched versions supplied by vendors.)&lt;br/&gt;&lt;br/&gt;

&lt;ul&gt;
&lt;li&gt;&lt;span style="color: green;"&gt;Microsoft Windows&lt;/span&gt;: Perfect
support, though it requires PKCS#12 (.p12) format certificates. Most 3rd party apps (subversion,
mozilla apps, etc) use own cert stores rather than the OS's for no good
reason.&lt;/li&gt;
&lt;li&gt;&lt;span style="color: red;"&gt;Mac OS X&lt;/span&gt;: Limited support in OS
keychain. No support in OS services like Apple Mail (IMAP+TLS, IMAPs,
SMTP+TLS or SMTPs), WebDAV over HTTPs, etc so I have no idea why they even bothered
adding support to keychain. Some apps have their own support, eg
Mozilla apps via NSS, but OS has none and most Apple apps have none. No 3rd
party apps seem to look in the system keystore for certificates.&lt;/li&gt;
&lt;li&gt;&lt;span style="color: orange;"&gt;Linux systems&lt;/span&gt;: All major
SSL/TLS libraries have support, but there's no system-wide or
desktop-wide keystore or key management. Netscape security suite apps
have good support but certificates must be installed individually in each app - even though the underlying libnss library supports&amp;nbsp; a shared user-account-level security store. GnuTLS- and OpenSSL-using
apps must implement their own certificate management but can support client certificates if they provide appropriate callbacks. As a result, support is very app
dependent and often buggy or very clumsy. Real-world support is inconsistent - eg Subversion supports client certs, but many
svn front ends don't handle the cert prompts; Thunderbird &amp;amp; Firefox support client certs via NSS;
Evolution supports via NSS but has broken nss init code that breaks client certificates and is often built with GnuTLS instead of NSS anyway; etc. Overall it's painful but usually usable.&lt;/li&gt;
&lt;li&gt;&lt;span style="color: green;"&gt;Symbian (Series 60) phones&lt;/span&gt;: Support is perfect in OS and apps. Very smooth.&lt;/li&gt;
&lt;li&gt;&lt;span style="color: red;"&gt;Sony Ericsson phones&lt;/span&gt;: Seem to have no concept of client certificates, and treat request for client cert by remote mail server as an SSL/TLS error.&lt;/li&gt;
&lt;li&gt;&lt;span style="color: green;"&gt;Windows Mobile phones&lt;/span&gt;: Basically perfect from all reports. Pity about the rest of the OS.&lt;/li&gt;
&lt;li&gt;&lt;span style="color: red;"&gt;Apple iPhone&lt;/span&gt;: Decent client
cert store support. Unclear how much access 3rd party apps have. It's used
for safari. Reported broken in Mail (comment by Martin).&lt;/li&gt;
&lt;li&gt;&lt;span style="color: red;"&gt;Android phones&lt;/span&gt;: are a near-total
information void. Apparently it's just assumed you'll use Google's
services, not (say) your own secure mail server with your work.
Because, you know, who needs confidentiality anyway? If you download
the SDK and phone emulator, you'll quickly find out that not only does
the OS &lt;a href="http://code.google.com/p/android/issues/detail?id=3620"&gt;lack any way to import a client certificate&lt;/a&gt; or use one in
negotiation, but&lt;a href="http://code.google.com/p/android/issues/detail?id=6207"&gt; &lt;i&gt;it lacks any way to even import new CA certificates&lt;/i&gt;&lt;/a&gt;.
That's stunningly, jaw-droppingly pathetic. Of course, this is a phone
with a read-only IMAP client so it's not clear what, exactly, it's
meant to do...&lt;/li&gt;
&lt;/ul&gt;
Sigh.&lt;br /&gt;
&lt;br /&gt;
Update: It looks like there's half-baked and mostly user-inaccessible support for importing CA certificates in some flavours of Android. &lt;a href="http://www.realmb.com/2010/01/android-certificate-installer/"&gt;This app&lt;/a&gt; exploits the facility. Client certificates don't appear to be so blessed.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-3536845461234397838?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/3536845461234397838/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/client-certificate-wtf.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3536845461234397838'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3536845461234397838'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/client-certificate-wtf.html' title='Client certificate WTF'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-427258967255390991.post-1419644849255844114</id><published>2010-04-16T13:59:00.000+08:00</published><updated>2010-04-16T13:59:52.881+08:00</updated><title type='text'>Google Android is not a smartphone OS</title><content type='html'>... it's a simple phone OS plus a web browser and some Google services. At least if the devkit phone simulator I used to see if this was something I might want to actually use is anything to go by.&lt;br /&gt;
&lt;br /&gt;


It appears to lack some pretty fundamental things you'd expect from a smartphone.&lt;br /&gt;


&lt;ul&gt;
&lt;li&gt;Ability to browse and view local files on phone memory or SD card, eg open HTML files, PDFs, etc&lt;/li&gt;
&lt;li&gt;An IMAP client that can &lt;i&gt;delete messages&lt;/i&gt;, mark them as read on the server, etc&lt;/li&gt;
&lt;li&gt;Any ability at all to support corporate private CAs (Certificate Authorities), since it can't import new X.509 CA certificates&lt;/li&gt;
&lt;li&gt;Any X.509 client certificate support for secure mail and intranet access&lt;/li&gt;
&lt;li&gt;Decent sync and backup facilities to a laptop/PC. Oh, wait, you only use Google services, right?&lt;/li&gt;
&lt;/ul&gt;
This is Google's fancy new phone platform? Call me again in a few
years, once you've grown up a bit - right now, even the iPhone OS is a
more solid choice.&lt;br /&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-1419644849255844114?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/1419644849255844114/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/google-android-is-not-smart-phone-os.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1419644849255844114'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/1419644849255844114'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/google-android-is-not-smart-phone-os.html' title='Google Android is not a &lt;i&gt;smart&lt;/i&gt;phone OS'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-3415324779833658449</id><published>2010-04-16T13:56:00.000+08:00</published><updated>2010-04-16T13:56:41.239+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='tech'/><category scheme='http://www.blogger.com/atom/ns#' term='desktop'/><category scheme='http://www.blogger.com/atom/ns#' term='ebook'/><category scheme='http://www.blogger.com/atom/ns#' term='wine'/><title type='text'>The best eBook reader for Linux is currently....</title><content type='html'>&amp;nbsp;&lt;b&gt;Microsoft Reader&lt;/b&gt; run under &lt;a href="http://winehq.org/" id="link_61"&gt;WINE&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Sigh. Not only is it the best, it's practically the &lt;i&gt;only&lt;/i&gt;
one unless you're content with fixed-format PDF. Few eBooks are
available in, or reasonably convertible to, HTML, and even if they were
there aren't any HTML renderers that can do half-decent &lt;acronym title="Hypenation and justification"&gt;H&amp;amp;J&lt;/acronym&gt;.
None at all can hyphenate even poorly, and justification support tends
to be limited to clumsy expansion-only justification that is ugly and
not very nice to read.&lt;br /&gt;&lt;br /&gt;So, to get a decent result one would
basically have to hand-convert a plain text or HTML format book
(possibly after pdf-to-text conversion) to TeX and typeset it for a
particular display. That's not exactly a nice, convenient way to sit
down for a good read. Even then, unless you use pdftex and read with
Adobe Reader it won't even remember your place!&lt;br /&gt;&lt;br /&gt;By contrast the
Microsoft Reader .lit format is fairly widespread, supports automatic
and somewhat decent H&amp;amp;J (though nothing on TeX / InDesign ),
remembers your place in each book in the library, tracks and manages
the library without forcing a particular on-disk structure on it,
supports easy drag-and-drop of a book onto the program even from
Nautilus, etc. It's friggin' emulated* Windows software that hasn't
been updated or improved since it was practically abandoned by MS in
2003 and it's still better than anything available natively for Linux.&lt;br /&gt;&lt;br /&gt;The
situation is just as dire for Linux-based ( and Symbian-based ) phones
and tablets. Given the spread of Qt to more and more devices, as well
as all major platforms, I'm increasingly tempted to start work on a
Qt-based reader with decent H&amp;amp;J, library management, place tracking
/ bookmarking / margin notes, etc. But how can there not be something
out there already? Am I just blind, or is there really a gaping hole
this big in free software capabilities?&lt;br /&gt;&lt;br /&gt;Any suggestions? Anyone interested in working on one?&lt;br /&gt;&lt;br /&gt;* I know, I know; I just don't care that W.I.N.E. Kudos to the WINE folks for their amazing work.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-3415324779833658449?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='related' href='http://www.microsoft.com/reader/' title='The best eBook reader for Linux is currently....'/><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/3415324779833658449/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/best-ebook-reader-for-linux-is.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3415324779833658449'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/3415324779833658449'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/best-ebook-reader-for-linux-is.html' title='The best eBook reader for Linux is currently....'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-8016813836507713949</id><published>2010-04-16T13:53:00.001+08:00</published><updated>2010-04-16T13:53:49.613+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='tech'/><category scheme='http://www.blogger.com/atom/ns#' term='internet'/><category scheme='http://www.blogger.com/atom/ns#' term='hosting'/><category scheme='http://www.blogger.com/atom/ns#' term='servers'/><title type='text'>Australian Bandwidth Pricing</title><content type='html'>I recently switched hosting of large files (12Mb or so PDFs) on my
employer's website from the existing Australian host to one in the USA.
Why? Because it's &lt;i&gt;cheaper to send data from the USA to Australian users than it is to send it from within Australia.&lt;/i&gt;.&lt;br /&gt;
&lt;br /&gt;
About 100x cheaper, in this case, when comparing Anchor Networks per-Gb pricing to SimpleCDN's.&lt;br /&gt;
&lt;br /&gt;
SimpleCDN
doesn't have any Australian node(s). Data gets requested by their US
nodes on first request, cached, and sent back to Australia. Yet they're
incredibly, vastly cheaper than anything local I can find.&lt;br /&gt;
&lt;br /&gt;
The
root of the problem appears to be that Australian hosting providers
charge all IP traffic as if it were to go via an international link.
There's no provision made for peering or intra-national traffic at the
majority of hosts. This may be an issue with the hosting provider its
self, or it may be with their upstream bandwidth suppliers, but &lt;i&gt;I don't care&lt;/i&gt;. Internet routing is &lt;i&gt;designed&lt;/i&gt; to solve this sort of problem - thankyou BGP - and peering points exist for a reason.&lt;br /&gt;
&lt;br /&gt;
It's actually way cheaper to store your data in the UK, Ireland, Germany, France, or pretty much anywhere &lt;i&gt;except&lt;/i&gt; Australia even if your users are 100% Australian. Isn't that kind of sad?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/427258967255390991-8016813836507713949?l=blog.ringerc.id.au' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://blog.ringerc.id.au/feeds/8016813836507713949/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://blog.ringerc.id.au/2010/04/i-recently-switched-hosting-of-large.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8016813836507713949'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/427258967255390991/posts/default/8016813836507713949'/><link rel='alternate' type='text/html' href='http://blog.ringerc.id.au/2010/04/i-recently-switched-hosting-of-large.html' title='Australian Bandwidth Pricing'/><author><name>Craig Ringer</name><uri>http://www.blogger.com/profile/02343803844223399065</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-427258967255390991.post-1864179205724647971</id><published>2010-04-16T13:51:00.002+08:00</published><updated>2010-04-16T13:54:40.404+08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='support'/><category scheme='http://www.blogger.com/atom/ns#' term='tech'/><category scheme='http://www.blogger.com/atom/ns#' term='proprietary software'/><title type='text'>Technical Support for Commercial / Proprietary Software</title><content type='html'>The Anchor Networks head sysadmin has an &lt;a href="http://www.anchor.com.au/blog/2009/02/the-value-of-commercial-software-support/"&gt;opinion on commercial support for software&lt;/a&gt; that's pretty similar to mine - it's garbage. Both of us have learned this from painful experience.&lt;br /&gt;
&lt;br /&gt;
The post is well worth a read if you're in a sysadmin/tech line of work. It mirrors my experiences with several vendors very closely, except that this particular case doesn't include any inter-vendor buck-passing or blame games. There's a reason more and more of the systems at work run on software I have source code to and can rely on myself to maintain - because that way, things actually get fixed.&lt;br /&gt;
&lt;br /&gt;
If you think Anchor's experience with dedicated commercial support organizations is bad, you should try contacting tech support for incredibly expensive commercial software you've licensed and asking them to support their product! I've had totally disinterested or completely useless suppor
