tag:blogger.com,1999:blog-66430713163181306982024-03-18T21:04:48.976-07:00Frank Kieviet's Engineering Notebookfkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.comBlogger58125tag:blogger.com,1999:blog-6643071316318130698.post-50901634629501864802023-12-28T16:14:00.000-08:002023-12-28T22:50:08.063-08:00My Keebio Nyquist 46 key split keyboard<p>In the summer of 2020, during the height of the pandemic, I started experimenting with a new keyboard. After several optimizations, this is what I ended up with:</p><p></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgDk_ZGZUTlteYFpVbnpSow4z3jqwCBQ_4IGKQVcRJj7YglJkhOtgcjNFVQSMRdDTOvryX6zUsm2IOQdxofi6Fewr9Ezxp9JvxId-lTp0lPFgIZAz3M7a1hdkMcFA1b3-LjFJDyI1T82MScct4GujM_tQr-sqHSmemMnx2JO1fuPMLqV52zyXXO1ZXNuJA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="347" data-original-width="706" src="https://blogger.googleusercontent.com/img/a/AVvXsEgDk_ZGZUTlteYFpVbnpSow4z3jqwCBQ_4IGKQVcRJj7YglJkhOtgcjNFVQSMRdDTOvryX6zUsm2IOQdxofi6Fewr9Ezxp9JvxId-lTp0lPFgIZAz3M7a1hdkMcFA1b3-LjFJDyI1T82MScct4GujM_tQr-sqHSmemMnx2JO1fuPMLqV52zyXXO1ZXNuJA=s16000" /></a></div></div><br /><p></p><p>This is a 46 key split keyboard based on the <a href="https://keeb.io/collections/nyquist-keyboard-collection">Nyquist (rev 3)</a> from <a href="http://keeb.io">keeb.io</a>.</p><p>In this post I'll describe why this configuration works very well for me:</p><p></p><ul style="text-align: left;"><li>It has an <b>optimized layout</b> that very much reduces awkward finger twists, leverages my previously almost entirely unused thumbs, and frees up my right hand to reduce moving my right hand between the keyboard and the mouse.</li><li>The split design and angled position allows for <b>reduced strain</b>.</li><li>The compact form factor gives me more <b>space</b> on my desk and makes it easier to carry when using a laptop.</li></ul><div>Let's get into some more details...</div><h2 style="text-align: left;">Layout</h2><p></p><h3 style="text-align: left;"><b>Shift and Control</b>: reducing awkward movements</h3><p>The Shift and Control keys are the two most commonly used modifiers. Yet the Shift key is operated with the pinkie finger. This is awkward: e.g. try to do shift-A with your left hand. Instead of using the pinkie finger for the Shift, can we use another finger? The ring finger, middle finger, and index finger are already operating other keys, but my left thumb was not really used for anything: I noticed I use the space bar exclusively with my right thumb. So why not use my left thumb for pressing Shift instead? This is why the Shift key on my keyboard is at the resting position of my left thumb.</p><p>On some keyboards, you can use the side of your hand to hit the Control-key. Let's make that explicit and let's make that even easier. That's why my keyboard has a large key with a gap next to it for the Control key.</p><h3 style="text-align: left;"><b>Navigation</b>: relieve the right hand and reduce unnecessary hand movement</h3><p>On most keyboards there's a dedicated "island" for cursor movements + backspace / delete. To use those keys, you have to move your hand to the island. The island is on the right side and I noticed I often found myself switching my right hand between my mouse and the "navigation island". Why not move the "navigation island" to the left hand and avoid hand movement altogether?</p><p>This can be done through "layers". Layers are a mechanism where keys can have multiple functions. For example, you can consider the Shift key as a layer for upper case letters. So Layer 1 on my keyboard is for navigation, and the layer is active when the left thumb holds the MO-1 button which is next to the Shift key:</p><p></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgOMaWv28Pg3mvQ81YdKPYxuwHWMXwu118IkNfEcTScV9wzfEMyJAEUhc6w0NFQZEYEW6vYCSvt6hH9atq8P7eZbz1oNom_LZtyRauCEdVJG_aeEKqoS7P7MqhAy7a1X-6YXSVJLcXF4RleTuQbNI2R5p3wx574189TUCuntXowG7hFRnZQTwqnQ7xdwi8" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="188" data-original-width="287" src="https://blogger.googleusercontent.com/img/a/AVvXsEgOMaWv28Pg3mvQ81YdKPYxuwHWMXwu118IkNfEcTScV9wzfEMyJAEUhc6w0NFQZEYEW6vYCSvt6hH9atq8P7eZbz1oNom_LZtyRauCEdVJG_aeEKqoS7P7MqhAy7a1X-6YXSVJLcXF4RleTuQbNI2R5p3wx574189TUCuntXowG7hFRnZQTwqnQ7xdwi8=s16000" title="Left pad" /></a></div><p style="clear: both; text-align: center;"><i>The left keypad (Layer 0); holding MO-1 changes it into:</i></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg2gUx-jQ2cjWDcVumUHPg6Fsbsbq3NdoJP3KgRsULTHMbbVd4EVWZqaTsLoUKu0LgeS46YxjeihdNSTKhU0acRy6bNQSAoWWqJ1lWsak9_SWK_cHBfb8KFc__z2owZFLjw3Oh9cHA2HEF_TyicwvrPcpPImdIqC5fudWQDD2xbx8OfQbV837rpQPtJRKw" style="margin-left: 1em; margin-right: 1em;"></a><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg2gUx-jQ2cjWDcVumUHPg6Fsbsbq3NdoJP3KgRsULTHMbbVd4EVWZqaTsLoUKu0LgeS46YxjeihdNSTKhU0acRy6bNQSAoWWqJ1lWsak9_SWK_cHBfb8KFc__z2owZFLjw3Oh9cHA2HEF_TyicwvrPcpPImdIqC5fudWQDD2xbx8OfQbV837rpQPtJRKw" style="margin-left: 1em; margin-right: 1em;"></a><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhB0dg3_Dts_EbL0h9X5kYodxB22Bzc64TjqV8kZZbVI6jxC0nAJvFck8fpg9KUcCHxrZX96wP01d0cnLz-awhh_h4vdLmQo-yhOr_m_zFXUWCIimLp1Ui16pNo2X-GOphBgGDsHZFhDOGKX1ckttpALoPlBNGxqUX4-2lJNSXBOuDPqwN7pcLQtPnX7JY" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="188" data-original-width="287" src="https://blogger.googleusercontent.com/img/a/AVvXsEhB0dg3_Dts_EbL0h9X5kYodxB22Bzc64TjqV8kZZbVI6jxC0nAJvFck8fpg9KUcCHxrZX96wP01d0cnLz-awhh_h4vdLmQo-yhOr_m_zFXUWCIimLp1Ui16pNo2X-GOphBgGDsHZFhDOGKX1ckttpALoPlBNGxqUX4-2lJNSXBOuDPqwN7pcLQtPnX7JY=s16000" /></a></div><p><i>The left keypad (Layer 1)</i></p></div><p style="text-align: left;">The position of the MO-1 button next to the Shift key is no coincidence: like everybody I often need to hold the shift while using the cursor keys. With the two keys next to each other, the thumb can simply press them <b>both</b> at the same time. This is also why these keys are wider than the other keys: it reduces the gap between the keys.</p><p style="text-align: left;">The MO-1 button also "happens" to be next to the Alt button. Because of this it's easy for me to use my left thumb to hold both Alt+MO-1 to hit alt+cursor key combinations.</p><h3 style="text-align: left;"><b>Reduce reaching</b>: Enter, Escape, Backspace, numbers</h3><p style="text-align: left;">With moving more responsibility to the left hand so that the right hand can stay on the mouse for things like spreadsheet operations (heavy on navigation, editing), the Enter button also moves to the left. It takes the place of the Caps lock key on regular keyboards. Like most people, I never really use that key anyway. This key is prime real estate: it is easily reached with just one position over from the left pinkie.</p><p style="text-align: left;">The backspace is typically found just out of reach of the right pinkie. I use the backspace key a lot: my typo rate is pretty high. So I moved that key to the bottom of the right hand where the right thumb is in its rest position. (I use MO-1+R if my right hand is on the mouse)</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEj5WMKHo0L1BzN7vgcszWgCSu_ixdd85PSPfP5wWtZ4ycnHUIlYiexlAoZFzgehV_hrwvSJM9BRzoGig-A8CTLXIotl4BSqMaRSlG5hnv87yIFonUaD-O4Z5U92L-GzPLm003KbzgqMTRL4djyFRna_4smEKqVx9gYxOnXrGpiF8Zg2D6A2ysFBRApxv18" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="186" data-original-width="609" src="https://blogger.googleusercontent.com/img/a/AVvXsEj5WMKHo0L1BzN7vgcszWgCSu_ixdd85PSPfP5wWtZ4ycnHUIlYiexlAoZFzgehV_hrwvSJM9BRzoGig-A8CTLXIotl4BSqMaRSlG5hnv87yIFonUaD-O4Z5U92L-GzPLm003KbzgqMTRL4djyFRna_4smEKqVx9gYxOnXrGpiF8Zg2D6A2ysFBRApxv18=s16000" /></a></div><p style="clear: both; text-align: center;"><i>Full layer 0</i></p>This leaves a single key for the space bar. It turns out I was hitting the space bar on exactly the same place every time anyway, so there's really no reason to give the space bar more than a single key.<p></p><h4 style="text-align: left;"><b>Numbers and function keys</b>: pulling them in reach</h4><p style="text-align: left;">With only three rows of keys, how do we use numbers and function keys? They're simply pulled down from where they are on a regular keyboard to the home row and top row using the <b>second layer</b>.</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjbKAJehRAllnOf8s_Ni1YRcM_SmAexOXIJawMEkpTjEqeuVCdJHfet9HUzvuZF7bjusuk74dyyagpHvh_XzkqIL-MX_Cl8vPCWZ2uKl9m8TLeTRjQ51ZnQSQXn8jJXu5rbrPrjnktTH1hErpwSC1yQ-LYeXxzpomCtOYSfx7LbQvtqkVO_gIAtaTI4Iyc" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="133" data-original-width="584" src="https://blogger.googleusercontent.com/img/a/AVvXsEjbKAJehRAllnOf8s_Ni1YRcM_SmAexOXIJawMEkpTjEqeuVCdJHfet9HUzvuZF7bjusuk74dyyagpHvh_XzkqIL-MX_Cl8vPCWZ2uKl9m8TLeTRjQ51ZnQSQXn8jJXu5rbrPrjnktTH1hErpwSC1yQ-LYeXxzpomCtOYSfx7LbQvtqkVO_gIAtaTI4Iyc=s16000" /></a></div><p style="clear: both; text-align: center;"><i>Full layer 2</i></p></div>The second layer is activated using the key on the right of the shift key. Again, this choice is intentional: to enter a symbol like an exclamation mark, I press the Shift and the MO-2 layer keys both with my left thumb and hit the 1 key with my left pinkie.<p></p><p style="text-align: left;">Memorizing the positions of these keys is easy because they simply have the same x-coordinate as on regular keyboards; they're just shifted down by two rows.</p><p style="text-align: left;">Reducing the number of keys on the home row to only 6 causes a minor problem with just a few keys: F12 no longer fits. And the square bracket keys need to be relocated. In practice, I never use F12. And the memorization of the new location of the bracket keys was not that difficult. They're actually easier to reach in their new positions.</p><p style="text-align: left;">Entering many digits goes quite fast since all eight fingers can be used on the home row. As an alternative I also have a numeric keypad on Layer 1 but in practice I never use it.</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEg4FqFgZWu9hjS023oNc7PZkufFgSu1nJfdeFum9bHxTQc59fIAxIq9WYECYJFSX78nqateWWZYbQCAFCzDGJzkH-0f-AS7ONJIuEvkDIWD-fvH_vtCoEhlHSfWVIK1u3IvOF9BHbd_MQSoICJf7JUJFxrUZPv1xtwhdPnKpfLTpbwl4nXp7AXohYGScnY" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="177" data-original-width="584" src="https://blogger.googleusercontent.com/img/a/AVvXsEg4FqFgZWu9hjS023oNc7PZkufFgSu1nJfdeFum9bHxTQc59fIAxIq9WYECYJFSX78nqateWWZYbQCAFCzDGJzkH-0f-AS7ONJIuEvkDIWD-fvH_vtCoEhlHSfWVIK1u3IvOF9BHbd_MQSoICJf7JUJFxrUZPv1xtwhdPnKpfLTpbwl4nXp7AXohYGScnY=s16000" /></a></div><p style="clear: both; text-align: center;"><i>Full layer 1</i></p>So while the number of keys is reduced to only 46 keys in total, there are plenty of unused keys left because of the use of layers. In fact, I put together a third layer for special keys which is reachable by holding the "Context menu" key with the right thumb.<p></p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEhswedvP_CqlMVnDxaeZ5jg_cuiru6qq9PKagE6C3jzZnTN1NkoGTO8AD-RboRn5NdStQJFuILfytxk6uafvNp3h8nhCztw16FXn_2xu0GDgvWRkMNwTS7ZGcGLsaGo6DjEg-HqKy7ZgSAE_cyqmq9TKNbSU4jyEilrGpvel3hyNhvIp2QRkeKxOo_QZPA" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="176" data-original-width="587" src="https://blogger.googleusercontent.com/img/a/AVvXsEhswedvP_CqlMVnDxaeZ5jg_cuiru6qq9PKagE6C3jzZnTN1NkoGTO8AD-RboRn5NdStQJFuILfytxk6uafvNp3h8nhCztw16FXn_2xu0GDgvWRkMNwTS7ZGcGLsaGo6DjEg-HqKy7ZgSAE_cyqmq9TKNbSU4jyEilrGpvel3hyNhvIp2QRkeKxOo_QZPA=s16000" /></a></div><p style="clear: both; text-align: center;"><i>Full layer 3</i></p>The only keys I use here are the multimedia keys and the "Right Alt". The latter makes it easy to occasionally use international characters without giving up the Alt key on the right keypad. So for the Euro sign € I hold the Context Menu key with my right thumb, roll the side of my right hand onto the right alt, press the Layer 2 button with my left thumb, and hit 5 with my left index finger.<p></p><p style="text-align: left;"></p><h2 style="text-align: left;">Reduced strain</h2><p style="text-align: left;">The keyboard layout reduces strain on my fingers because it eliminates the load on the pinkies and instead moves a lot of their load onto the much stronger thumbs. Almost all key combinations become very easy, e.g. Ctrl+Shift+V are easy to accomplish with only my left hand. And because of that, my right hand can stay engaged on the mouse.</p><p style="text-align: left;">Having two separate keypads also makes it easy to keep my hands straight as opposed to the angle that occurs with a straight keyboard. I like to move them a good distance apart: 10-12 inches is my preferred distance.</p><p style="text-align: left;">My preferred wrist rotation angle is 15°, so I made the inside legs of the keypads longer to match that angle.</p><p style="text-align: left;">The absence of navigation and numerical islands minimizes the space between the default position of my right hand and the mouse and hence minimizes travel between the mouse and keyboard.</p><p style="text-align: left;">Putting a keyboard together myself also allowed me to optimize the key switches. I went with Gateron White (35g of resistance force) for both pinkies and the left thumb. The latter is because it needs to often press two keys at the same time. The two keys I press with the side of my hands, i.e. the left Control key and the right Alt key, are Green (80g of resistance) and the only ones that are clicky. The other keys are all Reds (45g of force).</p><p style="text-align: left;"></p><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEggwZtx2O0SI33tb-fzAzRIlPgIbf5a78PhPD-eYdhHh4uczgMTQQZQemL6jHTMudTYUF1wxvECqkhA4ICWTgSFdJjLzU3GNLN-uyT53BYPYyUg9O_L23Yrh08VN-CN6bJBm2KIF1cG_pu9F0A1LCxrnJ7ZUCoDsFUbDs0A-DhlGdAT-0NnR8xEKXvmOl0" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="211" data-original-width="634" src="https://blogger.googleusercontent.com/img/a/AVvXsEggwZtx2O0SI33tb-fzAzRIlPgIbf5a78PhPD-eYdhHh4uczgMTQQZQemL6jHTMudTYUF1wxvECqkhA4ICWTgSFdJjLzU3GNLN-uyT53BYPYyUg9O_L23Yrh08VN-CN6bJBm2KIF1cG_pu9F0A1LCxrnJ7ZUCoDsFUbDs0A-DhlGdAT-0NnR8xEKXvmOl0=s16000" /></a></div><p><i>The two pads without key caps.</i></p></div></div>The addition of O-ring switch dampers under the key caps reduces the impact of key strokes. The rubber rings also reduce the sound a little bit.<p></p><p style="text-align: left;">Further customizations include reducing the space between the top and bottom plate to 8mm using M2*8mm and M3*8mm metal female standoffs.</p><h2 style="text-align: left;">Size</h2><p style="text-align: left;">My current keyboard is a lot smaller than my daily driver that I used in the 10+ years prior:</p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjT3glHCE50lwUzo4F4-AdC7zmPfRcBnZOjRcvX45nlEDlXL8sh2jhQ2YnEAkYhHhOKYiE8EyGrSRCceXFw4Ms-jKz6vBbZmd2G1oW31SOW5My10D8Ys_YENJXjgvQ7qrEj7-ZGDPzWZtVRn8WI0_7t8Y8jj8q6kgguVVbJ3alYhJ8j0DTEPmkSm9NEnCE" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="394" data-original-width="565" src="https://blogger.googleusercontent.com/img/a/AVvXsEjT3glHCE50lwUzo4F4-AdC7zmPfRcBnZOjRcvX45nlEDlXL8sh2jhQ2YnEAkYhHhOKYiE8EyGrSRCceXFw4Ms-jKz6vBbZmd2G1oW31SOW5My10D8Ys_YENJXjgvQ7qrEj7-ZGDPzWZtVRn8WI0_7t8Y8jj8q6kgguVVbJ3alYhJ8j0DTEPmkSm9NEnCE=s16000" /></a></div><p style="clear: both; text-align: center;"><i>Full size keyboard comparison</i></p>I can now have a notepad right in front of me or I can simply move the keyboard under my monitor so that I can reclaim my desk for writing.</div><div><br /></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEjQjg48P5rdkDFYmxOD6sFedKIwoOZ56v74il8u-5Z9fy9_OaAArCJaha12GMXfbvTxcDtUm6gmdJiNXVA_QuzPYOVyuYZAIez5yuBEr9nrr0v0ma6T4wwGE6fmGufRvmdSh8wZYXyl2WsihGeJnEhqqMlkfStL4Y56pKeVTDSLDi-nad0tyZgaZ1Ay5MY" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="268" data-original-width="541" src="https://blogger.googleusercontent.com/img/a/AVvXsEjQjg48P5rdkDFYmxOD6sFedKIwoOZ56v74il8u-5Z9fy9_OaAArCJaha12GMXfbvTxcDtUm6gmdJiNXVA_QuzPYOVyuYZAIez5yuBEr9nrr0v0ma6T4wwGE6fmGufRvmdSh8wZYXyl2WsihGeJnEhqqMlkfStL4Y56pKeVTDSLDi-nad0tyZgaZ1Ay5MY=s16000" /></a></div><p style="clear: both; text-align: center;"><i>A notepad fits easily in between</i></p><p style="text-align: left;">I like my current keyboard so much that I use it instead of my laptop keyboard when I'm in meeting rooms. I position the two pads on both sides of my laptop:</p></div><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgLYBfclrn40SazkBVWsZkiAyzTRs3wGeGg_M_z_2UCVN1BjtTH79yOmvrS6Sh-H64XFhDr59qnXqHEruUCgHH1HzwSfAFfXMJApOQ7065HHO-F8W91ikpprzmlqPjw8XLGI80sTadV7Q6MzGeWvrBdF9bhXjn2LVsoFzFM0C5-qGGZ82KL03mr9RPQk-0" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="357" data-original-width="554" src="https://blogger.googleusercontent.com/img/a/AVvXsEgLYBfclrn40SazkBVWsZkiAyzTRs3wGeGg_M_z_2UCVN1BjtTH79yOmvrS6Sh-H64XFhDr59qnXqHEruUCgHH1HzwSfAFfXMJApOQ7065HHO-F8W91ikpprzmlqPjw8XLGI80sTadV7Q6MzGeWvrBdF9bhXjn2LVsoFzFM0C5-qGGZ82KL03mr9RPQk-0=s16000" /></a></div><p style="clear: both; text-align: center;"><span style="text-align: left;"><i>(Not shown: USB hub plugged into laptop)</i></span></p></div><p style="text-align: left;">Carrying this combo between meeting rooms is pretty simple because the keypads fit on the lid of the laptop:</p><div><div class="separator" style="clear: both; text-align: center;"><a href="https://blogger.googleusercontent.com/img/a/AVvXsEgePdei3N-IMGEguqzzrLFdtbTQSZvC0zobbsHpGUgy7YqVEkRWpcNMC20q1A1S0nvu2M9Q8mM3j9i1RXzKeMeOr86y3i4t6-PTtaVObgYHAah0g3SJSfRJlpL0t8ZRX4NBfeBX0jbo1KoeWysV2aIy7oWct20-aYWlVlGB6JiC_MieKy5TZd4_u62fG4Q" style="margin-left: 1em; margin-right: 1em;"><img data-original-height="357" data-original-width="554" src="https://blogger.googleusercontent.com/img/a/AVvXsEgePdei3N-IMGEguqzzrLFdtbTQSZvC0zobbsHpGUgy7YqVEkRWpcNMC20q1A1S0nvu2M9Q8mM3j9i1RXzKeMeOr86y3i4t6-PTtaVObgYHAah0g3SJSfRJlpL0t8ZRX4NBfeBX0jbo1KoeWysV2aIy7oWct20-aYWlVlGB6JiC_MieKy5TZd4_u62fG4Q=s16000" /></a></div><p style="clear: both; text-align: center;"><i style="text-align: left;">(Not shown: USB hub plugged into laptop)</i></p><br /><br /><h2 style="text-align: left;">Postscript</h2></div><p style="text-align: left;">I have one regret about my keyboard: why did I stick so long with the traditional keyboards? They're bulky and painful to use!</p><p style="text-align: left;">Having said that, I have no illusion that I will convince anybody to start building their own custom keyboard. But through this post I can easily share more information with people who see my weird contraption in the office.</p><p></p>fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-23084684424604491902021-06-06T16:41:00.003-07:002021-06-06T16:41:58.059-07:00Regularly backing up a Google account<p>I've had my Google account for well over a decade and have accumulated a lot of emails, photos, etc. The risk that Google will lose my data is extremely small, but it's not unimaginable that I might accidentally delete data, or that I get locked out my account. Therefore, I periodically back up my account. Google provides https://takeout.google.com as a convenient way to do so. Here's how in more detail:</p><h4 style="text-align: left;">Incremental backup</h4><div>As it turns out gmail and photos use most space, so it's worthwhile to do an incremental backup on these items and simply do a full backup on everything else.</div><div><ol style="text-align: left;"><li>gmail</li><ol><li>empty trash</li><li>in gmail's search bar, type "after:2021/01/01" (adjust the date)<br /><div style="text-align: left;"><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE3vW9SJFqgyGiUygng3kumkZp5hvZqRi4Q11EGtpPy_QUTynrmWEiJ6dx9cDyF7lIriIyUucAVSD7dBJ0GLNOYuXc02VZzA6HHe3mB81Lb3sQ0AgXPIxnD_00JnCSiUOcEgxajcE9y34/" style="clear: left; display: inline !important; margin-bottom: 1em; margin-right: 1em; text-align: center;"><img alt="" data-original-height="211" data-original-width="914" height="74" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhE3vW9SJFqgyGiUygng3kumkZp5hvZqRi4Q11EGtpPy_QUTynrmWEiJ6dx9cDyF7lIriIyUucAVSD7dBJ0GLNOYuXc02VZzA6HHe3mB81Lb3sQ0AgXPIxnD_00JnCSiUOcEgxajcE9y34/" width="320" /></a><br /></div></li><li>hit the selection checkbox to select all mails</li><li>hit the "Select all conversations that match this search" link</li><li>hit the "label" button and select "Create new label"<br /></li><li>select a descriptive string, e.g. "backup-20210101-20210307"; this label will be used later</li></ol><li>photos:</li><ol><li>create an album for all photos since the last backup, e.g. "Backup 2021-01-01 to 2021-03-07"</li><li>tag all photos in the timeline after the last backup with this album; this album will be used later</li></ol><li>create an export in https://takeout.google.com</li><ol><li>select the backup label for mail</li><li>select the just created backup mail label for Mail</li><li>select the just created backup album for Photos</li><li>select all files in Drive but uncheck the photos (if that corresponds to Google Photos</li><li>choose Drive as the destination of the takout archive; zip files greater than 2Gb work fine</li></ol><li>wait for takeout to complete</li><li>copy the takeout zipfiles from Drive to another medium, e.g. an external drive</li><li>delete the takeout zipfiles in Drive</li><li>schedule the next backup in Google Calendar as a reminder</li></ol></div>fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-11578069638652588912016-02-26T16:49:00.001-08:002016-02-26T18:54:56.144-08:008 things you may not know about Google Tag (Assistant) Recordings<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Many people know Google Tag Assistant and use it as a tool to quickly look at their Google Analytics (and other) tags on their website. </span><span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">A few months ago we launched a new product: <a href="https://support.google.com/analytics/answer/6277302" rel="nofollow" target="_blank">Google Tag Recordings</a> which we integrated with Google Tag Assistant.</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">The idea behind Tag Recordings is that you as a GA user can record a journey through your website and then look at how GA would process the GA hits. This lets you validate that your site works correctly with GA. If there's an issue, you can troubleshoot it on the spot, make changes --for instance to the GA configuration-- and instantly see the effect of your changes.</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Here is a 2 minute demo:</span><br />
<div class="separator" style="clear: both; text-align: center;">
<iframe allowfullscreen="" class="YOUTUBE-iframe-video" data-thumbnail-src="https://i.ytimg.com/vi/EkNsaBDT8Cc/0.jpg" frameborder="0" height="266" src="https://www.youtube.com/embed/EkNsaBDT8Cc?feature=player_embedded" width="320"></iframe></div>
<br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Here are 8 things you perhaps didn't know about Google Tag (Assistant) Recordings:</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">1. It’s a service!</span></h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Although integrated with Tag Assistant, Tag Recordings is a standalone service. It lives at this URL: <a href="https://analytics.google.com/tagrecordings" rel="nofollow" target="_blank">https://analytics.google.com/tagrecordings</a>. It being a separate service instead of javascript in the browser, lets it use some of the same GA parsing and processing code that GA normally uses.</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">To use Tag Recordings, you need to have a recording of a journey through your website. The recording is in the form of a <a href="https://en.wikipedia.org/wiki/.har" rel="nofollow" target="_blank">HAR</a>. The easiest way to record is to use <a href="https://chrome.google.com/webstore/detail/tag-assistant-by-google/kejbdjndbnbjgmefkgdddjlbokphdefk" rel="nofollow" target="_blank">Google Tag Assistant</a>: just hit the record button. But you can also use the record functionality in the <a href="https://developers.google.com/web/tools/chrome-devtools/" rel="nofollow" target="_blank">Chrome Devtools</a>. You can also use <a href="https://toolbox.googleapps.com/apps/har_analyzer/" rel="nofollow" target="_blank">Firefox or IE</a> or construct your own HAR.</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">2. Dropped hits? Check hit parse errors!</span></h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">If you make an error in --for example-- the number format of an event sent from your web page, the hit will be rejected when it arrives at google-analytics.com because the hit cannot be parsed. Example:</span><br />
<span style="font-family: "courier new" , "courier" , monospace;"> ga('send', 'event', 'cat', 'act', 'lab', 5.2);</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">This fails to parse in the GA server because the last parameter must be an integer. </span><span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">The parse error does not make it back to your web browser, so you don't know that your hit will never make it into any reports.</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">While Tag Assistant does not show the error (it only does a small set of validations in the browser itself), Tag Recordings however does show the parse error: it uses the same code that the Google Analytics server uses.</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDZM0tMrXYKI6JI9-YS16k9GMYQuEXL10JVUOVCVxRrI-NPq-ZrHW0CEmVN4xY4IZ0UQdBM9dcFZ0GvBOH8AMmtsQ-7-tyh85czIkWAcMAG5NYYOzR826r6ECql9l8xFJ1gHrc1EGGzxg/s1600/Selection_069.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="307" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjDZM0tMrXYKI6JI9-YS16k9GMYQuEXL10JVUOVCVxRrI-NPq-ZrHW0CEmVN4xY4IZ0UQdBM9dcFZ0GvBOH8AMmtsQ-7-tyh85czIkWAcMAG5NYYOzR826r6ECql9l8xFJ1gHrc1EGGzxg/s400/Selection_069.jpg" width="400" /></a></div>
<br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">3. No hits? Find the filter that is to blame</span></h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">If you see no hits at all in a GA View, a filter may be to blame.</span><span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"> Views often have lots of different filters, making it difficult to quickly see which filter is to blame. In Tag Recordings you can see if a hit is dropped for a View, and if so, which filter is responsible.</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiCJEZuPFkBNdrNIThESYrhhjIxB5kD1gmCLThH-xNbCSKWCDsyOEnlxeYmkj_XNGeq5C_o-XT2Z1lhtz4MGDNTiXp8PIGhYk0jaOVrSGWLUq00W-RkLlTiE1UZRj6OUOE6VfqnkV7E4A/s1600/Selection_070.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="77" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiCJEZuPFkBNdrNIThESYrhhjIxB5kD1gmCLThH-xNbCSKWCDsyOEnlxeYmkj_XNGeq5C_o-XT2Z1lhtz4MGDNTiXp8PIGhYk0jaOVrSGWLUq00W-RkLlTiE1UZRj6OUOE6VfqnkV7E4A/s320/Selection_070.jpg" width="320" /></a></div>
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">4. Mutating filters</span></h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Filters may change attributes of a hit. Also View settings may mutate hits, for example for query parameter stripping. Tag Recordings shows exactly how a hit changes after it is sent.</span><br />
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid3m8RjOmCBfOklQ5sruSDVo3XWP7_YxUIU_ADqBCd_dne4WfdBwCIfyEm997oZQCqOHEi2LLA75T85FOrSpPJljKe6IwW8gLToft9nrsCSRInH8ZLrT0CYTN_onVNWz_QkI8fWRKGv4k/s1600/Selection_071.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="90" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEid3m8RjOmCBfOklQ5sruSDVo3XWP7_YxUIU_ADqBCd_dne4WfdBwCIfyEm997oZQCqOHEi2LLA75T85FOrSpPJljKe6IwW8gLToft9nrsCSRInH8ZLrT0CYTN_onVNWz_QkI8fWRKGv4k/s400/Selection_071.jpg" width="400" /></a></div>
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">5. Find session breaks</span></h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Going from one domain to another domain may cause GA to start a new session. This of course will have a big impact on your GA reports. Because Tag Recordings looks at all the hits in a recording, it can figure out where GA would create a new boundary. See for an example the video above or this <a href="https://analytics.google.com/tagrecordings/example?id=ecommerce&page=page_405957&hit=14" target="_blank">example report</a>.</span><br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiS994VSH94Q70lOztVcHE1XdqlchFlA7BNWMnE95GGcTBO1ErHzvtHqzHO3TVDDBEYHbCnAkpVdcdnmn_Rr-9fSgXTS5ZgWk5FY3EUeOTjiA7afhve9Peoprlk8TtdeSR6b77Qo39TIA/s1600/Selection_075.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="153" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhiS994VSH94Q70lOztVcHE1XdqlchFlA7BNWMnE95GGcTBO1ErHzvtHqzHO3TVDDBEYHbCnAkpVdcdnmn_Rr-9fSgXTS5ZgWk5FY3EUeOTjiA7afhve9Peoprlk8TtdeSR6b77Qo39TIA/s200/Selection_075.jpg" width="200" /></a></div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span>
<br />
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">6. Verify your goals, filters, etc</span></h3>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">When you create a new goal or filter, Google Analytics allows you to preview how the goal or filter will work using hits that were received and processed previously. For this to work the View already must have data, obviously. How the actual filter or goal will work, can only be seen after a day or so when new hits have come in and have been processed.</span></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">A powerful feature of Tag Recordings is that it operates on the <b>current</b> configuration of your property and view. So you can get instant feedback on what happens to hits if you change the configuration. That allows you to instantly validate changes to filters, goals, etc.</span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6nt24LmEOveYEpFceVUwy_wKMS75dYmwtLO1pPQh8Gw_yN9uKf47GdzrTBhyJwmr07NC9OLZ3EZP64ePwQWup2mj3biSMPC2ua5EfKRbPSv-D4bfcmzBVI40efK1rAhJPEQn2tkKPeSI/s1600/Selection_074.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="200" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEi6nt24LmEOveYEpFceVUwy_wKMS75dYmwtLO1pPQh8Gw_yN9uKf47GdzrTBhyJwmr07NC9OLZ3EZP64ePwQWup2mj3biSMPC2ua5EfKRbPSv-D4bfcmzBVI40efK1rAhJPEQn2tkKPeSI/s200/Selection_074.jpg" width="148" /></a></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span></div>
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">7. Pretend to be elsewhere</span></h3>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Once you have a recording, you can pretend that the recording was made from a different specific IP address, or you can pretend that the IP address belongs to a different geo. This makes for a quick approach to verify that your IP-specific or geo-specific filters work as you would expect them to work.</span></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji0qzFDcbfyxLanzUBLfeEfX3B2wQTiIgIkanMU98GlDwHx9cvjm_CHD7ob4_AYr9WbTfka-3-FelpCCythHwWLDa7qailIc115KtOp9tL3mmI2IeBdlZlohhU-0UV9hzG_4j8q2m_30c/s1600/Selection_072.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="239" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEji0qzFDcbfyxLanzUBLfeEfX3B2wQTiIgIkanMU98GlDwHx9cvjm_CHD7ob4_AYr9WbTfka-3-FelpCCythHwWLDa7qailIc115KtOp9tL3mmI2IeBdlZlohhU-0UV9hzG_4j8q2m_30c/s320/Selection_072.jpg" width="320" /></a></div>
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">8. Troubleshoot GCLID problems</span></h3>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">If you click on an ad in Google Search and you have setup autotagging, the GCLID needs to be propagated from googleadservices.com all the way to the the ads landing page. If you have redirects in between, for instance because all traffic for mobile devices is redirected to a different sub domain, the GCLID may be mangled or dropped. This results in seeing fewer sessions in your GA report than ad clicks.</span></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Tag Recordings reports all redirects and warns you if a GCLID gets dropped or mangled in a redirect. </span><a href="https://analytics.google.com/tagrecordings/example?id=ecommerce&page=page_405877" style="font-family: 'Helvetica Neue', Arial, Helvetica, sans-serif;" target="_blank">Example report</a></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><br /></span></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_R72JBqYdgFsYSlRXPST6LDuYx_Ddh5PAFcynSm1jKGjrER7XMPaYJDQ3-CAiLU5v4z0I6js66pldsdjQfkq174dKsmNzsiYPA8g_xAL58UcARYBt04n66da40k08IHDRq3uZ5AxmbZQ/s1600/Selection_076.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="424" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh_R72JBqYdgFsYSlRXPST6LDuYx_Ddh5PAFcynSm1jKGjrER7XMPaYJDQ3-CAiLU5v4z0I6js66pldsdjQfkq174dKsmNzsiYPA8g_xAL58UcARYBt04n66da40k08IHDRq3uZ5AxmbZQ/s640/Selection_076.jpg" width="640" /></a></div>
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">A demo site for Tag Recordings</span></h3>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">If you want to try out Tag Recordings yourself, you can record a journey through a sample site at <a href="http://search.kieviet.us/" target="_blank">http://search.kieviet.us</a>. This site will send various types of hits and show several different problems.</span></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">Before you go there, first create a new GA property. Next, enter the UA-string on the site so that the hits will be sent to this new property. This allows Tag Recordings to use the configuration of your property and display all the information associated with the hits. It also allows you to make changes to the GA configuration of the property so that you can see what happens due to the configuration change.</span></div>
<h3>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;">More information</span></h3>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><a href="https://chrome.google.com/webstore/detail/tag-assistant-by-google/kejbdjndbnbjgmefkgdddjlbokphdefk" target="_blank">Google Tag Assistant on the Chrome Store</a></span></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><a href="https://support.google.com/tagassistant/answer/2947093" target="_blank">Google Tag Assistant Help center</a></span></div>
<div>
<span style="font-family: "helvetica neue" , "arial" , "helvetica" , sans-serif;"><a href="https://support.google.com/analytics/answer/6277302" target="_blank">Tag Recordings Help Center</a></span></div>
<div>
<br /></div>
fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-55708873348219647292013-06-29T17:02:00.000-07:002013-06-29T17:02:03.361-07:00Cleaning out my Google Reader Starred ItemsTomorrow is the last day of Google Reader. I went through all my "starred items". Here are some that were worth saving. Enjoy!<br />
<br />
<br />
<br />
<br />
<br />
Mrs. O'Malley arrived in Boston from Ireland, and in no time at all her bean soup made her the talk of New England society. At a party celebrating the sale of her recipe to a fancy Charles Street restaurant, an old matron approached Mrs. O'Malley and said, "My dear girl, what is the secret of your soup?"<br />
<br />
Mrs. O'Malley said, "The secret o' me soup is that I use but two hundred thirty-nine beans to make it."<br />
<br />
The woman said, "Why only two hundred thirty-nine?"<br />
<br />
Mrs. O'Malley said, "Because one more would make it too farty."<br />
<br />
<br />
<br />
<br />
At the Olympic Games, Rhoda meets a man carrying an eight-foot-long metal stick.<br />
"Excuse me," says Rhoda to the man. "Are you a pole vaulter?"<br />
"No," says the man, "I'm German, but how did you know my name is Walter?"<br />
<br />
<br />
<br />
<br />
What happened to the butcher when he backed up into the meat grinder?<br />
He got a little behind in his orders.<br />
<br />
<br />
<br />
<br />
What's the difference between roast beef and pea soup?<br />
Anyone can roast beef.<br />
<br />
<br />
<br />
<br />
"So, what do you do for a living?"<br />
"I'm a cop."<br />
"Honest?"<br />
"No, the regular kind."<br />
<br />
<br />
<br />
<br />
*The caliber of your weapon is not as important as shot placement.*<br />
*The reliable Beretta Jetfire .22 Short pistol is a personal favorite of mine and I am never without it. It saved my life a few years ago when attacked by a Grizzly while hiking in the mountains with a family member. I was able to escape, walking at a brisk pace, after I shot my brother-in-law in the knee.*<br />
<br />
<br />
<br />
<br />
This little Italian boy and this little Jewish boy lived about a block apart in the neighborhood and basically grew up together. The Jewish boy was the son of a jeweler and the Italian boy was the son of a hitman. Oddly enough, they had the same birthday. Well, for their 12th birthday, the little Jewish boy gets a Rolex watch and the little Italian boy gets a .22 Baretta.<br />
<br />
The next day they are out on the street corner comparing their presents and neither is happy so they switch gifts with each other. The little Italian boy goes home to show his father and his father is NOT pleased!<br />
<br />
"What're you, nuts? Lemme tell you something, you idiot!! Some day you're gonna meet a nice girl, you're gonna wanna settle down and get married. You'll have a few kids, all that stuff. THEN one day, you're gonna come home and find your wife in bed with another man. What the heck ya gonna do??? Look at your watch and say, 'Hey, how long you gonna be?'<br />
<br />
<br />
<br />
<br />
<br />
Four brothers left home for college, and they became successful doctors and lawyers and prospered. Some years later, they chatted after having dinner together. They discussed the gifts they were able to give their elderly mother who lived far away in another city.<br />
The first said, "I had a big house built for Mama."<br />
The second said, " I had a hundred thousand dollar theater built in the house."<br />
The third said, "I had my Mercedes dealer deliver an SL600 to her."<br />
The fourth said, "You know how Mama loved reading the Bible and you know she can't read anymore because she can't see very well. I met this preacher who told me about a parrot that can recite the entire Bible. It took twenty preachers 12 years to teach him. I had to pledge to contribute $100,000 a year for twenty years to the church, but it was worth it. Mama just has to name the chapter and verse and the parrot will recite it."<br />
<br />
The other brothers were impressed. After the holidays Mom sent out her Thank You notes.<br />
<br />
She wrote: "Milton , the house you built is so huge I live in only one room, but I have to clean the whole house. Thanks anyway."<br />
<br />
"Marvin, I am too old to travel. I stay home, I have my groceries delivered, so never use the Mercedes. The thought was good. Thanks."<br />
<br />
"Michael, you gave me an expensive theater with Dolby sound, it could hold 50 people, but all of my friends are dead, I've lost my hearing and I'm nearly blind. I'll never use it. Thank you for the gesture just the same."<br />
<br />
"Dearest Melvin, you were the only son to have the good sense to give a little thought to your gift. The chicken was delicious. Thank you."<br />
<br />
Luv Ya, Mama<br />
<br />
<br />
<br />
<br />
A public union employee, a tea party activist, and a CEO are sitting at a table with a plate of a dozen cookies in the middle of it.<br />
The CEO takes 11 cookies, turns to the tea partier and says, "Watch out for that union guy. He wants a piece of your cookie."<br />
<br />
<br />
<br />
<br />
<br />
Dear Rubik's Cube,<br />
<br />
Done!<br />
<br />
Sincerely, Colorblind<br />
<br />
<br />
<br />
<br />
Dear Icebergs,<br />
<br />
Sorry to hear about the global warming. Karma's a bitch.<br />
<br />
Sincerely, The Titanic<br />
<br />
<br />
<br />
<br />
A couple preparing for a religious conversion meets with the orthodox rabbi for their final session.<br />
The rabbi asks if they have any final questions.<br />
The man asks, "Is it true that men and women don't dance together?"<br />
"Yes," says the rabbi, "For modesty reasons, men and women dance separately."<br />
"So I can't dance with my own wife?"<br />
"No."<br />
"Well, okay," says the man, "but what about sex?"<br />
"Fine," says the rabbi. "A mitzvah within the marriage!"<br />
"What about different positions?" the man asks.<br />
"No problem," says the rabbi.<br />
"Woman on top?" the man asks.<br />
"Why not?" replies the rabbi.<br />
"How about doggie-style?"<br />
"Of course!"<br />
"Well, what about standing up?"<br />
"NO!" says the rabbi....<br />
"Why Not???" asks the man.<br />
"Could lead to dancing!"<br />
<br />
<br />
<br />
<br />
<br />
A couple of biologists had twins. One they called John and the other control.<br />
<br />
<br />
<br />
<br />
A donkey had an IQ of 186. He had no friends at all though. Even in the animal kingdom, nobody likes a smart-ass.<br />
<br />
<br />
<br />
A key ring is a handy little gadget that allows you to lose all your keys at once.<br />
<br />
<br />
<br />
Due to a water shortage in Ireland, Dublin swimming pools have announced they are closing lanes 7 and 8.<br />
<br />
<br />
<br />
A termite walks into a barroom and asks, "Is the bartender here?"<br />
<br />
<br />
<br />
A gorilla walks into a bar.<br />
He orders a beer.<br />
The bartender says, "That'll be $10. You know, we don't get many gorillas coming in here."<br />
The gorilla says, "At $10 a beer, it's not hard to understand."<br />
<br />
<br />
<br />
<br />
<br />
Man driving down road.<br />
Woman driving up same road.<br />
They pass each other.<br />
Woman yells out window, "PIG!"<br />
Man yells out window, "BITCH!"<br />
Man rounds next curve.<br />
Man crashes into a HUGE PIG in middle of road and dies.<br />
<br />
<br />
<br />
<br />
<br />
<br />
A member of Parliament to Disraeli: "Sir, you will either die on the gallows or of some unspeakable disease."<br />
"That depends, Sir," said Disraeli, "whether I embrace your policies or your mistress."<br />
<br />
<br />
<br />
<br />
<br />
<br />
An Australian travel writer touring North America was checking out of the Spokane Hilton, and as he paid his bill said to the manager, "By the way, what's with the Indian chief sitting in the lobby? He's been there ever since I arrived."<br />
<br />
"Oh that's 'Big Chief Forget-me Not'," said the manager. "The hotel is built on an Indian reservation, and part of the agreement is to allow the chief free use of the premises for the rest of his life. He is known as 'Big Chief Forget-me Not' because of his phenomenal memory. He is 92 and can remember the slightest detail of his life."<br />
<br />
The travel writer took this in, and as he was waiting for his cab decided to put the chief's memory to the test.<br />
<br />
"'ello, mate!" said the Aussie, receiving only a slight nod in return. "What did you have for breakfast on your 21st birthday?"<br />
<br />
"Eggs," was the chief's instant reply, without even looking up, and indeed the Aussie was impressed.<br />
<br />
He went off on his travel writing itinerary, right across to the east coast and back, telling others of Big Chief Forget-me Not's great memory. (One local noted to him that 'How' was a more appropriate greeting for an Indian chief than ''ello mate.') On his return to the Spokane Hilton six months later, he was surprised to see 'Big Chief Forget-me Not' still sitting in the lobby, fully occupied with whittling away on a stick.<br />
<br />
"How?" said the Aussie.<br />
<br />
"Scrambled," said the Chief.<br />
<br />
<br />
<br />
<br />
"Doctor, Doctor! You've gotta help me! my husband thinks he's Moses!"<br />
<br />
"Tell him to stop taking the Tablets."<br />
<br />
<br />
<br />
<br />
I was in the restaurant yesterday when I suddenly realized I desperately needed to let off gas.. The music was really, really loud, so I timed it with the beat of the music.<br />
<br />
After a couple of songs, I started to feel lots better. I finished my coffee, and noticed that everybody was staring at me...<br />
<br />
Then I suddenly remembered that I was listening to my iPod.<br />
<br />
<br />
<br />
<br />
<br />
Doctor: "I'm stumped. I guess we're just going to have to wait for the autopsy results."<br />
<br />
<br />
<br />
<br />
<br />
<br />
Dear Diary,<br />
<br />
Last Sunday in Church I told my wife, "I just let out a silent fart; what do you think I should do?"<br />
<br />
She replied, "Get a new battery in your hearing aid."<br />
<br />
<br />
<br />
<br />
<br />
Teacher: Maria, go the map and find North America.<br />
<br />
Maria: Here it is.<br />
<br />
Teacher: Correct. Now class, who discovered America?<br />
<br />
Class: Maria<br />
<br />
<br />
<br />
<br />
<br />
<br />
Two little kids are in a hospital, lying on gurneys next to each other outside the operating room.<br />
<br />
The first kid leans over and asks, "What are you in here for?"<br />
<br />
The second kid says, "I'm in here to get my tonsils out and I'm a little nervous."<br />
<br />
The first kid says, "You've got nothing to worry about. I had that done when I was four. They put you to sleep, and when you wake up they give you lots of Jell-O and ice cream. It's a breeze."<br />
<br />
The second kid then asks, "What are you here for?"<br />
<br />
The first kid says," A circumcision."<br />
<br />
The second kid says, "Whoa, Good luck buddy! I had that done when I was born. Couldn't walk for a year."<br />
<br />
<br />
<br />
<br />
A wife was making a breakfast of fried eggs for her husband.<br />
<br />
Suddenly, her husband burst into the kitchen. 'Careful,' he said, 'CAREFUL! Put in some more butter! Oh my gosh! You're cooking too many at once. TOO MANY! Turn them! TURN THEM NOW! We need more butter. Oh my gosh! WHERE are we going to get MORE BUTTER? They're going to STICK! Careful. CAREFUL! I said be CAREFUL! You NEVER listen to me when you're cooking! Never! Turn them! Hurry up! Are you CRAZY? Have you LOST your mind? Don't forget to salt them. You know you always forget to salt them Use the! salt. USE THE SALT! THE SALT!'<br />
<br />
The wife stared at him. 'What in the world is wrong with you? You think I don't know how to fry a couple of eggs?'<br />
<br />
The husband calmly replied, 'I just wanted to show you what it feels like when I'm driving.'<br />
<br />
<br />
<br />
<br />
<br />
<br />
A young suitor was being led through the voluminous pages of the old family album by his girl's proud father. After seeing scores of members of the clan, the young man was finally shown the picture of a solid-looking old gentleman.<br />
<br />
"This," said the father proudly, "is the founder of the family."<br />
<br />
"What did he do?" asked the young man.<br />
<br />
"He founded the family," the older man said again.<br />
<br />
"I mean, sir," the suitor floundered, "what did he do to distinguish himself?"<br />
<br />
"He was the founder of the family," the father rasped in exasperation.<br />
<br />
"I understand that, sir," the suitor sighed. "I just wondered what the old gentleman did during the day."<br />
<br />
<br />
<br />
<br />
<br />
Q: Did you hear about the dyslexic who tried to commit suicide?<br />
A: He threw himself behind an oncoming train.<br />
<br />
<br />
<br />
<br />
Last night at the Pub somebody told a joke disparaging Italians, by purporting them as stupid.<br />
I was really offended and shouted, "Hey you! I'm Italian and I don't like you telling those Italian jokes!"<br />
Then I pulled out a razor and everybody was quite concerned, until they realized I couldn't find a place to plug it in.<br />
<br />
<br />
<br />
<br />
I was so depressed last night thinking about the economy, wars, jobs, my savings, Social Security, retirement funds, etc., I called the Suicide Lifeline. I got a call center in Pakistan , and when I told them I was suicidal, they got all excited, and asked if I could drive a truck.<br />
<br />
<br />
<br />
Charles Dickens goes into a bar: "I'd like a martini, please."<br />
Bartender: "Certainly, sir. Olive or twist?"<br />
<br />
<br />
<br />
<br />
"As good as this Pub is," said the Scotsman, "I still prefer the pubs back home. In Glasgow, there's a wee place called McTavish's. The landlord goes out of his way for the locals. When you buy four drinks, he'll buy the fifth drink."<br />
<br />
"Well, Angus," said the Englishman, "at my local spot in London, the Red Lion, the barman will buy you your third drink after you buy the first two."<br />
<br />
"Ahhh, dat's nothin'," said the Irishman, "back home in my favorite pub, the moment you set foot in the place, they'll buy you a drink, then another, all the drinks you like, actually. Then, when you've had enough drinks, they'll take you upstairs and see dat you gets laid, all on the house!"<br />
<br />
The Englishman and Scotsman were suspicious of the claims. The Irishman swore every word was true.<br />
<br />
"Did this actually happen to you?"<br />
<br />
"Well, not meself, personally, no," admitted the Irishman, "but it did happen to me sister quite a few times."<br />
<br />
<br />
<br />
<br />
<br />
This guy is in the emergency room after a car accident. When he wakes up, the surgeon is at his bedside and says, "I have some good news and some bad news."<br />
<br />
The patient says, "Give me the bad news first."<br />
<br />
"OK," says the surgeon, "We had to amputate both legs."<br />
<br />
"What's the good news?"<br />
<br />
The surgeon says, "I really like your shoes; I'll give you $300 for 'em."<br />
<br />
<br />
<br />
<br />
A young American tourist goes on a guided tour of a creepy old castle. At the end of the tour the guide asks her how she enjoyed it. She admits to being a bit worried about seeing a ghost in some of the dark cobwebby rooms and passages.<br />
<br />
"Don't worry" says the guide, "I've never seen a ghost all the time I've been here."<br />
<br />
"How long is that?" asks the girl.<br />
<br />
"About three hundred years."<br />
<br />
<br />
<br />
<br />
The devout cowboy lost his favorite Bible while he was mending fences out on the range.<br />
<br />
Three weeks later a cow walked up carrying the Bible in it's mouth.<br />
<br />
The cowboy couldn't believe his eyes.<br />
<br />
He took the book out of the cow's mouth, raised his eyes heavenward and exclaimed, "It's a miracle!"<br />
<br />
"Not really," said the cow.<br />
<br />
"Your name was written inside the cover."<br />
<br />
<br />
<br />
<br />
"Anyone with 'needs' to be prayed over, come forward, to the front at the altar," the Preacher says.<br />
<br />
Leroy gets in line, and when it's his turn, the preacher asks, "Leroy, what do you want me to pray about for you?"<br />
<br />
Leroy replies, "Preacher, I need you to pray for help with my hearing."<br />
<br />
The preacher puts one finger in Leroy's ear, and he places the other hand on top of Leroy's head and prays and prays and prays, he prays a blue streak for Leroy. The whole congregation joined in with enthusiasm.<br />
<br />
After a few minutes, the Preacher removes his hands, stands back and asks, "Leroy, how is your hearing now?"<br />
<br />
Leroy says, "I don't know, Reverend, it ain't 'til next Wednesday."<br />
<br />
<br />
<br />
<br />
<br />
The pastor asked if anyone in the congregation would like to express praise for answered prayers. Suzie Smith stood and walked to the podium.<br />
<br />
She said, "I have a praise. Two months ago, my husband, Tom, had a terrible bicycle wreck and his scrotum was completely crushed. The pain was excruciating and the doctors didn't know if they could help him."<br />
<br />
You could hear a muffled gasp from the men in the congregation as they imagine the pain that poor Tom must have experienced.<br />
<br />
"Tom was unable to hold me or the children," she went on, "and every move caused him terrible pain. We prayed as the doctors performed a delicate operation, and it turned out they were able to piece together the crushed remnants of Tom's scrotum, and wrap wire around it to hold it in place."<br />
<br />
Again, the men in the congregation cringed and squirmed uncomfortably as they imagined the horrible surgery performed on Tom.<br />
<br />
"Now," she announced in a quivering voice, "thank the Lord, Tom is out of the hospital and the doctors say that with time, his scrotum should recover completely."<br />
<br />
All the men sighed with unified relief. The pastor rose and tentatively asked if anyone else had something to say.<br />
<br />
A man stood up and walked slowly to the podium.<br />
<br />
He said, "I'm Tom Smith."<br />
<br />
The entire congregation held its breath.<br />
<br />
"I just want to tell my wife the word is sternum."<br />
<br />
<br />
<br />
<br />
<br />
When weeding, the best way to make sure you are removing a weed and not a valuable plant is to pull on it. If it comes out of the ground easily, it is a valuable plant.<br />
<br />
<br />
<br />
<br />
I lost the pub quiz last night by 1 point.<br />
<br />
The last question was "where do most women have curly hair?"<br />
<br />
Apparently the correct answer is Africa...<br />
<br />
<br />
<br />
<br />
<br />
<br />
We must believe in luck. For how else can we explain the success of those we don't like.<br />
<br />
<br />
<br />
<br />
<br />
Darryl and Harold were in a mental institution. The place had an unusual annual contest, picking two of the best patients and giving them two questions. If they got them correct, they were deemed cured and free to go.<br />
<br />
Darryl was called into the doctor s office first and asked if he understood that he'd be free if he answered the questions correctly. Darryl said "Yes" and the doctor proceeded. "Darryl, what would happen if I poked out one of your eyes?"<br />
<br />
Darryl said, "I'd be half blind."<br />
<br />
"That's correct. What if I poked out both eyes?"<br />
<br />
"I d be completely blind." The doctor stood up, shook Darryl s hand, and told him he was free to go.<br />
<br />
On Darryl's way out, as the doctor filled out the paperwork, Darryl mentioned the exam to Harold, who was seated in the waiting room. He told him what questions were going to be asked and gave him the answers.<br />
<br />
So Harold went into the doctor's office when he was called. The doctor went thru the formalities and then asked, "What would happen if I cut off one of your ears?" Remembering what Darryl had told him, he answered, "I'd be half blind."<br />
<br />
The doctor looked a little puzzled, but went on. "What if I cut off the other ear?"<br />
<br />
"I'd be completely blind," Harold answered."<br />
<br />
"Harold, can you explain how you'd be blind?"<br />
<br />
"My hat would fall down over my eyes."<br />
<br />
<br />
<br />
<br />
In 1986, Peter Davies was on holiday in Kenya after graduating from Northwestern University.<br />
<br />
On a hike through the bush, he came across a young bull elephant standing with one leg raised in the air. The elephant seemed distressed, so Peter approached it very carefully.<br />
<br />
He got down on one knee and inspected the elephant's foot and found a large piece of wood deeply embedded in it. As carefully and as gently as he could, Peter worked the wood out with his hunting knife, after which the elephant gingerly put down its foot. The elephant turned to face the man, and with a rather curious look on its face, stared at him for several tense moments. Peter stood frozen, thinking of nothing else but being trampled. Eventually the elephant trumpeted loudly, turned, and walked away Peter never forgot that elephant or the events of that day.<br />
<br />
Twenty years later, Peter was walking through the Chicago Zoo with his teenaged son. As they approached the elephant enclosure, one of the creatures turned and walked over to where Peter and his son Cameron were standing. The large bull elephant stared at Peter lifted its front foot off the ground and then put it down. The elephant did that several times then trumpeted loudly, all the while staring at the man.<br />
<br />
Remembering the encounter in 1986, Peter couldn't help wondering if this was the same elephant. Peter summoned up his courage, climbed over the railing and made his way into the enclosure. He walked right up to the elephant and stared back in wonder. The elephant trumpeted again, wrapped its trunk around one of Peter legs and slammed his stupid ass against the railing, killing him instantly.<br />
<br />
Probably wasn't the same elephant.<br />
<br />
<br />
<br />
<br />
One winter morning a husband and wife in Denver were listening to the radio during breakfast. They heard the announcer say, "We are going to have 8 to 10 inches of snow today. You must park your car on the even-numbered side of the street, so the snowplows can get through."<br />
<br />
So the good wife went out and moved her car.<br />
<br />
A week later while they are eating breakfast again, the radio announcer said, "We are expecting 10 to 12 inches of snow today. You must park your car on the odd-numbered side of the street, so the snowplows can get through."<br />
<br />
The good wife went out and moved her car again.<br />
<br />
The next week they are again having breakfast, when the radio announcer says, "We are expecting 12 to 14 inches of snow today. You must park...." Then the electric power went out. The good wife was very upset, and with a worried look on her face she said, "Honey, I don't know what to do. Which side of the street do I need to park on so the snowplows can get through?"<br />
<br />
With the love and understanding in his voice that all men who are married to blondes exhibit, the husband replied, "Why don't you just leave it in the garage this time?"<br />
<br />
<br />
<br />
<br />
One day an old German Shepherd starts chasing rabbits and, before long, discovers that he's lost. Wandering about, he notices a panther heading rapidly in his direction with the intention of having lunch.<br />
<br />
The old German Shepherd thinks, "Oh, oh! I'm in deep doo-doo now!" Noticing some bones on the ground close by, he immediately settles down to chew on the bones with his back to the approaching cat. Just as the panther is about to leap, the old German Shepherd exclaims loudly, "Boy, that was one delicious panther! I wonder if there are any more around here?"<br />
<br />
Hearing this, the young panther halts his attack in mid-strike; a look of terror comes over him, and he slinks away into the trees. "Whew!" says the panther, "That was close! That old German Shepherd nearly had me!"<br />
<br />
Meanwhile, a squirrel watching the whole scene from a nearby tree figures he can put this knowledge to good use and trade it for protection from the panther. So, off he goes. The squirrel soon catches up with the panther, spills the beans, and strikes a deal for himself with the panther.<br />
<br />
The young panther is furious at being made a fool of and says, "Here, squirrel, hop on my back and see what's going to happen to that conniving canine!"<br />
<br />
Now, the old German Shepherd sees the panther coming with the squirrel on his back and thinks, "What am I going to do now?" but instead of running, the dog sits down with his back to his attackers, pretending he hasn't seen them yet, and just when they get close enough to hear, the old German Shepherd says... "Where's that squirrel? I sent him off an hour ago to bring me another panther!"<br />
<br />
Moral of this story...<br />
<br />
Don't mess with the old dogs... Age and skill will always overcome youth and treachery! BS and brilliance come only with age and experience.<br />
<br />
<br />
<br />
<br />
Gracie Allen's Classic Recipe for Roast Beef:<br />
<br />
1 large roast of beef<br />
<br />
1 small roast of beef<br />
<br />
Take the two roasts and put them in the oven.<br />
<br />
When the little one burns, the big one is done.<br />
<br />
<br />
<br />
<br />
A man was driving down the road and ran out of gas. Just at that moment, a bee flew in his window.<br />
<br />
The bee said, 'What seems to be the problem?'<br />
<br />
'I'm out of gas,' the man replied.<br />
<br />
The bee told the man to wait right there and flew away. Minutes later, the man watched as an entire swarm of bees flew to his car and into his gas tank. After a few minutes, the bees flew out.<br />
<br />
'Try it now,' said one bee.<br />
<br />
The man turned the ignition key and the car started right up. 'Wow!' the man exclaimed, 'what did you put in my gas tank'?<br />
<br />
The bee answered, 'Bee Pee.'<br />
<br />
<br />
<br />
<br />
When I was young my intent was to go to medical school, but I didn't pass the entrance exam. One of the questions was:<br />
<br />
"Rearrange the letters P N E S I to spell out an important part of human body that is more useful when erect."<br />
<br />
Those who spelled SPINE became Doctors...<br />
<br />
The rest of us ended up working for the government.<br />
<br />
fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-38600892188143298222012-03-14T20:36:00.002-07:002012-03-14T22:25:43.204-07:00blog.sun.com/fkieviet became Oracle, and then…When Oracle took over Sun, the idea was that employee's blogs would survive the acquisition. They did, sort of. Right now, a lot of pictures on my blog at Oracle have disappeared.
<p>Time to finally move my blog to blogger where I have full control over it, and where I don't have to worry that some day it will disappear altogether. There are a few entries on here (about memory leaks) that still draw a few hundred hits per day.
<p>Missing pictures:</p>
<p><a href="http://lh6.ggpht.com/-mG-wKzAYhNw/T2FjJYsbV4I/AAAAAAAAFWI/aEGzY6WEQp4/s1600-h/bytespersecond%25255B3%25255D.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="bytespersecond" border="0" alt="bytespersecond" src="http://lh3.ggpht.com/-aY9Y_h54Jy4/T2FjJ7Vmr9I/AAAAAAAAFWM/dckj7UDQ7EE/bytespersecond_thumb%25255B1%25255D.jpg?imgmax=800" width="433" height="378"></a> <a href="http://lh4.ggpht.com/-ndUC7jEwpzc/T2FjL1vL3LI/AAAAAAAAFWQ/hx22SrAJqkk/s1600-h/cmstates-access%25255B3%25255D.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="cmstates-access" border="0" alt="cmstates-access" src="http://lh3.ggpht.com/-kYhVjGsmONg/T2FjMjVLOUI/AAAAAAAAFWU/NF8ePyOcAKc/cmstates-access_thumb%25255B1%25255D.jpg?imgmax=800" width="447" height="156"></a> <a href="http://lh3.ggpht.com/-382j_7ntmD8/T2FjNNQZgII/AAAAAAAAFWY/1NUndCx2N80/s1600-h/cmstates-enlist%25255B5%25255D.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="cmstates-enlist" border="0" alt="cmstates-enlist" src="http://lh4.ggpht.com/-CpFqpv7EMQQ/T2FjN8MP_MI/AAAAAAAAFWc/o1t86PnhNMw/cmstates-enlist_thumb%25255B3%25255D.jpg?imgmax=800" width="453" height="178"></a> <a href="http://lh6.ggpht.com/-04_nqGBAYg0/T2FjOvWqtaI/AAAAAAAAFWg/s_DSU4fnB2o/s1600-h/gcleakservlet%25255B4%25255D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="gcleakservlet" border="0" alt="gcleakservlet" src="http://lh4.ggpht.com/-D3J6uIizwj4/T2FjPYxlBzI/AAAAAAAAFWk/Sbv_iPs1vng/gcleakservlet_thumb%25255B2%25255D.png?imgmax=800" width="484" height="345"></a> <a href="http://lh3.ggpht.com/-i85KVADNd5s/T2FjP2YeUXI/AAAAAAAAFWo/XT79hQeY6XI/s1600-h/gcsimpleservlet%25255B3%25255D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="gcsimpleservlet" border="0" alt="gcsimpleservlet" src="http://lh3.ggpht.com/-Eb-38rZsuZs/T2FjQqsjAHI/AAAAAAAAFWs/GmSVdOUPbBw/gcsimpleservlet_thumb%25255B1%25255D.png?imgmax=800" width="494" height="249"></a> <a href="http://lh6.ggpht.com/-dIaE4JHtmKU/T2FjTZo_hPI/AAAAAAAAFWw/yjh4RXrr0SU/s1600-h/gflifecycle%25255B3%25255D.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="gflifecycle" border="0" alt="gflifecycle" src="http://lh5.ggpht.com/-6xkdn46G8jk/T2FjUEsj8RI/AAAAAAAAFW0/kO0QCuINNaw/gflifecycle_thumb%25255B1%25255D.jpg?imgmax=800" width="509" height="497"></a> <a href="http://lh6.ggpht.com/-tRClmhK9tHQ/T2FjU6mN1AI/AAAAAAAAFW4/Pa_J5ErZ-FA/s1600-h/hat1%25255B3%25255D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="hat1" border="0" alt="hat1" src="http://lh3.ggpht.com/-l4z70yPhSv4/T2FjVpDTSbI/AAAAAAAAFW8/ya0pnRSIO2s/hat1_thumb%25255B1%25255D.png?imgmax=800" width="491" height="430"></a> <a href="http://lh6.ggpht.com/-toK-5cVv4_Q/T2FjXWL_1MI/AAAAAAAAFXA/ZecREKMWafQ/s1600-h/hat2%25255B3%25255D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="hat2" border="0" alt="hat2" src="http://lh3.ggpht.com/-TIkHxQ8rkTY/T2FjgDLVq9I/AAAAAAAAFXE/-SZL22vijeo/hat2_thumb%25255B1%25255D.png?imgmax=800" width="505" height="442"></a> <a href="http://lh5.ggpht.com/-MwOgGFUDomM/T2FjhmelfoI/AAAAAAAAFXI/cdHoG8Wy3U8/s1600-h/hat3%25255B3%25255D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="hat3" border="0" alt="hat3" src="http://lh6.ggpht.com/-eJb1arxN97o/T2Fjjc4r3iI/AAAAAAAAFXM/xRf4B4tNFuI/hat3_thumb%25255B1%25255D.png?imgmax=800" width="521" height="456"></a> <a href="http://lh5.ggpht.com/-PpjN4SvKKYY/T2FjkS7WF2I/AAAAAAAAFXQ/sHQ9Z-p7mEI/s1600-h/hat4%25255B4%25255D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="hat4" border="0" alt="hat4" src="http://lh3.ggpht.com/-iCgzvsHmfeY/T2FjlNPBB_I/AAAAAAAAFXU/ByM25p4Sow4/hat4_thumb%25255B2%25255D.png?imgmax=800" width="502" height="569"></a> <a href="http://lh6.ggpht.com/-pKDB651Nn5w/T2Fjl2BZ5mI/AAAAAAAAFXY/PK6vTU_pgls/s1600-h/messagespersecond%25255B4%25255D.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="messagespersecond" border="0" alt="messagespersecond" src="http://lh4.ggpht.com/-zCzgf2IFijY/T2Fjnr_Y82I/AAAAAAAAFXc/jj7hkZt12lY/messagespersecond_thumb%25255B2%25255D.jpg?imgmax=800" width="517" height="359"></a> <a href="http://lh4.ggpht.com/-Xjc-UmpT0a4/T2FjoLOSP8I/AAAAAAAAFXg/AaHOLEA5Xic/s1600-h/simpleservlet%25255B3%25255D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="simpleservlet" border="0" alt="simpleservlet" src="http://lh3.ggpht.com/-47ek-2Vnkv8/T2FjozfK0zI/AAAAAAAAFXk/QA3bIInw6KE/simpleservlet_thumb%25255B1%25255D.png?imgmax=800" width="505" height="275"></a> <a href="http://lh3.ggpht.com/-cipBih8Mn4M/T2FjpXpcPvI/AAAAAAAAFVo/-c5neAEDj4A/s1600-h/speakingatjavaone2008%25255B2%25255D.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="speakingatjavaone2008" border="0" alt="speakingatjavaone2008" src="http://lh4.ggpht.com/-Mz2EbUFkvdA/T2FjqhewJjI/AAAAAAAAFVw/cy7wKtSIrvo/speakingatjavaone2008_thumb.jpg?imgmax=800" width="174" height="97"></a> <a href="http://lh3.ggpht.com/-LyJKYv7TAk4/T2FjrLCRd1I/AAAAAAAAFXo/8pMsevf1nyg/s1600-h/writespersecond%25255B4%25255D.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="writespersecond" border="0" alt="writespersecond" src="http://lh6.ggpht.com/-cxtThF33TX8/T2Fjry1N6tI/AAAAAAAAFXs/vv73VA_pWfY/writespersecond_thumb%25255B2%25255D.jpg?imgmax=800" width="471" height="379"></a> </p> <p></p> <p><a href="http://lh6.ggpht.com/-MlKRkK3JR-o/T2FpWdkTd9I/AAAAAAAAFXw/eUR6Qz0d4ro/s1600-h/leakservlet%25255B3%25255D.png"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="leakservlet" border="0" alt="leakservlet" src="http://lh6.ggpht.com/-NIIZrPoikT8/T2FpW7j1D1I/AAAAAAAAFX4/mIBoDPA6748/leakservlet_thumb%25255B1%25255D.png?imgmax=800" width="486" height="348"></a></p>fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com1tag:blogger.com,1999:blog-6643071316318130698.post-72993441948462755112010-07-31T22:02:00.001-07:002010-08-18T21:12:38.529-07:00GlassFish Security<p><a href="http://www.packtpub.com/">Pact Publishing</a> was kind enough to send me a copy of <a href="http://www.packtpub.com/glassfish-security-with-java-ee/book?utm_source=frankkieviet.blogspot.com&utm_medium=bookrev&utm_content=blog&utm_campaign=mdb_003506">the book "GlassFish Security"</a> that was released very recently. </p> <p><a title="GlassFish Security" href="http://www.packtpub.com/glassfish-security-with-java-ee/book?utm_source=frankkieviet.blogspot.com&utm_medium=bookrev&utm_content=blog&utm_campaign=mdb_003506"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="image" border="0" alt="image" src="http://lh5.ggpht.com/_k73ELjWNXV8/TGyumsR6hTI/AAAAAAAAB2k/VpBsLKa3d-4/image%5B1%5D.png?imgmax=800" width="125" height="152"></a> </p> <p>It's tough to find the time to read a book cover to cover. In fact, it's been a while since I've read a book from beginning to end. Typically I'm only interested in a few chapters which I then read. Later, when the need arises, I may get back to other chapter. It's like treating a book like an encyclopedia or a dictionary. I bet that most people read technical books that way nowadays. So it's important that a book lends itself to be read that way.</p> <p>Security is a very broad field with many many different topics, e.g. user authentication and authorization in web applications, integration with an external security server, web services security, and so on. Fortunately, very few people will have to deal with all these aspects at the same time. GlassFish Security covers many if not all of those aspects, and that's another reason why it should be possible to read this book like an encyclopedia.</p> <p>I went back in time and tried to remember all the times that I have had anything to do with security in GlassFish and checked what the book had to say about it. For instance, to start simple, there's the issue of configuring realms and users. There's the time I tried to get JSPWiki to work on GlassFish using declarative authentication and authorization, something that goes through server.policy. More advanced, there's the time we tried to integrate GlassFish with AccessManager. On all these topics, the book delivers. There are many other topics (e.g. integration with OpenSSO, securing JMX, etc), all described in detail without wasting much space on repetition or endless code or XML listings. The book would have saved me a lot of time had I had it at the time when I needed it.</p> <p>If you're using GlassFish, this book belongs on your bookshelf!</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-87441606169858206142010-04-28T08:44:00.001-07:002010-04-28T08:44:10.559-07:00Google TV Ads<p>On Tuesday I went to the Google office to talk about what I might be working on. It's likely going to be Google TV Ads. What is that? Here is a 3 minute video that explains it.</p> <div style="width: 620px; height: 395px; overflow: hidden" id="video_player_mask"><object id="SlateVPlayer" width="975" height="380" type="application/x-shockwave-flash" data="http://www.slatev.com/media/swfs/SlateVPlayer.swf"><param name="movie" value="http://www.slatev.com/media/swfs/SlateVPlayer.swf"><param name="flashVars" value="disableAd=true&autoPlay=false&videoId=72546547001&channel=arts-and-life&videoUrl=http://www.slatev.com/video/how-i-ran-ad-fox-news"><param name="wmode" value="transparent"><param name="base" value="http://admin.brightcove.com"><param name="allowFullScreen" value="true"><param name="allowScriptAcess" value="always"><param name="seamlesstabbing" value="false"><param name="swLiveConnect" value="true"></object></div> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com1tag:blogger.com,1999:blog-6643071316318130698.post-90100976317179258882010-04-26T14:58:00.001-07:002010-04-29T15:06:29.766-07:00Working with java.net: tips and tricks for project owners<p>In the past two years I've managed the OpenESB open source project. This project is hosted on java.net. In this post I outline a number of tips and tricks I've learned while managing this project on java.net. This is useful information for my successor, but it also may be useful to other people who are managing projects on java.net.</p> <h4>Web presence</h4> <p>An important factor in the success of an open source project is its web presence: it will be the first point of contact between a potential new user and the project.</p> <p>New visitors will want to see answers to these four items immediately:</p> <ul> <li>What is it? <li>License? <li>Getting started <li>Downloads</li></ul> <p>It is important that the links to these pages are static, i.e. that they do not change. That is because a new visitor may enter the site through a link on a different site or through a search engine. For this very reason, it is especially important to keep a single downloads page rather than a page per version, so that users do end up immediately on the latest version, rather than a previous (old) version.</p> <p>Looking at the front page of OpenESB, these four items are immediately apparent, and the links are permanent. Two more items are important: a visitor will judge a project for it being</p> <ul> <li>Alive <ul> <li>Active mailing list <li>Recent/frequent announcements</li></ul> <li>Professional <ul> <li>Well organized, thought through <li>Documentation easily found and comprehensive</li></ul> <li>Inviting <ul> <li>New users should feel welcome to not just use the product, but also to participate in the community. <p></p></li></ul></li></ul> <p>You be the judge how well these goals have been met. Let's look at the mechanics of java.net now.</p> <p><a href="http://lh3.ggpht.com/_k73ELjWNXV8/S9YMaDw0WXI/AAAAAAAAAEc/fFNCXXU1Zn8/s1600-h/openesbsite%5B5%5D.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="openesbsite" border="0" alt="openesbsite" src="http://lh3.ggpht.com/_k73ELjWNXV8/S9YMbHu8jGI/AAAAAAAAAEg/WClalxPi2m0/openesbsite_thumb%5B3%5D.jpg?imgmax=800" width="576" height="484"></a></p> <h4>java.net project web site</h4> <h5>How java.net processes HTML files</h5> <p>The HTML files for a project's web site are stored in the project's VCS system in a directory called www. There's a big twist here: although the HTML files are ordinary HTML files, i.e. with an <HTML>, <HEAD> and <BODY> tag, the java.net web server reads the HTML files and does a number of substitutions on the file before it writes the resulting file to the client.</p> <p>Conceptually, there is a java.net template HTML file which has its own <HEAD> element, its own <BODY> element with static content, and some placeholders in these two elements for:</p> <ul> <li>some elements of <HEAD> element of the user defined HTML file, e.g. <TITLE> <li>the contents of the project_tools.html file <li>the contents of the <BODY> element from the user defined HTML file</li></ul> <p></p> <p>As such, the project owner has to perform special tricks to have the page displayed in other ways than the standard java.net way.</p> <p>In OpenESB we have several java.net projects. All of them should have the same look and feel, i.e. the OpenESB "brand" and a common navigation. The same goes for the OpenESB wiki.</p> <h5>How OpenESB organizes HTML files</h5> <p>Each project has the same project_tools.html file. This file takes care of the following:</p> <ul> <li>It includes custom CSS files. These files override the java.net styles. <li>It hides the standard java.net navigation bar. For admins, the navbar display can be turned on: the state is maintained in a cookie. The value in the cookie can be toggled by loading the admin.html file in OpenESB: <a title="https://open-esb.dev.java.net/admin.html" href="https://open-esb.dev.java.net/admin.html">https://open-esb.dev.java.net/admin.html</a>. <li>Loads the menu. The menu is defined in a separate JavaScript file. <li>Displays the search box <li>Sets an event handler that is invoked when the page's loading is complete. The event handler manipulates the layout of the page and invokes Google Analytics.</li></ul> <p>As a result, the other HTML files in OpenESB or any of its sub projects don't have to bother with the common look and feel, the menu, etc. All of that is taken care of by project_tools.html, which as mentioned should be duplicated to all the OpenESB projects.</p> <p>Some or if you will, many, HTML files in OpenESB have another common style element: a right hand side bar that displays common advertising for downloading GlassFish ESB, the wealth of components, and the a partner highlight. This right side bar is taken care of a separate JavaScript file. Files that should display this right hand side bar should explicitly be formatted to do so: it should contain a table in which the right column loads the JavaScript of the right hand side bar.</p> <h5>Previewing HTML</h5> <p>The fact that the look and feel of an HTML file is now defined in project_tools.html makes it difficult to see what a file will actually look like when deployed (i.e. checked in into the project's VCS) on java.net. There is a workaround: I created a directory that when deployed on a web server, emulates the java.net environment. To edit a file with the ability to preview it without checking in the file, follow this procedure:</p> <ol> <li>Load the file in your browser through java.net's web server, e.g. <a title="https://open-esb.dev.java.net/Downloads.html" href="https://open-esb.dev.java.net/Downloads.html">https://open-esb.dev.java.net/Downloads.html</a>. <li>Save this file to the emulation directory, e.g. Tomcat/webapps/ROOT/Downloads.html <li>Now load the file into your browser through your local web server, e.g. <a title="https://open-esb.dev.java.net/Downloads.html" href="http://localhost:9080/Downloads.html">http://localhost:9080/Downloads.html</a>. This should look identical to what was loaded from java.net. <li>Locate the text in the file to be edited. Change the text and save the file. Reload the file in your browser. Repeat this step until the file looks OK. <li>Do a diff using a tool like Araxis Merge, or Beyond Compare between the local file and the file that was checked in into VCS, e.g. diff Tomcat/webapps/ROOT/Downloads.html and open-esb/www/Downloads.html. <li>Merge the changes into the VCS file, i.e. open-esb/www/Downloads.html. Save and check in into VCS.</li></ol> <p>Here is the <a href="http://mediacast.sun.com/users/Frank.Kieviet/media/ROOT.zip">directory for Tomcat</a>. If you use it for projects other than OpenESB, you can remove many of the files. Make sure to leave branding and css directories in tact. If you intend to reuse project_tools.html, don't forget to change the Google Analytics account ID.</p> <h5>SSL</h5> <p>Unfortunately, java.net uses SSL for all its files. This is unfortunate for two reasons:</p> <ul> <li>Files do not get cached in the browser: every time a user opens his browser and goes to OpenESB, all the images, style sheets, etc are reloaded again. <li>To avoid security warnings in Internet Explorer, files that are included in an HTML file also should come through SSL. This rule is violated on the OpenESB main page (of all places!) where it loads a dynamic list of news item from a non-https server.</li></ul> <p>There's no workaround for this. Collabnet promised a long time ago that SSL would be removed but this still has not happened.</p> <h5>Changing the menu</h5> <p>The menu is defined in a json like format in a file called menu.js. This file is duplicated in the wiki. See below. Make sure to use absolute URLs because the file is used from different projects, i.e. different roots.</p> <h4>Wiki (not on java.net)</h4> <p>OpenESB is not using the java.net wiki infrastructure. I don't recall why this decision was made. New projects should probably evaluate the java.net wiki infrastructure before considering setting up a wiki elsewhere.</p> <p>The OpenESB wiki is hosted on a machine hosted at Sun/Oracle, not at Collab net. It is collocated with other wikis, e.g. GlassFish, UpdateCenter, etc. The wiki engine is JSP wiki. The templates have been adapted to look and feel like the java.net OpenESB web site.</p> <p>Since java.net has / used to have a lot of downtime, and to avoid having this downtime impact the wiki, the menu.js file is duplicated on the wiki. That java.net uses ssl is another reason to duplicate the file. For the same reason, the icons displayed in the menu are also duplicated on the wiki server.</p> <p>Users are managed separately from java.net. User management / restrictions are necessary to avoid spam. Spam has been a been big problem on the OpenESB wiki and the other wikis collocated with it. Cleaning up spam can be done by directly manipulating the text files that make up the wiki page.</p> <p>JSPWiki security is configured to use JAAS. This fact is important if the wiki needs to be setup on a different GlassFish server. In that case make sure to update the server.policy file of GlassFish. The policy file specifies that only "validated" users can edit pages. Users are stored in a user database and a group database. The latter specifies group membership, of which the "validated" group is important. Both files are in the etc directory of the wiki directory.</p> <p>To add a user, the group database needs to be updated. This can be done through the web interface, <a title="http://wiki.open-esb.java.net/EditGroup.jsp?group=Validated" href="http://wiki.open-esb.java.net/EditGroup.jsp?group=Validated">http://wiki.open-esb.java.net/EditGroup.jsp?group=Validated</a> or can be done by editing the group file directly.</p> <p>The raw data files and user management files of the wiki can be accessed through SSH.</p> <p>Since the user management of the wiki is separate from java.net, users can choose a different userid on the wiki than they do on java.net. Allowing this is a mistake in retrospect: editing the wiki requires a SCA (Sun Contributor Agreement) or employment in Sun/Oracle, and having to manage/audit two sets of users instead of one is a waste of time. Again, in retrospect.</p> <p><a href="http://lh3.ggpht.com/_k73ELjWNXV8/S9YMbndCchI/AAAAAAAAAEk/G4ntBSEwOZE/s1600-h/openesbwiki%5B7%5D.jpg"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="openesbwiki" border="0" alt="openesbwiki" src="http://lh3.ggpht.com/_k73ELjWNXV8/S9YMeOF85dI/AAAAAAAAAEo/Bwapwmfn23c/openesbwiki_thumb%5B5%5D.jpg?imgmax=800" width="576" height="484"></a> </p> <h4>java.net user management</h4> <p>Anybody on java.net can request developer privileges. When that happens, an email is sent by java.net to the project owner. An automated process is setup that replies automatically to the user list asking the applicant for his motivation, background, etc. This reply is specified in role-approval.policy in the www directory.</p> <p>For some reason, many people ask for developer privileges. They are not known, have not contributed or communicated before, and are never heard from again.</p> <p>Consequently, this automated process sounds nice, but is useless in practice. A better process is that someone first communicates on the mailing list, proves he/she is serious and able to contribute, after which the project owner grants privileges. Before privileges are granted, a signed SCA needs to be on file. <a href="https://open-esb.dev.java.net/ContributionProcess.html">This process is documented</a> on the OpenESB web site.</p> <p>The link to the membership management page is displayed in the java.net nav bar. Recall that the nav bar is hidden. To toggle display of the nav bar, load <a title="https://open-esb.dev.java.net/admin.html" href="https://open-esb.dev.java.net/admin.html">https://open-esb.dev.java.net/admin.html</a>. Alternatively, jump to the page directly: <a title="https://open-esb.dev.java.net/servlets/ProjectMemberList" href="https://open-esb.dev.java.net/servlets/ProjectMemberList">https://open-esb.dev.java.net/servlets/ProjectMemberList</a>.</p> <p>When a user is granted commit privileges to a project, he/she also automatically gets commit privileges to the code repositories of the sub projects. E.g. granting commit privileges to open-esb also gives commit privileges to open-jbi-components, a sub project of open-esb. This is only the case if the VCS of the child project is of the same type as the parent project. Since open-jbi-components and open-esb both use CVS, open-esb committers have access to open-jbi-components. However, if the VCS system is different, e.g. openesb-builds uses SVN, separate access to this project needs to be granted next to open-esb.</p> <h4>java.net email management</h4> <p>Email lists are managed by an email list manager running in java.net. This email list manager is only partially integrated in java.net. Users can click on a button on the java.net site to subscribe to an email list. What effectively happens is that it will register the email address that is associated with that user <em>at that moment</em> in the list server. Similarly, a user can unsubscribe through a button, which will remove the email list associated with that user <em>at that moment. </em>Where this knowledge comes in handy? There is confusion if the user changes his email address associated with his java.net id between subscribing and unsubscribing. Sometimes users get so confused that they'll need help unsubscribing from the list. After all the last thing you want is repeated posts of "how do I unsubscribe from this list?".</p> <p>Here is another task with respect to email: spam. Protecting the list to spam is very important. Therefore lists should be setup as "discuss" or "moderated". A setting of "discuss" means that only "subscribed" or "allowed" posters can post. If someone posts a message who is not in the "subscribed" or "allowed" list, the message will be forwarded to the list owner(s) for review. Replying to that message will allow the message to be posted. To avoid getting too many of these "review messages", frequent posters can be added to the "allowed" list. The "allowed" list is accessible from the email management page.</p> <p>The link to the email list management page is displayed in the java.net nav bar. Recall that the nav bar is hidden. To toggle display of the nav bar, load <a title="https://open-esb.dev.java.net/admin.html" href="https://open-esb.dev.java.net/admin.html">https://open-esb.dev.java.net/admin.html</a>. Alternatively, jump to the page directly: <a title="https://open-esb.dev.java.net/servlets/ProjectMailingListList" href="https://open-esb.dev.java.net/servlets/ProjectMailingListList">https://open-esb.dev.java.net/servlets/ProjectMailingListList</a>.</p> <p>Nabble is a view on the mailing list. OpenESB has <a href="https://open-esb.dev.java.net/MailingLists.html">integrated</a> it on the web site. Note that posters are identified by their "from" email address only, but posters on Nabble somehow do not need to be in the "allowed" of "subscribed" list. This is fortunate, but puzzling from a technical point of view.</p> <p><a href="http://lh6.ggpht.com/_k73ELjWNXV8/S9YMfGyDwYI/AAAAAAAAAEs/0KLWvykA_BE/s1600-h/openesbemail%5B3%5D.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="openesbemail" border="0" alt="openesbemail" src="http://lh6.ggpht.com/_k73ELjWNXV8/S9YMf_lAhrI/AAAAAAAAAEw/QGBTy6GwuVw/openesbemail_thumb%5B1%5D.png?imgmax=800" width="576" height="484"></a></p> <p>This brings me to one more email list related tasks: once in a while, someone posts an email that he/she wants to revoke. For example, someone could accidentally send a private email to the list, or include confidential (customer information for example) information in an email.</p> <p>Of course there's no such thing as undoing the sending of an email. Subscribed users will have received the email by the time that the sender notices the mistake. However, there is an option to remove the email from Nabble and from the java.net mail browser. Note that there are two places where the email needs to be removed: both Nabble and java.net. Removing the mail there at least makes the mistake less visible and will make it harder for the mistaken email to be picked up by search engines.</p> <h4>java.net automation</h4> <p>Some of the management tasks on java.net can be automated. Kohsuke has developed an extensive set of tools that provide a Java api to java.net web interface: <a title="https://javanettasks.dev.java.net/" href="https://javanettasks.dev.java.net/">https://javanettasks.dev.java.net/</a>.</p> <p>I've written a few tools to make community management easier. One such tool is used to make an inventory of all the users in the community with commit privileges. Everybody in this list should either be a Sun/Oracle employee, or should have signed an SCA. The tool loads and parses the list of signatories (from <a href="https://sca.dev.java.net/CA_signatories.htm">https://sca.dev.java.net/CA_signatories.htm</a>) and compares this list with the one extracted from java.net. Discrepancies are people who are currently or have been Sun/Oracle employees. Since it is difficult to track who is no longer an employee (especially with the layoffs that used to happen every now and then), the tool can email all "discrepancies" and ask them to either submit an SCA or to verify employment by clicking a link pointing to an internal server. These tools are available on <a href="https://open-esb-build.dev.java.net/svn/open-esb-build/trunk/community-mgt/">https://open-esb-build.dev.java.net/svn/open-esb-build/trunk/community-mgt/</a>. </p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com2tag:blogger.com,1999:blog-6643071316318130698.post-33140567097967962462010-04-21T13:52:00.001-07:002010-04-21T13:52:02.882-07:00OpenESB under Oracle<p>It's been a few months since Oracle acquired Sun. At the time there were a lot of questions about what Oracle would do with OpenESB. On Feb 15, I posted <a href="http://n2.nabble.com/News-update-on-OpenESB-and-Oracle-tp4578158p4578158.html">a plan to the users mailing list</a>. What was the plan? And how is the plan coming together? How is OpenESB doing a few months after the acquisition? What will the future hold for OpenESB?</p> <p>First, let's try to understand Oracle's perspective. Oracle already has an integration product: the SOA Suite. Through the BEA acquisition it got another one, and through the Sun acquisition it got yet two more: CAPS and OpenESB. Of course there's no sense in keeping high levels of investments in <em>all</em> these products; it makes much more sense to focus on one product. What is this strategic product? Oracle is very clear about that: Oracle's strategic SOA middleware platform is the Oracle SOA Suite. Consequently, Oracle has reduced the level of investment in OpenESB. <p>That was the bad news. Now for the good news. Oracle could have just pulled out from OpenESB and let it fall into a black hole. Or worse, it could have taken the site down, removed the downloads, or put other obstacles in place. It did not. It did quite the contrary. <p>Although Sun was the main sponsor behind OpenESB, Oracle recognized that a lot of people made investments into OpenESB, either in the community or into their own company by <em>using</em> OpenESB, and with a feeling of responsibility and fairness, Oracle decided to help the community to find a way to stand on its own, so that all these investments would bear fruit for as long a time as possible. <p>Did you invest in OpenESB? Let's look at what will change for you depending on the kind of investment you made. <h5>What will change for you</h5> <p>What will change for you if you bought GlassFish ESB support? Nothing will change: you are still fully supported. The same Sun SOA Support department is still available to help on issues. Should patches be created to address issues, the full Sun SOA Sustaining department is still there to create patches. <p>Are you using GlassFish ESB but did not buy support? Unlike Sun, Oracle is no longer trying to sell GlassFish ESB licenses to new customers. Instead, you may rely on community support, or commercial support provided by one of the OpenESB community partners. The user mailing list is nowadays less frequented by Sun/Oracle engineers, but other community members have stepped up and are keeping the mailing list responsive. More about that below. <p>Are you investing in OpenESB by contributing code or other artifacts, or are considering to do so? You will find that it has become easier to contribute. I've put together a <a title="OpenESB governance" href="https://open-esb.dev.java.net/Governance.html">new governance document</a> that gives greater freedom to community contributors. Oracle can still exert influence, but that is intended to keep the peace in the community should that be necessary. Overall, you will find it easier to propose and implement new changes, to commit code, and last but not least, to influence the roadmap. This brings us to the future of OpenESB. <h5>The future of OpenESB</h5> <p>The future of OpenESB revolves around OpenESB becoming an open source community that can stand on its own, i.e. without Sun or Oracle as the single major sponsor. That transformation will of course not happen overnight, and Oracle is committed to help with this transformation. For instance, Oracle will do periodic builds and post these on the downloads site. Another commitment is that Oracle will merge patches that it makes for customers, back into the open source repository. <p>What did Oracle do so far? Next to the governance document I already mentioned, the sources of GlassFish ESB v2.2 can now be found in the open source repository. The HL7 BC and the WLM SE are also back in the open source repository, and the binaries can be downloaded from the downloads page. The process of an automatic periodic build has incurred some delays due to technical issues, but is well on its way. <h5>OpenESB today</h5> <p>How is OpenESB doing today? Let's look at the users mailing list. From <a href="http://markmail.org/search/?q=open-esb#query:open-esb%20list%3Anet.java.dev.open-esb.users+page:1+state:facets">the Users mailing list on Markmail</a>, it can be seen that the list activity has declined a bit, but is not much below the activity of that of a year ago. <p><a href="http://lh4.ggpht.com/_k73ELjWNXV8/S89las_aguI/AAAAAAAAAEE/Sk80c1O-ZVA/s1600-h/emailactivity3.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="emailactivity" border="0" alt="emailactivity" src="http://lh4.ggpht.com/_k73ELjWNXV8/S89la5HpC7I/AAAAAAAAAEI/VyGz1wQHBA8/emailactivity_thumb1.png?imgmax=800" width="422" height="195"></a></p> <p>What we also can learn from Markmail is that community members who are not on the Oracle payroll are stepping up. For example, here are <a href="http://markmail.org/search/?q=open-esb+list%3Anet.java.dev.open-esb.users+date%3A200801-201003+#query:open-esb%20list%3Anet.java.dev.open-esb.users%20date%3A201003%20+page:1+state:facets">the most active posters for March</a>:</p> <p><a href="http://lh6.ggpht.com/_k73ELjWNXV8/S89lbE26nCI/AAAAAAAAAEM/Byn8tssVN2s/s1600-h/emailactivity23.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="emailactivity2" border="0" alt="emailactivity2" src="http://lh3.ggpht.com/_k73ELjWNXV8/S89lbo6JLII/AAAAAAAAAEQ/iY5txdZhQgc/emailactivity2_thumb1.png?imgmax=800" width="226" height="289"></a> </p> <p>Another metric that we can look at is the number of users. In GlassFish ESB v2.1 we introduced a feature in which NetBeans checks for updates upon startup. By looking at the traffic to the updates-server, we can estimate how many users there are of GlassFish ESB. I defined the number of users as the number of NetBeans installations that ping the server at least three times in a time window greater than five days. As can be seen, the number of users is going up.</p> <p><a href="http://lh4.ggpht.com/_k73ELjWNXV8/S89lbyDR8PI/AAAAAAAAAEU/WoDhL5iCPkI/s1600-h/useractivty3.png"><img style="border-right-width: 0px; display: inline; border-top-width: 0px; border-bottom-width: 0px; border-left-width: 0px" title="useractivty" border="0" alt="useractivty" src="http://lh4.ggpht.com/_k73ELjWNXV8/S89lcXqjqhI/AAAAAAAAAEY/7U2oBNrbd_4/useractivty_thumb1.png?imgmax=800" width="451" height="263"></a> </p> <p>In terms of a roadmap there are no concrete proposals from the community yet, but several members have expressed interest in continuing with Fuji. <p>More good news: a few new committers have joined the project and are contributing code. fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com24tag:blogger.com,1999:blog-6643071316318130698.post-88110922104298174152010-04-21T13:50:00.001-07:002010-04-26T20:46:41.901-07:00Moving on… next stop: Google<p>I've decided it's time to move on. Yesterday I put in my resignation at Oracle. I'll be starting at Google in two weeks.</p> <p>Why? Was Oracle such a bad place to work? Was it such a culture shock for a Sun employee that it just could not work? Could I not stand my manager? Could I not get along with my new co-workers? </p> <p>If you're expecting a bitching session about big-bad-Oracle, I have to disappoint you. In the months that I've been at Oracle, I've come to the conclusion that it is a pretty decent company to work for. I like my manager, my co-workers are intelligent and pleasant, and the culture at Oracle is very similar as it was at Sun. So, honestly, no complaints there.</p> <p>The reason for my leaving is that I've been doing SOA for 7+ years now. I've been involved in the complete lifecycle of two products: CAPS and OpenESB. Now, with my transition into Oracle, I was about to commit to a third SOA product. Before I dove into that, I did some soul searching, and realized that I ready for something new, something in a different field.</p> <p>As I was looking for a change, I looked at what interesting companies there are here in Orange County. Google and Amazon came to mind. Google is said to be the engineering Valhalla, so I submitted my resume there. And surprise, surprise, I got an offer.</p> <p>It's a big jump. It's starting from the bottom again… jumping into something brand new. Will I like the change? I'm eager to find out!</p> <p><a href="http://lh6.ggpht.com/_k73ELjWNXV8/S9ZeHezeNsI/AAAAAAAAAE0/xxMv59WPUlU/s1600-h/feelinglucky%5B4%5D.png"><img style="border-bottom: 0px; border-left: 0px; display: block; float: none; margin-left: auto; border-top: 0px; margin-right: auto; border-right: 0px" title="feelinglucky" border="0" alt="feelinglucky" src="http://lh6.ggpht.com/_k73ELjWNXV8/S9ZeHj5debI/AAAAAAAAAE4/5Tz_fcTIOh0/feelinglucky_thumb%5B2%5D.png?imgmax=800" width="146" height="35"></a></p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com21tag:blogger.com,1999:blog-6643071316318130698.post-14839375138955853472010-02-25T11:28:00.001-08:002010-03-03T08:44:01.231-08:00I got a patent<p>Today I got a pleasant surprise when I got into the office: I found a big box on my desk. It turns out it was a nice engraved plaque of my patent on "Protected Concurrent FIFO" message processing.</p> <p><a href="http://lh5.ggpht.com/_k73ELjWNXV8/S46RylzA_YI/AAAAAAAAAD4/S7rHimL8GuE/s1600-h/Image1%5B3%5D.jpg"><img style="border-bottom: 0px; border-left: 0px; display: inline; border-top: 0px; border-right: 0px" title="Image1" border="0" alt="Image1" src="http://lh6.ggpht.com/_k73ELjWNXV8/S46R0OI3XUI/AAAAAAAAAD8/odu_tJ0KlrY/Image1_thumb%5B1%5D.jpg?imgmax=800" width="408" height="527"></a> </p> <p>The funny thing is that I didn't even know that the patent was awarded. After the patent application in 2004 or 2005, I don't even remember when it was, I lost track of its status. Emails to the patent lawyer went unanswered.</p> <p>I googled for the text of <a href="http://mediacast.sun.com/users/Frank.Kieviet/media/Patent-US7644197.pdf">the actual patent (US7644197 Queue management by multiple processors)</a> and found a PDF which I copied to Mediacast.</p> <p>A thank-you to Jerry Waldorf (my boss during the SeeBeyond days and co-applicant of the patent) for asking me to work on this subject, and a thank-you to Sun for the plaque!</p> <p>By the way, I have often said that almost all software patents are nonsense and inhibit progress rather than promote it. I still feel this way. Does that make me a hypocrite? Not necessarily: companies are forced to submit patents applications so that they can defend themselves against patent litigation from competitors. In that sense I'm happy that Sun got another patent. And that I'm happy that my name is on it: well, yes, I guess that's an ego-thing.</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com9tag:blogger.com,1999:blog-6643071316318130698.post-30769130944430880792010-01-15T10:41:00.001-08:002010-01-15T10:41:01.017-08:00How to enlist and delist XA resources: trial and error<p></p> <p>Once in a while a problem pops up with how a middleware stack supports XA. The problem I'm discussing here is the one of how to exactly enlist and delist <code>XAResource-</code>s with <code>isSameRM()</code>returning <font face="Courier New">true</font>. The XA spec is vague on this very topic, so this warrants some exploration.</p> <p>First of all, what do I mean with "enlist and delist <code>XAResource-</code>s with <code>isSameRM()</code>returning <font face="Courier New">true</font>?" </p> <h4>Enlisting and delisting</h4> <p>When using an XA capable system, the connection needs to be enlisted in the transaction before it is used. A typical sequence on the <font face="Courier New">XAResource</font>, let's call it <tt>r1</tt>, is:</p> <div id="codeSnippetWrapper"> <div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> r1.start(xid, TMNOFLAGS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> ... <span style="color: #008000">// use (r1)</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> r1.end(xid, TMSUCCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum4" style="color: #606060"> 4:</span> r1.commit(xid, true);</pre><!--CRLF--></div></div>
<p>With a second resource in the mix, the call sequence may become:</p>
<div id="codeSnippetWrapper">
<div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> r1.start(xid1, TMNOFLAGS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> ... <span style="color: #008000">// use r1</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> r2.start(xid2, TMNOFLAGS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum4" style="color: #606060"> 4:</span> ... <span style="color: #008000">// use r2</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum5" style="color: #606060"> 5:</span> r2.end(xid2, TMSUCCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum6" style="color: #606060"> 6:</span> r1.end(xid1, TMSUCCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum7" style="color: #606060"> 7:</span> r1.prepare(xid1);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum8" style="color: #606060"> 8:</span> r2.prepare(xid2);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum9" style="color: #606060"> 9:</span> p1.commit(xid1, false);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum10" style="color: #606060"> 10:</span> p2.commit(xid2, false);</pre><!--CRLF--></div></div>
<p></p>
<p>The transaction is now escalated to a full two-phase transaction. But if <tt>r1 </tt>and <tt>r2</tt> represent the same resource manager, a shortcut can be taken: one resource can "piggy back ride" on the other resource. The <font face="Courier New">isSameRM()</font> method can be used to check if this should be attempted. If it returns true, the sequence may become:</p>
<div id="codeSnippetWrapper">
<div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #008000">// MULTIPLE ACTIVE</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> r1.start(xid, TMNOFLAGS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> r1.isSameRM(r2); <span style="color: #008000">// returns true</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum4" style="color: #606060"> 4:</span> r2.start(xid, TMJOIN);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum5" style="color: #606060"> 5:</span> r2.end(xid, TMSUCCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum6" style="color: #606060"> 6:</span> r1.end(xid, TMSUCCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum7" style="color: #606060"> 7:</span> r1.commit(xid, true);</pre><!--CRLF--></div></div>
<p></p>
<p>The transaction now again is a single-phase transaction, and the performance difference with the two-phase commit case is usually very significant. </p>
<p>This sequence doesn't work for a number of popular systems, e.g. MQSeries and Oracle. Unfortunately the XA specification, but that specification is not quite clear at all about what resource managers should be able to do, so there is to some extent some trial and error involved. </p>
<h4>Trial and error</h4>
<p>How should <font face="Courier New">isSameRM() </font><font face="Verdana">be used then? By testing a number of different systems, it turns out <strong>that for some systems, there can be only one enlisted <font face="Courier New">XAResource</font> <em>active</em> in the transaction</strong>. If a second <font face="Courier New">XAResource</font> joins the transaction, the first one should be deactivated. Here is an example:</font></p>
<div id="codeSnippetWrapper">
<div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #008000">// SINGLE ACTIVE 1</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> r1.start(xid, TMNOFLAGS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> r1.isSameRM(r2); <span style="color: #008000">// should return true</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum4" style="color: #606060"> 4:</span> ... <span style="color: #008000">// use r1</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum5" style="color: #606060"> 5:</span> r1.end(xid, TMSUSPEND);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum6" style="color: #606060"> 6:</span> </pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum7" style="color: #606060"> 7:</span> r2.start(xid, TMJOIN);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum8" style="color: #606060"> 8:</span> ... <span style="color: #008000">// use r2</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum9" style="color: #606060"> 9:</span> r2.end(xid, TMSUCCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum10" style="color: #606060"> 10:</span> </pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum11" style="color: #606060"> 11:</span> r1.start(xid, TMRESUME);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum12" style="color: #606060"> 12:</span> ... <span style="color: #008000">// use r1 again</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum13" style="color: #606060"> 13:</span> r1.end(xid, TMSUCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum14" style="color: #606060"> 14:</span> </pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum15" style="color: #606060"> 15:</span> r1.commit(xid, true);</pre><!--CRLF--></div></div>
<div>A variation of this is calling <font face="Courier New">TMSUCCESS</font> instead of <font face="Courier New">TMSUSPEND</font>. In that case <font face="Courier New">TMJOIN</font> should be called instead of <font face="Courier New">TMRESUME</font>:</div>
<div id="codeSnippetWrapper">
<div id="codeSnippet" style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum1" style="color: #606060"> 1:</span> <span style="color: #008000">// SINGLE ACTIVE 2</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum2" style="color: #606060"> 2:</span> r1.start(xid, TMNOFLAGS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum3" style="color: #606060"> 3:</span> r1.isSameRM(r2); <span style="color: #008000">// should return true</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum4" style="color: #606060"> 4:</span> ... <span style="color: #008000">// use r1</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum5" style="color: #606060"> 5:</span> r1.end(xid, TMSUCCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum6" style="color: #606060"> 6:</span> </pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum7" style="color: #606060"> 7:</span> r2.start(xid, TMJOIN);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum8" style="color: #606060"> 8:</span> ... <span style="color: #008000">// use r2</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum9" style="color: #606060"> 9:</span> r2.end(xid, TMSUCCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum10" style="color: #606060"> 10:</span> </pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum11" style="color: #606060"> 11:</span> r1.start(xid, TMJOIN);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum12" style="color: #606060"> 12:</span> ... <span style="color: #008000">// use r1 again</span></pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum13" style="color: #606060"> 13:</span> r1.end(xid, TMSUCESS);</pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum14" style="color: #606060"> 14:</span> </pre><!--CRLF--><pre style="padding-right: 0px; padding-left: 0px; font-size: 8pt; padding-bottom: 0px; margin: 0em; overflow: visible; width: 100%; color: black; direction: ltr; border-top-style: none; line-height: 12pt; padding-top: 0px; font-family: 'Courier New', courier, monospace; border-right-style: none; border-left-style: none; background-color: #f4f4f4; text-align: left; border-bottom-style: none"><span id="lnum15" style="color: #606060"> 15:</span> r1.commit(xid, true);</pre><!--CRLF--></div></div>
<div>I ran these tests on a number of systems, and here is what I found:</div>
<table cellspacing="0" cellpadding="2" width="573" border="1">
<tbody>
<tr>
<td valign="top" width="137"> </td>
<td valign="top" width="100"><tt>isSameRM()</tt></td>
<td valign="top" width="113">Test: multiple active</td>
<td valign="top" width="121">Test: single active 1</td>
<td valign="top" width="100">Test: single active 2</td></tr>
<tr>
<td valign="top" width="143">STCMS</td>
<td valign="top" width="105">yes</td>
<td valign="top" width="116">yes</td>
<td valign="top" width="122">yes</td>
<td valign="top" width="103">yes</td></tr>
<tr>
<td valign="top" width="146">JMQ</td>
<td valign="top" width="107">as of 4.4</td>
<td valign="top" width="116">yes</td>
<td valign="top" width="121">no: throws on line 7</td>
<td valign="top" width="104">yes</td></tr>
<tr>
<td valign="top" width="147">WebSphereMQ 6</td>
<td valign="top" width="109">yes</td>
<td valign="top" width="115">no: blocks on line 4</td>
<td valign="top" width="120">no: blocks on line 7</td>
<td valign="top" width="104">yes</td></tr>
<tr>
<td valign="top" width="149">Derby 10.5.3.0</td>
<td valign="top" width="110">yes</td>
<td valign="top" width="115">no: blocks on line 4 </td>
<td valign="top" width="120">yes</td>
<td valign="top" width="104">yes</td></tr>
<tr>
<td valign="top" width="148">Oracle 11.1.0.6</td>
<td valign="top" width="111">yes</td>
<td valign="top" width="115">no: blocks on line 4</td>
<td valign="top" width="119">yes</td>
<td valign="top" width="103">yes</td></tr>
<tr>
<td valign="top" width="150">MySQL 5.1</td>
<td valign="top" width="111">yes</td>
<td valign="top" width="114">no: throws on line 4</td>
<td valign="top" width="119">no: throws on line 5</td>
<td valign="top" width="103">no: throws on line 7</td></tr></tbody></table>
<p>As can be seen, the <font face="Courier New">SINGLE ACTIVE 2</font> code sequence works best.</p>
<p>As a side note, MySQL is showing surprising behavior: <tt>TMJOIN</tt> and <font face="Courier New">TMSUSPEND </font>are not supported (as documented in the <a href="http://dev.mysql.com/doc/refman/5.1/en/xa-restrictions.html">MySQL documentation</a>), so why does it bother to return true on <font face="Courier New">isSameRM()</font>? Behavior like this makes it difficult to write portable code: a container now has to provide a wrapper around the DataSource that corrects for this. It would have been much better if it simply had returned <font face="Courier New">false</font> on <font face="Courier New">isSameRM()</font>.</p>
<p>How does this relate to what application code can do in for instance a Java EE container? That's the topic of a different blog post.</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com3tag:blogger.com,1999:blog-6643071316318130698.post-26420643500410035952010-01-12T17:46:00.001-08:002010-01-12T17:46:58.534-08:00Regex editor now on code.google.com<p>By popular demand, the sources of the Regular Expression Editor I discussed <a href="http://frankkieviet.blogspot.com/2009/01/interactive-regular-expression-editor.html">in a previous post</a>, is now <a href="http://code.google.com/p/regexeditor/">available on code.google.com</a>.</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com2tag:blogger.com,1999:blog-6643071316318130698.post-78534792105551182522010-01-12T11:59:00.001-08:002010-01-12T11:59:37.860-08:00Book "SOA with Java": Rough cut now online<p><a href="http://lh5.ggpht.com/_k73ELjWNXV8/S0zUpl4GP4I/AAAAAAAAAC8/xtZn7HmBSoQ/s1600-h/image%5B2%5D.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="244" alt="image" src="http://lh3.ggpht.com/_k73ELjWNXV8/S0zUqN_OsGI/AAAAAAAAADA/TToB7hQ6fMo/image_thumb.png?imgmax=800" width="244" border="0"></a></p> <p>A few months ago I got a request from Satadru Roy, one of the authors of the book "SOA with Java", to write a section on Open Source ESBs for this book.</p> <p>This was a good opportunity to advertise OpenESB, so I eagerly said yes. Satadru suggested that a practical example should be the core of the chapter, so I sat down with two of my colleagues, Murali Pottlapelli and Sujit Biswas, to discuss how the example would look like.</p> <p>The result is a 17 page chapter that gives an overview of OpenESB. The book is now available for <a href="http://my.safaribooksonline.com/9780137045914/ch13lev1sec6">review on Safari</a>. This link will probably be invalidated when the review period ends, but perhaps the <a href="http://my.safaribooksonline.com/9780137045914">link to the book itself</a> is more permanent (the <a href="http://www.amazon.com/Service-Oriented-Architecture-J2EE-Thomas-Erl/dp/0132435896/ref=sr_1_1?ie=UTF8&qid=1263326145&sr=8-1">book is also on Amazon</a>).</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-85284310352691535922009-11-06T17:53:00.001-08:002009-11-06T17:53:37.346-08:00Remote learning, graduate projects, and open source<p>Last week, an ex-colleague asked me if I could be a reviewer on a graduation project at <a href="http://www.nu.edu/">National University</a> where he is now a professor. Flattered, and finding myself with a little bit more time on my hands than usual, I accepted.</p> <p>The goal of the project was to let a team of seven students build a real-life application as a learning experience about how software development is done in the industry, for a "real" customer. The task was to develop a tracking application for the Boys & Girls Clubs of the Sequoias.</p> <p>There were several interesting aspects of this project: first of all, the end result was a working application that in quality and functionality exceeds a good portion of similar applications built by IT departments. Key to this was the use of off-the-shelf frameworks such as Spring MVC, Hibernate, BIRT, and jQuery. I'm glad that students are using frameworks like these rather than reinventing their own, although that might have been even more fun.</p> <p>Another interesting aspect was that the project was done as <a href="http://code.google.com/p/nucsc480project/">an open source project on</a> <a href="http://code.google.com/p/nucsc480project/">code.google.com</a>. The practical advantages are the infrastructure is provided completely ready by the code forge (in this case code.google.com), so that little time needs to be spent on setting up an infrastructure. This is especially important for students that are remote and hence don't have a shared work environment (e.g. office). Of course there's a lot more work to be done to make it a successful open source project, but that's not important in the scope of the project. I'm glad universities expose students to an open source way of development.</p> <p>Lastly, the presentation of the project was done using a tool called <a href="http://www.elluminate.com/">ClassLive Pro</a>. This is a JNLP application that provides similar functionality to WebEx. Although I've developed a few JNLP applications myself, and I'm convinced of its great utility, I haven't encountered any other JNLP applications so I was glad I ran into this application.</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-84224333918485779982009-09-22T14:29:00.001-07:002009-09-22T14:29:36.929-07:00A SEED certificate<p>With the Oracle acquisition looming on the horizon, and the uncertainty that comes with it, it feels like many organizations within Sun are bringing projects in a state in which they can be <em>transitioned i</em>n a clean way. <em>Transitioned</em> meaning adopted or abandoned.</p> <p>I also find myself doing this: for instance I'm trying to put the finishing touches on JMSJCA, and trying to put Hulp in a state where other people can use it, etc.</p> <p>Maybe it was in that light that a few days ago, I got a certificate from the SEED organization within Sun. SEED is a mentoring program within Sun in which I had the good fortune to participate. I also was a SEED mentor. Or perhaps I should let the speak for itself.</p> <p><a href="http://lh4.ggpht.com/_k73ELjWNXV8/SrlBvRLpXvI/AAAAAAAAACc/kT-KVHiq5Zs/s1600-h/image%5B7%5D.png"><img title="image" style="border-right: 0px; border-top: 0px; display: inline; border-left: 0px; border-bottom: 0px" height="842" alt="image" src="http://lh6.ggpht.com/_k73ELjWNXV8/SrlBwP9QULI/AAAAAAAAACg/mE2YF3h43Y4/image_thumb%5B5%5D.png?imgmax=800" width="643" border="0"></a></p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-52568694020002883782009-09-21T13:54:00.001-07:002009-09-21T13:54:45.301-07:00Lessons of an interesting deadlock problem<p>Two years ago I wrote a <a href="http://blogs.sun.com/fkieviet/entry/using_nested_diagnostics_contexts_in">blog entry about Nested Diagnostics Contexts</a> in GlassFish. The approach was based on custom <tt>Logger</tt> objects. Now, two years later when running GlassFish on AIX, an issue with that turned up: a deadlock. Digging into it, there are two lessons I'd like to share: one about deadlocks, and the other one about workarounds.</p> <h3>Deadlock in the JDK</h3> <p>At the time of my writing the NDC facility, calling <tt>addLogger()</tt> and <tt>getLogger()</tt> from different threads may cause a deadlock in the JDK classes. Why is this?</p> <p>This is how the code in the Sun JDK was:</p><pre>public class Logger {
...
public <b>static synchronized</b> Logger getLogger(String name) {
LogManager manager = LogManager.getLogManager();
Logger result = manager.getLogger(name);
if (result == null) {
result = new Logger(name, null);
manager.addLogger(result);
result = manager.getLogger(name);
}
return result;
}
}
</pre><pre>public class LogManager {
...
public <b>synchronized</b> boolean addLogger(Logger logger) {
...
Logger plogger = Logger.getLogger(pname);
}
...
}
</pre>
<p>There is a problem if there are two threads calling these two methods. Say Thread 1 calls <tt>Logger.getLogger()</tt> in its application code, e.g.</p><pre>public void doSomething() {
Logger.getLogger("com.stc.test").info("doSomething!");
}
</pre>
<p>Now let's consider another thread Thread 2 tries to register a custom logger using <tt>addLogger()</tt>, e.g.</p><pre>LogManager.getLogManager().addLogger(new Logger() {
...
});
</pre>
<p>There are two locks that come into the picture: one is the lock on <tt>Logger.class</tt>, and the other one on <tt>LogManager.getLogManager()</tt>. Thread 1 simply calls <tt>Logger.getLogger()</tt> which first locks <tt>Logger.class</tt>, and while having this lock, will call <tt>LogManager.addLogger()</tt>. This call will get a lock on <tt>LogManager.getLogManager()</tt>. This will try to lock <tt>Logger.class</tt> again, which is no problem because it already had the lock on that. Now consider Thread 2: it will first lock <tt>LogManager.getLogManager()</tt> through its call to <tt>addLogger()</tt>, and while having this lock, it will try to lock <tt>Logger.class</tt> through its call to <tt>Logger.getLogger()</tt>.</p>
<p>So Thread 1 will lock first A and then B, while Thread 2 will first lock B and then A. A classical deadlock situation.</p>
<p>This is clearly a bug in the JDK. With the knowledge of this bug, as the developer of the custom logger, we can use a simple workaround for this problem. Before calling <tt>addLogger()</tt>, first lock <tt>Logger.class</tt>. By doing that, we can guarantee that the locks are called in the same order: first <tt>Logger.class</tt> and then <tt>LogManager.getLogManager()</tt>.</p>
<p>This bug was actually reported by a customer, see <a href="http://bugs.sun.com/view_bug.do?bug_id=6487638">bug report</a>, and was fixed in 6u11 and 5.0u18. The LogManager now no longer calls <tt>Logger.getLogger()</tt>, so there's no locking of <tt>Logger.class</tt> anymore.</p>
<h3>Deadlock in GlassFish</h3>
<p>GlassFish installs its own <tt>LogManager</tt>, and there's some locking going on there too. It has an internal lock which it locks in <tt>addLogger()</tt>:</p><pre> public boolean addLogger(Logger logger) {
...
super.addLogger(logger);
...
synchronized (_unInitializedLoggers) {
...
doInitializeLogger(logger);
}
}
</pre>
<p>And this calls <tt>Logger.getLogger()</tt> again.</p><pre> protected void doInitializeLogger(Logger logger) {
...
Logger newLogger =
Logger.getLogger(logger.getName(),
getLoggerResourceBundleName(logger.getName()));
...
}
</pre>
<p>As a developer of the custom logger, we can still use the same fix: first lock <tt>Logger.class</tt> before calling <tt>addLogger()</tt> so that the locking sequence becomes: <tt>Logger.class</tt>, transient lock on <tt>LogManager.getLogManager()</tt> followed by a lock on <tt>_unInitializedLoggers</tt>, lock on <tt>Logger.class()</tt>.</p>
<p>A fix in GlassFish would simply make <tt>addLogger()</tt> synchronized. An alternative fix is to make the <tt>synchronized</tt> block smaller: it should not extend over <tt>doInitializeLogger()</tt>.</p>
<h3>Deadlock with the IBM JDK</h3>
<p>All was well until this was run on the IBM JDK. As it turns out, <tt>Logger.getLogger()</tt> doesn't lock <tt>Logger.class</tt>, it locks <tt>LogManager.getLogManager()</tt>. Now there is a new deadlock: Thread 1 calls <tt>Logger.getLogger()</tt> and by doing so, locks <tt>LogManager.getLogManager()</tt>, and then tries to lock <tt>_unInitializedLoggers</tt> and later tries to lock <tt>LogManager.getLogManager()</tt> again. Thread 2 calls <tt>addLogger()</tt> and by doing so locks first <tt>_unInitializedLoggers</tt> which later tries to lock <tt>LogManager.getLogManager()</tt>. The problem is back: the sequence of locking is reversed.</p>
<h3>Lessons learned</h3>
<p>The first lesson is that of the deadlock itself: one should be extremely careful when locking an object, and then calling into another object, especially if that object is meant to be a baseclass in a public API. Both <tt>Logger</tt> and <tt>LogManager</tt> may be overridden, and are clearly very public.</p>
<p>The second lesson is that instead of building workarounds, try to get a fix the original bug. Or at least try to have the original issue addressed while adding a workaround. In my case, I never filed a ticket against the JDK or against GlassFish. I should have known better.</p>
<h3>... or not?</h3>
<p>At this very moment, GlassFish v2.1.1 is about to be shipped, and we need GlassFish v2.1.1 on AIX for the upcoming CAPS release. A bug fix in GlassFish v2.1.1 is probably not going to get in. So I'm making yet another workaround: don't use a custom logger at all as to avoid the whole <tt>addLogger()</tt> problem. Instead, use standard <tt>Logger</tt> objects, but provide a custom filter that manipulates the log record. This will work on both unpatched versions of GlassFish, and also on unpatched versions of the JDK. Still, I hope the GlassFish problem will be fixed.</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com1tag:blogger.com,1999:blog-6643071316318130698.post-82524117502628367082009-08-28T12:48:00.001-07:002009-08-28T12:48:15.093-07:00JavaOne 2009 TS-5341: Rethinking the ESB: lessons learned challenging the limitations and pitfalls – audio recording uploaded<p><a href="http://lh4.ggpht.com/_k73ELjWNXV8/Spg0fQneNzI/AAAAAAAAACU/6MWCSbd2vVE/s1600-h/image%5B2%5D.png"><img title="image" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="92" alt="image" src="http://lh5.ggpht.com/_k73ELjWNXV8/Spg0freaeFI/AAAAAAAAACY/NbyiDeau_a4/image_thumb.png?imgmax=800" width="104" border="0"></a> </p> <p>At JavaOne 2009 I gave a number of presentations. One was "Rethinking the ESB: lessons learned challenging the limitations and pitfalls" which I did with Andi Egloff. JavaOne sessions were not recorded this year, that is not by the JavaOne Organization. But I used an mp3 player to record the sessions I was involved in. I finally processed the audio and uploaded it. I also uploaded a PDF of the presentation:</p> <ul> <li><a href="http://mediacast.sun.com/users/Frank.Kieviet/media/TS-5341.pdf">Presentation (PDF) of TS-5341 Rethinking the ESB: lessons learned challenging the limitations and pitfalls</a></li> <li><a href="http://mediacast.sun.com/users/Frank.Kieviet/media/JavaOne2009-LessonsLearned/details">Audio recording (mp3) of TS-5341 Rethinking the ESB: lessons learned challenging the limitations and pitfalls</a></li></ul> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-6693534534918675042009-08-06T10:08:00.001-07:002009-08-06T10:10:20.194-07:00Message interceptors with JMS<p>Interceptors are a way to add behavior to a system without directly invoking this behavior in from code. The typical anti-example is that if you would want to log the entry and exit of methods on a class, you could do that of course by adding log statements in each and every method. The downside is that you would have to change each and every method, and that all these methods now have repetitive code in there. With the interceptor approach on the other hand, you could do that by adding an interceptor that is invoked before and after the method is invoked. In the code of the interceptor you would then add the log statement. The advantage is that the existing methods are not changed, that repetitive code is avoided, and that the logging <em>concern</em> is concentrated in one part of the code base rather than spread all over.</p> <p>I think that this logging example is pretty dumb, but I guess it drives home the message of adding behavior non-invasively, and a separation of concerns. I'll discuss some more interesting examples in a minute.</p> <h4>Interceptors in EE</h4> <p>Interceptors have long been the domain of AOP (Aspect Oriented Programming) packages that add interceptors through byte code manipulation, either at runtime or as a post-compile step. For EE developers, things changed with the advent of EE5 in which interceptors were added to EJBs.</p> <p>An interceptor can be added to all the methods or individual methods of an EJB by adding an annotation to either the class or to individual methods. In the following example, the <tt>NoConcurrency</tt> interceptor is invoked when the <tt>getUnclaimedAccounts()</tt> method is called.</p><pre>@Interceptors(NoConcurrency.class)<br>public void getUnclaimedAccounts(String category) {...</pre>
<p>The interceptor class is a simple Java class that has to have a method with the following signature:</p><pre>@AroundInvoke<br>Object <METHOD>(javax.interceptor.InvocationContext) throws Exception</pre>
<p>The interceptor method is called when a client calls an EJB method. The interceptor typically calls <tt>InvocationContext.proceed()</tt>. This will call into the EJB method (or the next interceptor, should there be one).</p>
<p>Interceptor classes are typically packaged as part of an EAR file: they are part of the application.</p>
<p>Interceptors are referenced in an EJB using the <tt>@Interceptors</tt> annotation. They can also be referenced from the EJB's deployment descriptor. In either case, <strong>the application needs to be modified to specify interceptors</strong>.</p>
<p>Another limitation of EJB interceptors is that they <strong>only work on EJB methods</strong>. When common behavior needs to be added to other interactions, for instance if an EJB invokes something like an outbound resource adapter, a different mechanism must be used.</p>
<h4>Interceptors in CAPS</h4>
<p>A Java CAPS customer had an interesting case for using interceptors. The customer had hundreds of integration applications that all received and sent JMS messages. If a message cannot be processed, the message is sent to an error handling system using JMSJCA's dead letter queue facility. An operator will monitor the dead letter queue, and may resubmit the message. If a resubmitted message is processed successfully, the error handling system needs to be notified of that fact. One way of adding this behavior would be to add a chunk of code to all MDBs. That would of course lead to a lot of duplicated code. It also leads to mixing this system level behavior with the business logic in the integration applications. This is clearly undesirable.</p>
<p>Because Repository based projects in Java CAPS generate all the MDB code and assembles the applications, it is difficult to make use of EJB interceptors. What is needed is to specify the use of interceptors on a global basis, that is outside of the applications, and preferably for all applications at the same time.</p>
<p>Another requirement is that the customer wanted to add common behavior not just when messages are received, but also when messages were sent from an EJB. Upon sending a message, payload validation should happen. Since EJB interceptors can only be used for EJB methods, this was another reason that EJB interceptors could not be used.</p>
<p>The solution was to add a new feature to JMSJCA to support interceptors.</p>
<h4>Interceptors in JMSCA</h4>
<p>Interceptors in JMSJCA are invoked not only before / after a message is delivered to the MDB, but also any time a message is sent.</p>
<p>Rather than introducing a new Java interface, which would bring with it the complication of compile time dependencies on JMSJCA jars, the JMSJCA interceptors use the same annotations as in EJB3: any class with a method with the right signature and the <tt>@AroundInvoke</tt> annotation can serve as an interceptor.</p>
<p>Which classes will JMSJCA consider as interceptors? For this, it uses the service locator mechanism from JDK 6. This is done without introducing dependencies on JDK 6 by the way. With the service locator mechanism, a jar that holds the interceptor should have a special manifest file that lists the class names of the interceptors. The name of this manifest should be <tt>META-INF/services/myinterceptor</tt> where <tt>myinterceptor</tt> is the name of the interceptor. This name needs to be specified in the JMSJCA configuration. If no name is specified, the default name <tt>jmsjca.interceptor</tt> is used. This means that interceptors using the service name <tt>jmsjca.interceptor</tt> are automatically loaded and invoked if not overridden in the JMSJCA configuration.</p>
<p>With this, it's easy to add interceptors to all applications running in the application server, whenever they send or receive messages through JMSJCA, without having to change any application.</p>
<p>For more information, see <a href="http://jmsjca.dev.java.net/">http://jmsjca.dev.java.net/</a> .</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com2tag:blogger.com,1999:blog-6643071316318130698.post-45563053059610958502009-05-31T19:46:00.001-07:002009-05-31T19:46:34.072-07:00JavaOne 2009<p>I'm writing this on my way to JavaOne 2009. San Francisco airport was fogged in so my flight was cancelled. I have some time to burn: I'm waiting for a train to take me from Santa Clara to San Francisco.</p> <p>As usual it has been immensely busy in the two months before JavaOne. </p> <p>For this JavaOne, I'm the lead of the SOA/Services track. A track lead is responsible for assembling a program of sessions and BOFs. Or in more practical terms, going through the proposals with the other volunteers that make up the selection committee, and picking the proposals so that not only the quality but also the variety of the track is optimal. The selection committee consists of subject matter experts. There are three from Sun and four from other companies: RedHat, Cap Gemini, Adobe.</p> <p>This year there again were many hundreds of proposals for sessions and BOFs. Going through them to make a first cut took a lot of time. Refining the first cut to the final selection is difficult: there were many good and interesting proposals. But in the end only 22 sessions could prevail. Getting consensus on the selections, scheduling them, ensuring that presentations are ready, reviewing the presentations, the inevitable change requests, etc. took a lot more time than I expected. But I think these efforts have paid off: I think we have a very interesting lineup of presentations.</p> <p>My second JavaOne activity is presenting at Java University, the day before JavaOne. Like last year, Joe Boulenouar invited me to present a few hours on SOA and integration middleware. The course title is "Using Java EE and SOA to Help Architect and Design Robust Enterprise Applications." Last year there were four hundred or so people in attendance. I haven't heard yet how many people there will be this year. Perhaps fewer on account of the poor the economy.</p> <p>Thirdly, I'm presenting a session titled "Glassfish ESB: get your apps on the bus". The original plan was that Keith Babo would pull that wagon, but unfortunately he had to pull out. I'm now doing that session with Sujit Biswas. In this session I'm making a case for ESBs or integration middleware in general. This has led me to some more thoughts on how we can widen the applicability of ESBs. More about that in a future post.</p> <p>I'm also doing a session titled "Rethinking the ESB: lessons learned challenging the limitations and pitfalls". Andi Egloff is co-presenter. The idea of this session is to show a number of things that went wrong in previous product releases and how we changed that in subsequent releases. The interesting part for the audience is that it gives a set of requirements or "things to avoid" in middleware products.</p> <p>The fifth activity I'm involved in is the OpenESB community BOF. The idea of this BOF is to discuss experiences with OpenESB and discuss what's on the wish list for OpenESB.</p> <p>So, yes, it was busy running up to JavaOne. I'm sure that JavaOne itself will also be very busy: not only will there be the presentations I need to give, others I have to be in attendance for, but there will also be the numerous customer meetings. </p> <p>I think I'll need a break after JavaOne!</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-72536266828552731032009-03-22T12:50:00.001-07:002009-03-22T12:50:12.590-07:00The java.lang.LinkageError: loader constraint violation" demystified<p>My colleague Murali Pottlapelli ran into an interesting problem the other day. He added Rhino to his BPEL Service Engine, and saw this error happen:</p> <blockquote> <p><tt>java.lang.LinkageError: loader constraint violation: loader (instance of <bootloader>) previously initiated loading for a different type with name "org/w3c/dom/UserDataHandler"</tt></p></blockquote> <p>The weird thing was that this exception was thrown from a call to <font face="Courier New">Class.getMethods()</font> on a class shipped with the JVM!</p> <p>Googling this problem revealed that there are a lot of people running into this issue, often when using OSGi. Most search results referred to email lists postings where people ran into this problem. None of the web pages properly explained what the problem was. Intuitively I felt we could solve our problem by moving a jar to a different classloader, but was that merely <em>hiding</em> the problem? As with my post on "<a href="http://blogs.sun.com/fkieviet/entry/classloader_leaks_the_dreaded_java">How to fix the dreaded "java.lang.OutOfMemoryError: PermGen space" exception (classloader leaks)</a>", I was convinced that <em>understanding the problem</em> is key. So Murali and I set out to dig in this problem deeper until we completely grasped it.</p> <p>As it turns out, there are some aspects about this problem that make it very confusing:</p> <ol> <li>In a dynamic component system, a change in one component may cause the other components to fail in areas that used to work properly before. At the same time, the changed component appears to be working properly. <li>The order in which components are activated determines where and how the problem shows up. <li>The effects of the problem may show up in innocuous and seemingly unrelated calls such as <font face="Courier New">Class.getMethods()</font>.</li></ol> <p>In the following sections I'll first illustrate the problem, and then explain in detail what's causing the problem.</p> <h4>The problem</h4> <p>Let's look at a model example. In stead of looking at an OSGi example or a JBI example, let's look at EE because it will be more familiar. Imagine we have an EAR file with an EJB and two WAR files. The WAR files are identical, and have a servlet that uses an EJB to log in. As such we have three classes:</p> <p style="margin-bottom: 0in" align="left"><font color="#000000"></font><font face="Courier New, monospace"><font size="2"><font color="#7f0055"><b>public</b></font><font color="#000000"> </font><font color="#7f0055"><b>static</b></font><font color="#000000"> </font><font color="#7f0055"><b>class</b></font><font color="#000000"> User {<br></font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">}</font></font></font></p> <p style="margin-bottom: 0in" align="left"><font color="#000000"></font></p> <p style="margin-bottom: 0in" align="left"><font color="#000000"></font><font face="Courier New, monospace"><font size="2"><font color="#7f0055"><b>public</b></font><font color="#000000"> </font><font color="#7f0055"><b>static</b></font><font color="#000000"> </font><font color="#7f0055"><b>class</b></font><font color="#000000"> LoginEJB {<br></font></font></font></p><font face="Courier New, monospace"><font size="2"><font color="#000000"> </font></font></font><font face="Courier New, monospace"><font size="2"><font color="#7f0055"><b>static</b></font><font color="#000000"> {<br><font color="#000000"><font face="Courier New, monospace"><font color="#000000"> </font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">System.</font><font color="#0000c0"><i>out</i></font><font color="#000000">.println(</font><font color="#2a00ff">"LoginEJB loaded"</font><font color="#000000">);<br></font></font></font> </font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">}<br></font></font></font> <p style="margin-bottom: 0in" align="left"><font face="Courier New, monospace"><font size="2"><font color="#000000"> </font></font></font><font face="Courier New, monospace"><font size="2"><font color="#7f0055"><b>public</b></font><font color="#000000"> </font><font color="#7f0055"><b>static</b></font><font color="#000000"> </font><font color="#7f0055"><b>void</b></font><font color="#000000"> login(User u) {<br> </font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">}<br></font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">}</font></font></font></p> <p style="margin-bottom: 0in" align="left"><font color="#000000"></font></p> <p style="margin-bottom: 0in" align="left"><font color="#000000"></font><font face="Courier New, monospace"><font size="2"><font color="#7f0055"><b>public</b></font><font color="#000000"> </font><font color="#7f0055"><b>static</b></font><font color="#000000"> </font><font color="#7f0055"><b>class</b></font><font color="#000000"> Servlet {<br> </font></font></font><font face="Courier New, monospace"><font size="2"><font color="#7f0055"><b>public</b></font><font color="#000000"> </font><font color="#7f0055"><b>static</b></font><font color="#000000"> </font><font color="#7f0055"><b>void</b></font><font color="#000000"> doGet() {<br> </font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">User u = </font><font color="#7f0055"><b>new</b></font><font color="#000000"> User();<br> </font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">System.</font><font color="#0000c0"><i>out</i></font><font color="#000000">.println(</font><font color="#2a00ff">"User in "</font><font color="#000000"> + u.getClass().getClassLoader());<br> </font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">LoginEJB.<i>login</i>(u);<br> </font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">}<br></font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">}</font></font></font></p> <p style="margin-bottom: 0in">Let's say that one WAR is configured with a self-first classloader, and the other one uses the default parent-first class loading model.<br></p> <p><a href="http://lh3.ggpht.com/_k73ELjWNXV8/ScaWbxppC4I/AAAAAAAAAB0/xf-ceLc-NmA/s1600-h/image%5B4%5D.png"><img title="image" style="border-top-width: 0px; display: block; border-left-width: 0px; float: none; border-bottom-width: 0px; margin-left: auto; margin-right: auto; border-right-width: 0px" height="374" alt="image" src="http://lh4.ggpht.com/_k73ELjWNXV8/ScaWcqV_WtI/AAAAAAAAAB4/LvQ9L4LBzCw/image_thumb%5B2%5D.png?imgmax=800" width="223" border="0"></a> </p> <p>Now consider these three scenarios:</p> <ol> <li>We log in using the parent-first servlet, and then inspect the EJB with <font face="Courier New">Class.getMethods()</font>. Everything works as expected, but when we then try to login on the second servlet, we see the linkage error. <li>We log in using the self-first servlet. Then when we call <font face="Courier New">Class.getMethods()</font> on the EJB, this fails. Also, we can no longer log in on the parent-first servlet! <li>We first call <font face="Courier New">Class.getMethods()</font> on the EJB. We can no longer login using the self-first servlet, but the parent-first servlet still works. </li></ol> <p>What is going on? To explain, let's first revisit some classloader basics. If parent-first and self-first is in your daily vocabulary, feel free to skip the next section.</p> <h4>Self-first versus parent-first delegation</h4> <p>What is meant with self-first delegating classloaders? Here's the skinny on classloaders. In Java you can create your own classloader for two reasons: this allows multiple versions of the same class to co-exist in memory, as is often found in OSGi. It also allows classes to be unloaded, as is found in application servers. A classloader typically represents a set of jars that make up the module, the component, or the application. Each classloader must have a parent classloader. Hence, classloaders form a tree with the bootstrap classloader as the root. See the picture above.</p> <p>When a classloader is asked to load a class, it can first ask its parent to load the class. If the parent fails to load the class, that classloader will then try to load the class. In this scheme, called <strong>parent-first</strong> class loading, common classes are <em>always </em>loaded by the parent classloader. This allows one application or module to talk to another application or module in the same VM.</p> <p>Instead of asking the parent classloader first, a classloader can also try to find a class itself first, and only if it cannot find the class would it ask the parent classloader to find the class. A <strong>self-first</strong> classloader allows for an application to have a different version of a class than found in the parent classloader.</p> <h4>Classloader lab</h4> <p>To show what's going on, I've developed a small demo that emulates the scenario with the two WARs and the EJB. Key in this demo is a custom classloader. The constructor takes a list of classes that should be <em>defined</em> by that classloader, i.e. the classloader behaves as self-first for those classes, and delegates to the parent classloader for the other classes. The custom classloader is listed in the code at the bottom of this post.</p> <p>This is how the system is setup: a classloader for the EJB that loads the <font face="Courier New">LoginEJB</font> and the <font face="Courier New">User</font> class. A classloader for the parent-first WAR that loads the <font face="Courier New">Servlet</font> only, and a self-first classloader that loads the <font face="Courier New">Servlet</font> and the <font face="Courier New">User</font> class.</p><!-- =============================== --><pre>CustomCL ejbCL = <span class="keyword-directive">new</span> CustomCL(<span class="character">"</span><span class="character">EJB </span><span class="character">"</span>, Demo.<span class="keyword-directive">class</span>.getClassLoader(), <span class="character">"</span><span class="character">com.stc.Demo$User</span><span class="character">"</span>, <span class="character">"</span><span class="character">com.stc.Demo$LoginEJB</span><span class="character">"</span>);<br>
CustomCL pfWebCL = <span class="keyword-directive">new</span> CustomCL(<span class="character">"</span><span class="character">PFWeb</span><span class="character">"</span>, ejbCL, <span class="character">"</span><span class="character">com.stc.Demo$Servlet</span><span class="character">"</span>);<br>
CustomCL sfWebCL = <span class="keyword-directive">new</span> CustomCL(<span class="character">"</span><span class="character">SFWeb</span><span class="character">"</span>, ejbCL, <span class="character">"</span><span class="character">com.stc.Demo$User</span><span class="character">"</span>, <span class="character">"</span><span class="character">com.stc.Demo$Servlet</span><span class="character">"</span>);</pre><!-- =============================== -->
<p>The custom classloader prints all class loading requests so it can be easily seen what's happening. </p>
<h4>Classloading eagerness</h4>
<p>What exactly happens when the <font face="Courier New">LoginEJB</font> class is loaded? In the demo program, the following line causes the following output:</p><font face="Courier New">ejbCL.loadClass(<span class="character">"</span><span class="character">com.stc.Demo$LoginEJB</span><span class="character">"</span>, <span class="keyword-directive">true</span>).newInstance();<br></font>
<blockquote>
<p><font face="Courier New">EJB : Loading com.stc.Demo$LoginEJB in custom classloader<br>EJB : super.loadclass(java.lang.Object)<br>EJB : super.loadclass(java.lang.System)<br>EJB : super.loadclass(java.io.PrintStream)<br>LoginEJB loaded</font></p></blockquote>
<p>When the JVM loads the <font face="Courier New">LoginEJB</font> class, it goes over references in the class and loads those classes too: the <font face="Courier New">java.lang.Object</font> class because it's the super class of the EJB, and the <font face="Courier New">java.lang.System</font> and <font face="Courier New">java.io.PrintStream</font> class because they are used in the static block. That these "JVM" classes are loaded is in itself remarkable and shows an interesting aspect of how classloading works. "JVM" classes are not treated specially, and it is not relevant that they are already loaded in the bootstrap classloader and are used all over the place.</p>
<p>When the EJB classloader receives the request to load these "JVM" classes, that classloader <em>of course</em> delegates those requests to the parent classloader. In fact, it's a <em>requirement</em> to delegate all class load requests to the parent classloader for all classes that are in <font face="Courier New">java.*</font> and <font face="Courier New">javax.*</font>.</p>
<p>Something also remarkable is what is <em>not</em> loaded: the <font face="Courier New">User</font> class. Apparently, the fact that this class is used in a method is not enough to cause this class to be loaded when the EJB class is loaded. It is difficult to predict what classes are loaded as the result of loading a particular class. I think the spec leaves a lot of room to implementers to decide when to do so.</p>
<p>It's important to realize that when the EJB class is loaded, the <font face="Courier New">User</font> class is <em>not</em> loaded yet.</p>
<h4>Linking classes</h4>
<p>Next, let's use the parent-first Servlet to log in:</p><font face="Courier New">pfWebCL.loadClass(<span class="character">"</span><span class="character">com.stc.Demo$Servlet</span><span class="character">"</span>, <span class="keyword-directive">false</span>).getMethod(<span class="character">"</span><span class="character">doGet</span><span class="character">"</span>).invoke(<span class="keyword-directive">null</span>);<br></font>
<blockquote>
<p><font face="Courier New">PFWeb: Loading com.stc.Demo$Servlet in custom classloader<br>PFWeb: super.loadclass(java.lang.Object)<br>EJB : already loaded(java.lang.Object)<br></font><font face="Courier New"><strong>PFWeb: super.loadclass(com.stc.Demo$User)<br>EJB : Loading com.stc.Demo$User in custom classloader<br></strong>PFWeb: super.loadclass(java.lang.System)<br>EJB : already loaded(java.lang.System)<br>...<br>Logging in with User loaded in EJB<br>PFWeb: super.loadclass(com.stc.Demo$LoginEJB)<br>EJB : already loaded(com.stc.Demo$LoginEJB)</font></p></blockquote>
<p>Ignoring the "JVM" classes, it can be seen that the Servlet causes the User class to be loaded, and that that class is loaded in the EJB classloader. Nothing unexpected here.</p>
<p>If <em>subsequently</em> the self-first Servlet gets a go, the following happens:</p><font face="Courier New">sfWebCL.loadClass(<span class="character">"</span><span class="character">com.stc.Demo$Servlet</span><span class="character">"</span>, <span class="keyword-directive">false</span>).getMethod(<span class="character">"</span><span class="character">doGet</span><span class="character">"</span>).invoke(<span class="keyword-directive">null</span>);<br></font>
<blockquote>
<p><font face="Courier New">SFWeb: Loading com.stc.Demo$Servlet in custom classloader<br>SFWeb: super.loadclass(java.lang.Object)<br>EJB : already loaded(java.lang.Object)<br><strong>SFWeb: Loading com.stc.Demo$User in custom classloader<br></strong>SFWeb: super.loadclass(java.lang.System)<br>...<br>Logging in with User loaded in SFWeb<br>SFWeb: super.loadclass(com.stc.Demo$LoginEJB)<br>EJB : already loaded(com.stc.Demo$LoginEJB)<br><strong>java.lang.reflect.InvocationTargetException<br></strong> at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)<br>...<br><strong>Caused by: java.lang.LinkageError: loader constraints violated when linking com/stc/Demo$User class<br></strong> at com.stc.Demo$Servlet.doGet(Demo.java:82)<br> ... 6 more</font><font face="Courier New"><br></font></p></blockquote>
<p>The <font face="Courier New">User</font> class is loaded and defined in the self-first WAR classloader, and when the <font face="Courier New">login()</font> method is called, an instance of <em>that class</em> is passed to the EJB. Why does the linkage error happen?</p>
<p>When the parent-first servlet invoked the EJB, it passed in a <font face="Courier New">User</font> object. At that moment, the JVM links the reference to <font face="Courier New">com.stc.Demo$User</font> to a class instance. A class instance is identified using the fully qualified name <em>and</em> the classloader instance in which it was loaded. Upon invocation of <font face="Courier New">login(User)</font>, the JVM will check that the class object of the <font face="Courier New">User</font> passed in, matches the class object that was linked to <font face="Courier New">com.stc.Demo$User</font>. If they don't match, the JVM will throw a <font face="Courier New">LinkageError</font>.</p>
<p>This linking happened on the first invocation. We can also force the linking to happen by calling <font face="Courier New">LoginEJB.class.getMethods()</font>. I can illustrate that by changing the test program to fist inspect the EJB class, and then make the self-first servlet to login.</p><font face="Courier New">
<p style="margin-bottom: 0in"><font color="#000000"><font face="Courier New, monospace">System.<font color="#0000c0"><i>out</i></font><font color="#000000">.println(</font><font color="#2a00ff">"Loading EJB"</font><font color="#000000">);<br></font></font></font><font color="#000000"><font face="Courier New, monospace">ejbCL.loadClass(<font color="#2a00ff">"com.stc.Demo$LoginEJB"</font><font color="#000000">, </font><font color="#7f0055"><b>true</b></font><font color="#000000">).newInstance();<br></font></font></font><font color="#000000"><font face="Courier New, monospace">System.<font color="#0000c0"><i>out</i></font><font color="#000000">.println(</font><font color="#2a00ff">"Examining methods of LoginEJB"</font><font color="#000000">);<br></font></font></font><font color="#000000"><font face="Courier New, monospace">ejbCL.loadClass(<font color="#2a00ff">"com.stc.Demo$LoginEJB"</font><font color="#000000">, </font><font color="#7f0055"><b>false</b></font><font color="#000000">).getMethods();<br></font></font></font><font color="#000000"><font face="Courier New, monospace">System.<font color="#0000c0"><i>out</i></font><font color="#000000">.println(</font><font color="#2a00ff">"Logging in, self-first"</font><font color="#000000">);<br></font></font></font><font color="#000000"><font face="Courier New, monospace">sfWebCL.loadClass(<font color="#2a00ff">"com.stc.Demo$Servlet"</font><font color="#000000">, </font><font color="#7f0055"><b>false</b></font><font color="#000000">).getMethod(</font><font color="#2a00ff">"doGet"</font><font color="#000000">).invoke(</font><font color="#7f0055"><b>null</b></font>);</font></font></p></font>
<blockquote>
<p><font face="Courier New">Loading EJB<br>EJB : Loading com.stc.Demo$LoginEJB in custom classloader<br>LoginEJB loaded<br>Examining methods of LoginEJB<br>EJB : already loaded(com.stc.Demo$LoginEJB)<br><strong>EJB : Loading com.stc.Demo$User in custom classloader<br></strong>Logging in<br>SFWeb: Loading com.stc.Demo$Servlet in custom classloader<br><strong>SFWeb: Loading com.stc.Demo$User in custom classloader<br></strong>Logging in with User loaded in SFWeb<br>SFWeb: super.loadclass(com.stc.Demo$LoginEJB)<br>EJB : already loaded(com.stc.Demo$LoginEJB)<br><strong>java.lang.reflect.InvocationTargetException<br>Caused by: java.lang.LinkageError: loader constraints violated when linking com/stc/Demo$User class</strong></font></p></blockquote>
<p>The login fails because the <font face="Courier New">LoginEJB.class.getMethods() </font>invocation causes the <font face="Courier New">com.stc.Demo$User</font> reference to be linked with the <font face="Courier New">User</font> class loaded in the EJB classloader. When the self-first servlet invokes the method, the two class objects don't match, causing the Error to be thrown.</p>
<h4>Small mistake results in "poisoning" a shared class</h4>
<p>By now it should be obvious that the User class should not have been packaged in the self-first WAR. What may not be obvious yet, is that this small mistake has big consequences. If a login happens on the self-first servlet before anything else, the linking happens with the erroneous <font face="Courier New">User</font> class object from the self-first classloader. This will cause the login of the parent-first WAR to fail. It will also cause the <font face="Courier New">LoginEJB.class.getMethods() </font>invocation to fail. </p><!-- ============================================= -->
<style type="text/css">
<!--
body {color: #000000; background-color: #ffffff; font-family: monospaced}
table {color: #000000; background-color: #e9e8e2; font-family: monospaced}
.comment {color: #969696}
.character {color: #ce7b00}
.keyword-directive {color: #0000e6}
body pre { overflow:auto; }
}
-->
</style>
<font face="Courier New">
<p style="margin-bottom: 0in"><font color="#000000"><font face="Courier New, monospace"><font size="2">System.</font><font color="#0000c0"><i>out</i></font><font color="#000000">.println(</font><font color="#2a00ff">"Loading EJB"</font><font color="#000000">);<br></font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">ejbCL.loadClass(</font><font color="#2a00ff">"com.stc.Demo$LoginEJB"</font><font color="#000000">, </font><font color="#7f0055"><b>true</b></font><font color="#000000">).newInstance();<br></font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">System.</font><font color="#0000c0"><i>out</i></font><font color="#000000">.println(</font><font color="#2a00ff">"Logging in, self-first"</font><font color="#000000">);<br></font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">sfWebCL.loadClass(</font><font color="#2a00ff">"com.stc.Demo$Servlet"</font><font color="#000000">, </font><font color="#7f0055"><b>false</b></font><font color="#000000">).getMethod(</font><font color="#2a00ff">"doGet"</font><font color="#000000">).invoke(</font><font color="#7f0055"><b>null</b></font><font color="#000000">);<br></font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">System.</font><font color="#0000c0"><i>out</i></font><font color="#000000">.println(</font><font color="#2a00ff">"Examining methods of LoginEJB"</font><font color="#000000">);<br></font></font></font><font color="#000000"><font face="Courier New, monospace"><font size="2">ejbCL.loadClass(</font><font color="#2a00ff">"com.stc.Demo$LoginEJB"</font><font color="#000000">, </font><font color="#7f0055"><b>false</b></font><font color="#000000">).getMethods();</font></font></font><br></p></font>
<blockquote>
<p><font face="Courier New">Loading EJB<br>EJB : Loading com.stc.Demo$LoginEJB in custom classloader<br>LoginEJB loaded<br>Logging in, self-first<br>SFWeb: Loading com.stc.Demo$Servlet in custom classloader<br><strong>SFWeb: Loading com.stc.Demo$User in custom classloader<br></strong>Logging in with User loaded in SFWeb<br>SFWeb: super.loadclass(com.stc.Demo$LoginEJB)<br>EJB : already loaded(com.stc.Demo$LoginEJB)<br>Examining methods of LoginEJB<br>EJB : already loaded(com.stc.Demo$LoginEJB)<br><strong>EJB : Loading com.stc.Demo$User in custom classloader<br>java.lang.LinkageError: Class com/stc/Demo$User violates loader constraints<br></strong> at java.lang.ClassLoader.defineClass1(Native Method)<br> at java.lang.ClassLoader.defineClass(ClassLoader.java:620)<br> at java.lang.ClassLoader.defineClass(ClassLoader.java:465)<br> at com.stc.Demo$CustomCL.findClass(Demo.java:36)<br> at com.stc.Demo$CustomCL.loadClass(Demo.java:54)<br> at java.lang.ClassLoader.loadClass(ClassLoader.java:251)<br> at java.lang.ClassLoader.loadClassInternal(ClassLoader.java:319)<br> at java.lang.Class.getDeclaredMethods0(Native Method)<br> at java.lang.Class.privateGetDeclaredMethods(Class.java:2395)<br> at java.lang.Class.privateGetPublicMethods(Class.java:2519)<br> at java.lang.Class.getMethods(Class.java:1406)<br> at com.stc.Demo.test1(Demo.java:98)<br> at com.stc.Demo.main(Demo.java:111)</font></p></blockquote>
<p>The first login with the self-first WAR has effectively poisoned the EJB, making it unusable. In everyday life, the problem is likely not so clear cut. For instance, a developer adds a jar to a component, or changes the classloading delegation model of a component, and all tests with that component may succeed. The problem may only show up in next day's build when integration tests are run. And as the example above shows, the stacktrace does not tell much about where the real cause of the error is.</p>
<h4>Formalization and references</h4>
<p>A formal description of loading constraints can be found in detail in section 5.3.4 of The Java Virtual Machine Specification (online available at <a title="http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html" href="http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html">http://java.sun.com/docs/books/jvms/second_edition/html/ConstantPool.doc.html</a>). In simple words, a linkage error can occur if two different classes interact with each other, and in this interaction the classes refer to types with the same symbolic name but with different class objects. In the example, the self-first servlet referred to <font face="Courier New">EJB:LoginEJB.login(sfWeb:User)</font>, but the EJB's representation was <font face="Courier New">EJB:loginEJB(EJB:User)</font>.</p>
<p>Other places where linkage errors may occur are in class hierarchies. If class <font face="Courier New">Derived</font> overrides a method <font face="Courier New">f(A a, B b)</font> in class <font face="Courier New">Super</font>, <font face="Courier New">A</font> and <font face="Courier New">B</font> as seen from <font face="Courier New">Super</font> must be the same <font face="Courier New">A</font> and <font face="Courier New">B</font> as seen from <font face="Courier New">Derived</font>. References to static variables are another example.</p>
<p>More information can also be found in the book <a href="http://www.amazon.com/Inside-Java-Virtual-Machine-Venners/dp/0071350934/ref=sr_1_2?ie=UTF8&s=books&qid=1237178714&sr=1-2">Inside the Java Virtual Machine</a> by Bill Venners. Chapters from this book are also available at <a href="http://www.artima.com/insidejvm/ed2/linkmod.html">Artima</a>.</p>
<h4>Classloader lab</h4>
<p>If you like to experiment, here's the source of the Demo program. (<a onclick="return toggledisplay('gfnbldoc');" href="javascript:none">click to expand</a>)</p><script>
function toggledisplay(id) {
var totoggle = document.getElementById(id);
if (!totoggle.style.display || totoggle.style.display == 'none') {
totoggle.style.display = 'block'
} else {
totoggle.style.display = 'none'
}
}
</script>
<div id="gfnbldoc" style="border-top-width: 1px; display: none; border-left-width: 1px; border-bottom-width: 1px; border-right-width: 1px"><pre><br>
<span class="keyword-directive">package</span> com.stc;<br> <br> <span class="keyword-directive">import</span> java.io.InputStream;<br> <span class="keyword-directive">import</span> java.util.Arrays;<br> <span class="keyword-directive">import</span> java.util.HashSet;<br> <span class="keyword-directive">import</span> java.util.Set;<br> <br> <span class="comment">/**</span><br> <span class="comment"> * </span><span class="comment">Demonstrates</span> <span class="comment">Linkeage</span> <span class="comment">errors</span><br> <span class="comment"> * </span><br> <span class="comment"> * </span><span class="ST0">@author</span> <span class="comment">fkieviet</span><br> <span class="comment">*/</span><br> <span class="keyword-directive">public</span> <span class="keyword-directive">class</span> Demo {<br> <span class="comment">/**</span><br> <span class="comment"> * </span><span class="comment">A</span> <span class="comment">self</span><span class="comment">-</span><span class="comment">first</span> <span class="comment">delegating</span> <span class="comment">classloader</span><span class="comment">.</span> <span class="comment">It</span> <span class="comment">only</span> <span class="comment">loads</span> <span class="comment">specified</span> <span class="comment">classes</span> <span class="comment">self</span><span class="comment">-</span><span class="comment">first</span><span class="comment">;</span><br> <span class="comment"> * </span><span class="comment">other</span> <span class="comment">classes</span> <span class="comment">are</span> <span class="comment">loaded</span> <span class="comment">from</span> <span class="comment">the</span> <span class="comment">parent</span><span class="comment">.</span> <br> <span class="comment">*/</span><br> <span class="keyword-directive">private</span> <span class="keyword-directive">static</span> <span class="keyword-directive">class</span> CustomCL <span class="keyword-directive">extends</span> ClassLoader {<br> <span class="keyword-directive">private</span> Set<String> selfFirstClasses;<br> <span class="keyword-directive">private</span> String label;<br> <br> <span class="keyword-directive">public</span> CustomCL(String name, ClassLoader parent, String... selfFirsNames) {<br> <span class="keyword-directive">super</span>(parent);<br> <span class="keyword-directive">this</span>.label = name;<br> <span class="keyword-directive">this</span>.selfFirstClasses = <span class="keyword-directive">new</span> HashSet<String>(Arrays.asList(selfFirsNames));<br> }<br> <br> <span class="keyword-directive">public</span> Class<?> findClass(String name) <span class="keyword-directive">throws</span> ClassNotFoundException {<br> <span class="keyword-directive">if</span> (selfFirstClasses.contains(name)) {<br> <span class="keyword-directive">try</span> {<br> <span class="keyword-directive">byte</span>[] buf = <span class="keyword-directive">new</span> <span class="keyword-directive">byte</span>[100000];<br> String loc = name.replace(<span class="character">'.'</span>, <span class="character">'/'</span>) + <span class="character">"</span><span class="character">.class</span><span class="character">"</span>;<br> InputStream inp = Demo.<span class="keyword-directive">class</span>.getClassLoader().getResourceAsStream(loc);<br> <span class="keyword-directive">int</span> n = inp.read(buf);<br> inp.close();<br> System.out.println(label + <span class="character">"</span><span class="character">: Loading </span><span class="character">"</span> + name + <span class="character">"</span><span class="character"> in custom classloader</span><span class="character">"</span>);<br> <span class="keyword-directive">return</span> defineClass(name, buf, 0, n);<br> } <span class="keyword-directive">catch</span> (Exception e) {<br> <span class="keyword-directive">throw</span> <span class="keyword-directive">new</span> ClassNotFoundException(name, e);<br> }<br> }<br> <br> <span class="comment">// Is never executed in this test</span><br> <span class="keyword-directive">throw</span> <span class="keyword-directive">new</span> ClassNotFoundException(name);<br> }<br> <br> <span class="keyword-directive">public</span> Class<?> loadClass(String name, <span class="keyword-directive">boolean</span> resolve) <span class="keyword-directive">throws</span> ClassNotFoundException {<br> <span class="keyword-directive">if</span> (findLoadedClass(name) != <span class="keyword-directive">null</span>) {<br> System.out.println(label + <span class="character">"</span><span class="character">: already loaded(</span><span class="character">"</span> + name + <span class="character">"</span><span class="character">)</span><span class="character">"</span>);<br> <span class="keyword-directive">return</span> findLoadedClass(name);<br> }<br> <br> <span class="comment">// Override parent-first behavior into self-first only for specified classes</span><br> <span class="keyword-directive">if</span> (selfFirstClasses.contains(name)) {<br> <span class="keyword-directive">return</span> findClass(name);<br> } <span class="keyword-directive">else</span> {<br> System.out.println(label + <span class="character">"</span><span class="character">: super.loadclass(</span><span class="character">"</span> + name + <span class="character">"</span><span class="character">)</span><span class="character">"</span>);<br> <span class="keyword-directive">return</span> <span class="keyword-directive">super</span>.loadClass(name, resolve);<br> }<br> }<br> <br> <span class="keyword-directive">public</span> String toString() {<br> <span class="keyword-directive">return</span> label;<br> }<br> }<br> <br> <br> <span class="keyword-directive">public</span> <span class="keyword-directive">static</span> <span class="keyword-directive">class</span> User {<br> }<br> <br> <span class="keyword-directive">public</span> <span class="keyword-directive">static</span> <span class="keyword-directive">class</span> LoginEJB {<br> <span class="keyword-directive">static</span> {<br> System.out.println(<span class="character">"</span><span class="character">LoginEJB loaded</span><span class="character">"</span>);<br> }<br> <span class="keyword-directive">public</span> <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> login(User u) {<br> }<br> }<br> <br> <span class="keyword-directive">public</span> <span class="keyword-directive">static</span> <span class="keyword-directive">class</span> Servlet {<br> <span class="keyword-directive">public</span> <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> doGet() {<br> User u = <span class="keyword-directive">new</span> User();<br> System.out.println(<span class="character">"</span><span class="character">Logging in with User loaded in </span><span class="character">"</span> + u.getClass().getClassLoader());<br> LoginEJB.login(u);<br> }<br> }<br> <br> <span class="keyword-directive">public</span> <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> test1() <span class="keyword-directive">throws</span> Exception {<br> CustomCL ejbCL = <span class="keyword-directive">new</span> CustomCL(<span class="character">"</span><span class="character">EJB </span><span class="character">"</span>, Demo.<span class="keyword-directive">class</span>.getClassLoader(), <span class="character">"</span><span class="character">com.stc.Demo$User</span><span class="character">"</span>, <span class="character">"</span><span class="character">com.stc.Demo$LoginEJB</span><span class="character">"</span>);<br> CustomCL pfWebCL = <span class="keyword-directive">new</span> CustomCL(<span class="character">"</span><span class="character">PFWeb</span><span class="character">"</span>, ejbCL, <span class="character">"</span><span class="character">com.stc.Demo$Servlet</span><span class="character">"</span>);<br> CustomCL sfWebCL = <span class="keyword-directive">new</span> CustomCL(<span class="character">"</span><span class="character">SFWeb</span><span class="character">"</span>, ejbCL, <span class="character">"</span><span class="character">com.stc.Demo$User</span><span class="character">"</span>, <span class="character">"</span><span class="character">com.stc.Demo$Servlet</span><span class="character">"</span>);<br> <br> System.out.println(<span class="character">"</span><span class="character">Loading EJB</span><span class="character">"</span>);<br> ejbCL.loadClass(<span class="character">"</span><span class="character">com.stc.Demo$LoginEJB</span><span class="character">"</span>, <span class="keyword-directive">true</span>).newInstance();<br> <br> System.out.println(<span class="character">"</span><span class="character">Logging in, self-first</span><span class="character">"</span>);<br> sfWebCL.loadClass(<span class="character">"</span><span class="character">com.stc.Demo$Servlet</span><span class="character">"</span>, <span class="keyword-directive">false</span>).getMethod(<span class="character">"</span><span class="character">doGet</span><span class="character">"</span>).invoke(<span class="keyword-directive">null</span>);<br> <br> System.out.println(<span class="character">"</span><span class="character">Examining methods of LoginEJB</span><span class="character">"</span>);<br> ejbCL.loadClass(<span class="character">"</span><span class="character">com.stc.Demo$LoginEJB</span><span class="character">"</span>, <span class="keyword-directive">false</span>).getMethods();<br> <br> System.out.println(<span class="character">"</span><span class="character">Logging in</span><span class="character">"</span>);<br> pfWebCL.loadClass(<span class="character">"</span><span class="character">com.stc.Demo$Servlet</span><span class="character">"</span>, <span class="keyword-directive">false</span>).getMethod(<span class="character">"</span><span class="character">doGet</span><span class="character">"</span>).invoke(<span class="keyword-directive">null</span>);<br> }<br> <br> <span class="keyword-directive">public</span> <span class="keyword-directive">static</span> <span class="keyword-directive">void</span> main(String[] args) {<br> <span class="keyword-directive">try</span> {<br> test1();<br> } <span class="keyword-directive">catch</span> (Throwable e) {<br> e.printStackTrace(System.out);<br> }<br> }<br> }<br> </pre></div> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com19tag:blogger.com,1999:blog-6643071316318130698.post-2618787452377162112009-03-19T13:20:00.001-07:002009-03-19T13:20:02.866-07:00Time running backwards in Microsoft's Hyper-V<p>You would think that subsequent calls to <tt>System.nanoTime()</tt> would return ever increasing values. After all, time cannot run backwards. However, when you run a JVM on the Hyper-V virtualization platform, it turns out that time may actually run backwards.</p> <p>Hence, if you use this timer to measure time differences, you need to account for negative differences. This was a problem that I had not accounted for in the Hulp Measuring package.</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-31322656154512069272009-03-17T10:06:00.001-07:002009-03-17T10:06:58.916-07:00Interview with DZone<p>Last week <a href="http://soa.dzone.com/users/Kalali">Masoud Kalali</a> asked me for an interview about OpenESB. Today the <a href="http://soa.dzone.com/articles/qa-interview-frank-kieviet">interview was published</a>. Nothing new for those that already known OpenESB and Java CAPS, but for people who look at it for the first time it may provide some useful background information.</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0tag:blogger.com,1999:blog-6643071316318130698.post-77698468970305757132009-01-20T15:27:00.001-08:002009-01-20T15:36:24.057-08:00An interactive Regular Expression Editor<p>The other day I had to filter an HTTP access log. I figured I could write a dozen or two lines of Java code to do the job, to be written with a dozen or so test-fix cycles. But I also knew that it could be done in just a few lines when using a regular expression. The things that I don't like about using regular expressions in Java code is the fact that the regular expression becomes difficult to edit because the backslash and quote characters need to be escaped with an extra backslash, and secondly I typically need even more test-fix cycles to get the expression right. Thirdly, a non-trivial regular expression becomes difficult to read. <br></p> <p>It occurred to me that much of my reservations could be eliminated with an interactive editor that would allow me to edit the regular expression in plain text, and that would show me the results immediately. Since I've been wanting to look at NetBeans' Matisse editor, I thought I'd be able to whip out an editor in less than half an hour, and by doing so, save time not just for the project I was working on, but also for whenever I need to edit a regular expression in the future.</p> <p>The Matisse editor in NetBeans is as easy as advertised. It took me only minutes to put together a basic editor. To my satisfaction, the time savings in editing regular expressions immediately proved to be enormous!</p> <p>That was a few weeks ago. This week I was on vacation, and to kill time in the airplane, I've been upgrading this editor further. What I've added is the ability to convert the regular expression in the editor and convert it into Java. That is, the tool now takes care of escaping special characters such as backslashes. The tool is now sort of a code generator. But how about round-trip engineering? I also threw in an option to take a snippet of Java code with a regular expression, and remove the escaping characters. This makes it easy when you later need to revisit a regular expression: you copy the expression from the Java code, paste it into the editor, hit <em>convert</em> and you can edit the plain text regular expression. When done, generate the expression to Java code, paste it back in the Java source file.</p> <p>Sounds easy, no? What else could you wish for? Of course the whole thing could be integrated into NetBeans and / or Eclipse. Maybe an exercise for another plain trip.</p> <h3>Use the Interactive Regular Expression Editor!</h3> <p>I’ve made the application into a JNLP application, so you can launch it from the browser.</p> <p><a href="http://mediacast.sun.com/users/Frank.Kieviet/media/regex.jnlp">Launch Interactive Regular Expression Editor</a></p> <p><a href="http://mediacast.sun.com/users/Frank.Kieviet/media/regex.jnlp"><img title="Sample screenshot. Click to launch the editor" style="border-top-width: 0px; display: inline; border-left-width: 0px; border-bottom-width: 0px; border-right-width: 0px" height="596" alt="Sample screenshot. Click to launch the editor" src="http://lh4.ggpht.com/_k73ELjWNXV8/SXZd1HDJAzI/AAAAAAAAABo/-cEEQNn9o28/image4.png?imgmax=800" width="670" border="0"></a></p> <p>How do you use the editor? The screen consists of three edit panes. The middle editor is where you edit the regular expression. Any time that you change the expression, the sample input in the top pane is evaluated, and the results are displayed in the bottom edit pane. When you're done editing the expression, click the "generate" button, and Java code will appear in the bottom pane. Copy that code and use it in your Java programs. If you later on need to edit the expression again, simply paste the Pattern definition in the regular expression pane, and hit the "extract" button. The code in the middle pane will be replaced with the clear text version.</p> <h3>Tips for working with Regular Expressions</h3> <p>Here are some tips for working with regular expressions: use the <tt>Pattern.COMMENTS</tt> flag: it allows you to break up your regular expression into multiple lines. Doing so makes it possible to document the parts that the regular expression consists of. This leads me to the second tip: document the parts of your expression so that you'll make it a lot easier on yourself if you later have to revisit the expression. Commenting is easy: use the # sign just like you would use // in Java. Whitespace in the expression is ignored, so if you want to insert a literal space, escape it with a backslash. <br></p> <h3>Parsing text with regular expressions</h3> <p>I knew that regular expressions are great for <em>matching</em> text, but now that I have the interactive regular expression editor, I also came to appreciate the regular expressions to <em>parse</em> strings -- I mean to extract fragments out of a string. It surely is a lot easier than finding positions in a string and then using String.subString(). <br></p> <p>For example, here is a regular expression with capturing groups to parse the GlassFish log file:</p><pre># Begin marker
\[\#
# Date and time
\| (\d\d\d\d-\d\d-\d\d)\D(\d\d:\d\d:\d\d\.\d\d\d\D\d\d\d\d)
# Level
\| (.+?)
# Product
\| (.+?)
# Category
\| (.+?)
# Key-value pairs
\| (.+)?
# Msg text
\| (.+?)
# Optional stack trace
(^\t at \s \p{javaLowerCase} .* \.java .*)?
# End marker
\|\#\]</pre>
<p></p>
<p>This will result in 8 groups. This looks like a difficult expression, but with the Interactive regular expression editor, it's pretty simple to write this expression.And showing off the Interactive regular expression editor, this is what Java code it produces: </p>
<p></p><pre>public static Pattern REGEX = Pattern.compile("# Begin marker\r\n" +
"\\[\\#\r\n" +
"\r\n" +
"# Date and time\r\n" +
"\\| (\\d\\d\\d\\d-\\d\\d-\\d\\d)\\D(\\d\\d:\\d\\d:\\d\\d\\.\\d\\d\\d\\D\\d\\d\\d\\d)\r\n" +
"\r\n" +
"# Level\r\n" +
"\\| (.+?)\r\n" +
"\r\n" +
"# Product\r\n" +
"\\| (.+?)\r\n" +
"\r\n" +
"# Category\r\n" +
"\\| (.+?)\r\n" +
"\r\n" +
"# Key-value pairs\r\n" +
"\\| (.+)?\r\n" +
"\r\n" +
"# Msg text\r\n" +
"\\| (.+?)\r\n" +
"\r\n" +
"# Optional stack trace\r\n" +
"(^\\t at \\s \\p{javaLowerCase} .* \\.java .*)?\r\n" +
"\r\n" +
"# End marker\r\n" +
"\\|\\#\\]", 0 | Pattern.COMMENTS | Pattern.DOTALL | Pattern.MULTILINE);</pre> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com3tag:blogger.com,1999:blog-6643071316318130698.post-30176954401141508872009-01-20T13:01:00.001-08:002009-01-20T13:01:33.705-08:00Blogger.com works after all<p>I had this issue with blogspot where it would insert a great number of line breaks just before a table. As I found out (thanks to <a href="http://blogs.sun.com/edwardchou/">Edward Chou</a>), this behavior can be turned off in the settings page of Blogspot.</p> <p>I also found out how to create Atom feeds from labels. The help text on Google is incorrect, and I found out to reference a category like Sun, the corresponding URL is: <a href="http://frankkieviet.blogspot.com/feeds/posts/default/-/Sun">http://frankkieviet.blogspot.com/feeds/posts/default/-/Sun</a></p> <p>Now I’m wondering if I should stick with Blogspot or with Wordpress…</p> fkieviethttp://www.blogger.com/profile/07479805042474878714noreply@blogger.com0