<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-112080040011486548</id><updated>2012-01-28T13:16:08.620+01:00</updated><category term='p2'/><category term='flash'/><category term='attachment'/><category term='encoding'/><category term='acl'/><category term='profiler'/><category term='SOAP::Lite'/><category term='localization'/><category term='apt-get'/><category term='junit'/><category term='selenium'/><category term='open source'/><category term='DTrace'/><category term='pipe'/><category term='resolution'/><category term='library'/><category term='troubleshooting'/><category term='firefox'/><category term='psychology'/><category term='lvm2'/><category term='configuration'/><category term='work attitude'/><category term='utf8'/><category term='software engineering'/><category term='grep'/><category term='observability'/><category term='strace'/><category term='dpkg'/><category term='mailman'/><category term='performance'/><category term='eclipse'/><category term='modeline'/><category term='xp'/><category term='64 bit'/><category term='firefox 4'/><category term='SRP'/><category term='MySQL'/><category term='easybcd'/><category term='x11'/><category term='wlan0'/><category term='webdav'/><category term='UDF'/><category term='CVS'/><category term='Perl'/><category term='memory'/><category term='gui'/><category term='OpenSolaris'/><category term='Drupal'/><category term='msie'/><category term='access control'/><category term='permissions'/><category term='integration'/><category term='node_access'/><category term='html'/><category term='programming style'/><category term='drupal 6'/><category term='version control'/><category term='ahah'/><category term='digestBuilder'/><category term='linked table'/><category term='udev'/><category term='automation'/><category term='error'/><category term='pde'/><category term='vista'/><category term='e4'/><category term='hp'/><category term='update site'/><category term='grub'/><category term='javascript'/><category term='debugging'/><category term='serialization'/><category term='snapshot'/><category term='form api'/><category term='CCK'/><category term='views 2'/><category term='EPIC'/><category term='Access'/><category term='download'/><category term='excel'/><category term='FIFO'/><category term='autoit'/><category term='debian'/><category term='website baker'/><category term='footer'/><category term='WPO'/><category term='usability'/><category term='database'/><category term='merge'/><category term='linux'/><category term='vba'/><category term='rausb0'/><category term='cadaver'/><category term='panels'/><category term='tabs'/><category term='usb'/><category term='siteOptimizer'/><category term='ajax'/><category term='explain'/><category term='howto'/><category term='tutorial'/><category term='mount'/><category term='trigger'/><category term='multi-site'/><category term='gtk'/><category term='software design'/><category term='startup.jar'/><category term='OO'/><category term='dynamic forms'/><category term='SOAP'/><category term='xorg'/><category term='conflict'/><category term='booting'/><category term='rt73'/><category term='features'/><category term='charset'/><category term='mod_perl'/><category term='problem'/><title type='text'>plosquare.com blog</title><subtitle type='html'>Short technical articles about troubleshooting and creating software systems by Jan Ploski at plosquare.com.</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>57</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-2013292228362249188</id><published>2011-09-11T20:20:00.009+02:00</published><updated>2011-09-11T21:55:57.921+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='psychology'/><category scheme='http://www.blogger.com/atom/ns#' term='software design'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><title type='text'>On errors masked and revealed</title><content type='html'>&lt;p&gt;In a &lt;a href="http://edge.org/conversation/the-marvels-and-flaws-of-intuitive-thinking"&gt;recent talk&lt;/a&gt; professor Daniel Kahneman looks back at his research in cognitive psychology. In a side remark he notes that errors are "much more diagnostic about the mechanisms, so there might be different mechanisms for doing the same thing perfectly, and you're not going to be able to distinguish the mechanism by observing it". The approach of focusing on errors (or anomalies) in trying to accurately model something is of course a long-standing one. For example, we have the study of anomalies in medicine; natural experiments in social sciences; and of course the practice of intentionally breaking and perturbing things in about any other scientific discipline, including software engineering.&lt;/p&gt;&lt;p&gt;However, one of the striking characteristics of software engineers (perhaps shared with other kinds of designers, too) is that, unlike those scientists who attempt to peek under the hood of nature's work, we are obsessed with the idea of hiding the "implementation details" of our own creations - that is, abstracting away the mechanisms by which they operate to the greatest possible extent at all conceivable levels. I think that the driving force behind this is not so much the traditionally touted flexibility of exchanging implementations (which, to be honest, seldom needs to be done) and interoperability. Rather, it is our desire to simplify our own mental models, to reduce the cognitive load of having to think about (and worse, test) too many elements and interactions at once. Maybe so much so as to shift some of the processing from System 2 to System 1, in Kahneman's terms. We strive to reduce the probability of mistakes - which on the economic level also means reducing development and maintenance costs for our clients.&lt;/p&gt;&lt;p&gt;When software systems fail, the tables may be turned on us - the hidden mechanisms then become apparent, and the usually blessed lack of familiarity with the implementation details becomes a curse. We are then forced to learn things about our systems that we have never bothered to understand. In more extreme cases, this aspect may even represent a significant liability and risk to the maintainer, if contractual obligations demand quick, correct reactions.&lt;/p&gt;&lt;p&gt;I think a few conclusions may be drawn from the above:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;Less intricate, "uglier" designs that reveal more of the underlying mechanisms, contain fewer layers of indirection, and put a smaller demands on supporting technology and tools might be actually preferable for economic reasons - more attractive from the point of view of contingency planning (if a total or maximum time-to-repair is a concern).&lt;/li&gt;&lt;li&gt;The skills of a software developer and those of a maintainer or troubleshooter are intricately linked, but nevertheless quite distinct. Great software developers know which internal aspects of (their) software to hide and which to reveal to make its normal operations easy to grasp and the rare breakdown possible to handle. Great troubleshooters know how to reveal those aspects that were intentionally hidden by the (possibly not-so-great) developers in order to understand the causal connections and required interventions.  Ideally, they also have an extensive knowledge of the under-the-hood mechanisms of a particular software product or system which they maintain.&lt;/li&gt;&lt;li&gt;The skill set of a software developer is rightfully viewed as a superset of (and thus more valuable than) that of a troubleshooter or system administrator: a developer typically not only has to troubleshoot an analyze other developers' software because of the interfacing and teamwork requirements, but he also has to anticipate and support the future troubleshooting scenarios that will occur with respect to his own software. On the other hand, he can usually avoid accumulating truly significant amounts of factual knowledge about particular products or operating environments and rely on more general procedural knowledge.&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;I don't know about others, but I do seem to spend most of my software development time thinking about errors and mistakes - real and possible, static, dynamic, happening inside of my code and in the users' world around it.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-2013292228362249188?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/2013292228362249188/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/09/on-errors-masked-and-revealed.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/2013292228362249188'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/2013292228362249188'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/09/on-errors-masked-and-revealed.html' title='On errors masked and revealed'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-169132134750157029</id><published>2011-07-01T18:29:00.004+02:00</published><updated>2011-07-01T18:36:52.957+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='mount'/><title type='text'>Fix for "No such file or directory" while trying to mount --bind</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; The command &lt;code&gt;mount --bind /mnt/data/usr /usr&lt;/code&gt; in Linux fails with "No such file directory". &lt;code&gt;/mnt/data&lt;/code&gt; is a mount point of a dm-crypt device. strace shows ENOENT for the mount syscall, no other clues. Variants that don't refer to &lt;code&gt;/usr&lt;/code&gt; (for example, to &lt;code&gt;/usr2&lt;/code&gt; instead), do work as expected. The same error occurs after and during boot process, so it's not explainable by used files.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; The problem disappeared after upgrading the kernel from 2.6.34 to 2.6.38.8.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-169132134750157029?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/169132134750157029/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/07/fix-for-no-such-file-or-directory-while.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/169132134750157029'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/169132134750157029'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/07/fix-for-no-such-file-or-directory-while.html' title='Fix for &quot;No such file or directory&quot; while trying to mount --bind'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-6724985343095630857</id><published>2011-06-26T01:42:00.006+02:00</published><updated>2011-06-26T02:24:28.722+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='debian'/><title type='text'>Fix for random frequent freezes in GTK applications (Debian 'testing')</title><content type='html'>&lt;p&gt;Sometimes software configuration problems are solved thanks to a deep insight into how things work and methodical experimentation. At other times they are solved through a shotgun approach in which hunches, sheer luck and brute force are combined to get rid of a problem without ever truly understanding its cause. This leaves no useful information behind for &lt;em&gt;avoiding&lt;/em&gt; the problem in the future, but maybe we don't care. The following describes such a situation.&lt;/p&gt;
&lt;p&gt;After upgrading some packages, notably xorg, in Debian ('testing'), most GTK applications (e.g. Firefox, SeaMonkey, Eclipse, ...), started crashing intermittently, with just the infamous unresponsive UI symptom (aka freeze, hang). After some Googling I noticed that adding the &lt;code&gt;--sync&lt;/code&gt; command line option alleviated the problem, and for a period of time I just used this as a crude but effective workaround. I got motivated to look for a real fix when I ran into the same symptoms while trying to run a hosted Eclipse workbench (a PDE JUnit test suite). Here the hosted workbench would freeze almost immediately after startup (the window rendered, but without the menu bar, after which no more repaint events were processed). It would happen always without the &lt;code&gt;--sync&lt;/code&gt; option, but also very often despite it being supplied. I observed in the JDT debugger that the lock-up would always occur when the main thread was callnig a GTK/GDK function - not always the same function (sometimes &lt;code&gt;gdk_flush&lt;/code&gt;, sometimes &lt;code&gt;gdk_pixbuf_render_to_drawable&lt;/code&gt;, sometimes the main event loop). Stepping through the code would cause the problem to disappear (tell-tale signs of a timing bug; those are always the best ones). To make it even more "interesting", sometimes Eclipse would crash altogether with a "BadLength (poly request too large..." error from Xlib. Intuitive remedies, such as upgrading GTK/GDK packages or xorg or xlib, have not been successful, but this idea turned out to be (almost) right.&lt;/p&gt;
&lt;p&gt;The breakthrough came after running &lt;code&gt;ldd libswt-pi-gtk-3735.so&lt;/code&gt; (this is the SWT library used by Eclipse, which in turn depends on GTK), next determining which package each of the prerequisite libraries shown by ldd belonged to, and successively upgrading each of them. I didn't take care to repeat the test after each upgrade, but here is the list of packages that I replaced or tried to replace - after which the problem disappeared:&lt;/p&gt;
&lt;pre&gt;alsa-oss
binutils
gdk-imlib11
libatk1.0-0
libc-dev-bin
libc0.1-dev
libcairo2
libcairo2-dev
libfontconfig1
libfreetype6
libgdk-pixbuf-dev
libgdk-pixbuf-gnome2
libgdk-pixbuf2
libgdk-pixbuf2.0-0
libgdk-pixbuf2.0-dev
libglib2.0-0
libgtk2.0-0
libgtk2.0-common
libnspr4-0d
libpango1.0-0
libpango1.0-common
libpango1.0-dev
libpth20
libpthread-stubs0
libpthread-stubs0-dev
libx11-6
libx1106
libxcb1
libxcomposite1
libxdamage1
libxext6
libxfixes3
libxi6
libxrender1
libxtst6
xorg&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-6724985343095630857?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/6724985343095630857/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/06/fix-for-random-frequent-freezes-in-gtk.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6724985343095630857'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6724985343095630857'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/06/fix-for-random-frequent-freezes-in-gtk.html' title='Fix for random frequent freezes in GTK applications (Debian &apos;testing&apos;)'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-4107423615202246948</id><published>2011-06-25T17:15:00.004+02:00</published><updated>2011-06-25T17:26:43.144+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='pde'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='junit'/><title type='text'>Fix for ClassNotFoundException in PDE JUnit Test Runner</title><content type='html'>&lt;p&gt;When trying to run a PDE JUnit test suite in Eclipse, the requested unit test is not executed, the hosted workbench terminates immediately with the following error to the console (where &lt;code&gt;org.epic.perleditor.editors.TestEditorAssociation&lt;/code&gt; is the name of the unit test). The plug-in which contains the allegedly missing class is present in the workspace. (This error also falls into the "it used to work before" category.)&lt;/p&gt;
&lt;div style="overflow: scroll; height: 25em; width:100%; white-space: nowrap"&gt;&lt;pre&gt;Class not found org.epic.perleditor.editors.TestEditorAssociation
java.lang.ClassNotFoundException: org.epic.perleditor.editors.TestEditorAssociation
 at org.eclipse.osgi.framework.internal.core.BundleLoader.findClassInternal(BundleLoader.java:481)
 at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:397)
 at org.eclipse.osgi.framework.internal.core.BundleLoader.findClass(BundleLoader.java:385)
 at org.eclipse.osgi.internal.baseadaptor.DefaultClassLoader.loadClass(DefaultClassLoader.java:87)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
 at org.eclipse.osgi.framework.internal.core.BundleLoader.loadClass(BundleLoader.java:313)
 at org.eclipse.osgi.framework.internal.core.BundleHost.loadClass(BundleHost.java:227)
 at org.eclipse.osgi.framework.internal.core.AbstractBundle.loadClass(AbstractBundle.java:1274)
 at org.eclipse.pde.internal.junit.runtime.RemotePluginTestRunner$BundleClassLoader.findClass(RemotePluginTestRunner.java:38)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:307)
 at java.lang.ClassLoader.loadClass(ClassLoader.java:248)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClass(RemoteTestRunner.java:683)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.loadClasses(RemoteTestRunner.java:425)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:445)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.runTests(RemoteTestRunner.java:673)
 at org.eclipse.jdt.internal.junit.runner.RemoteTestRunner.run(RemoteTestRunner.java:386)
 at org.eclipse.pde.internal.junit.runtime.RemotePluginTestRunner.main(RemotePluginTestRunner.java:62)
 at org.eclipse.pde.internal.junit.runtime.UITestApplication$1.run(UITestApplication.java:114)
 at org.eclipse.swt.widgets.RunnableLock.run(RunnableLock.java:35)
 at org.eclipse.swt.widgets.Synchronizer.runAsyncMessages(Synchronizer.java:133)
 at org.eclipse.swt.widgets.Display.runAsyncMessages(Display.java:3378)
 at org.eclipse.swt.widgets.Display.readAndDispatch(Display.java:3036)
 at org.eclipse.ui.internal.Workbench.runEventLoop(Workbench.java:2382)
 at org.eclipse.ui.internal.Workbench.runUI(Workbench.java:2346)
 at org.eclipse.ui.internal.Workbench.access$4(Workbench.java:2198)
 at org.eclipse.ui.internal.Workbench$5.run(Workbench.java:493)
 at org.eclipse.core.databinding.observable.Realm.runWithDefault(Realm.java:288)
 at org.eclipse.ui.internal.Workbench.createAndRunWorkbench(Workbench.java:488)
 at org.eclipse.ui.PlatformUI.createAndRunWorkbench(PlatformUI.java:149)
 at org.eclipse.ui.internal.ide.application.IDEApplication.start(IDEApplication.java:113)
 at org.eclipse.pde.internal.junit.runtime.UITestApplication.start(UITestApplication.java:46)
 at org.eclipse.equinox.internal.app.EclipseAppHandle.run(EclipseAppHandle.java:193)
 at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.runApplication(EclipseAppLauncher.java:110)
 at org.eclipse.core.runtime.internal.adaptor.EclipseAppLauncher.start(EclipseAppLauncher.java:79)
 at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:386)
 at org.eclipse.core.runtime.adaptor.EclipseStarter.run(EclipseStarter.java:179)
 at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
 at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:39)
 at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:25)
 at java.lang.reflect.Method.invoke(Method.java:597)
 at org.eclipse.equinox.launcher.Main.invokeFramework(Main.java:549)
 at org.eclipse.equinox.launcher.Main.basicRun(Main.java:504)
 at org.eclipse.equinox.launcher.Main.run(Main.java:1236)
 at org.eclipse.equinox.launcher.Main.main(Main.java:1212)&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; edit &lt;code&gt;META-INF/MANIFEST.MF&lt;/code&gt; of the plug-in containing the not found class and add the class folder (i.e. &lt;code&gt;bin&lt;/code&gt;) to the &lt;code&gt;Bundle-ClassPath:&lt;/code&gt; directive, like so:&lt;/p&gt;
&lt;pre&gt;Bundle-ClassPath: cglib-full-2.0.2.jar,
 easymock.jar,
 bin/&lt;/pre&gt;
&lt;p&gt;If you need to debug this problem in more detail, the method responsible for finding the class (which works in a good configuration, and fails otherwise) is &lt;code&gt;org.eclipse.osgi.framework.internal.core.BundleLoader.findLocalClass&lt;/code&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-4107423615202246948?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/4107423615202246948/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/06/fix-for-classnotfoundexception-in-pde.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4107423615202246948'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4107423615202246948'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/06/fix-for-classnotfoundexception-in-pde.html' title='Fix for ClassNotFoundException in PDE JUnit Test Runner'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-971096947470899944</id><published>2011-04-22T13:44:00.011+02:00</published><updated>2011-04-22T15:16:04.318+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='p2'/><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='e4'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><title type='text'>Fix for "Cannot complete the install because of a conflicting dependency."</title><content type='html'>&lt;p&gt;When plug-in installation fails in newer versions of Eclipse (such as "e4"), you may have to deal with cryptic error messages such as this one:&lt;/p&gt;&lt;div style="overflow: scroll; height: 25em; width:100%; white-space: nowrap"&gt;&lt;pre&gt;Cannot complete the install because of a conflicting dependency.
 Software being installed: EPIC 0.6.39 (org.epic.feature.main.feature.group 0.6.39)
 Software currently installed: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
 Only one of the following can be installed at once:
   Core Runtime 3.6.100.v20101122 (org.eclipse.core.runtime 3.6.100.v20101122)
   Core Runtime 3.7.0.v20110110 (org.eclipse.core.runtime 3.7.0.v20110110)
 Only one of the following can be installed at once:
   Equinox Launcher Linux X86 Fragment 1.1.100.v20110321 (org.eclipse.equinox.launcher.gtk.linux.x86 1.1.100.v20110321)
   Equinox Launcher Linux X86 Fragment 1.1.100.v20101018 (org.eclipse.equinox.launcher.gtk.linux.x86 1.1.100.v20101018)
 Only one of the following can be installed at once:
   Equinox Java Authentication and Authorization Service (JAAS) 1.1.0.v20110411 (org.eclipse.equinox.security 1.1.0.v20110411)
   Equinox Java Authentication and Authorization Service (JAAS) 1.1.0.v20101004 (org.eclipse.equinox.security 1.1.0.v20101004)
   Equinox Java Authentication and Authorization Service (JAAS) 1.1.0.v20110124-0830 (org.eclipse.equinox.security 1.1.0.v20110124-0830)
 Only one of the following can be installed at once:
   Refactoring UI 3.5.100.v20101012-0800 (org.eclipse.ltk.ui.refactoring 3.5.100.v20101012-0800)
   Refactoring UI 3.6.0.v20110307-2000a (org.eclipse.ltk.ui.refactoring 3.6.0.v20110307-2000a)
   Refactoring UI 3.5.100.v20110111-0800 (org.eclipse.ltk.ui.refactoring 3.5.100.v20110111-0800)
 Only one of the following can be installed at once:
   Eclipse Platform 4.1.0.v20110420-1420 (org.eclipse.platform 4.1.0.v20110420-1420)
   Eclipse Platform 4.1.0.v20110415-1401 (org.eclipse.platform 4.1.0.v20110415-1401)
   Eclipse Platform 3.7.0.v201104210100 (org.eclipse.platform 3.7.0.v201104210100)
   Eclipse Platform 3.7.0.v201103101119 (org.eclipse.platform 3.7.0.v201103101119)
   Eclipse Platform 4.1.0.v20110407-2200 (org.eclipse.platform 4.1.0.v20110407-2200)
   Eclipse Platform 4.1.0.v20110415-1030 (org.eclipse.platform 4.1.0.v20110415-1030)
   Eclipse Platform 3.7.0.v201103220800 (org.eclipse.platform 3.7.0.v201103220800)
   Eclipse Platform 3.7.0.v201104061223 (org.eclipse.platform 3.7.0.v201104061223)
   Eclipse Platform 4.1.0.v20110412-2200 (org.eclipse.platform 4.1.0.v20110412-2200)
   Eclipse Platform 3.7.0.v201104121532 (org.eclipse.platform 3.7.0.v201104121532)
   Eclipse Platform 3.7.0.v201104211800 (org.eclipse.platform 3.7.0.v201104211800)
   Eclipse Platform 4.1.0.v20110127-2200 (org.eclipse.platform 4.1.0.v20110127-2200)
   Eclipse Platform 3.7.0.v201102150800 (org.eclipse.platform 3.7.0.v201102150800)
   Eclipse Platform 4.1.0.v20101202-1530 (org.eclipse.platform 4.1.0.v20101202-1530)
   Eclipse Platform 4.1.0.v20110312-1300 (org.eclipse.platform 4.1.0.v20110312-1300)
   Eclipse Platform 4.1.0.v20110325-1411 (org.eclipse.platform 4.1.0.v20110325-1411)
   Eclipse Platform 4.1.0.v20110419-2200 (org.eclipse.platform 4.1.0.v20110419-2200)
   Eclipse Platform 3.7.0.v201012081300 (org.eclipse.platform 3.7.0.v201012081300)
   Eclipse Platform 4.1.0.v20110422-0200 (org.eclipse.platform 4.1.0.v20110422-0200)
   Eclipse Platform 4.1.0.v20110421-0500 (org.eclipse.platform 4.1.0.v20110421-0500)
   Eclipse Platform 4.1.0.v20110414-2200 (org.eclipse.platform 4.1.0.v20110414-2200)
   Eclipse Platform 3.7.0.v201104191004 (org.eclipse.platform 3.7.0.v201104191004)
 Cannot satisfy dependency:
   From: Equinox p2 Provisioning for IDEs 2.1.0.v20110228-897TFncFdHjO2qRuCh_UvYT (org.eclipse.equinox.p2.user.ui.feature.group 2.1.0.v20110228-897TFncFdHjO2qRuCh_UvYT)
   To: org.eclipse.equinox.security [1.1.0.v20110124-0830]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.r20110302-9gF7SHCIFt6ms-lIjrC6vK_XO-IabJMKu (org.eclipse.platform.feature.group 3.7.0.r20110302-9gF7SHCIFt6ms-lIjrC6vK_XO-IabJMKu)
   To: org.eclipse.platform [3.7.0.v201103101119]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20100923-9fF7MHDqFsAkplGz0n61z-yU57WHoz0JdMfaI (org.eclipse.platform.feature.group 3.7.0.v20100923-9fF7MHDqFsAkplGz0n61z-yU57WHoz0JdMfaI)
   To: org.eclipse.rcp.feature.group [3.7.0.v20101115-9FB-FqhFr3P05j0S-HRVgSR]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110209-9fF7QHFQFsAlpyfO4rKw4lNW200gZcvGr (org.eclipse.platform.feature.group 3.7.0.v20110209-9fF7QHFQFsAlpyfO4rKw4lNW200gZcvGr)
   To: org.eclipse.ltk.ui.refactoring [3.5.100.v20110111-0800]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHCJFt3cwDesZ5LrhrcdfooveV1uWqcqPF (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHCJFt3cwDesZ5LrhrcdfooveV1uWqcqPF)
   To: org.eclipse.rcp.feature.group [3.7.0.v20110216-9DB5FiuFpBGyIDVb_FRbwWP]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHIZFt3cwDktE9WrW9XEj1iOiW9fgbhxME (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHIZFt3cwDktE9WrW9XEj1iOiW9fgbhxME)
   To: org.eclipse.rcp.feature.group [3.7.0.v20110216-9DB5FiuFpBGyImVoOcTaz-ZS]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHLgFt4cwDmoWF-DvhVMEUrQfXBVSritR5 (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHLgFt4cwDmoWF-DvhVMEUrQfXBVSritR5)
   To: org.eclipse.rcp.feature.group [3.7.0.v20110216-9DB5FiuFpBGyIxIrTOSarWQ]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHNFFt4cwDooaDrDsaVdEWi0oMLWlh6mbT (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHNFFt4cwDooaDrDsaVdEWi0oMLWlh6mbT)
   To: org.eclipse.equinox.security [1.1.0.v20110411]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHNFFt4cwDqmhLPCtURdWWmWmO0Plh6mbT (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHNFFt4cwDqmhLPCtURdWWmWmO0Plh6mbT)
   To: org.eclipse.equinox.security [1.1.0.v20110411]
 Cannot satisfy dependency:
   From: Eclipse Platform 3.7.0.v20110315-9gF7SHNFFt4cwDrmlStE-Ufz0RVqQoc8RS8f2gQ (org.eclipse.platform.feature.group 3.7.0.v20110315-9gF7SHNFFt4cwDrmlStE-Ufz0RVqQoc8RS8f2gQ)
   To: org.eclipse.rcp.feature.group [3.7.0.v20110216-9DB5FiuFpBGyY_VrXOSeaUQ]
 Cannot satisfy dependency:
   From: Eclipse Platform 4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU (org.eclipse.platform.feature.group 4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU)
   To: org.eclipse.ltk.ui.refactoring [3.6.0.v20110307-2000a]
 Cannot satisfy dependency:
   From: Eclipse Platform 4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU (org.eclipse.platform.feature.group 4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU)
   To: org.eclipse.platform [4.1.0.v20110312-1300]
 Cannot satisfy dependency:
   From: Eclipse Product Configuration 1.0.0.I20110310-1119 (org.eclipse.rcp.configuration.feature.group 1.0.0.I20110310-1119)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20101018]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20101115-9FB-FqhFr3P05j0S-HRVgSR (org.eclipse.rcp.feature.group 3.7.0.v20101115-9FB-FqhFr3P05j0S-HRVgSR)
   To: org.eclipse.core.runtime [3.6.100.v20101122]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20110216-9DB5FiuFpBGyIDVb_FRbwWP (org.eclipse.rcp.feature.group 3.7.0.v20110216-9DB5FiuFpBGyIDVb_FRbwWP)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20110321]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20110216-9DB5FiuFpBGyImVoOcTaz-ZS (org.eclipse.rcp.feature.group 3.7.0.v20110216-9DB5FiuFpBGyImVoOcTaz-ZS)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20110321]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20110216-9DB5FiuFpBGyIxIrTOSarWQ (org.eclipse.rcp.feature.group 3.7.0.v20110216-9DB5FiuFpBGyIxIrTOSarWQ)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20110321]
 Cannot satisfy dependency:
   From: Eclipse RCP 3.7.0.v20110216-9DB5FiuFpBGyY_VrXOSeaUQ (org.eclipse.rcp.feature.group 3.7.0.v20110216-9DB5FiuFpBGyY_VrXOSeaUQ)
   To: org.eclipse.equinox.launcher.gtk.linux.x86 [1.1.100.v20110321]
 Cannot satisfy dependency:
   From: Eclipse Project SDK 4.1.0.v20110303-7T7fA7F8Yw_bVbCrFz-gFceimF7IZRyogX1MAdUyRWqYu (org.eclipse.sdk.feature.group 4.1.0.v20110303-7T7fA7F8Yw_bVbCrFz-gFceimF7IZRyogX1MAdUyRWqYu)
   To: org.eclipse.platform.feature.group [4.1.0.v20110303-9IF70GdMFnUTMd104-mz-W9mNz0ESwPlAOa-_otA7mYeU]
 Cannot satisfy dependency:
   From: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
   To: org.eclipse.equinox.p2.user.ui.feature.group [2.1.0.v20110228-897TFncFdHjO2qRuCh_UvYT]
 Cannot satisfy dependency:
   From: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
   To: org.eclipse.rcp.configuration.feature.group [1.0.0.I20110310-1119]
 Cannot satisfy dependency:
   From: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
   To: org.eclipse.sdk.feature.group [4.1.0.v20110303-7T7fA7F8Yw_bVbCrFz-gFceimF7IZRyogX1MAdUyRWqYu]
 Cannot satisfy dependency:
   From: Eclipse SDK 4.1.0.I20110312-1300 (org.eclipse.sdk.ide 4.1.0.I20110312-1300)
   To: toolingorg.eclipse.sdk.ide.configuration [4.1.0.I20110312-1300]
 Cannot satisfy dependency:
   From: EPIC 0.6.39 (org.epic.feature.main.feature.group 0.6.39)
   To: org.eclipse.platform.feature.group [3.2.0,4.0.0)
 Cannot satisfy dependency:
   From: toolinggtk.linux.x86org.eclipse.core.runtime 4.1.0.I20110312-1300
   To: bundle org.eclipse.core.runtime 3.7.0.v20110110
 Cannot satisfy dependency:
   From: toolingorg.eclipse.sdk.ide.configuration 4.1.0.I20110312-1300
   To: toolinggtk.linux.x86org.eclipse.core.runtime [4.1.0.I20110312-1300]
&lt;/pre&gt;&lt;/div&gt;
&lt;p&gt;The usability problems with this error message are evident: excessive length, lack of focus, little clue for the developer about possible solutions (and even less for a mere user who is just trying to install a plug-in). Paradoxically, the best way to even know which parts of the error message are relevant at all is to have already solved the problem at hand. Having done just this, here are some possibly helpful inferences:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Chances that you are going to solve a problem of this sort as a normal user are slight. The error likely lies in dependency declarations contained in feature.xml descriptors that are being processed by the installer and you cannot/are not supposed to manually edit those. It's safe to assume that it's the developers' job to sort out this mess. The remaining bullet points address how.&lt;/li&gt;
&lt;li&gt;The "Only one of the following can be installed at once" parts of the message are misleading and should be ignored as noise.&lt;/li&gt;
&lt;li&gt;The "Cannot satisfy dependency" parts might be relevant, however most of them are bogus, resulting from error propagation, and should be ignored as noise as well.&lt;/li&gt;
&lt;li&gt;The root cause is likely contained in the feature.xml descriptor of whatever it is that you are trying to install. In the above example my installation attempt concerns the &lt;a href="http://www.epic-ide.org/"&gt;EPIC IDE&lt;/a&gt;, and only the following short snippet from the &lt;em&gt;bottom of the error message&lt;/em&gt; actually matters:&lt;pre&gt; Cannot satisfy dependency:
   From: EPIC 0.6.39 (org.epic.feature.main.feature.group 0.6.39)
   To: org.eclipse.platform.feature.group [3.2.0,4.0.0)
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;The above dependency comes from a line in content.xml, a file which is found within content.jar downloaded by the installer from the update site. In my case the relevant line of content.xml looked like so:&lt;pre&gt;&amp;lt;required namespace='org.eclipse.equinox.p2.iu'
         name='org.eclipse.platform.feature.group'
         range='[3.2.0,4.0.0)'/&amp;gt;
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;The file content.xml is a generated one, so to fix the problem (by removing the line) the developer must track back to the input files that are under his control. In my case it was feature.xml, which contained the following "offending" snippet:&lt;pre&gt;  &amp;lt;requires&amp;gt;
     &amp;lt;import feature="org.eclipse.platform"
                version="3.2.0"
                match="compatible"/&amp;gt;
  &amp;lt;/requires&amp;gt;
&lt;/pre&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Eventually, changing match="compatible" to match="greaterOrEqual" in feature.xml and rebuilding content.jar fixed the cause of the original messy error message, as was to be expected ("compatible" means only &lt;a href="http://help.eclipse.org/helios/topic/org.eclipse.platform.doc.isv/reference/misc/feature_manifest.html%3Fresultof%3D%22compatible%22%20%22matching%20rule%22"&gt;compatible within the major version&lt;/a&gt;, i.e. "3.x.x"). After the fix content.xml contains range="3.2.0", which is fine for installation in 4.1.0.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-971096947470899944?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/971096947470899944/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/04/fix-for-cannot-complete-install-because.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/971096947470899944'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/971096947470899944'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/04/fix-for-cannot-complete-install-because.html' title='Fix for &quot;Cannot complete the install because of a conflicting dependency.&quot;'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-6530676486587104559</id><published>2011-04-05T19:11:00.004+02:00</published><updated>2011-04-05T19:26:40.116+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><title type='text'>Don't use Perl to compute (much)</title><content type='html'>&lt;p&gt;There are times when we want to &lt;a href="http://www.flirble.org/~nick/P/Fast_Enough/"&gt;understand and optimize performance of Perl programs&lt;/a&gt;, and there are other times when we just want a program to run faster, fast. Having a computation-intensive task at hand (hint: nested loops, numbers, arithmetics) speaks for a brute force approach - simply rewriting the algorithm in C is likely to yield great benefits with little intellectual strain.&lt;/p&gt;&lt;p&gt;Here's a little case study: a Perl version of a program which performs some 360,000,000 arithmetic operations takes 135 wallclock seconds to execute. After moving the computation to C the execution time drops to 1.7 seconds - a &lt;strong&gt;speedup of more than 70&lt;/strong&gt;. Hint: to keep life simple given such a case, don't even bother with perlxs or worry about efficient I/O... just port your code to C, invoke the C program via system() and communicate through files or stdin/stdout.&lt;/p&gt;&lt;p&gt;(Of course, the same insight certainly applies to any interpreted language, not just Perl.)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-6530676486587104559?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/6530676486587104559/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/04/dont-use-perl-to-compute-much.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6530676486587104559'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6530676486587104559'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/04/dont-use-perl-to-compute-much.html' title='Don&apos;t use Perl to compute (much)'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-8066606617358643287</id><published>2011-03-22T18:36:00.007+01:00</published><updated>2011-03-23T10:17:40.938+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='usability'/><category scheme='http://www.blogger.com/atom/ns#' term='firefox 4'/><title type='text'>Fix hover display of target links in Firefox 4</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; After upgrading to Firefox 4 the first "improvement" that you'll notice is possibly the all-too-distracting popup which displays the full target URL whenever you move the mouse pointer over any link. Apparently they got rid of the status bar, but decided that for security's sake the information must still be displayed somewhere and chose a popup. Unfortunately, a popup which keeps appearing and disappearing over the main page display area feels much more intrusive than a fixed status bar area (which you could also hide altogether).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Install the poorly named add-on &lt;a href="https://addons.mozilla.org/en-us/firefox/addon/status-4-evar/" rel="nofollow"&gt;Status-4-Evar&lt;/a&gt;. It will instantly restore the original status bar without any further configuration.&lt;/p&gt;&lt;p&gt;And by the way, you can also disable "View &gt; Toolbars &gt; Tabs on Top" to restore the original proper location of tabs. To move the Home and Reload buttons back to where they belong left of the address entry field, you can right-click on the little arrow within that field (or almost anywhere else on the toolbar), select "Customize" and then drag the buttons back.&lt;/p&gt;&lt;p&gt;The most comfortable GUIs are the ones we're used to.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-8066606617358643287?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/8066606617358643287/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/03/fix-hover-display-of-target-links-in.html#comment-form' title='4 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8066606617358643287'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8066606617358643287'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/03/fix-hover-display-of-target-links-in.html' title='Fix hover display of target links in Firefox 4'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>4</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-8237192858458313290</id><published>2011-01-10T20:01:00.004+01:00</published><updated>2011-01-10T20:14:37.135+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='debian'/><category scheme='http://www.blogger.com/atom/ns#' term='flash'/><title type='text'>Fullscreen Flash Player on Debian lenny</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Flash plugin crashes in Firefox on 64 bit Debian lenny whenever entering fullscreen mode. The console displays an error message like "libflashplayer.so: undefined symbol: gtk_widget_get_window". Both Firefox and Flash plugin were installed manually rather than from a Debian package on the affected system.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; This problem occurs due to an outdated version of 32 bit libgtk2 in Debian lenny (ia32-libs-gtk), which doesn't have the required symbol. As a remedy, you should install 64 bit versions of libgtk2, Firefox and Flash player from Debian packages. However, these packages are not found in lenny repository, so you first need to &lt;a href="http://backports.debian.org/Instructions/"&gt;add the backports repository&lt;/a&gt; to your apt (important: also add &lt;code&gt;contrib&lt;/code&gt; behind &lt;code&gt;main&lt;/code&gt; in the repository definition!) After that, just go ahead with&lt;pre&gt;
apt-get -t lenny-backports install flashplugin-nonfree iceweasel libgtk2.0-0
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-8237192858458313290?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/8237192858458313290/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/01/fullscreen-flash-player-on-debian-lenny.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8237192858458313290'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8237192858458313290'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/01/fullscreen-flash-player-on-debian-lenny.html' title='Fullscreen Flash Player on Debian lenny'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-1294339735142336486</id><published>2011-01-06T19:32:00.005+01:00</published><updated>2011-01-06T19:40:49.991+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='WPO'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><title type='text'>Building scalable web applications</title><content type='html'>I stumbled upon a presentation by Pressflow founder David Strauss with a nice checklist of common (open source) technologies and buzzwords for building highly scalable web applications. The slides focus on deployment and load/configuration management rather than software design considerations: &lt;a href="http://fourkitchens.com/sites/default/files/PHP%20TEK-X%20-%20Planning%20Infrastructure.pdf"&gt;Scalable LAMP infrastructure&lt;/a&gt;.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-1294339735142336486?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/1294339735142336486/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/01/building-scalable-web-applications.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1294339735142336486'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1294339735142336486'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/01/building-scalable-web-applications.html' title='Building scalable web applications'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-8238233903143325075</id><published>2011-01-05T01:47:00.002+01:00</published><updated>2011-01-05T01:58:50.656+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='panels'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><title type='text'>Surface drilling Drupal Panels 3</title><content type='html'>&lt;p&gt;This article contains some notes from a short reverse-engineering session targeted at Panels 6.x-3.8 and Chaos Tools 6.x-1.8. The goal was to capture some first impressions and record them for potential future exploration and understanding of that module. The motivation came from the current lack of technical documentation from the powers that be.&lt;/p&gt;&lt;p&gt;Before deploying the drills, I wish to share with you a few contemplative remarks about Drupal documentation. I feel disappointed by the supposedly "user-friendly" and sometimes overenthusiastic case-oriented explanations so prevalent in descriptions of Drupal ("if you want X, click on Y... how cool!"). Frankly, I find them much inferior to what I like to call model-oriented explanations ("the system consists of parts X, which have state Y and interact according to rules Z") based on strict terminology and aiming to identify intermediate causes at work. A good model capitalizes on your prior software engineering and site building experience by allowing you to generate tons of individual cases in a reasonable amount of time. On the other hand, a set of cases helps you solve only a limited number of problems quickly by imitation. A large database of cases takes much effort to collect, maintain and remember, and it does a poor job at eliminating residual risk. When you're hit with a problem not yet documented in the database and forced to somehow augment your existing cases, this task is going to take an extraordinary amount of time or require to call in heavy artillery (a consultant). In contrast, a well-constructed model takes just a little effort to acquire (but not to create!) and promises much more substantial risk reductions; model refinements do not require quantum leaps of imagination. By abstracting well, you can keep most issues out of your mind most of the time, and with a good choice of metaphors, related pieces of a solution should come together quite effortlessly. In effect, adopting a cohesive model-oriented approach allows you to deliver more consistent problem solving for your clients. Similarly, when you hire someone, they better bring this secret weapon in their arsenal rather than just Google + d.o.&lt;/p&gt;&lt;p&gt;Back to the main topic. A panel is a named, persistent set of specifications that govern a joint presentation of multiple pieces of content on the same page. These specs cover
&lt;/p&gt;&lt;ul&gt;&lt;li&gt;which kinds of content or even which individual pieces of content should be retrieved from the database;&lt;/li&gt;
&lt;li&gt;where (in which pane of a multi-pane layout) each piece of the retrieved content should appear;&lt;/li&gt;
&lt;li&gt;how each piece of content should be rendered;&lt;/li&gt;
&lt;li&gt;under what circumstances and for whom each piece of content should (dis)appear;&lt;/li&gt;
&lt;li&gt;(for panel pages) which URI paths should trigger displaying the specified content.&lt;/li&gt; &lt;/ul&gt;&lt;p&gt;A panel is defined by a site builder. It is stored either in the database (by default) or in code (when exported, e.g. for the sake of &lt;a href="http://en.wikipedia.org/wiki/Revision_control"&gt;VCS&lt;/a&gt;). It is then used by Drupal as a blueprint for page construction whenever its associated path is requested by the browser. The specifications that comprise a panel are further subdivided into a (non-empty) set of &lt;em&gt;variants&lt;/em&gt;, each of which may differ from the other variants in certain respects while sharing the common base configuration to foster reuse. (However, variants are unlike displays from Views in that they all have the same rank; there is no default variant that others would inherit from and override.)&lt;/p&gt;&lt;p&gt;Panels are somewhat at odds with Drupal core, meaning that they seek to replace rather than just expand upon and integrate with some of the previous ideas:
&lt;/p&gt;&lt;ul&gt;&lt;li&gt;In their capability to &lt;em&gt;lay out&lt;/em&gt; pieces of content, panels build upon—but also to an extent compete with—page templates defined by a theme. In particular, panes of a panel layout are conceptually equivalent to regions of a page template. Because a panel is placed
within a region, it can be also thought of as a means for subdividing the region into multiple sub-regions (which are not known to the theme subsystem).&lt;/li&gt;
&lt;li&gt;In their capability to &lt;em&gt;retrieve&lt;/em&gt; and &lt;em&gt;render&lt;/em&gt; pieces of content, Panels developers abandon Drupal's core idea that all content should be represented as either nodes (for user-defined content) or blocks (for reusable pieces of UI). Other kinds of HTML content, not belonging to either category and possibly computed on-the-fly, can also be made available to and rendered in a panel.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To put the above and following explanations in context, consider the following screenshot of the Panels UI (with some section numbering added):&lt;/p&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_o_c25p_Zq5Y/TSPCHQZWKkI/AAAAAAAAAC8/0Tradnhva0s/s1600/panel.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 222px;" src="http://1.bp.blogspot.com/_o_c25p_Zq5Y/TSPCHQZWKkI/AAAAAAAAAC8/0Tradnhva0s/s400/panel.png" border="0" alt="Panels UI" id="BLOGGER_PHOTO_ID_5558499795021474370" /&gt;&lt;/a&gt;
&lt;p&gt;The configuration sections are subdivided into three main areas: Summary, Settings, and Variants. The information under Summary and Settings concerns the panel as a whole, while the subsections under Variants pertain to each of the defined named variants. With a few exceptions, the section labels are not self-explanatory at first glance, and the information contained within each section cannot be easily understood separately from the other sections. In this sense,
the UI is non-intuitive. It &lt;em&gt;won't&lt;/em&gt; teach what can be done with panels in a learning-by-doing approach. It &lt;em&gt;will&lt;/em&gt; cause concern with regard to unexplained settings and overly abstract terminology. The module author's line of defense is that you don't have to understand every piece of the UI to successfully use panels for simple layout scenarios. While true, the suggested adopt-now-explore-later approach also conveys an ominous message: if the documentation is not ever written—and if the adoption or future development of the module suffers—you may find yourself in a bad position by having placed bets on it. (It is not much consolation for new users that the &lt;a href="http://www.lullabot.com/podcasts/podcast-87-panels-vs-context-cage-match"&gt;friendly competition&lt;/a&gt; doesn't fare much better with regard to making their alternative comprehensible and that the Panels module is well-entrenched with a significant user base.) The question is: can we in the meanwhile remedy this situation by sacrificing some time on the altar of open source and simply peeking into Panels code? Or is the code as poorly done as the documentation? The rest of the article is my first shot at answers (for the impatient: yes and no, respectively). I start by exploring some of the more confusing—while at the same time important—sections of the Panels UI and conclude with a brief foray into code design and implementation issues.&lt;/p&gt;&lt;p&gt;It turns out that the key to understanding Panels configuration are the concepts of "contexts" and "content". These technical terms are not at all related to the Context module (best erase it out of your mind while studying Panels), only weakly related to the content types of the CCK module, and they might not match any colloquial meaning taken from a dictionary. Such is the nature of terms unleashed on the unsuspecting world at night by well-meaning software developers. Let's focus on "contexts" first. In Panels terminology, the term &lt;em&gt;context&lt;/em&gt; refers to a specification of a piece of (renderable) content that should be loaded and become known under a chosen id within the panel variant before it is output as HTML. For example, if your panel should display three nodes next to each other, it would obviously first have to load them, which you would specify by adding three contexts of type node to some panel variant. The Contexts section (2) lets you &lt;em&gt;directly&lt;/em&gt; specify the pieces of content that should be loaded; for example specify a node by its nid or title. Alternatively, you could parametrize the panel to load different pieces
of content depending on what has been provided through the URL. To achieve this, you would include placeholders in the panel's path (as shown on the screenshot) and then use the Arguments section (1) to associate each placeholder with a context type (e.g. specify that the first placeholder called &lt;code&gt;%node1&lt;/code&gt; really refers to a node ID rather than a taxonomy term ID; at request handling time, the URL is first taken apart by Drupal's menu subsystem and for each placeholder all modules, including Panels, are consulted – see &lt;code&gt;_menu_load_objects&lt;/code&gt; in &lt;code&gt;includes/menu.inc&lt;/code&gt; for details). Finally, by using the Relationships area of the Contexts section (2) you could also specify a context that loads a piece of content related to another, previously specified, piece of content (say, load the user who is an author of the second node loaded by the node context). Based on the preceding description, a better term for "contexts" might have been "content loaders"; alas, the release night is over, and it is too late for changes now.&lt;/p&gt;&lt;p&gt;Now let's turn to &lt;em&gt;content&lt;/em&gt;. In Panels terminology it refers to "something that can be placed (and somehow rendered in) a pane" through the Content section (3). Unsurprisingly, the pieces of content loaded by contexts as well as their individual parts (such as CCK fields of nodes;
or the user picture from a profile) fall into this category. For this reason you are generally well-advised to first define contexts before even turning to the Content section. However, other pieces of content (such as blocks or HTML snippets) are also available without the need (or possibility) to obtain them through a context. The advantage of loading panel content through contexts is that the attributes of the loaded objects then become available within all panes as variables that can be used in string substitutions (e.g. &lt;code&gt;%node1:title&lt;/code&gt; or &lt;code&gt;%user1:name&lt;/code&gt;, where the prefix equals context id). For example, having established the three node contexts and one user context as mentioned above, you could add a HTML snippet (aka "custom content") with the node titles and the user name to a pane.&lt;/p&gt;&lt;p&gt;Having acquired a basic understanding of the context/content pair of concepts and experimented a bit with the Panels UI, here are the next few natural followup questions that come to mind:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Where do all the selectable context and content types in the Panels UI (and their configuration forms) come from?&lt;/li&gt;
&lt;li&gt;How does the Panels module know to offer "Node", "Taxonomy term" and "User" contexts and how do you know what data &lt;em&gt;exactly&lt;/em&gt;
they load?&lt;/li&gt;
&lt;li&gt;What the heck is a "Node add form" context supposed to load and how is it different from "Node"?&lt;/li&gt;
&lt;li&gt;What determines which items appear on the "Miscellaneous" tab when adding content as opposed to the "Widgets"
tab?&lt;/li&gt;
&lt;li&gt;What if you want to add something that is &lt;em&gt;not at all&lt;/em&gt; listed in Context or Content sections?&lt;/li&gt;
&lt;li&gt;How exactly is the Relationships combo populated?&lt;/li&gt;
&lt;li&gt;Should we degrade ourselves to click monkeys and figure out all of the above by trial and error?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;I won't even come close to providing detailed (boring?) answers to all of the above questions and will instead address the last (somewhat rhetorical) one by providing a little push in the right direction. As announced before, the solution is sought (and found) by examining the code. It turns out that most of the items and forms you come across while interacting with the Panels UI are not hard-coded anywhere in the Panels module, but rather contributed dynamically by code. "I know – panel hooks!" might be your first guess, but unfortunately it's not that simple. There is a lot more indirection in the Panels code than meets the eye and a more refined mechanism than good old Drupal hooks is at play. Furthermore, it turns out that neither contexts nor content types are designed to be panel-specific. In fact, most of them are not even implemented in the Panels module, but rather belong to the Chaos Tools module, upon which the Panels module depends. In general it's safe to say that the indirection and abstraction sauce is good for flexibility and extensibility and also a definitive poison for code comprehension. The case of Panels is no exception, and so I guess that the author might be playing with fire to some extent ("we have this wonderfully flexible super-duper API for every conceivable task, which unfortunately noone besides us can wrap their head around). I'm glad to report that I have seen much worse and that the code design and inline documentation are definitely more solid than what you see in "advanced help" today. In short, you &lt;em&gt;can&lt;/em&gt; wrap your head around it.&lt;/p&gt;&lt;p&gt;To navigate around Panels code—and to see how the administrative UI is built and what each selectable item stands for—you have to first grasp the much more general concept of CTools Plugins (&lt;a href="http://en.wikipedia.org/wiki/Reification_%28computer_science%29#Reification_in_conceptual_modeling"&gt;reified&lt;/a&gt; hooks). This part of the Chaos Tools module is an example of the &lt;a href="http://en.wikipedia.org/wiki/Strategy_pattern"&gt;Strategy pattern&lt;/a&gt; at work. It aims to facilitate a division of labor between module developers and site builders. A plugin instance is an exchangeable persistent object responsible for performing a set of tasks in its own particular way. Plugins are typically selectable (and so instantiated) through the UI of those Drupal modules that depend upon them (like Panels or Views).  They may also contain callbacks that construct an administrative form-based UI to allow site builders detailed configuration of each plugin instance. Alternatively, plugins may be organized hierarchically (in a type:subtype or parent:child fashion), for the benefit of a more structured selection UI. The exact set of responsibilities for a plugin type is usually specified by the module developer who invokes plugins of that type from her module. Particular implementations of those responsibilities are then provided as plugins by (potentially) different developers.
A plugin is usually implemented in a single .inc file. Plugin names must be unique within a plugin type, but do not need to be unique across types. Plugins are used by modules or by each other. The CTools library acts as an intermediary and a directory service for publishing, locating
and invoking plugins. This terminology and the discussed relationships are illustrated by the following examples:&lt;/p&gt;&lt;table border="1" style="border:1px solid black"&gt;
&lt;tbody&gt;&lt;tr&gt;
 &lt;th align="left"&gt;Plugin type&lt;/th&gt;
 &lt;th align="left"&gt;Responsibility of plugin type&lt;/th&gt;
 &lt;th align="left"&gt;Plugin&lt;/th&gt;
 &lt;th align="left"&gt;Plugin functionality&lt;/th&gt;
 &lt;th align="left"&gt;Plugin implemented in&lt;/th&gt;
 &lt;th align="left"&gt;Plugin used by&lt;/th&gt;
 &lt;th align="left"&gt;Plugin instance (examples)&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;
 &lt;td&gt;contexts&lt;/td&gt;
 &lt;td&gt;Provide renderable content for a panel.&lt;/td&gt;
 &lt;td&gt;node&lt;/td&gt;
 &lt;td&gt;Load and provide data from a node specified by nid or title.&lt;/td&gt;
 &lt;td&gt;ctools/plugins/contexts/node.inc&lt;/td&gt;
 &lt;td&gt;panels.module (indirectly, through its own plugin &lt;i&gt;panels_context&lt;/i&gt; of type &lt;i&gt;task_handlers&lt;/i&gt;)&lt;/td&gt;
 &lt;td&gt;node1 and node2 in Variant1 of panelpage1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
 &lt;td&gt;"&lt;/td&gt;
 &lt;td&gt;"&lt;/td&gt;
 &lt;td&gt;user&lt;/td&gt;
 &lt;td&gt;Load and provide data from a user profile by uid or name.&lt;/td&gt;
 &lt;td&gt;ctools/plugins/contexts/user.inc&lt;/td&gt;
 &lt;td&gt;panels.module (indirectly, through its own plugin &lt;i&gt;panels_context&lt;/i&gt; of type &lt;i&gt;task_handlers&lt;/i&gt;)&lt;/td&gt;
 &lt;td&gt;user1 in Variant1 of panelpage1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
 &lt;td&gt;content_types&lt;/td&gt;
 &lt;td&gt;Render available content within an individual panel pane.&lt;/td&gt;
 &lt;td&gt;node&lt;/td&gt;
 &lt;td&gt;Render available node content.&lt;/td&gt;
 &lt;td&gt;ctools/plugins/content_types/node.inc&lt;/td&gt;
 &lt;td&gt;panels.module (indirectly, through its own plugin &lt;i&gt;standard&lt;/i&gt; of type &lt;i&gt;display_renderers&lt;/i&gt;)&lt;/td&gt;
 &lt;td&gt;"Node 1: ID" content in left pane and "Node 2: ID" content in right pane of Variant1 of panelpage1&lt;/td&gt;
&lt;/tr&gt;
&lt;tr&gt;
 &lt;td&gt;"&lt;/td&gt;
 &lt;td&gt;"&lt;/td&gt;
 &lt;td&gt;user&lt;/td&gt;
 &lt;td&gt;Render a picture from user profile.&lt;/td&gt;
 &lt;td&gt;ctools/plugins/content_types/user_context/user_picture.inc&lt;/td&gt;
 &lt;td&gt;panels.module (indirectly, through its own plugin &lt;i&gt;standard&lt;/i&gt; of type &lt;i&gt;display_renderers&lt;/i&gt;)&lt;/td&gt;
 &lt;td&gt;"Node1 author" user picture in Variant1 of panelpage1&lt;/td&gt;
&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The required naming conventions and steps for defining plugins (and plugin types) and invoking plugin instances are documented in "advanced help" shipped with the CTools module, which you are advised to read carefully. The CTools library is used pervasively and consistently throughout Earl Miles's (aka &lt;a href="http://drupal.org/user/26979"&gt;merlinofchaos&lt;/a&gt;) modules (and also in some others). Thus, if we view Drupal as a domain specific language, CTools should count as a potentially prominent dialect. Becoming familiar with it will not only help you understand Panels, but also Views and potentially future of Drupal core, if it should spread into more mainstream use.&lt;/p&gt;&lt;p style="font-size: 75%;"&gt;Note: while looking at the CTools module, I also found that it supports third-party module developers in evolving and versioning their own modules' APIs, including catering to different client modules using different versions of an API at the same time. The term API here refers to the set of hooks which the developed module invokes (i.e. the hooks provided [not the hooks implemented or library functions offered!] by that module). When CTools versioning support is utilized by a module, hook implementors must partition their code into multiple files, each of which corresponds to a particular version of the API. When the hook provider is about to invoke a (particular version of) a hook, it can query CTools for all matching implementations, while excluding any non-matching (e.g. too old or too new) implementations. This mechanism, documented in &amp;quote;advanced help", appears to be almost unused, and probably will remain so due to its confusing official description. Nevertheless, it is interesting to see an idea of
this sort in place, given Drupal's track record of neglect for backwards-capability (at least between major versions).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-8238233903143325075?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/8238233903143325075/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2011/01/surface-drilling-drupal-panels-3.html#comment-form' title='13 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8238233903143325075'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8238233903143325075'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2011/01/surface-drilling-drupal-panels-3.html' title='Surface drilling Drupal Panels 3'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_o_c25p_Zq5Y/TSPCHQZWKkI/AAAAAAAAAC8/0Tradnhva0s/s72-c/panel.png' height='72' width='72'/><thr:total>13</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-8972253232345179296</id><published>2010-12-30T16:27:00.003+01:00</published><updated>2010-12-30T17:24:00.440+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='multi-site'/><category scheme='http://www.blogger.com/atom/ns#' term='Drupal'/><title type='text'>Drupal multi-sites in a nutshell</title><content type='html'>Although Drupal multi-sites are conceptually trivial, I found no satisfactory (short!) explanation in the official documentation or on the web. You can listen to this &lt;a href="http://gotdrupal.com/videos/multisites-vs-multiple-sites"&gt;half an hour long presentation&lt;/a&gt; like I did, but I'd rather have read the following summary. I suppose you're already familiar with single-site Drupal setups and would like to understand multi-site now: 
&lt;ul&gt;
&lt;li&gt;A multi-site configuration consists of a single code base along with multiple databases. Essentially, there is one database per site, although &lt;a href="http://drupal.org/node/291373"&gt;relocating some shared tables to a common database&lt;/a&gt; (in the same database server) is also possible. This approach should be contrasted with a shared-nothing "multiple site" configuration.&lt;/li&gt;
&lt;li&gt;The key to the entire concept is how Drupal locates the &lt;code&gt;settings.php&lt;/code&gt; file based on the requested URL. You can steer it to a different &lt;code&gt;settings.php&lt;/code&gt; (hence, a different database) for any combination of hostname, port and URI prefix. The exact lookup algorithm and naming conventions are described in &lt;code&gt;settings.php&lt;/code&gt; itself.&lt;/li&gt;
&lt;li&gt;There is no "master" configuration or "master" site, although the "default" site is used as fallback when a HTTP request matches no specific site (the normal case for non-multi-site setups). Except for that, all sites have the same rank and are unaware of each other. Each site uses the same core code and also the same &lt;code&gt;sites/all/modules&lt;/code&gt; and &lt;code&gt;sites/all/themes&lt;/code&gt;, while it may also add its own &lt;code&gt;sites/&lt;i&gt;sitedirectory&lt;/i&gt;/modules&lt;/code&gt; and &lt;code&gt;sites/&lt;i&gt;sitedirectory&lt;/i&gt;/themes&lt;/code&gt;. If the same module or theme is in present both locations, the site-specific location takes precedence, so you can have diverging versions of modules or themes between sites.&lt;/li&gt;
&lt;li&gt;To add another site (or convert your single-site setup to a multi-site setup), simply create a new database for it, add &lt;code&gt;sites/&lt;i&gt;sitedirectory&lt;/i&gt;&lt;/code&gt;, copy &lt;code&gt;sites/&lt;i&gt;sitedirectory&lt;/i&gt;/default.settings.php&lt;/code&gt; to &lt;code&gt;sites/&lt;i&gt;sitedirectory&lt;/i&gt;/settings.php&lt;/code&gt; and visit the new site's URL in the browser. The usual Drupal installation procedure will be offered.&lt;/li&gt;
&lt;li&gt;The &lt;code&gt;files&lt;/code&gt; folder is usually placed under the site-specific folder, i.e. separate for each site (if you want it shared, you should also take care to share the &lt;code&gt;files&lt;/code&gt; database table and possibly others).&lt;/li&gt;
&lt;li&gt;Because the databases are separate, each site can have a different subset of all the available modules/themes enabled.&lt;/li&gt;
&lt;li&gt;Because the databases are separate, you have to run update.php separately for each site when upgrading Drupal core or contributed modules.&lt;/li&gt;
&lt;li&gt;Because the Drupal core is shared, you may run into dependency problems if at least one site requires a different version of Drupal core than the others.&lt;/li&gt;
&lt;li&gt;Because the web server user and file permissions are shared, the multi-site configuration is &lt;em&gt;potentially insecure&lt;/em&gt;: any site administrator who can run PHP code may access files (and, by peeking into settings.php, also gain full access to the database) of any site.&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-8972253232345179296?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/8972253232345179296/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/12/drupal-multi-sites-in-nutshell.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8972253232345179296'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8972253232345179296'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/12/drupal-multi-sites-in-nutshell.html' title='Drupal multi-sites in a nutshell'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-7355831760817019022</id><published>2010-12-19T15:13:00.002+01:00</published><updated>2010-12-19T15:30:37.178+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SRP'/><category scheme='http://www.blogger.com/atom/ns#' term='software design'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='OO'/><title type='text'>Still uneasy about SRP</title><content type='html'>&lt;p&gt;I originally intended this as a short comment to &lt;a href="http://devlicio.us/blogs/hadi_hariri/archive/2010/12/18/srp-as-easy-as-123.aspx"&gt;SRP, as easy as 123&lt;/a&gt;, but since that blog won't let me post it as comment for some reason, I decided to put it here. Go read the original article for context. Essentially, the author wishes to enlighten us about the proper interpretation of the Single Responsibility Principle, which is one of the many "tried and true" pieces of wisdom promoted by OO gurus.&lt;/p&gt;&lt;p&gt;While the considerations described in the article are all fine and a nice example of the kinds of reasoning involved in building software, it also exposes a real problem with the "principle" as such, and it's a pity that it doesn't hit the target more directly.&lt;/p&gt;&lt;p&gt;It turns out plainly that the principle cannot be interpreted or applied without a good deal of informed speculation about what is going to happen in the future. Whether or not a particular structuring of code is good (enough) depends heavily on that future stream of events and interactions, some of which are more likely to occur than others (and their probabilities better be conditioned on individual case circumstances). However, notice how the principle misleads people to think solely in terms of some mystical absolute characteristics of the programming language classes. The result are pointless and confusing discussions on that shallow "ontological" level (is my class more responsible than yours? and how exactly do you count "reasons for change"?). The same kind of insidious error also hides in other OO principles ("is class X more abstract than class Y?").&lt;/p&gt;&lt;p&gt;In conclusion, it would be much easier to discuss matters not in terms of perceived (or misperceived) structural properties, but simply by addressing the known future risks and possible strategies of risk avoidance, some of which may be implemented in structure, others in the engineering process or tools. For example, there is also a risk associated with dividing code into unnecessarily many interacting modules (or deep class hierarchies), especially if the future maintainers lack adequate tools to piece together what the developer has pulled apart. Furthermore, there is also an immediate cost to making the structure more amenable to change or extension; if the expected kinds of change never happen in the future, you have wasted effort. The problem with SRP is that it tends to focus your mind on one thing and forget these alternate risks (at least for a while), and the problem with principles in general is that they may foster irresponsibility or magical thinking ("if only we obey the Rules, everything will go well; and if something hasn't gone well, we must have disobeyed the Rules").&lt;/p&gt;&lt;p&gt;Software design comes down to decision making under uncertainty, and software engineering principles are just an attempt to capture some aspects of that process using short catchphrases. They may serve us well as reminders if we're already capable of the kind of complex decision making required, but they are not a substitute for careful thinking and a poor learning aid. In the end, the narrator of the original article has demonstrated his usefulness to his colleague, while SRP has not.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-7355831760817019022?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/7355831760817019022/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/12/still-uneasy-about-srp.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7355831760817019022'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7355831760817019022'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/12/still-uneasy-about-srp.html' title='Still uneasy about SRP'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-661582360677788408</id><published>2010-12-11T18:35:00.004+01:00</published><updated>2010-12-11T18:38:31.944+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='form api'/><category scheme='http://www.blogger.com/atom/ns#' term='howto'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><category scheme='http://www.blogger.com/atom/ns#' term='CCK'/><title type='text'>Restricting CCK forms in Drupal 6</title><content type='html'>&lt;p&gt;This article contains a programming howto for stripping unwanted fields from forms generated by the CCK module in Drupal 6. It is natural for a content type to gather as many fields as the logical data schema requires (essentially, the decision about adding a field to an existing content type or creating a new content type, is dictated by considerations about data cardinality and independence in life cycles of individual pieces of data). Real-world applications will usually require that certain subsets of these fields should not be editable by everyone, or that they should only be editable at certain times or when certain conditions are met (for example, based on another field's value within the node being edited). Understandably, CCK doesn't know anything about such special needs out-of-the-box, and so it will render all node fields as equals on a common form.&lt;/p&gt;&lt;p&gt;Consider a case where you have already created a complex default form for editing the entire content type, possibly with field groups and a custom theme which renders them in a fancy hierarchical way. Now let's say you have another user role which is only supposed to understand and edit a few fields from the said content type. You want a new form which displays only those fields and saves the submitted values to the node, validating them in exactly the same way as they would be in the full-node edit scenario. To make things even more interesting, suppose that one of the fields is a select box for which you want to restrict the list of allowed values when it is presented on the restricted form.&lt;/p&gt;&lt;p&gt;The correct solution is short in code, but rather tricky conceptually. It requires at least an intermediate level of understanding of the Drupal Form API and theming layer. The following steps can be used as a guideline:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Choose a path for the new form. It is a logical choice to extend the default node edit path. That is, let &lt;code&gt;/node/1234/edit&lt;/code&gt; refer to the default form and let &lt;code&gt;/node/1234/edit/custom&lt;/code&gt; refer to your custom form. In your module, implement &lt;code&gt;hook_menu()&lt;/code&gt; and copy the item from &lt;code&gt;node.module&lt;/code&gt; which refers to &lt;code&gt;/node/%node/edit&lt;/code&gt;. At this point you have an opportunity to turn it into a MENU_CALLBACK (if you don't want the new form to be accessible through the standard tabs) and also to restrict access by defining a new access callback. Don't forget to specify &lt;code&gt;'file path' =&amp;gt; drupal_get_path('module', 'node')&lt;/code&gt;, otherwise the file &lt;code&gt;node.pages.inc&lt;/code&gt; defined by &lt;code&gt;node.module&lt;/code&gt; would not be found. The new item should look like so:&lt;pre&gt;
  $items['node/%node/edit/custom'] = array(
    'title' =&amp;gt; 'Edit custom',
    'page callback' =&amp;gt; 'node_page_edit',
    'page arguments' =&amp;gt; array(1),
    'access callback' =&amp;gt; 'yourmodule_edit_custom_access',
    'weight' =&amp;gt; 1,
    'file' =&amp;gt; 'node.pages.inc',
    'file path' =&amp;gt; drupal_get_path('module', 'node'),
    'type' =&amp;gt; MENU_CALLBACK,
  );
&lt;/pre&gt;&lt;/li&gt;&lt;li&gt;Implement &lt;code&gt;hook_form_alter()&lt;/code&gt; to disable access to unwanted fields on your custom form. You will also probably want to set a distinct theme function for the new form, so that you can arrange the wanted fields in a sensible way. The code in &lt;code&gt;yourmodule_form_alter&lt;/code&gt; should look like so:&lt;pre&gt;
  if ($form_id == 'yourcontenttype_node_form' &amp;amp;&amp;amp; arg(3) == 'custom') {
    $form['#theme'] = 'yourmodule_custom_form';
    $allowed_fields = array('field_first', 'field_second', 'field_third');
    foreach ($form as $key =&amp;gt; &amp;amp;$field) {
      if (strpos($key, 'field_') === 0 &amp;amp;&amp;amp; !in_array($key, $allowed_fields))
        $field['#access'] = FALSE;
    }
  }
&lt;/pre&gt;The original form might also contain other form elements that need to be disabled and whose names don't begin with &lt;code&gt;field_&lt;/code&gt;. You can find out what they are visually by looking at the form and by dumping the keys of &lt;code&gt;$form&lt;/code&gt;. One good example is the &lt;code&gt;buttons&lt;/code&gt; key, which contains the submit, preview and delete buttons. It is possible that some form elements contributed by other modules are not yet present when your &lt;code&gt;hook_form_alter()&lt;/code&gt; is called. In this case, you will likely have to implement another &lt;code&gt;hook_form_alter()&lt;/code&gt; in another module and set this module's weight to a big value in the &lt;code&gt;system&lt;/code&gt; table, to ensure that it executes last.&lt;/li&gt;
&lt;li&gt;Register the theme function &lt;code&gt;yourcustom_form&lt;/code&gt; specified above via &lt;code&gt;hook_theme()&lt;/code&gt; and also provide an actual implementation in a function called &lt;code&gt;theme_yourmodule_custom_form&lt;/code&gt;, which receives &lt;code&gt;$form&lt;/code&gt; as argument. Alternatively, you could declare it in &lt;code&gt;hook_theme()&lt;/code&gt; as a template like so:&lt;pre&gt;
  'yourmodule_custom_form' =&amp;gt; array(
    'arguments' =&amp;gt; array('form' =&amp;gt; NULL, 'user' =&amp;gt; NULL),
    'template' =&amp;gt; 'custom_form',
  ),
&lt;/pre&gt;and then create a template file &lt;code&gt;custom_form.tpl.php&lt;/code&gt; in your theme folder.&lt;/li&gt;
&lt;li&gt;Within your theme function or template, render the whole form or individual fields like so:&lt;pre&gt;
  // Whole form
  // print drupal_render($form);

  // Individual fields
  print drupal_render($form['fieldgroup_tabs']['group_somegroup']);
  print drupal_render($form['buttons']['submit']);
&lt;/pre&gt;In general, if you cannot render the whole form at once because it would result in unwanted artifacts (e.g. unwanted group headers from the original form or some such), it is a good approach to render the wanted fields individually first and &lt;em&gt;then&lt;/em&gt; call &lt;code&gt;drupal_render($form)&lt;/code&gt; within a &lt;code&gt;display:none&lt;/code&gt; &lt;code&gt;div&lt;/code&gt; element, which will only output the as-of-yet unrendered fields. This will also ensure that any hidden fields are output at all. Mind the possible security implications; if you failed to set &lt;code&gt;#access&lt;/code&gt; to FALSE on some fields in the step above, you'd get invisible, yet still updateable fields (the user might use Firebug to get at them). Accordingly, only rely on the &lt;code&gt;display:none&lt;/code&gt; trick for visuals, but not as a method of concealing fields.&lt;/li&gt;
&lt;li&gt;In the problem statement a requirement to restrict allowed values for a particular field was mentioned. This can be done through the &lt;code&gt;#afterbuild&lt;/code&gt; callback installed on the field in question, like so:&lt;pre&gt;
  // In hook_form_alter() if branch: 
  $form['field_first']['#after_build'][] =
     'yourmodule_field_first_after_build';
  // ...

  function yourmodule_field_first_after_build($element, &amp;amp;$form_state) {
    unset($element['value']['#options']['']);
    return $element;
  } 
&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;A word of caution: if you are using a checkbox CCK field in your content type, you might to run into &lt;a href="http://drupal.org/node/716408"&gt;a bug&lt;/a&gt; when you set &lt;code&gt;#access&lt;/code&gt; to FALSE, which would prevent the custom form from validating. As a workaround, two patches for optionwidgets.module were available at writing time (pick your favorite).&lt;/p&gt;&lt;p&gt;In general, when working with form data structures, remember that the content of these structures varies considerably depending on the current processing stage within the form engine. (Unfortunately, these variations and the separation between API and internals are rather badly documented, which makes it easy to write unstable code.) It's also good to realize that FAPI's "form elements" are hierarchical in nature and act as a &lt;del&gt;mind-numbingly complicated&lt;/del&gt; powerful adapter layer between the database fields and HTML form inputs. In particular do realize that a single FAPI form element might expand into multiple form inputs and/or supply values for mutliple database fields.&lt;/p&gt;&lt;p&gt;For further information, I refer you to the following sources:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;&lt;a href="http://drupal.org/node/169815"&gt;Creating custom elements using Drupal 6&lt;/a&gt; (besides of explaining custom element types, it also goes into the FAPI processing logic)&lt;/li&gt;
&lt;li&gt;&lt;a href="http://drupal.org/node/165104"&gt;FAPI workflow illustration&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;&lt;a href="http://api.drupal.org/api/drupal/developer--topics--forms_api_reference.html/6"&gt;FAPI reference&lt;/a&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-661582360677788408?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/661582360677788408/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/12/restricting-cck-forms-in-drupal-6.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/661582360677788408'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/661582360677788408'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/12/restricting-cck-forms-in-drupal-6.html' title='Restricting CCK forms in Drupal 6'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-2994864805318929171</id><published>2010-11-13T22:03:00.004+01:00</published><updated>2010-11-13T22:26:04.240+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><title type='text'>Solution for unavailable CCK fields in Drupal 6 Views</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: Using Drupal 6 Views UI, you want to add a particular CCK field (say, as a filter), but this field seems to be not offered at all for some reason. Because most Drupal code is not protected by assertions (amateur coding?), corrupt data is likely to flow far before a visible problem is eventually triggered.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: My particular trouble was caused by a &lt;a href="http://drupal.org/node/508708"&gt;Drupal 5 to Drupal 6 CCK migration bug&lt;/a&gt; concerning &lt;code&gt;number_integer&lt;/code&gt; fields. However, given the general nature of this problem I decided to record some general troubleshooting tips here.&lt;/p&gt;&lt;p&gt;One thing to check for is metadata corruption - the Views module might be working properly, but the CCK module might not be delivering a correct description of the affected field for some reason.&lt;/p&gt;&lt;p&gt;As a first guess, examine the field's entry in the &lt;code&gt;content_node_field&lt;/code&gt; table (and perhaps also &lt;code&gt;content_node_field_instance&lt;/code&gt;), particularly look at the &lt;code&gt;db_columns&lt;/code&gt; attribute. Does it appear unusual when compared to other, correctly working fields?&lt;/p&gt;&lt;p&gt;If you are out of luck so far, here are two locations in code where to install debug output to gather more information:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Right after the call to &lt;code&gt;views_fetch_fields&lt;/code&gt; in function &lt;code&gt;views_ui_add_item_form&lt;/code&gt; - does your field appear in the &lt;code&gt;$options&lt;/code&gt; array, as it should? (probably not)&lt;/li&gt;&lt;li&gt;content_views_field_views_data - how does the returned structure for your field compare to those of other (working) fields?&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;To trigger calls to these function interact with the Views UI (e.g. click the add filter button), but do not forget to /devel/cache/clear each time! Otherwise the critical code paths will not be traversed due to caching.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-2994864805318929171?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/2994864805318929171/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/11/solution-for-unavailable-cck-fields-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/2994864805318929171'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/2994864805318929171'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/11/solution-for-unavailable-cck-fields-in.html' title='Solution for unavailable CCK fields in Drupal 6 Views'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-5056120497035225688</id><published>2010-11-06T19:52:00.004+01:00</published><updated>2011-01-06T23:56:04.047+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='website baker'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><title type='text'>Why Drupal seems slow</title><content type='html'>&lt;p&gt;The minimum response time metric (rather than, say, average response time) does a good job of describing whether a web site feels slow or snappy. Below are two pictures that illustrate how the mighty &lt;a href="http://www.drupal.org/"&gt;Drupal 6&lt;/a&gt; is inferior in this respect to the much simpler &lt;a href="http://www.websitebaker.org/"&gt;Website Baker&lt;/a&gt; CMS. These measurements were taken for a typical web page filled with some images and a moderate amout text in case of Website Baker, and on an almost empty page for Drupal 6 with an installed Ubercart (shopping cart) suite of modules (with no caching). KCachegrind was used for visualization:&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_o_c25p_Zq5Y/TNWkNe99I0I/AAAAAAAAACs/Mp6SfW0SW-A/s1600/scrshot-wb.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 259px;" src="http://2.bp.blogspot.com/_o_c25p_Zq5Y/TNWkNe99I0I/AAAAAAAAACs/Mp6SfW0SW-A/s400/scrshot-wb.png" alt="" id="BLOGGER_PHOTO_ID_5536511868480070466" border="0" /&gt;&lt;/a&gt;&lt;center&gt;Website Baker 2.7 response time (under profiler): 60 ms&lt;/center&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_o_c25p_Zq5Y/TNWkNB7WgaI/AAAAAAAAACk/tRkcn7sBr0M/s1600/scrshot-drupal.png"&gt;&lt;img style="display: block; margin: 0px auto 10px; text-align: center; cursor: pointer; width: 400px; height: 261px;" src="http://2.bp.blogspot.com/_o_c25p_Zq5Y/TNWkNB7WgaI/AAAAAAAAACk/tRkcn7sBr0M/s400/scrshot-drupal.png" alt="" id="BLOGGER_PHOTO_ID_5536511860684521890" border="0" /&gt;&lt;/a&gt;&lt;center&gt;Drupal 6 response time (under profiler): 510 ms&lt;/center&gt;
&lt;p&gt;Also compare the number of "boxes", which reflect nested procedure calls in both cases.&lt;/p&gt;&lt;p&gt;Does it mean that given a choice you should use Website Baker instead of Drupal? The answer obviously will depend on the required features. The main insight here is that this overhead is very unlikely to go away in the future. It arises from Drupal's overall architecture involving interactions among many small modules, most of which are contributed by third parties and neither explicitly tested nor certified for performance. While offering lots of functionality and extensibility out-of-the-box, Drupal puts much strain in terms of the required quality assurance on the application designer - with performance being just one aspect of the challenge.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Update 2011/01:&lt;/strong&gt; the latency of rendering simple pages apparently got even worse in Drupal 7 (see comments to &lt;a href="http://fourkitchens.com/blog/2010/02/03/making-drupal-pressflow-more-mundane"&gt;this article&lt;/a&gt;). I'm aware that it is rather easy to boost Drupal performance for anonymous, non-targeted content several orders of magnitude just by using a reverse proxy cache and so reduce response time to equal network latency. However, I doubt that an average small Drupal site would be able to configure and deploy such optimizations.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-5056120497035225688?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/5056120497035225688/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/11/why-drupal-seems-slow.html#comment-form' title='5 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/5056120497035225688'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/5056120497035225688'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/11/why-drupal-seems-slow.html' title='Why Drupal seems slow'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_o_c25p_Zq5Y/TNWkNe99I0I/AAAAAAAAACs/Mp6SfW0SW-A/s72-c/scrshot-wb.png' height='72' width='72'/><thr:total>5</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-9020364478000523420</id><published>2010-11-06T19:23:00.004+01:00</published><updated>2010-11-06T19:31:43.734+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>Slow rendering of large tables in Firefox</title><content type='html'>&lt;p&gt;A few performance measurements of rendering a table with 15 columns and 1927 rows (a dense matrix with styled cell content) in different browsers:&lt;/p&gt;&lt;table style="width: 200px; border: 1px solid gray"&gt;&lt;tr&gt;&lt;td&gt;MSIE 7.0&lt;/td&gt;&lt;td&gt;13,6s&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Opera 10.62&lt;/td&gt;&lt;td&gt;13,9s&lt;/td&gt;&lt;/tr&gt;&lt;tr&gt;&lt;td&gt;Firefox 3.6.8&lt;/td&gt;&lt;td&gt;40.3s&lt;/td&gt;&lt;/tr&gt;&lt;/table&gt;&lt;p&gt;Firefox, you're doing it wrong!&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-9020364478000523420?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/9020364478000523420/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/11/slow-rendering-of-large-tables-in.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/9020364478000523420'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/9020364478000523420'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/11/slow-rendering-of-large-tables-in.html' title='Slow rendering of large tables in Firefox'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-3793595473290065850</id><published>2010-08-03T14:40:00.003+02:00</published><updated>2010-08-03T14:50:24.538+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><title type='text'>Solution for XHR ERROR: Response_Code = -1 Error_Message = Request Error</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; When trying to open a URL using Selenium RC 1.0.3 with Firefox 3.6, the following error is reported through the server log:&lt;pre&gt;XHR ERROR: Response_Code = -1 Error_Message = Request Error
&lt;/pre&gt;after which the browser windows disappear and the session is aborted. Interestingly, this only happens when trying to access remote sites, not sites from localhost.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Workaround:&lt;/strong&gt; This error seems to be caused by something in the Firefox profile template provided with option &lt;code&gt;-firefoxProfileTemplate&lt;/code&gt;. I temporarily edited &lt;code&gt;selenium-browserbot.js&lt;/code&gt; within the &lt;code&gt;selenium-server.jar&lt;/code&gt; to make it ignore the error (new line highlighted in red):&lt;/p&gt;&lt;pre&gt;    this.isNewPageLoaded = function() {
        if (this.pageLoadError) {
            LOG.error("isNewPageLoaded found an old pageLoadError");
            var e = this.pageLoadError;
            this.pageLoadError = null;
            throw e;
        }

        if (self.ignoreResponseCode) {
            return self.newPageLoaded;
        } else {
            if (self.isXhrSent &amp;&amp; self.isXhrDone) {
                &lt;span style="color:red"&gt;if (self.xhrResponseCode == -1) return self.newPageLoaded;&lt;/span&gt;
                if (!((self.xhrResponseCode &gt;= 200 &amp;&amp;
                    self.xhrResponseCode &lt;= 399) ||
                    self.xhrResponseCode == 0)) {
&lt;/pre&gt;&lt;p&gt;With this change, the error ceased to be reported. I could no longer reproduce the problem later after removing this temporary fix.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-3793595473290065850?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/3793595473290065850/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/08/solution-for-xhr-error-responsecode-1.html#comment-form' title='9 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3793595473290065850'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3793595473290065850'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/08/solution-for-xhr-error-responsecode-1.html' title='Solution for XHR ERROR: Response_Code = -1 Error_Message = Request Error'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>9</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-554358923644996327</id><published>2010-06-24T00:18:00.005+02:00</published><updated>2010-06-24T00:43:34.196+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='profiler'/><category scheme='http://www.blogger.com/atom/ns#' term='memory'/><title type='text'>Perl memory profiling</title><content type='html'>&lt;p&gt;Perl's garbage collector uses reference counting to keep track of which data structures are "live" on the heap and which can be deallocated. This means that memory will never be freed if your data structures contain cyclic references, even when all variables referencing those data structures go out of scope. It is generally up to you as a developer to break (or "weaken") such references if you want timely memory deallocation. Otherwise, memory deallocation will happen when your script terminates, but that might be too late, especially in case of long-running processes. (This is a good reason to avoid long-running processes, by the way. Paritioning your code into scripts/processes is a practical example of a fault-tolerance technique known as "micro-reboots".)&lt;/p&gt;&lt;p&gt;To check the overall (maximum) memory footprint of a Perl interpreter, use &lt;code&gt;valgrind --tool=massif perl yourscript.pl&lt;/code&gt;, then &lt;code&gt;ms_print massif.out.&lt;i&gt;pid&lt;/i&gt;&lt;/code&gt;. However, note that it slows down the execution significantly and adds even more memory overhead, so handle with care.&lt;/p&gt;&lt;p&gt;Here is a list of developer-oriented Perl modules that will help with troubleshooting memory leaks in Perl:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;code&gt;Devel::Gladiator&lt;/code&gt; shows you counts of all live objects on the heap, by object class/category, including leaked ones. This is somewhat similar to - but more limited than - the jmap tool for Java VMs. You can also get the actual references, which you can then pass to...&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Devel::Size&lt;/code&gt;, which lets you calculate the amount of memory taken up by a data structure (one that is not yet leaked).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Devel::Cycle&lt;/code&gt; examines a data structure for cyclical references (if such exist, the data structure is bound to cause memory leaks).&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Devel::FindRef&lt;/code&gt; helps figure out where references to a data structure are being held.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Devel::Events::Objects&lt;/code&gt; offers instrumentation for constructor/destructor invocations.&lt;/li&gt;
&lt;li&gt;&lt;code&gt;Devel::Leak&lt;/code&gt; gives you the total number of currently live data structures.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;Finally, you may wish to pass the &lt;code&gt;-Dm&lt;/code&gt; command-line switch to Perl. However, unlike the modules, it requires a version compiled with debugging support and gives low-level information that is difficult to correlate with application-level code.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-554358923644996327?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/554358923644996327/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/06/perl-memory-profiling.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/554358923644996327'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/554358923644996327'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/06/perl-memory-profiling.html' title='Perl memory profiling'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-3896771277396900348</id><published>2010-06-15T14:47:00.002+02:00</published><updated>2010-06-15T14:55:44.012+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='booting'/><category scheme='http://www.blogger.com/atom/ns#' term='hp'/><category scheme='http://www.blogger.com/atom/ns#' term='usb'/><title type='text'>Solution for very slow Win XP boot, black screen</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; a HP desktop PC suddenly and consistently takes a very long time to boot. The HP invent logo stays for about 30 seconds when the machine is turned on (it should only flash briefly). Furthermore, the during Windows XP boot sequence the screen is blanked and stays black for more than a minute before the login prompt is displayed. Supplementary info: According to Bootvis, the boot process takes 130+ seconds. Moreover, in Bootvis CPU activity trace, CPU load is reported 100% during the black screen phase (with no disk activity). The same delays also occur when starting Windows in safe mode.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; in this particular case, it was an external USB hard disk that somehow got stuck. Turning off the disk and turning it back again fixed both delays (HP invent logo and black screen), bringing boot to normal speed. Lesson learned: always first detach USB devices when troubleshooting this kind of a problem.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-3896771277396900348?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/3896771277396900348/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/06/solution-for-very-slow-win-xp-boot.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3896771277396900348'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3896771277396900348'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/06/solution-for-very-slow-win-xp-boot.html' title='Solution for very slow Win XP boot, black screen'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-6966661156214425103</id><published>2010-06-07T16:10:00.002+02:00</published><updated>2010-06-07T16:15:35.191+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='cadaver'/><category scheme='http://www.blogger.com/atom/ns#' term='webdav'/><title type='text'>Solution for GnuTLS internal error in cadaver</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem&lt;/strong&gt;: the Linux WebDav client cadaver (Debian 0.23.3-1 binary package or compiled from source) fails the handshake abruptly instead of asking for the client certificate configured with &lt;code&gt;set client-cert&lt;/code&gt;. When compiled from source, both GnuTLS and OpenSSL versions fail, although with slightly different error meesages. It used to work. The following error message is displayed by the GnuTLS version:&lt;/p&gt;&lt;pre&gt;Could not open collection:
SSL handshake failed, client certificate was requested: SSL error: GnuTLS internal error.
&lt;/pre&gt;&lt;p&gt;&lt;strong&gt;Solution&lt;/strong&gt;: the issue seems to be with libneon 0.27 and/or GnuTLS library. Downgrade to cadaver 0.22.5, which uses libneon 0.26, to make it work again.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-6966661156214425103?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/6966661156214425103/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/06/solution-for-gnutls-internal-error-in.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6966661156214425103'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6966661156214425103'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/06/solution-for-gnutls-internal-error-in.html' title='Solution for GnuTLS internal error in cadaver'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-1725369163938919421</id><published>2010-05-31T21:07:00.009+02:00</published><updated>2010-06-03T23:04:50.603+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='OpenSolaris'/><category scheme='http://www.blogger.com/atom/ns#' term='DTrace'/><title type='text'>DTrace-enabled Perl in OpenSolaris</title><content type='html'>&lt;p&gt;This post contains a few state-of-the-art notes concerning Perl-specific DTrace probes available in OpenSolaris today (2010-05-31). For instructions how to &lt;em&gt;quickly&lt;/em&gt; install a working DTrace-enabled version of Perl, skip to the next section.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;The OpenSolaris (2010.03, snv_134) Perl binary package comes with DTrace support disabled. Probably a good thing because the current implementation incurs a reported 5% worst-case performance hit - even with no enabled probes!&lt;/li&gt;
&lt;li&gt;Perl source packages from 5.12.x on (or maybe already in 5.10.x?) have some &lt;a href="http://bleaklow.com/2005/09/09/dtrace_and_perl.html"&gt;basic DTrace support&lt;/a&gt;, good for instrumenting calls to and returns from subroutines.&lt;/li&gt;
&lt;li&gt;More probes were made available through a &lt;a href="http://fosiki.com/blog/2008/02/15/blead-perl-dtrace-probes/"&gt;patch from Sven Dowideit&lt;/a&gt;. However, his patch does not apply cleanly to 5.12.x.&lt;/li&gt;
&lt;li&gt;Building Perl from source with DTrace support fails due to a &lt;a href="http://rt.perl.org/rt3/Ticket/Display.html?id=73630"&gt;known bug in &lt;code&gt;Makefile.SH&lt;/code&gt;&lt;/a&gt;.&lt;/li&gt;
&lt;li&gt;Why bother? To see system calls and kernel (e.g. I/O) activity incurred by a particular Perl subroutine call, for example. Or to monitor memory allocations. For pure Perl performance profiling better tools exist (such as &lt;a href="http://search.cpan.org/%7Etimb/Devel-NYTProf-3.11/lib/Devel/NYTProf.pm"&gt;NYTProf&lt;/a&gt;).&lt;/li&gt;
&lt;/ul&gt;&lt;h2&gt;Building DTrace-enabled Perl in OpenSolaris&lt;/h2&gt;&lt;ol&gt;&lt;li&gt;Download a recent &lt;a href="http://www.cpan.org/src/README.html"&gt;Perl source code package&lt;/a&gt;. I grabbed 5.12.1.&lt;/li&gt;
&lt;li&gt;If you just want the subroutine entry/return probe, apply the mini-patch from &lt;a href="http://rt.perl.org/rt3/Ticket/Display.html?id=73630"&gt;Peter Bray's bug report&lt;/a&gt; to avoid a build error about missing op.o and perl.o, then skip the next step. On the other hand, if you'd like to try Sven Dowideit's probes as well:&lt;/li&gt;
&lt;li&gt;Download and apply &lt;a href="http://www.plosquare.com/download/perl-dtrace-osol-5.12.1_2.diff"&gt;this patch (version 2)&lt;/a&gt; to your freshly unpacked Perl source. This is a version of Sven's patch updated by myself to apply cleanly to 5.12.x, with an additional fix to &lt;code&gt;Makefile.SH&lt;/code&gt; to avoid build interruptions and a fix to avoid a strange segfault in &lt;code&gt;Perl_ck_require&lt;/code&gt; when tracing of loaded modules is enabled. Please note that I have not tested Sven's patch extensively. We are probably talking about a development build here that may aid your Perl code debugging efforts rather than something to put in production.&lt;/li&gt;
&lt;li&gt;To compile:&lt;pre&gt;
make distclean
./Configure -des -Dprefix="/opt/perl-5.12.1" -Dusedtrace -Duseshrplib
make
&lt;/pre&gt;&lt;/li&gt;&lt;/ol&gt;&lt;h2&gt;Testing whether it works&lt;/h2&gt;&lt;p&gt;Generally, the new probes become visible when a Perl process is running. You should be able to see them with the following command:&lt;/p&gt;&lt;pre&gt;pfexec dtrace -l | grep perl
&lt;/pre&gt;&lt;p&gt;Here is a little test Perl script:&lt;/p&gt;&lt;pre&gt;#!/opt/perl/bin/perl

foo() for 1..50;

sub foo
{
  print BAD_HANDLE "In foo\n";
  sleep(1);
}
&lt;/pre&gt;&lt;p&gt;A little test DTrace script:&lt;/p&gt;&lt;pre&gt;#!/usr/sbin/dtrace -s

perl$target:::sub-entry, perl$target:::sub-return
{
  printf("%s %s (%s:%d)\n", probename == "sub-entry"
      ? "-&gt;" : "&lt;-", copyinstr(arg0), copyinstr(arg1), arg2); } &lt;/pre&gt;&lt;p&gt;Running both of them together:&lt;/p&gt;&lt;pre&gt;pfexec dtrace -Z -s ./script.d -c ./script.pl
&lt;/pre&gt;&lt;p&gt;Several nice sample scripts for DTrace-enabled Perl are included in the &lt;a href="http://hub.opensolaris.org/bin/view/Community+Group+dtrace/dtracetoolkit"&gt;DTrace Toolkit&lt;/a&gt;. Check it out.&lt;/p&gt;&lt;p&gt;If you want to see Sven's probes in action: download, read and run &lt;a href="http://distributedinformation.com/DTrace/perl.d"&gt;his example Perl DTrace script&lt;/a&gt;. Alternatively, you might try my &lt;a href="http://www.plosquare.com/download/require_time.d"&gt;DTrace script to measure time consumed by use/require statements&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-1725369163938919421?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/1725369163938919421/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/05/dtrace-enabled-perl-in-opensolaris.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1725369163938919421'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1725369163938919421'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/05/dtrace-enabled-perl-in-opensolaris.html' title='DTrace-enabled Perl in OpenSolaris'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-3737769670722258466</id><published>2010-05-17T23:08:00.004+02:00</published><updated>2010-05-17T23:19:27.493+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='explain'/><title type='text'>Web-based front-end to MySQL's EXPLAIN</title><content type='html'>&lt;p&gt;If you are troubleshooting slow MySQL queries and are confused with the tabular output of the EXPLAIN command, you may wish to check out my little &lt;a href="http://explain.plosquare.com"&gt;MySQL EXPLAINer&lt;/a&gt; service. You upload a CSV file with whatever output &lt;code&gt;EXPLAIN SELECT&lt;/code&gt; has given you, and the query execution plan are is presented in form of a tree.&lt;/p&gt;&lt;p&gt;The CGI script is actually just a web front-end to the nice command-line tool &lt;a href="http://www.maatkit.org/doc/mk-visual-explain.html"&gt;mk-visual-explain&lt;/a&gt;, which does all the hard work of parsing EXPLAIN output. All due credit goes to the developers of &lt;a href="http://www.maatkit.org/"&gt;Maatkit&lt;/a&gt; - read their documentation and slides referenced from their site if you are really interested in the technical details.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-3737769670722258466?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/3737769670722258466/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/05/web-based-front-end-to-mysqls-explain.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3737769670722258466'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3737769670722258466'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/05/web-based-front-end-to-mysqls-explain.html' title='Web-based front-end to MySQL&apos;s EXPLAIN'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-1130245331755269640</id><published>2010-05-11T17:00:00.006+02:00</published><updated>2010-05-11T17:26:47.928+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><title type='text'>Solution for MySQL query slow, index does not help</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; a MySQL query containing many joined tables is extremely slow (more than 1 minute). Proper indexes are present and being used according to EXPLAIN. The number of processed and returned rows is small (the problem persists with a &lt;code&gt;COUNT(*)&lt;/code&gt; query).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Diagnosis:&lt;/strong&gt; you may be hitting a performance problem with the MySQL query optimizer, which computes an "optimal" execution plan before the query is executed. It is difficult to find out without instrumentation (see example below on how to measure the time spent in optimizer), but it is easy to try whether the recommended solution helps.&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; change the MySQL parameter &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/controlling-optimizer.html"&gt;&lt;code&gt;optimizer_search_depth&lt;/code&gt;&lt;/a&gt;, e.g. &lt;code&gt;SET optimizer_search_depth=0&lt;/code&gt; in the session before executing the query.&lt;/p&gt;&lt;h3&gt;How to measure time spent in the MySQL query optimizer&lt;/h3&gt;&lt;p&gt;On &lt;a href="http://en.wikipedia.org/wiki/DTrace#Supported_platforms"&gt;platforms which support DTrace&lt;/a&gt; and with an appopriate version of MySQL (I tested with 5.1.46, OpenSolaris), you can use the following DTrace script:&lt;/p&gt;&lt;pre&gt;
#!/usr/sbin/dtrace -s
/*
 * Measures and shows time spent in the mysql query optimizer.
 *
 * Usage: ./mysql-optimizer-check.d -p `pgrep -x mysqld`
*/
#pragma D option quiet

pid$target:mysqld:*mysql_parse*:entry
{
    printf("Query: %s\n", copyinstr(arg1));
}

pid$target:mysqld:*JOIN*optimiz*:entry
{
    self-&gt;start = timestamp;
    printf("Query optimizer entered\n");
}

pid$target:mysqld:*JOIN*optimiz*:return
{
    this-&gt;elapsed = timestamp;
    this-&gt;nano = this-&gt;elapsed - self-&gt;start;
    this-&gt;time1 = (this-&gt;elapsed - self-&gt;start)/1000000000;
    this-&gt;time2 = ((this-&gt;nano % 1000000000) * 100) / 1000000000;
    printf("Time spent in optimizer: %d.%ds\n", this-&gt;time1, this-&gt;time2);
    this-&gt;elapsed = 0;
    self-&gt;start = 0;
}
&lt;/pre&gt;&lt;p&gt;To run this script in OpenSolaris:&lt;/p&gt;&lt;pre&gt;
pfexec ./mysql-optimizer-time.d -p `pgrep -x mysqld`
&lt;/pre&gt;&lt;p&gt;Sample output (first query executes instantly, second query hangs and is aborted by myself after 7.72s):&lt;/p&gt;&lt;pre&gt;
Query: select @@version_comment limit 1
Query optimizer entered
Time spent in optimizer: 0.0s
Query: SELECT
    DISTINCT appl_app_id_appl.appl as 'appl.app_id_foreign',
    appl.app_id as 'appl.app_id',
    appl_app_id_appl.app_id as 'appl_app_id_appl.app_id',
    appl_app_id_appl.mandant_id as 'appl_app_id_appl.mandant_id',
    appl_app_id_appl.appl as
Query optimizer entered
Query: KILL /*!50000 QUERY */ 9
Time spent in optimizer: 7.72s
^C
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-1130245331755269640?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/1130245331755269640/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/05/solution-for-mysql-query-slow-index.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1130245331755269640'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1130245331755269640'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/05/solution-for-mysql-query-slow-index.html' title='Solution for MySQL query slow, index does not help'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-6084675149816588608</id><published>2010-05-04T14:39:00.004+02:00</published><updated>2010-05-20T20:33:26.642+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='grub'/><title type='text'>Troubleshooting GRUB hangs</title><content type='html'>&lt;p&gt;This post describes one possible scenario in which GRUB hangs (freezes) with the four letters GRUB and a blinking cursor displayed on the screen, even before the boot menu appears. It might also display "Loading stage2..." and then hang. You might or might not be able to restart the computer with CTRL+ALT+DEL when this occurs. An explanation and a procedure for identifying or excluding the scenario is provided.&lt;/p&gt;&lt;p&gt;When GRUB hangs before displaying the boot menu, the immediate cause most likely is a failed attempt to load the &lt;code&gt;stage2&lt;/code&gt; file from the root partition ("root" refers to whatever has been specified when installing GRUB into boot sector). If you get no error messages at all, just a blinking cursor as mentioned above, then the underlying problem may lie in the implementation of the BIOS interrupt 13h, on which the installed GRUB (i.e. &lt;code&gt;stage1&lt;/code&gt; stored in the boot sector) depends to read raw data from disk. GRUB cannot avoid the crash if its humble attempt to read sector &lt;i&gt;n&lt;/i&gt; from disk leads to a locked-up computer. When this happens, some garbage characters might also appear on the screen (printed by BIOS, not GRUB).&lt;/p&gt;&lt;p&gt;It seems that some BIOSes, even versions released as late as 2005, have big trouble reading sectors (blocks) beyond a certain boundary. In my test case (AMIBIOS, 80 GB Maxtor disk) the last readable sector turned out to be 66059279.&lt;/p&gt;&lt;p&gt;In order to find out whether or not you are experiencing the same problem, you should perform these additional tests:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;Check the sector number where your root partition begins and ends (using &lt;code&gt;fdisk&lt;/code&gt;, type &lt;code&gt;u&lt;/code&gt; to change display units to sectors). (If it is the first disk partition, it's unlikely that you have the problem discussed here.)&lt;/li&gt;
&lt;li&gt;Insert a boot CD with a working GRUB menu (a simple &lt;code&gt;/usr/sbin/grub&lt;/code&gt; shell is not enough; see note below). Press the key 'c' after the GRUB menu appears while booting from the CD. Now enter the command &lt;code&gt;root (hd&lt;i&gt;m&lt;/i&gt;,&lt;i&gt;n&lt;/i&gt;)&lt;/code&gt;, replacing &lt;i&gt;m&lt;/i&gt; with the disk number (in Linux: 0 = hda, 1 = hdc) and &lt;i&gt;n&lt;/i&gt; with the partition number (in Linux: 0 = hd&lt;i&gt;m&lt;/i&gt;1, 1 = hd&lt;i&gt;m&lt;/i&gt;2, etc.) on which the file &lt;code&gt;/boot/grub/stage2&lt;/code&gt; is supposed to be found. If it hangs immediately after entering the command, but does not hang for &lt;code&gt;root (hd0,0)&lt;/code&gt;, it is likely that you have the described problem.&lt;/li&gt;
&lt;li&gt;You can refine the diagnosis further by attempting to read individual sectors using the command &lt;code&gt;cat (hd0)&lt;i&gt;sector_num&lt;/i&gt;+1&lt;/code&gt;. For example: &lt;code&gt;cat (hd0)66059280+1&lt;/code&gt; produced the error message "Error 18: Selected cylinder exceeds maximum supported by BIOS" in my case, attempts with a smaller sector number worked, and attempts to read a much higher sector number (where the actual root partition started) caused hanging. When you see this behavior, you can be quite certain, that you have a broken BIOS.&lt;/li&gt;&lt;/ul&gt;
&lt;p&gt;Note that performing the above tests from a grub shell after having successfully booted (say, to another OS on the same PC or through a Live CD) will &lt;em&gt;not&lt;/em&gt; give the symptoms and therefore will &lt;em&gt;not&lt;/em&gt; help troubleshooting. The grub shell binary uses different system calls to read sectors than the actual &lt;code&gt;stage2&lt;/code&gt; binary and these may work well where the native BIOS interrupt fails.&lt;/p&gt;&lt;p&gt;In my case, it didn't help to upgrade BIOS. However, repartitioning and placing the root partition into lower sectors (swapping hdc2 with hdc3) solved the problem... or so I thought.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;In a cruel twist of fate, the problem re-occurred with exactly the same symptoms just a few days later. It turned out that I moved the root partition toward the disk's beginning, but did not resize it, so that the end still stretched beyond the fatal sector. Editing &lt;code&gt;menu.lst&lt;/code&gt; or possibly simply rebooting (OpenSolaris) moved one of the files required by GRUB to higher sectors, making the partition unbootable again. Lesson: if you have the problem described here (which due to its nature you might only find out when your system suddenly and inexplicably stops booting), the &lt;em&gt;whole&lt;/em&gt; partition which contains GRUB files must be contained in low sectors!&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Update: the recommendation of moving the boot partition paritition into low sectors is apparently not enough with OpenSolaris. My machine won't boot again. The file system code in GRUB actually reads sectors that lie outside of the boot partition boundary. And even after hacking it stop reading such sectors, the final configuration doesn't boot. Although both &lt;code&gt;kernel$&lt;/code&gt; and &lt;code&gt;module$&lt;/code&gt; can be set and files are reported found, all I get on a boot attempt is a blinking cursor. I suppose some of the OpenSolaris kernel/boot loader may be trying to read the high sectors as well. This is where I say farewell to OpenSolaris (on that machine ;-)).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-6084675149816588608?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/6084675149816588608/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/05/troubleshooting-grub-hangs.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6084675149816588608'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6084675149816588608'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/05/troubleshooting-grub-hangs.html' title='Troubleshooting GRUB hangs'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-824766333543391901</id><published>2010-04-17T17:27:00.002+02:00</published><updated>2010-04-17T17:33:10.162+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='automation'/><category scheme='http://www.blogger.com/atom/ns#' term='autoit'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><title type='text'>Windows GUI automation with AutoIt and Perl</title><content type='html'>&lt;p&gt;This post contains some notes and thoughts from a short evaluation session for AutoIt3, a scripting tool that can be used to automate Windows GUI tasks by simulating user key strokes and mouse actions.&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;AutoIt is actively maintained, has a broad users base, and supports all versions of Windows.&lt;/li&gt;
&lt;li&gt;AutoIt consists of a function library, a Basic-like programming language, and a compiler which allows you to create redistributable .exe scripts.&lt;/li&gt;
&lt;li&gt;The recommended editor for AutoIt is a modified version of SciTE, available through the AutoIt web site.&lt;/li&gt;
&lt;li&gt;As of April 2010, there is no Eclipse plug-in for AutoIt. The probability of such a plug-in ever being developed is low.&lt;/li&gt;
&lt;li&gt;The function library focuses on window, keyboard, mouse, process management.&lt;/li&gt;
&lt;li&gt;You can also create simple GUIs with AutoIt (if you must)&lt;/li&gt;
&lt;li&gt;AutoIt scripts can call COM objects, providing access to additional functionality. There is a nice tutorial on &lt;code&gt;oleview.exe&lt;/code&gt; included in AutoIt's help file, which may be of interest even if you don't need AutoIt as such.&lt;/li&gt;
&lt;li&gt;Support for finding and manipulating controls other than by keyboard/mouse simulation and reading contents of GUIs seems missing or limited at best. For these reasons, AutoIt should be considered an automation tool to speed up repetitive tasks, not a full-featured GUI testing tool.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;The longer I stay involved in software development business, the more I dislike the idea of inventing new specialized programming languages. Generally, by inventing a new language you are making life harder for everyone out there for two reasons:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;established, well-known tools that took thousands of hours of development effort are likely to be useless with your new language&lt;/li&gt;
&lt;li&gt;established, well-known libraries that took thousands of hours of development effort are likely to be more difficult to access from your new langugage&lt;/li&gt;
&lt;li&gt;your new language will suffer from performance problems solved by thousands of hours of development effort invested into compilers for existing languages&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;A good reason for a custom programming language is if your users are so unsophisticated and their programming needs so limited that they would be overwhelmed by having to learn an existing scripting language. This might have actually been the argument when the first version of AutoIt was developed, but doesn't seem to be the case any longer.&lt;/p&gt;&lt;p&gt;Another good reason for a custom programming language is if you want to be cut dependencies. For example, write a custom binary compiler, which is just not there for a general-purpose scripting language. This is an advantage that AutoIt's author realized by making it possible to compile and redistribute AutoIt scripts as (small!) .exe files.&lt;/p&gt;&lt;p&gt;Anyway, when it comes to real-world scenarios found in my projects, I thought I might prefer to use AutoIt's automation functionality from a real-world language with a familiar syntax and direct access to tons of third-party libraries. Perl is one such language, hence I spent a little time testing AutoIt-Perl integration.&lt;/p&gt;&lt;p&gt;AutoIt ships with AutoItX, a DLL containing a COM object through which you can script AutoIt itself from within programs written in a language such as Perl. I experimented to see how well it goes. It does work, although there are some caveats. Below I include an example with some notes in comments.&lt;/p&gt;&lt;pre&gt;use Win32::OLE;
use Win32::GUI;

# Note 1: we initialize OLE using Single-Threaded Appartment (STA) explicitly,
# as this is what the AutoIt COM object requires. If we don't do it,
# the default init via Win32::GUI::Window-&gt;new would initialize using
# Multi-Threaded Appartment (MTA), which would cause AutoIt calls to fail silently.

Win32::OLE-&gt;Initialize(Win32::OLE::COINIT_OLEINITIALIZE());

$win = Win32::GUI::Window-&gt;new();

# Note 2: we must rely on ShellExecute from Win32::GUI rather than from AutoIt.
# Although there is a ShellExecute function available in AutoIt, it is not exported
# via the COM interface (AutoItX). In general, not everything available in AutoIt
# is exposed via AutoItX (which is bad).

# Note 3: when launching MS Access to open a database in a "trusted location"
# (i.e. macros allowed), then the path to the opened database must match exactly
# the path configured for the "trusted location". In particular, backslashes rather
# than slashes must be used as separators.

# Open MS Access database

$win-&gt;ShellExecute(
    "open",
    "msaccess.exe",
    "k:\\workspace\\nca_bo\\nca_bo.mdb",
    "k:\\workspace\\nca_bo",
    10);
    
# Note 4: see ShellExecute API doc for the meaning of constant 10
# in the above invocation.

my $Au3 = Win32::OLE-&gt;new("AutoItX3.Control");
# Wait till a pop-up message box appears
$Au3-&gt;WinWaitActive("Microsoft Office Access", "vom Web-Portal importieren");
# Simulate Alt-N keystroke to close message box
$Au3-&gt;Send("!n");
# Wait till an Access form appears
$Au3-&gt;WinWait("Hauptmenü");
# Simulate Alt-E keystroke to push a button on the form
$Au3-&gt;Send("!e");
# Wait till a pop-up message box appears
$Au3-&gt;WinWaitActive("Microsoft Office Access", "zum Web-Portal exportieren");
# Simulate Alt-N keystroke to close message box
$Au3-&gt;Send("!n");
&lt;/pre&gt;&lt;p&gt;Closing remarks:&lt;/p&gt;&lt;ul&gt;
&lt;li&gt;To find out about the AutoItX COM interface, consult a .chm help file shipped with AutoIt, but separate from the main AutoIt help.&lt;/li&gt;
&lt;li&gt;Lack of proper error reporting is a major risk when using Perl-AutoItX together, and possibly also for AutoIt alone. I got into situations where a command simply did not work, without any apparent error message (but in each case I could find a reason through closer investigation of the expected/actual syntax, Google).&lt;/li&gt;
&lt;li&gt;I haven't tested the reliability of transferring data between AutoIt and Perl. Typically, integration between programming languages shows its flaws in the call API when it comes to exchanging more complicated data structures, passing parameters by reference etc.&lt;/li&gt;
&lt;li&gt;Depending on the complexity level of the actual scripts, it may be actually tolerable to rely on coarse-grained integration: write scripts using AutoIt syntax and SciTE, then encapsulate them as functions into Perl.&lt;/li&gt;
&lt;li&gt;In any case, putting serious functionality into AutoIt scripts that can be just as easily be implemented in a more mainstream language should be avoided when possible. For the particular combination of AutoIt and Perl, the general rule of thumb should be "as little AutoIt as necessary, as much Perl as possible".&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-824766333543391901?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/824766333543391901/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/04/windows-gui-automation-with-autoit-and.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/824766333543391901'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/824766333543391901'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/04/windows-gui-automation-with-autoit-and.html' title='Windows GUI automation with AutoIt and Perl'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-9076481851171615184</id><published>2010-04-12T02:01:00.005+02:00</published><updated>2010-05-12T16:10:50.329+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='features'/><category scheme='http://www.blogger.com/atom/ns#' term='version control'/><category scheme='http://www.blogger.com/atom/ns#' term='software engineering'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><title type='text'>Version control in Drupal 6 using the Features module</title><content type='html'>&lt;p&gt;&lt;i&gt;Warning: in this article I assume that you understand the concepts of modules and hooks in Drupal, and that you also know the Views module, which is used as an example. It also assume that you are at least somewhat familiar with the general principles of software version control and are looking to apply them to developing Drupal 6 sites. It is also advisable that you have a brief look at the monkey-see-monkey-do &lt;a href="http://developmentseed.org/blog/2009/may/29/making-and-using-features-drupal"&gt;screencast&lt;/a&gt; about the features module, which currently substitutes comprehensible documentation.&lt;/i&gt;&lt;/p&gt;&lt;p&gt;In 1972 programmers realized that efficient collabaration within software projects is difficult without tools that efficiently support exchanging and comparing changes made to source code. In that year the first &lt;a href="http://en.wikipedia.org/wiki/Source_Code_Control_System"&gt;source code control system&lt;/a&gt; was constructed. With the later release of &lt;a href="http://www.nongnu.org/cvs/"&gt;CVS&lt;/a&gt;, which remains popular until today, the problem has pretty much been solved in a satisfactory way (though Mr. Linus "&lt;a href="http://git-scm.com/"&gt;Git&lt;/a&gt;" Torvalds and others like to
&lt;a href="http://www.youtube.com/watch?v=4XpnKHJAok8"&gt;disagree&lt;/a&gt;).&lt;/p&gt;&lt;p&gt;All traditional version control tools exploit a simple observation about the nature of source code evolution: most of the time, in most well-structured programs, meaningful changes are represented as alterations of consecutive lines of source code. Changes, and their effects, tend to be &lt;em&gt;local&lt;/em&gt;. Most programmers also naturally work and think in terms of changing lines of source code within files. Thus, all you need to satisfy most programmers is a system which quickly and conveniently presents which lines have been changed by whom, given two or more points in time. With that ability, you can also package sets of changes into compact files called &lt;em&gt;patches&lt;/em&gt; and then apply them to an original version of a system to reliably reproduce someone else's work or store away to track individual contributions (and blame for bugs).&lt;/p&gt;&lt;p&gt;The idea easily generalizes to tools for managing &lt;em&gt;configuration items&lt;/em&gt; rather than source code. The term &lt;em&gt;configuration item&lt;/em&gt; is rather related to ideas such as "configuration of electrons in an atom" or "configuration of stars", not to be confused with the idea of configuration files. It refers to any artifact which might change over time and changes to which must be propagated between individual developers or systems. If changes to configuration items are also local, it is most straightforward to map them to simple text files and use a traditional version control tool to manage them. The opposite, bad approach, would be to convert simple text files into something that the version control tool cannot effectively deal with (something neither text- nor line-oriented) and thus to produce a classic configuration management problem.&lt;/p&gt;&lt;p&gt;Fast forward to 2009, Drupal 6 (and other Content Mangement Systems, to be fair). It appears that in this new context &lt;a href="http://developmentseed.org/blog/2009/may/29/making-and-using-features-drupal"&gt;young&lt;/a&gt; &lt;a href="http://doi.ieeecomputersociety.org/10.1109/MS.2007.17"&gt;people&lt;/a&gt; are now making some &lt;a href="http://groups.drupal.org/node/57948"&gt;noise&lt;/a&gt; about the SCM trap. Putting configuration items, such as Drupal CCK content types, views, permissions settings - just about anything - into a relational database reliably defeats traditional version control tools. It becomes as cumbersome to distribute and coordinate changes to these items as it used to be for source code changes pre-1972. Yet, luckily for everyone, approaches and tools for solving this age-old problem are also being (re-)invented. The unfortunately named &lt;a href="http://developmentseed.org/blog/2009/may/29/making-and-using-features-drupal"&gt;features&lt;/a&gt; module for Drupal 6 (SEO, anyone?) provides a workable solution, which I elaborate in the rest of this article.&lt;/p&gt;&lt;p&gt;However. before we go any further, a word of caution regarding terminology is in order. The established term "configuration item" (by Professor W. Tichy, during the 80's) is apparently unfamiliar to or not good enough for inventive young Drupal developers. They like to call configuration items "exportables" or (much worse) "components". This is the terminology found in the original code of the &lt;em&gt;features&lt;/em&gt; module, the documentation, and the API. You better get used to it if you want to understand these sources, but I will try to shield you from confusion as much as possible by academically sticking to &lt;em&gt;configuration items&lt;/em&gt; in this article. It is intriguing why so many otherwise intelligent and dedicated people choose to pollute the world with lazy rubbish terminology. In case of key projects it is likely to then become perpetrated by hundreds (thousands?) of unsuspecting follower-developers. A worthy topic of research for a psychologist, no doubt. But I digress.&lt;/p&gt;&lt;p&gt;The Drupal &lt;em&gt;features&lt;/em&gt; module allows you to&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Export configuration items from the database to a set of text files.
(Ideally, one file per configuration item should be used to reflect the locality of changes and to reduce the risk of file-level conflicts. Grouping of similar configuration items in one file is also acceptable, and is indeed employed by Drupal's &lt;em&gt;features&lt;/em&gt; module.)&lt;/li&gt;
&lt;li&gt;Exchange and manage contents of the exported text files using traditional version control tools.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;At this point in our design reconstruction, different approaches seem viable. An intuitive one, which I and my colleagues have been successfully using in another (non-Drupal) project for a few years, is to provide a symmetric import operation which loads the (now merged/resolved) configuration items from the file system back into the database. However, the solution used by Drupal's &lt;em&gt;features&lt;/em&gt; is even neater, as it eliminates the need to perform manual imports (and thus also the danger of forgetting an import):&lt;/p&gt;&lt;ul&gt;&lt;li&gt;Use the exported files as the authoritative (live) version of the represented configuration items...&lt;/li&gt;&lt;li&gt;...unless another, more up-to-date version of those same items exists in the database. In this case, treat the database version as authoritative. That is, database settings override, or take precedence over, file system settings.&lt;/li&gt;&lt;li&gt;Make it possible to re-export the modified configuration items from the database. Consequently, the exported files' content becomes identical to the database, and so again, these files may be treated as authoritative.&lt;/li&gt;
&lt;/ul&gt;&lt;p&gt;In summary, people are supposed to edit configuration items in the database and always re-export them before synchronizing with the version control repository. In this way their changes become available to others and others' changes can be merged in through the file system, while the GUI used for editing may still be implemented in the browser (making it more or less comfortable, but certainly more fool-proof than source code editing).&lt;/p&gt;&lt;p&gt;A specification of &lt;em&gt;which&lt;/em&gt; changes are actually exported (i.e. "belong to a feature"), is maintained within a (custom) feature module; not to be confused with Drupal's &lt;em&gt;features&lt;/em&gt; module, which is used to generate the former one. A custom feature modules furthermore includes a plain-text version of the actual configuration items, not just references to them. More precisely, a source code representation of those items is kept within the feature module. By the way, note that a feature module is in itself a configuration item represented in the file system and thus readily accessible to version control. However, it is not a configuration item in the sense of possibly having a shadow copy within the database. It's more akin to a normal Drupal module. However, as mentioned, its content is generated by the &lt;em&gt;features&lt;/em&gt; module and it also does contain some additional metadata that allows the &lt;em&gt;features&lt;/em&gt; module to treat it as its own creation when it comes to re-exporting.&lt;/p&gt;&lt;p&gt;Asking inconvenient questions early is a virtue. Given the astonishing number of Drupal modules, each of which may contribute their own configuration items to a web site by introducing its own database tables, or even worse, extending existing ones, how does the &lt;em&gt;features&lt;/em&gt; module find out how to export all those alien configuration items? And how do those alien modules know how to use the disk versions stored within the exported feature module instead of looking for versions in the database? Under which conditions is an exported configuration item reusable across different versions of an originating module? Is it even sufficient to check that the originating module is installed in the right version, or must we ensure that other supporting modules are also available on an item-per-item basis? More generally, is the beta version of the &lt;em&gt;features&lt;/em&gt; module ready for use in real projects. Does it introduce any significant risks (such as data loss, lack-of support time bombs), glitches or limitations? To address these issues, in lack of case studies, a closer look under the hood of the &lt;em&gt;features&lt;/em&gt; module is necessary.&lt;/p&gt;&lt;p&gt;To see what happens (and where and why things might go wrong), let's walk through a scenario of creating and using a feature which contains just a couple of view definitions:&lt;/p&gt;&lt;ol&gt;&lt;li&gt;&lt;p&gt;To begin creating our feature, we fill out the export form located at &lt;code&gt;/admin/build/features/create&lt;/code&gt;. Here we see an "Add components" combo box filled with entries such as "Content types", "Permissions", "Views", "Dependencies". In short, what is referred to as components here is really &lt;em&gt;the kinds of configuration items&lt;/em&gt; that can be exported as part of a feature, Elsewhere, the developers carelessly use the same bland term &lt;em&gt;component&lt;/em&gt; to refer to an individual configuration item, thus committing the &lt;del&gt;modelling shortcut&lt;/del&gt; embarassing error of confusing types and instances, and wreaking havoc in minds of their readers. To populate the combo box, the &lt;em&gt;features&lt;/em&gt; module invokes &lt;code&gt;hook_features_api&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;Interestingly, implementations of this hook are not contained within the contributing modules, but rather within the &lt;em&gt;features&lt;/em&gt; module itself. This might be interpreted as a commitment of the &lt;em&gt;features&lt;/em&gt; module's maintainers to keep up-to-date with changes in important (core) third-party modules. It might be also interpreted as a non-commitment of the third-party modules' authors to play nice with &lt;em&gt;features&lt;/em&gt;. As the &lt;em&gt;features&lt;/em&gt; module is rather new, the choice by its original inventors to put the burden of first-time integration on their own shoulders is understandable and smart from the division of labor viewpoint. In the long term, it might represent a weak point of the overall scheme and a stability risk, especially given the currently inadequate amount of examples/documentation for third-party integration.&lt;/p&gt;&lt;p&gt;Anyway, we already see an answer to one of the questions posed above about how the &lt;em&gt;features&lt;/em&gt; module knows what to export. It either includes direct support for a particular kind of configuration items, or (preferably) relies on third-party module authors to describe their own kinds of configuration items through an API.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;When we select a kind of configuration item in the "Add components" combo box, a list of individual exportable configuration items of that kind below is displayed. The content of this list is generated by &lt;code&gt;hook_features_export_options&lt;/code&gt;. In case of views, the &lt;em&gt;features&lt;/em&gt; module implements this hook directly to return names of all enabled views. This hook also provides an opportunity for the implementor to influence in which file the exported configuration items will be stored withing the feature module (e.g. in the same or separate file from configuration items of the other kinds). The views-specific implementation opts to store all exported views definitions in a single, separate file.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;Suppose we click on a particular view in the list. Through an ahah callback the server is now consulted about further details of the selected configuration item, and the right-side pane of the form is updated with those details. More precisely, &lt;code&gt;hook_features_export&lt;/code&gt; is invoked with a list of identifiers of all currently selected configuration items (e.g. view names). An empty output container is also passed in as a parameter. The hook's implementation is supposed to transfer the input list to the output container. At this stage additional items, not present in the original list, might be added. Furthermore, module dependencies of the selected configuration items are determined and recorded in the output container.&lt;/p&gt;&lt;p&gt;The provision of dependency recording gives a (partial) answer to the question of reusability of the exported configuration items in new contexts. The capability to record module dependencies allows the &lt;em&gt;features&lt;/em&gt; module to disallow enabling a feature which contains configuration items not supported by available modules (or alternatively, to prompt the user to download the required modules).&lt;/p&gt;&lt;p&gt;The hook implementation may also designate follow-up functions to be called after return to continue the process of filling in the output container. This mechanism is used if the exported configuration items in fact consist of sub-items, which must be handled by  other modules. In case of views, there is no need for such a cascade. The implementation simply transfers the requested view names to the output container and records names of modules on which the associated view definitions depend.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;When we finally submit the export form, &lt;code&gt;hook_features_export_render&lt;/code&gt; is called to query implementing modules for the actual exported source code to be stored within the feature module. The views-specific implementation calls the &lt;code&gt;export&lt;/code&gt; method on each view to obtain this source code. Generally, it's entirely up to the implementor how each configuration item is represented in the source code. The views-specific implementation generates source code that populates and returns an array of view definitions.&lt;/p&gt;&lt;p&gt;Besides of the source file which holds all configuration items of a kind, the &lt;em&gt;features&lt;/em&gt; module also generates three other files: an &lt;code&gt;.info&lt;/code&gt; file, which includes a description of module dependencies, a boilerplate &lt;code&gt;.module&lt;/code&gt; file, and a &lt;code&gt;.features.inc&lt;/code&gt; file. This last file is of particular interest, as it defines a single hook for each kind of configuration item that was declared using &lt;code&gt;hook_features_api&lt;/code&gt; (the same hook used to populate the combo box in first step, remember?). In case of views, the generated hook's name is declared to be &lt;code&gt;views_default_views&lt;/code&gt;. This hook is what the &lt;em&gt;views&lt;/em&gt; module will later call back to look for any exported views definitions. The hook's code is boilerplate - it delegates to the real function, which returns the array with configuration items (view definitions, in this case).&lt;/p&gt;&lt;p&gt;Having generated all those files, the &lt;em&gt;features&lt;/em&gt; module packs them together and offers the resulting feature module for download and installation. The installation (enabling of features) occurs through the &lt;code&gt;/admin/build/features&lt;/code&gt; page, rather than the usual modules page.&lt;/p&gt;&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;Thus far, we have roughly examined what happens during (re-)creating or (re-)defining a feature. Now it's time to look what happens when a feature is enabled. As it turns out, not so much. The function &lt;code&gt;features_install_modules&lt;/code&gt; is called with a list of all feature modules that you have chosen to enable. This function silently ignores all already enabled feature modules and only processes the additional ones. For each new feature module, it determines a transitive closure of dependencies and enables all those required modules if need be. The new feature module itself is also enabled.&lt;/p&gt;&lt;p&gt;The next step is to examine how modules that own and export configuration items utilize their exported representations. More particularly, how does the &lt;em&gt;views&lt;/em&gt; module determine whether to use a view definition stored in the database or the one contributed by a feature module through the file system? The solution is rather simple and implemented with little support from the &lt;em&gt;features&lt;/em&gt; module. As already mentioned, a hook (&lt;code&gt;views_default_views&lt;/code&gt;) hook is generated during the export. The views module calls this hook to find out which views are represented as exported configuration items (aka "default views"). Depending on whether or not a databased version of a view is also available, it is returned from &lt;code&gt;views_get_view&lt;/code&gt; instead of the default one from the file system. When a view is updated, a version of it is created in the database, possibly using the default view as a prototype. No surprises.&lt;/p&gt;&lt;p&gt;Finally, let's tackle the remaining few operations that can be performed on an enabled feature: reverting, rebuilding (aka recreating or re-exporting), and disabling:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;Reverting&lt;/em&gt; a feature means restoring all configuration items to their original exported state. Any changes made to the database versions of the configuration items are undone. The implementation within the &lt;em&gt;features&lt;/em&gt; module simply calls the hook &lt;em&gt;feature_revert&lt;/em&gt; and lets the modules that own the configuration items do the job. In case of views, the database version of each view included in the feature is deleted, thus giving precedence back to the default view. However, according to my tests, the &lt;em&gt;views&lt;/em&gt; module seems to remember whether a view has a database version equal to the file system version originally, and in such case does not delete the database version during &lt;em&gt;feature_revert&lt;/em&gt;, though you can delete it using the Revert action in the views GUI.&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;Recreating&lt;/em&gt; a feature means re-exporting the current versions of all included configuration items. The only difference from the original export is that now you don't have to choose which item to include in the feature (although you are given this opportunity). The process gives you a new .tgz feature module with which you should overwrite the installed module (unless it contains external changes, in which case you should merge both using your version control tool).&lt;/p&gt;&lt;/li&gt;&lt;li&gt;&lt;p&gt;&lt;em&gt;Disabling&lt;/em&gt; a feature is quite simple - it just disables the feature module, making the database versions of the configuration items all that remains active. A word of warning: if after enabling the feature you remove the database versions of the configuration items and subsequently disable the feature, you will effectively remove these configuration items from your site. For example, if you revert a view to only rely on the file system version, and disable the enclosing feature, the view will be gone (until you re-enable the feature, that is). This is to be expected. Also, if you choose to recreate such a disabled feature, the set of exported configuration items will not match the original one - in particular, the views which don't have versions in the database, will not be re-exported, unless you explicitly add them again to the export set.&lt;/p&gt;&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;Having provided (admittedly roundabout) answers to the questions posed in the beginning of the article, let's turn in conclusion to the bigger issue: is the &lt;em&gt;features&lt;/em&gt; module safe to use today?&lt;/p&gt;&lt;p&gt;Obviously, an answer would benefit from more real-world experience to better estimate the frequency and severity of bugs. Still, even after a short examination of the overall design (and some peeks at the source code as well), it appears to me that the core concept of features is solid and ready for prime time. The unfortunate terminology and confusing documentation are flaws, but they are not insurmountable and certainly not show-stoppers, judging from the quality of other popular Drupal modules. I shall give the &lt;em&gt;features&lt;/em&gt; module a serious try in my project to alleviate the pressing need for conveniently exchanging views and content types definitions between developers and tracking changes. One apparent weakness is that at present some important kind of configuration items might not be supported (e.g. localized strings). However, the &lt;em&gt;features&lt;/em&gt; module can be augmented with other export-to-file-system approaches where they are available (.po files), and with background information included in this study, integration with new modules should also be relatively easy.&lt;/p&gt;&lt;hr /&gt;&lt;p&gt;Having written this article, I actually started using the Features module in one project to keep the development, test and production sites in sync. The following updates document some discovered glitches and "lessons learned".&lt;/p&gt;&lt;h3&gt;Update 2010-04-18, uploading updated CCK content types to production&lt;/h3&gt;&lt;p&gt;I discovered the following caveat when using features to synchronize CCK content types from development to production: when you add a new field to a CCK content type, recreate the feature, then upload the updated feature module to a production site, the new field is not added to the content type immediately (neither is it added to the content type's database table). Instead, the feature appears marked as "needs review" and you have to "revert" it manually in order to make changes active. I would expect the "needs review" state to only occur if there have been independent changes to the feature's configuration items at the production site since the last upload, that is, when there is room for conflict, so this behavior is surprising.&lt;/p&gt;&lt;h3&gt;Update 2010-05-12, added CCK fields not included in feature automatically&lt;/h3&gt;&lt;p&gt;Suppose you have an existing feature which contains a content type. You add a new field to the content type in your database. What state would you expect to be reported for the feature then? Well, I would like it to be treated the same way as the case of a view to which I added a new field - the feature should show up as "overridden", and recreating the feature should export the new CCK field. Yet it doesn't. Apparently you have to add the new field to &lt;code&gt;feature.info&lt;/code&gt; manually for this configuration (sub)item to be included in the feature at all.&lt;/p&gt;&lt;p&gt;I think that the reason for the current state of affairs might be the developers' speculation that a single content type may be governed by more than one feature (as well as the user herself). However, the same could be said about fields of a view. In any case, the current behavior is inconsistent.&lt;/p&gt;&lt;h3&gt;Update 2010-05-12, modules added as dependencies not enabled automatically&lt;/h3&gt;&lt;p&gt;Suppose you enable a module in your development site and add it as dependency to a feature. You then upload your feature to a production site which has the module in disabled state. What would you expect to happen when you revert the feature to "default" state? I would expect the module to become enabled (or some kind of error message to be displayed, had the module been unavailable). However, what happens instead is that the module stays disabled and - worse - can no longer be enabled manually because it is "locked" through the feature. You have to remmber to enable the module manually &lt;em&gt;before&lt;/em&gt; uploading the updated feature which depends on it. This is exactly the kind of thing which you should not have to remember when relying on a configuration management tool.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-9076481851171615184?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/9076481851171615184/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/04/version-control-in-drupal-6-using.html#comment-form' title='11 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/9076481851171615184'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/9076481851171615184'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/04/version-control-in-drupal-6-using.html' title='Version control in Drupal 6 using the Features module'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>11</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-4120374329115330015</id><published>2010-03-04T18:49:00.008+01:00</published><updated>2010-04-12T13:35:16.635+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='localization'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><title type='text'>Drupal 6: Translating user-defined (dynamic, variable) strings</title><content type='html'>&lt;p&gt;There are times during Drupal module development when you want to translate a string which is defined by the user and read from the database. You are not supposed to apply the &lt;code&gt;t()&lt;/code&gt; function in this case because it should only be used for static strings with variable (non-locale-specific) replacements. (Break this rule if you want to confuse the localization tools reponsible for extracting strings from code.)&lt;/p&gt;&lt;p&gt;As an example of the problem, consider Drupal's core &lt;code&gt;contact.module&lt;/code&gt;, which at the time of writing is (regrettably) not fully localized. It allows the user to define categories, whose names are then displayed in the GUI:&lt;/p&gt;&lt;p&gt;Original:&lt;/p&gt;&lt;pre&gt;
    $categories[$category-&gt;cid] = $category-&gt;category;&lt;/pre&gt;
&lt;p&gt;Wrong solution:&lt;/p&gt;&lt;pre&gt;
    $categories[$category-&gt;cid] = t($category-&gt;category);&lt;/pre&gt;
&lt;p&gt;(At least partially) Correct solution:&lt;/p&gt;&lt;pre&gt;
    $categories[$category-&gt;cid] = i18nstrings_ts(
       'contact:category:' . $category-&gt;cid . ':name',
       $category-&gt;category, NULL, TRUE);&lt;/pre&gt;
&lt;p&gt;The last solution (relying on &lt;code&gt;i18n.module&lt;/code&gt; API) is good. You will be able to find and translate the strings using the localization UI. It has a cosmetic flaw that the localization UI is not really aware of the new 'contact' textgroup, so the group name will not be displayed at all. If you want a full-fledged solution, follow instructions on &lt;a href="http://drupal.org/node/304002"&gt;making your custom data translatable&lt;/a&gt; and study &lt;code&gt;i18ncontent.module&lt;/code&gt; or &lt;code&gt;i18views.module&lt;/code&gt; for further details.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-4120374329115330015?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/4120374329115330015/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/03/drupal-6-translating-user-defined.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4120374329115330015'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4120374329115330015'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/03/drupal-6-translating-user-defined.html' title='Drupal 6: Translating user-defined (dynamic, variable) strings'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-4070617299025904041</id><published>2010-03-03T14:49:00.004+01:00</published><updated>2010-03-03T14:54:26.443+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='Drupal'/><title type='text'>Solution for $_POST empty after drupal_http_request</title><content type='html'>&lt;p&gt;The function &lt;a href="http://api.drupal.org/api/function/drupal_http_request/6"&gt;&lt;code&gt;drupal_http_request&lt;/code&gt;&lt;/a&gt; can be used to POST data to another Drupal script. However, what the documentation does not mention is that when you invoke it without the Content-Type header, the receiving script might not see any of the posted content.&lt;/p&gt;&lt;p&gt;
Wrong:&lt;/p&gt;&lt;pre&gt;
  $result = drupal_http_request(
    url('some/url', array(absolute =&gt; TRUE)),
    array(),
    'POST',
    'param1=value1&amp;param2=value2');&lt;/pre&gt;
&lt;p&gt;Correct:&lt;/p&gt;&lt;pre&gt;
  $result = drupal_http_request(
    url('some/url', array(absolute =&gt; TRUE)),
    array('Content-Type' =&gt; 'application/x-www-form-urlencoded'),
    'POST',
    'param1=value1&amp;param2=value2');&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-4070617299025904041?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/4070617299025904041/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/03/solution-for-post-empty-after.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4070617299025904041'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4070617299025904041'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/03/solution-for-post-empty-after.html' title='Solution for $_POST empty after drupal_http_request'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-7843134107423638963</id><published>2010-02-17T15:05:00.005+01:00</published><updated>2010-02-17T15:30:11.943+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='dynamic forms'/><category scheme='http://www.blogger.com/atom/ns#' term='form api'/><category scheme='http://www.blogger.com/atom/ns#' term='ajax'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><category scheme='http://www.blogger.com/atom/ns#' term='ahah'/><title type='text'>Drupal 6: Adding form fields dynamically</title><content type='html'>&lt;p&gt;There are times when you want to understand how something works, but there are plent more times when you simply want to get things done. When it came to dynamically extending Drupal 6 forms per Ajax/AHAH, I found some half-baked explanations of the "ahah voodoo" (aka implementation details) on the web, references to poll.module as the place which contains such frightening "voodoo", but no simple examples which would provide the usual delights of copy-paste reuse.&lt;/p&gt;&lt;p&gt;The AHAH idea is very simple - call back the server to generate a HTML snippet with a few more form fields to be inserted and subsequently submitted by client. Alas, implementing even such a basic scenario in Drupal 6 is complicated. To improve the matters a little, I implemented a &lt;a href="http://www.plosquare.com/download/nca_ahah-6.x-1.0.tar.gz"&gt;Simple AHAH API module&lt;/a&gt; (download link). Example client code for an order form with dynamically added order items is shown below. It is almost self-explanatory (for further instructions, see included README.txt). Overall, it should reduce the implementation time for the most usual case (dynamically adding form fields) from several tiresome hours of debugging to a few minutes of thoughtless fill-in-the-blanks.&lt;/p&gt;&lt;pre&gt;
function nca_ahah_init() {
  module_load_include('inc', 'nca_ahah', 'nca_ahah');
}

function nca_ahah_menu() {
  $items['nca_ahah/nca_ahah_order_item'] = array(
    'title' =&gt; 'Javascript Order Item Form',
    'page callback' =&gt; 'nca_ahah_callback',
    'page arguments' =&gt; array('nca_ahah_order_item'),
    'access arguments' =&gt; array('access content'),
    'type' =&gt; MENU_CALLBACK,
  );

  $items['nca_ahah/order'] = array(
    'title' =&gt; t('Order items example'),    
    'page callback' =&gt; 'drupal_get_form',
    'page arguments' =&gt; array('nca_ahah_order_form'),
    'access arguments' =&gt; array('access content'),
    'type' =&gt; MENU_CALLBACK,
  );
  
  return $items;
}

function nca_ahah_theme() {
  return array(
    'nca_ahah_order_item' =&gt; array(
      'arguments' =&gt; array('form' =&gt; NULL),
    ),
  );
}

function nca_ahah_order_form($form_state) {
  $form = array();
  
  nca_ahah_wrapper($form, $form_state, 'nca_ahah_order_item');

  $form['submit'] = array(
    '#type' =&gt; 'submit',
    '#value' =&gt; t('Proceed to checkout'),
    '#weight' =&gt; 2,
    );

  // Avoid submitting the form to AHAH handler on checkout. 
  $form['#action'] = url('nca_ahah/order');
  
  return $form;
}

function nca_ahah_order_form_submit($form, &amp;$form_state) {
  if ($form_state['clicked_button']['#id'] == 'edit-submit') {
    // Real submit button was clicked.
    dpm($form_state['values']);
  }
  else {
    // "Add more" button was clicked.
  }
}

function nca_ahah_order_form_validate($form_id, &amp;$form_state) { 
  if ($form_state['clicked_button']['#id'] == 'edit-submit') {
    // Real submit button was clicked.
  }
  else {
    // "Add more" button was clicked.
  }
}

function nca_ahah_order_item_form(&amp;$item_form, $i, $item) {
  $item_form['item'] = array(
    '#title' =&gt; t('Item name'),
    '#type' =&gt; 'select',
    '#options' =&gt; array(0 =&gt; '', 1 =&gt; 'First Item', 2 =&gt; 'Second Item', 3 =&gt; 'Third Item'),
    '#default_value' =&gt; isset($item['item']) ? $item['item'] : 0,
    '#parents' =&gt; array('nca_ahah_order_item', $i, 'item'), // unfortunately needed
    );

  $item_form['qty'] = array(
    '#title' =&gt; t('Quantity'),
    '#type' =&gt; 'textfield',
    '#default_value' =&gt; isset($item['qty']) ? $item['qty'] : 0,
    '#size' =&gt; 5,
    '#maxlength' =&gt; 7,
    '#parents' =&gt; array('nca_ahah_order_item', $i, 'qty'), // unfortunately needed
  );
}

function theme_nca_ahah_order_item($item_form) {
  // You can do more fancy layout/HTML formatting here
  return drupal_render($item_form);
}
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-7843134107423638963?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/7843134107423638963/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/02/drupal-6-adding-form-fields-dynamically.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7843134107423638963'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7843134107423638963'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/02/drupal-6-adding-form-fields-dynamically.html' title='Drupal 6: Adding form fields dynamically'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-1451468844171831455</id><published>2010-02-13T00:02:00.005+01:00</published><updated>2010-02-20T20:57:11.429+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='form api'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><title type='text'>Drupal 6: Debugging form submissions</title><content type='html'>&lt;p&gt;This post contains a few notes taken from a debugging session for a problem of the kind "submitted form is being returned to the sender without any error message" in a Drupal 6 installation. Furthermore, it contains some tips on debugging the "form values disappear into the void" symptom. These are surprisingly difficult issues to debug if you don't know where to start, as Drupal's control flow is notoriously convoluted due to indirection caused by hooks (as a matter of fact, quite a few software frameworks pay the same price for their flexibility) and because of generous use of recursion.&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Check if &lt;code&gt;form_set_error&lt;/code&gt; is called (with a non-empty argument) by any module during processing.&lt;/li&gt;
&lt;li&gt;Instrument or break in function &lt;code&gt;form_execute_handlers&lt;/code&gt; in &lt;code&gt;form.inc&lt;/code&gt;. This is from where the validate and submit functions contributed by various modules are invoked from.&lt;/li&gt;
&lt;li&gt;If the form validates, but an expected submit handler is not in the list, it may be due to the form id not matching the function name. If you cannot change either, you can still bring them together in the form_alter hook by appending to #submit, like so:&lt;pre&gt;
$form['#submit'][] = 'user_register_submit';&lt;/pre&gt;
&lt;li&gt;If the form values in &lt;code&gt;$form_state&lt;/code&gt; seem to be wrong or missing, debug the &lt;code&gt;$form['#value']&lt;/code&gt; assignments in &lt;code&gt;_form_builder_handle_input_element&lt;/code&gt; (to conditionally break on a particular element, you can evaluate &lt;code&gt;$form['#name']&lt;/code&gt;). In particular, for multistep forms pay attention to the &lt;code&gt;#access&lt;/code&gt; attribute, which controls whether the values are populated from the form cache or from the POST request.&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-1451468844171831455?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/1451468844171831455/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/02/drupal-6-debugging-form-submissions.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1451468844171831455'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1451468844171831455'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/02/drupal-6-debugging-form-submissions.html' title='Drupal 6: Debugging form submissions'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-125275301931636950</id><published>2010-01-29T15:17:00.003+01:00</published><updated>2010-01-29T15:22:35.151+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='msie'/><category scheme='http://www.blogger.com/atom/ns#' term='html'/><title type='text'>Solution for MSIE 7 only applies style on refresh</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; Internet Explorer 7 only renders some pages correctly after they are reloaded. The originally loaded page seems to have some of the stylesheet applied to it (e.g. colors), but not everthing (e.g. font). Furthermore, the View Source menu option does not work - MSIE appears to be stuck rendering the page. It happens to some pages of a web site, while others are rendered correctly for no apparent reason. Everything works perfectly in Firefox (or even in MSIE 6).&lt;/p&gt;&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; This particular problem has been caused by a link to the stylesheet incorrectly placed inside of the BODY tag instead of inside of the HEAD tag. Simply moving the LINK tag in page's source fixed it.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-125275301931636950?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/125275301931636950/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/01/solution-for-msie-7-only-applies-style.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/125275301931636950'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/125275301931636950'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/01/solution-for-msie-7-only-applies-style.html' title='Solution for MSIE 7 only applies style on refresh'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-6806626550240772689</id><published>2010-01-24T18:05:00.003+01:00</published><updated>2010-01-24T18:21:52.014+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='lvm2'/><category scheme='http://www.blogger.com/atom/ns#' term='snapshot'/><title type='text'>LVM2 snapshots: device-mapper: create ioctl failed: Device or resource busy / Failed to suspend origin</title><content type='html'>&lt;p&gt;If you are getting the error "device-mapper: create ioctl failed: Device or resource busy" when using the &lt;code&gt;lvcreate&lt;/code&gt; command to set up an LVM2 snapshot, and also seeing an EBUSY on a second call to DM_DEV_CREATE ioctl when you strace the command, and finally seeing "unknown filesystem type 'DM_snapshot_cow'" when you try to mount the snapshot, then here is a clue of what may be wrong and a possible workaround:&lt;/p&gt;&lt;p&gt;I ran into this problem when I set up the origin device (i.e. the one of which the snapshot should be taken) manually using &lt;code&gt;dmsetup&lt;/code&gt; (after having it manually removed - to be able to detach the loop device under it). However, as it turned out, the proper way to set up such a device is through the command &lt;code&gt;vgchange -ay&lt;/code&gt;.&lt;/p&gt;&lt;p&gt;To find out whether you're having the same issue and to reach a clean initial state, first remove the offending device(s) using &lt;code&gt;dmsetup&lt;/code&gt; (you may have to remove any partially set up logical volumes with &lt;code&gt;lvremove&lt;/code&gt; before that). You should begin with a state in which &lt;code&gt;vgdisplay -v&lt;/code&gt; displays your origin volume as inactive/unavailable and &lt;code&gt;dmsetup ls&lt;/code&gt; displays no devices. After that, invoke &lt;code&gt;vgchange -ay&lt;/code&gt;. The origin device status should now change to available and &lt;code&gt;dmsetup ls&lt;/code&gt; should be showing the corresponding device. You can also mount it at this stage. Finally, you can (hopefully) create a snapshot without running into the above described problem.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-6806626550240772689?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/6806626550240772689/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/01/lvm2-snapshots-device-mapper-create.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6806626550240772689'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6806626550240772689'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/01/lvm2-snapshots-device-mapper-create.html' title='LVM2 snapshots: device-mapper: create ioctl failed: Device or resource busy / Failed to suspend origin'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-7701857140990436496</id><published>2010-01-04T18:07:00.006+01:00</published><updated>2010-01-04T18:37:19.716+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='howto'/><category scheme='http://www.blogger.com/atom/ns#' term='views 2'/><category scheme='http://www.blogger.com/atom/ns#' term='gui'/><category scheme='http://www.blogger.com/atom/ns#' term='drupal 6'/><category scheme='http://www.blogger.com/atom/ns#' term='tabs'/><title type='text'>Drupal 6: Master/detail tabbed navigation</title><content type='html'>&lt;p&gt;Master/detail navigation is a common scenario in graphical user interfaces for databases. After selecting a master record, the user is given the choice to view all detail records of a particular type that are in some way related to the master record. A natural design choice is to offer tabbed navigation to support this scenario. The first tab contains data from the master record, while subsequent tabs represent different types of associated detail records in tabular format.&lt;/p&gt;&lt;p&gt;The rest of this post explains how to implement the above scenario in Drupal 6. To be more concrete, let's assume that we have a CCK content type Tournament, which is associated by node reference with a CCK content type Result (a tournament contains multiple game results). We also have two views, Tournaments and Results, which list all nodes of the respective type using a table display and furthermore contain exposed filters for all fields - including the tournament title field. The intended final GUI is shown in the following two screenshots (click to enlarge):&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_o_c25p_Zq5Y/S0ImIA1S-BI/AAAAAAAAABc/2VEOFp6LbWg/s1600-h/scrshot2.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 119px;" src="http://3.bp.blogspot.com/_o_c25p_Zq5Y/S0ImIA1S-BI/AAAAAAAAABc/2VEOFp6LbWg/s400/scrshot2.png" border="1" alt="Master tab screenshot" title="Master tab screenshot" id="BLOGGER_PHOTO_ID_5422938820411848722" /&gt;&lt;/a&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_o_c25p_Zq5Y/S0ImUUCJDXI/AAAAAAAAABk/eG8ra-EjAR8/s1600-h/scrshot1.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 129px;" src="http://3.bp.blogspot.com/_o_c25p_Zq5Y/S0ImUUCJDXI/AAAAAAAAABk/eG8ra-EjAR8/s400/scrshot1.png" border="1" alt="Detail tab screenshot" title="Detail tab screenshot" id="BLOGGER_PHOTO_ID_5422939031724428658" /&gt;&lt;/a&gt;
&lt;p&gt;The steps to implement the tabbed master/detail navigation are outlined below. Note that the solution does not use the official Views 2 API (which is still quite undocumented at the time of writing). It is based on trial-and-error experimentation - use at your own risk.&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;&lt;p&gt;Add a &lt;i&gt;Tournament&lt;/i&gt; argument to the Results view definition. The argument should be configured with "Display all values if not present", and employ a validator for the node type &lt;i&gt;Tournament&lt;/i&gt;. When set up correctly, the results of a particular tournament may be displayed by specifying the view's path followed by the tournament node ID. For example, invoking the view
through path /results/12345 would only display results of tournament 12345.&lt;/p&gt;&lt;p&gt;Note, however, that the &lt;i&gt;Tournament&lt;/i&gt; filter and &lt;i&gt;Tournament&lt;/i&gt; field, though redundant in the argument-based view, still appear. We will deal with them a bit later.&lt;/p&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Add a "local task" type menu item to create a tab for the Tournament node type. We limit the appearance of the tab to Tournament nodes by specifying a custom access callback.&lt;/p&gt;
&lt;pre&gt;function bbo_scoring_hook_menu() {
  // ...
  $items['node/%node/bbo_scoring/results'] = array(
    'title' =&gt; t('Results'),
    'page callback' =&gt; 'bbo_scoring_tournament_results_page',
    'page arguments' =&gt; array(1),
    'access callback' =&gt; 'bbo_scoring_tournament_results_access',
    'access arguments' =&gt; array(1),
    'type' =&gt; MENU_LOCAL_TASK,
    'weight' =&gt; 100,
  );
  // ...
}

function bbo_scoring_tournament_results_access($node) {
  return $node-&gt;type == 'tournament';
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Implement the page function so that it outputs the embedded Results view. By the way, we hide the redundant exposed filter for tournament and the tournament field on this page. Regrettably, for some reason this trick does not work in view pre-hooks, which would seem a more universal solution.&lt;/p&gt;
&lt;pre&gt;function bbo_scoring_tournament_results_page($node) {
  $view = views_get_view('results');
  $view-&gt;display['default']-&gt;display_options['filters']['field_tournament_nid']['exposed'] = FALSE;
  unset($view-&gt;display['default']-&gt;display_options['fields']['field_tournament_nid']);
  $display_id = 'page_1';
  if (!$view || !$view-&gt;access($display_id)) {
    return '';
  }
  return $view-&gt;preview($display_id, array($node-&gt;nid));
}
&lt;/pre&gt;&lt;/li&gt;
&lt;li&gt;&lt;p&gt;Alter the filter form's action to lead back to the tab page. Without this step, the default URL of the view would be displayed after applying the filter.&lt;/p&gt;
&lt;pre&gt;function bbo_scoring_form_views_exposed_form_alter(&amp;$form, $form_state) {
  if (arg(0) == 'node' &amp;&amp; arg(2) == 'bbo_scoring' &amp;&amp; arg(3) == 'results') {
    $form['#action'] = '/node/' . arg(1) . '/bbo_scoring/results';
  }
}
&lt;/pre&gt;&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-7701857140990436496?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/7701857140990436496/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2010/01/drupal-6-masterdetail-tabbed-navigation.html#comment-form' title='1 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7701857140990436496'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7701857140990436496'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2010/01/drupal-6-masterdetail-tabbed-navigation.html' title='Drupal 6: Master/detail tabbed navigation'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_o_c25p_Zq5Y/S0ImIA1S-BI/AAAAAAAAABc/2VEOFp6LbWg/s72-c/scrshot2.png' height='72' width='72'/><thr:total>1</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-6792613517560532801</id><published>2009-09-30T15:38:00.006+02:00</published><updated>2009-09-30T16:02:10.732+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Drupal'/><category scheme='http://www.blogger.com/atom/ns#' term='database'/><category scheme='http://www.blogger.com/atom/ns#' term='CCK'/><title type='text'>Drupal CCK - concepts and database tables</title><content type='html'>&lt;p&gt;What follows is a brief summary of a nice (but verbose) introduction to the Drupal &lt;a href="http://drupal.org/project/cck"&gt;CCK&lt;/a&gt; module, which can be found at &lt;a href="http://www.lullabot.com/articles/an_introduction_to_the_content_construction_kit"&gt;What is the Content Construction Kit? A View from the Database&lt;/a&gt;:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;CCK allows the site administrator to define custom content types (aka node types) and fields. Content types define data structures and furthermore contain user input/validation rules needed to generate UI forms for populating those structures. Content types are like 'tables' or 'relations' within a relational database, while fields are like 'attributes'.&lt;/li&gt;
&lt;li&gt;For each user-defined content type, CCK adds a table named &lt;code&gt;content_type_&lt;i&gt;type name&lt;/i&gt;&lt;/code&gt;&lt;/li&gt;
&lt;li&gt;Different field types correspond to different data types. In particular, a node reference type is available for implementing 'foreign keys'.&lt;/li&gt;
&lt;li&gt;For each field, CCK stores type-specific metadata in tables &lt;code&gt;node_field&lt;/code&gt; (global data for each field, e.g. storage details) and &lt;code&gt;node_field_instance&lt;/code&gt; (content type specific, e.g. contextual presentation details). The same field may be reused by multiple content types.&lt;/li&gt;
&lt;li&gt;For fields of type 'single' (1:1 association between nodes and field values) that are not shared between content types, CCK stores field values and in columns field_&lt;i&gt;field name&lt;/i&gt;_value of the respective content type table. Additional columns may also be stored there for the field (e.g. field format).&lt;/li&gt;
&lt;li&gt;For fields of type 'multiple' (1:n association between nodes and field values) &lt;em&gt;or&lt;/em&gt; shared between content types, CCK adds a table named &lt;code&gt;content_field_&lt;i&gt;field name&lt;/i&gt;&lt;/li&gt;
&lt;/ul&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-6792613517560532801?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/6792613517560532801/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/09/drupal-cck-concepts-and-database-tables.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6792613517560532801'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6792613517560532801'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/09/drupal-cck-concepts-and-database-tables.html' title='Drupal CCK - concepts and database tables'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-3429449950973587529</id><published>2009-08-31T15:08:00.008+02:00</published><updated>2009-08-31T16:50:54.908+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='profiler'/><category scheme='http://www.blogger.com/atom/ns#' term='Drupal'/><title type='text'>Profiling Drupal performance</title><content type='html'>&lt;p&gt;&lt;strong&gt;Example problem&lt;/strong&gt;: Rendering the Administer page is very slow - it takes upwards of 20 seconds on a development machine without any load.&lt;/p&gt;
&lt;h2&gt;Solution&lt;/h2&gt;
&lt;p&gt;Browsing Drupal forums, you will find plenty of theories and more or less helpful tips. However, it is usually more productive to work from first principles, that is, according to the general approach described below.&lt;/p&gt;
&lt;p&gt;Begin by enabling query and page build time logging in &lt;a href="http://drupal.org/project/devel"&gt;Devel&lt;/a&gt; module's configuration. For extra certainty, also enable query logging in the MySQL server. If the problematic page's queries execute fast, the problem must lie somewhere in the PHP code. PHP code can be easily profiled using the &lt;a href="http://www.xdebug.org/docs/profiler"&gt;XDebug profiler&lt;/a&gt;. After installing the XDebug extension, simply enable the profiler by adding the following in &lt;code&gt;xdebug.ini&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;
xdebug.profiler_output_dir=/tmp
xdebug.profiler_enable=1
&lt;/pre&gt;
&lt;p&gt;(Beware of doing it in production environment; profiling tends to be resource-intensive.)&lt;/p&gt;
&lt;p&gt;Restart Apache, reload the problematic page and wait until it finishes rendering. You will see that the profile &lt;code&gt;/tmp/cachegrind.out.&lt;i&gt;nnnn&lt;/i&gt;&lt;/code&gt; with execution time/count measurements was created. The profiler can now be disabled unless more data is required.&lt;/p&gt;
&lt;p&gt;Inspect the profile by opening it with &lt;a href="http://kcachegrind.sourceforge.net/"&gt;KCachegrind&lt;/a&gt;:&lt;/p&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_o_c25p_Zq5Y/SpvPCxVL3FI/AAAAAAAAABE/OoEcsEHjdgQ/s1600-h/kcachegrind.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 171px;" src="http://4.bp.blogspot.com/_o_c25p_Zq5Y/SpvPCxVL3FI/AAAAAAAAABE/OoEcsEHjdgQ/s320/kcachegrind.png" border="0" alt="kcachegrind screenshot" id="BLOGGER_PHOTO_ID_5376118226705701970" /&gt;&lt;center&gt;Click to enlarge&lt;/center&gt;&lt;/a&gt;
&lt;p&gt;In the above screenshot KCachegrind pinpoints the function &lt;code&gt;drupal_get_schema_versions&lt;/code&gt; as consuming almost 24 of the total 27 seconds execution time. A quick &lt;a href="http://www.google.com/search?q=drupal_get_schema_versions+slow"&gt;Google search&lt;/a&gt; reveals that this performance problem has been observed and solved for Drupal 7. After a quick-and-dirty test that disabling that function indeed fixes the performance problem, you can pursue the issue further, for example by adapting the most recently submitted patch to your installation. (Take into account the increased cost for future upgrades if you follow that route.)&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-3429449950973587529?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/3429449950973587529/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/08/profiling-drupal-performance.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3429449950973587529'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3429449950973587529'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/08/profiling-drupal-performance.html' title='Profiling Drupal performance'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_o_c25p_Zq5Y/SpvPCxVL3FI/AAAAAAAAABE/OoEcsEHjdgQ/s72-c/kcachegrind.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-7590261842013572922</id><published>2009-08-29T15:58:00.016+02:00</published><updated>2009-10-05T16:10:58.263+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='node_access'/><category scheme='http://www.blogger.com/atom/ns#' term='permissions'/><category scheme='http://www.blogger.com/atom/ns#' term='access control'/><category scheme='http://www.blogger.com/atom/ns#' term='acl'/><category scheme='http://www.blogger.com/atom/ns#' term='Drupal'/><title type='text'>Understanding Node Access Control in Drupal</title><content type='html'>&lt;p&gt;This post contains the (pseudo-code) algorithm used by Drupal 5 to determine whether or not a given user may view/edit/delete a given node. The description is based on the Pro Drupal Development book (p. 104), while later parts about access control modules are based on source code. It assumes familiarity with Drupal's concepts of nodes, modules, and hooks.&lt;/p&gt;
&lt;h2&gt;Node access control algorithm in Drupal core&lt;/h2&gt;
&lt;div style="float:right; border: 1px solid gray; margin-left:5px; padding:5px"&gt;Columns of &lt;code&gt;node_access&lt;/code&gt;:&lt;pre&gt;nid
gid
realm
grant_view
grant_update
grant_delete
&lt;/pre&gt;&lt;/div&gt;&lt;p&gt;First, the user may be granted access based on the information obtained from modules implementing the &lt;code&gt;&lt;a href="http://api.drupal.org/api/function/hook_access/5"&gt;hook_access&lt;/a&gt;&lt;/code&gt; callback. If no information is obtained that way, then per-node permissions stored in the &lt;code&gt;node_access&lt;/code&gt; table are consulted. This table's contents are maintained incrementally by modules implementing the &lt;code&gt;&lt;a href="http://api.drupal.org/api/function/hook_node_access_records/5"&gt;hook_node_access_records&lt;/a&gt;&lt;/code&gt; hook, which is invoked whenever a node is saved. Each row in that table has the following meaning: "If the user owns the &lt;i&gt;grant&lt;/i&gt; &lt;code&gt;$gid&lt;/code&gt;, then she may view/edit/delete node &lt;code&gt;$nid&lt;/code&gt;". It is sufficient that the user matches one such row to be allowed access. Accordingly, it is not possible for one module to deny access allowed by another module using this mechanism (unless it implements &lt;code&gt;&lt;a href="http://api.drupal.org/api/function/hook_access/5"&gt;hook_access&lt;/a&gt;&lt;/code&gt; to have the first say and skip the grants-based access altogether - which is unfortunately &lt;a href="http://drupal.org/node/143075"&gt;impossible for CCK node types&lt;/a&gt; without patching). The set of grants owned by the current user is determined by modules implementing the &lt;code&gt;&lt;a href="http://api.drupal.org/api/function/hook_node_grants/5"&gt;hook_node_grants&lt;/a&gt;&lt;/code&gt; callback. This set is tied to the user and its content is independent of any particular node.&lt;/p&gt;
&lt;p&gt;Note that the table &lt;code&gt;node_access&lt;/code&gt; is shared by multiple independent modules. To avoid collisions, each module's grant IDs are only unique within its namespace, called a realm. Realms play no further role in access control.&lt;/p&gt;
&lt;p&gt;The "intrinsic" meaning of a grant ID depends on the module which contributed it and is not relevant for the generic access control module. For example, the &lt;code&gt;&lt;a href="http://drupal.org/project/tac_lite"&gt;Taxonomy Access Control Lite&lt;/a&gt;&lt;/code&gt; module uses term IDs as grant IDs ("the user has such-and-such terms associated with her; the node can be viewed/updated/deleted by any user who has such-and-such terms associated with her"), while the &lt;code&gt;&lt;a href="http://drupal.org/project/workflow"&gt;Workflow&lt;/a&gt;&lt;/code&gt; module uses role IDs as grant IDs ("the user has such-and-such roles associated with her; the node can be [in its current workflow state] viewed/updated/deleted by any user who has such-and-such roles associated with her").&lt;/p&gt;
&lt;p&gt;The incremental updating of the &lt;code&gt;node_access&lt;/code&gt; table may (and does) lead to inconsistency if updating a node fails in a non-atomic fashion (that is, without rolling back the database updates that have been already triggered by the update). As a practical example, consider a change of a node's workflow state. This triggers a node update, but may also trigger a user-defined action associated with the state transition. If the action fails (e.g. due to an error in the PHP code), then execution is aborted even before &lt;code&gt;hook_node_access_records&lt;/code&gt; has done its job. Drupal 5 lets the administrator &lt;a href="http://drupal.org/node/122476"&gt;rebuild node_access&lt;/a&gt; upon request. This is semantically equivalent to clearing the table and saving each node once. Of course, it doesn't help with &lt;em&gt;detecting&lt;/em&gt; why the data corruption has occured in the first place, so it should be considered a half-measure.&lt;/p&gt;
&lt;p&gt;Here is the simplified access control algorithm (the real implementation can be found in function &lt;code&gt;node_access&lt;/code&gt; in &lt;code&gt;node.module&lt;/code&gt;):&lt;/p&gt;&lt;pre&gt;
if ($op == 'update' &amp;&amp; !has_access_to_input_format($user))
{
    return 0; // access denied
}

if (has_administer_nodes_permission($user))
{
    return 1; // access granted
}

if (!has_access_content_permission($user))
{
    return 0;
}

if (is_published($node))
{
    $result = hook_access($user, $op, $node); // for node type

    if (!is_null($result))
    {
        return $result;
    }

    // get user's grants
    $grants = hook_node_access_grants($user);

    // check whether the user may view/edit/delete node based on her grants
    return is_permission_granted($grants, $op, $node);
}
else
{
    return is_node_author($user, $node);
}
&lt;/pre&gt;
&lt;h2&gt;The ACL module&lt;/h2&gt;
&lt;p&gt;The &lt;a href="http://drupal.org/project/acl"&gt;ACL&lt;/a&gt; module provides a slightly higher-level API for maintaining user-node permissions. It is intended to be used by client modules that need to maintain (possibly directly editable) sets of individual users and grant node access permissions based on whether a user is a member of a particular set. Like any access control module in Drupal, it relies on the node_access mechanism described earlier, and can be thought as an "adapter" or translation layer above it. It offers the following functionality:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Provides an API for other (client) modules to maintain so-called ACLs. An ACL is simply a named set of users. The &lt;code&gt;acl_id&lt;/code&gt; designating an ACL is automatically generated, while the name of the ACL - and its meaning - can be chosen freely by a client module. The ACL definitions are stored in tables &lt;code&gt;acl&lt;/code&gt; and &lt;code&gt;acl_user&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Provides an API to associate ACLs with nodes and view/update/delete permissions. For example, a client module might define that users on a particular ACL may update a particular node. This information is stored in table &lt;code&gt;acl_node&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Translates contents of the &lt;code&gt;acl&lt;/code&gt; and &lt;code&gt;acl_user&lt;/code&gt; tables into grant IDs and contents of the &lt;code&gt;acl_node&lt;/code&gt; table into &lt;code&gt;node_access&lt;/code&gt; records. Here, the string 'acl' is used as the realm name and &lt;code&gt;acl_id&lt;/code&gt;s are used as grant IDs.&lt;/li&gt;
&lt;li&gt;Contains some functions that man be used by client modules to produce UI forms for manual editing of the ACLs.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Despite being a small API module intended for other clients, the ACL module is poorly documented and mainly used by other modules of the same maintainer, one of which is discussed next.&lt;/p&gt;
&lt;h2&gt;The Forum Access module&lt;/h2&gt;
&lt;p&gt;The &lt;a href="http://drupal.org/project/forum_access"&gt;Forum Access&lt;/a&gt; module, maintained by the same person as the ACL module, is one of its few known clients. Let us briefly examine the relationship between these two modules.&lt;/p&gt;
&lt;p&gt;The simplistic assumption that Forum Access "builds upon" ACL is proved wrong by the fact that it maintains its own 'forum_access' realm in the node_access table and does it &lt;em&gt;without&lt;/em&gt; going through the ACL module. User role IDs are used as grant IDs in this realm and the role-forum permissions are maintained privately in the &lt;code&gt;forum_access&lt;/code&gt; table (again, not an ACL). This mechanism is used to enforce role-based access control.&lt;/p&gt;
&lt;p&gt;However, &lt;em&gt;in addition to that&lt;/em&gt; the Forum Access module maintains one ACL per forum, which is named using the forum's ID (which, by the way, happens to be a taxonomy term ID). Users on that ACL are the "forum moderators", with full view/update/delete permissions on all forum nodes. Thus, forum moderation privileges may be granted regardless of the user's system-wide role.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-7590261842013572922?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/7590261842013572922/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/08/understanding-node-access-control-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7590261842013572922'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7590261842013572922'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/08/understanding-node-access-control-in.html' title='Understanding Node Access Control in Drupal'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-7060474386559040009</id><published>2009-08-05T17:24:00.003+02:00</published><updated>2009-08-05T18:48:56.617+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='Access'/><title type='text'>Solution for "Join expression not supported", can't open query</title><content type='html'>&lt;p&gt;&lt;b&gt;Problem:&lt;/b&gt; An attempt to open a query in Microsoft Access 2007 results in the "Join expression not supported" error. You can't see the query results nor edit the query to correct the alleged problem because the query view closes immediately. However, you can execute the query from VBA and even export its results to Excel.&lt;/p&gt;
&lt;p&gt;&lt;b&gt;Solution:&lt;/b&gt;&lt;pre&gt;
Debug.Print CurrentDb().QueryDefs("YourQueryName").SQL
&lt;/pre&gt;&lt;p&gt;in the VBA editor will let you see the (incorrect) query definition. You can set the same &lt;code&gt;SQL&lt;/code&gt; property to a new string to override the query definition. However, due to a bug in MS Access in handling JOINs the query might stay unopenable despite attempts to repair:&lt;/p&gt;
&lt;p&gt;This problem occurs when a query contains a JOIN expression which links two tables using more than a single column in each of them. For example:&lt;/p&gt;&lt;pre&gt;
SELECT a.* FROM a LEFT JOIN b ON (a.col1=b.col1 AND b.col2='value')
&lt;/pre&gt;
&lt;p&gt;Access tends to automatically and incorrectly strip the parentheses from the join expression, which leads to the error message mentioned earlier.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-7060474386559040009?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/7060474386559040009/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/08/solution-for-join-expression-not.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7060474386559040009'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7060474386559040009'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/08/solution-for-join-expression-not.html' title='Solution for &quot;Join expression not supported&quot;, can&apos;t open query'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-5312616219188330289</id><published>2009-07-21T20:49:00.010+02:00</published><updated>2010-06-09T14:04:03.831+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='selenium'/><category scheme='http://www.blogger.com/atom/ns#' term='observability'/><title type='text'>Solution for Selenium RC Firefox startup crash</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; A &lt;a href="http://seleniumhq.org/docs/05_selenium_rc.html"&gt;Selenium RC&lt;/a&gt; server under Linux starts a Firefox 3 (or 3.5) process, which then crashes immediately. A useless error dialog mentioning Gnome bug-buddy appears, the test script hangs. When started manually, the same version of Firefox works without flaw (including the Selenium IDE add-on).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; &lt;font size="-1"&gt;(note: this was written for Selenium RC 1.0.1; you may wish to try the most current version (eg. 1.0.3) before troubleshooting)&lt;/font&gt;&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Check your &lt;code&gt;~/mozilla/.plugins&lt;/code&gt; and remove plug-ins (symlinks) that you don't need. In my case, the Java 1.4.2 Plug-in caused the crash.&lt;/li&gt;
&lt;li&gt;If the above doesn't help, gathering more information related to the crash may provide a clue, as described in the following steps.&lt;/li&gt;
&lt;li&gt;Write a little wrapper for the &lt;code&gt;firefox&lt;/code&gt; script. Save it in &lt;code&gt;/usr/local/firefox/firefoxg&lt;/code&gt; or somewhere else:&lt;pre&gt;
#!/bin/sh
exec /usr/local/firefox/firefox -g $*
&lt;/pre&gt; If you start Firefox using this wrapper, you will notice that instead of the browser a debugger opens, in my case, the &lt;code&gt;ddd&lt;/code&gt; debugger frontend. You can then launch the executable by typing &lt;code&gt;run&lt;/code&gt; in the debugger console. In case of a crash (segmentation fault), you will see a stack trace which may contain interesting clues.&lt;/li&gt;
&lt;li&gt;Update your Selenium RC test script to reference the wrapper, e.g. &lt;code&gt;browser =&gt; "*firefox /usr/local/firefox/firefoxg"&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;In the shell where you launch the Selenium RC server, set the environment variable &lt;code&gt;GNOME_DISABLE_CRASH_DIALOG=1&lt;/code&gt;, so that Gnome bug-buddy doesn't bother you on crash.&lt;/li&gt;
&lt;li&gt;Go ahead and start the test script. The debugger will open. Actually, two instances of the debugger will open, the second one only after the first one is terminated. Type &lt;code&gt;run&lt;/code&gt; in the debugger console to continue execution in each case. In my environment the first instance just crashed gdb. However, the second instance produced a backtrace which helped to put blame on the Java plug-in as shown below:&lt;/li&gt;
&lt;/ol&gt;
&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_o_c25p_Zq5Y/SmYRwzAsx0I/AAAAAAAAAA0/6XdJzsLlBo8/s1600-h/ddd.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 212px;" src="http://4.bp.blogspot.com/_o_c25p_Zq5Y/SmYRwzAsx0I/AAAAAAAAAA0/6XdJzsLlBo8/s320/ddd.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5360991936455690050" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-5312616219188330289?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/5312616219188330289/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/07/solution-for-selenium-rc-firefox.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/5312616219188330289'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/5312616219188330289'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/07/solution-for-selenium-rc-firefox.html' title='Solution for Selenium RC Firefox startup crash'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://4.bp.blogspot.com/_o_c25p_Zq5Y/SmYRwzAsx0I/AAAAAAAAAA0/6XdJzsLlBo8/s72-c/ddd.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-1947474158267785152</id><published>2009-05-29T22:14:00.006+02:00</published><updated>2009-08-20T21:29:00.924+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='download'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='grep'/><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><title type='text'>Grep through a MySQL database</title><content type='html'>&lt;p&gt;Looking for a particular string in a set of locations is a common theme when debugging or reverse-engineering software. One of the interesting search locations might be a MySQL database. &lt;code&gt;dbgrep.pl&lt;/code&gt; is a little Perl script that will select all data from all (or just the specified) tables and then attempt to find a string (or regular expression) match in any field. Needless to say, it is rather resource-intensive, but still fast enough to sift through moderately sized development databases (at a processing speed of 3-5 MB/s).&lt;/p&gt;
&lt;p&gt;You can &lt;a href="http://www.plosquare.com/download/dbgrep.pl" onClick="javascript:urchinTracker('/download/dbgrep.pl')"&gt;download dbgrep.pl&lt;/a&gt; from my web site.&lt;/p&gt;&lt;pre&gt;
This is dbgrep.pl, version 1.0, which scans for strings in a MySQL database.
Copyright (c) 2009 Jan Ploski (plosquare.com)
This software is licensed to you under Artistic License 2.0,
which is published at &lt;a href="http://www.perlfoundation.org/artistic_license_2_0"&gt;http://www.perlfoundation.org/artistic_license_2_0&lt;/a&gt;

Usage: dbgrep.pl [-u &amp;lt;db user&amp;gt;] [-h &amp;lt;db hostname&amp;gt;]
                 [-p] [-x] [-a] [-v] [-q]
                 &amp;lt;expr&amp;gt; &amp;lt;db name&amp;gt; [&amp;lt;table name&amp;gt; ...]
Switches: 
       -p a password is needed for authorization, read from console
       -x interpret &amp;lt;expr&amp;gt; as a Perl regular expression rather than string
       -a grep through all fields, not just text/(var)char/blob fields
       -v print matching values, not just table/field/primary key locations
       -q only print names of matching tables and fields
&lt;/pre&gt;
&lt;p&gt;Some invocation examples:&lt;/p&gt;&lt;pre&gt;
./dbgrep.pl -u dbuser -h localhost -p somestring dbname
./dbgrep.pl -u root -xaq '^123' dbname
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-1947474158267785152?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/1947474158267785152/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/05/grep-through-mysql-database.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1947474158267785152'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1947474158267785152'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/05/grep-through-mysql-database.html' title='Grep through a MySQL database'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-5566415105194789564</id><published>2009-05-20T22:14:00.005+02:00</published><updated>2009-05-20T22:23:58.611+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='Access'/><category scheme='http://www.blogger.com/atom/ns#' term='excel'/><category scheme='http://www.blogger.com/atom/ns#' term='vba'/><title type='text'>Solution for invisible "Save As" dialog in Access/Excel</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; While trying to &lt;a href="http://www.zmey.1977.ru/Access_To_Excel.htm"&gt;programmatically export data from an MS Access database to Excel&lt;/a&gt; displaying Excel's "Save As" dialog using code like &lt;code&gt;XL.Dialogs(xlDialogSaveAs).Show&lt;/code&gt; does not work correctly. This call might work in the VBA debug console, but in a production database the dialog (e.g. triggered by an event) would not appear on screen, although it would show up as process in task manager and furthermore block (freeze) the UI of your Access application. I originally observed this issue in Access 2007 and Windows Vista. At first I suspected threading or parent dialog's modality as causes, but that turned out wrong.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; A hint for solving this problem comes from an observation that it ceases to occur once the Visual Basic editor has been opened. Obviously, this is not going to happen in a production database, but it can be emulated for the purpose of working around the described problem. Just add the following (pointless looking) code to your database's initialization for the later dialog invocation to respond as expected:&lt;/p&gt;&lt;pre&gt;
    Dim ref As Reference
    For Each ref In Application.References
        If ref.Name = "" Then
            ' nothing needs to be done, just loop
        End If
    Next
&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-5566415105194789564?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/5566415105194789564/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/05/solution-for-invisible-save-as-dialog.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/5566415105194789564'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/5566415105194789564'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/05/solution-for-invisible-save-as-dialog.html' title='Solution for invisible &quot;Save As&quot; dialog in Access/Excel'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-4381791440137500603</id><published>2009-05-13T13:23:00.005+02:00</published><updated>2009-05-13T13:36:31.108+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><title type='text'>Speed up iteration over options of a SELECT element in MSIE</title><content type='html'>&lt;p&gt;&lt;strong&gt;Problem:&lt;/strong&gt; An application opens a new browser window in which the user makes some choice. That choice is used to select an OPTION within a SELECT element (combo box, list) in another browser window. The target SELECT contains many (thousands) of values. Because options are not sorted by value, finding the correct one using JavaScript requires a linear loop. This loop takes a very long time (8 seconds), especially in Internet Explorer (version 6 and 7).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; Avoid inter-frame access to the options list of a SELECT element. Instead, define the resource hungry loop within the same frame where the SELECT element is contained. This brings the execution time into the milliseconds range.&lt;/p&gt;
&lt;p&gt;I suspect that inter-frame communication in JavaScript triggers some sort of security/access control checks within the browser, accounting for the slowdowns.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-4381791440137500603?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/4381791440137500603/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/05/speed-up-iteration-over-options-of.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4381791440137500603'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4381791440137500603'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/05/speed-up-iteration-over-options-of.html' title='Speed up iteration over options of a SELECT element in MSIE'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-442098124378620886</id><published>2009-05-07T22:43:00.002+02:00</published><updated>2009-05-07T22:47:29.682+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='p2'/><category scheme='http://www.blogger.com/atom/ns#' term='howto'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='update site'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><title type='text'>Migrating Eclipse update sites to P2</title><content type='html'>&lt;p&gt;This article describes how to upgrade an Eclipse update site from the classic, pre-P2 layout (&lt;code&gt;site.xml&lt;/code&gt; together with &lt;code&gt;plugins&lt;/code&gt; and &lt;code&gt;features&lt;/code&gt; directories) to the "new and improved" (read: unnecessarily complicated) P2 layout. Considering that P2, the new update manager introduced in Eclipse 3.4, is backwards-compatible with the old layout of update sites (more or less), why bother at all? In my experience, browsing update sites with many plug-ins and features used to be somewhat slow before, but with P2 it has become excruciatingly slow. Fortunately, P2 also supports a new update site format designed to alleviate this problem, that is, to speed up update site browsing. However, as far as I know, no comprehensive documentation of the upgrade path for those of us still using the classic update site layout exists. More confusingly, the article &lt;a href="http://wiki.eclipse.org/Update_Site_Optimization"&gt;Update Site Optimization&lt;/a&gt; (coming from the source?) only tells half of the story. As I found out, following the somewhat outdated instructions listed in there won't leave you with a working update site. Information from different sources had to be pieced together. Hopefully, no longer, if you read on.&lt;/p&gt;
&lt;h2&gt;Classic update site vs. P2 update site&lt;/h2&gt;
&lt;p&gt;Let's start by comparing the classic update site layout with the newer layout tailored for P2. I suppose you have a working update site organized in the classic way and want to switch to the new P2 one for reasons mentioned above:&lt;/p&gt;
&lt;table border="1"&gt;
&lt;tr valign="top"&gt;
&lt;th&gt;Classic update site&lt;/th&gt;&lt;th&gt;P2 update site&lt;/th&gt;
&lt;/tr&gt;
&lt;tr&gt;&lt;td width="50%"&gt;update site = a world-accessible directory on a web server&lt;/td&gt;&lt;td width="50%"&gt;same as before&lt;/td&gt;&lt;/tr&gt;
&lt;tr valign="top"&gt;
&lt;td&gt;update site contains &lt;code&gt;site.xml&lt;/code&gt;, which contains a list of versioned features installable from this site&lt;/td&gt;
&lt;td&gt;update site contains &lt;code&gt;content.jar&lt;/code&gt; and &lt;code&gt;artifacts.jar&lt;/code&gt;, which together supersede &lt;code&gt;site.xml&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign="top"&gt;
&lt;td&gt;subdirectory &lt;code&gt;features&lt;/code&gt; contains one JAR file per versioned feature, referenced from &lt;code&gt;site.xml&lt;/code&gt;&lt;/td&gt;
&lt;td&gt;same as before, but the references now originate from &lt;code&gt;content.jar&lt;/code&gt; and &lt;code&gt;artifacts.jar&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign="top"&gt;
&lt;td&gt;subdirectory &lt;code&gt;plugins&lt;/code&gt; contains one JAR file per versioned plug-in, referenced from &lt;code&gt;feature.xml&lt;/code&gt; files contained in feature JARs&lt;/td&gt;
&lt;td&gt;subdirectory &lt;code&gt;plugins&lt;/code&gt; contains one .jar.pack.gz file per versioned plug-in, referenced from &lt;code&gt;feature.xml&lt;/code&gt; as before, but also from &lt;code&gt;content.jar&lt;/code&gt; and &lt;code&gt;artifacts.jar&lt;/code&gt;&lt;/td&gt;
&lt;/tr&gt;
&lt;tr valign="top"&gt;
&lt;td&gt;&amp;nbsp;&lt;/td&gt;
&lt;td&gt;update site (optionally?) contains &lt;code&gt;digest.zip&lt;/code&gt; - see description below&lt;/td&gt;
&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;While the syntax of &lt;code&gt;feature.xml&lt;/code&gt; and &lt;code&gt;site.xml&lt;/code&gt; is easy to understand and these files are easy to generate/process by your own tools if need be, the new &lt;code&gt;content.jar&lt;/code&gt; and &lt;code&gt;artifacts.jar&lt;/code&gt; leave no such hopes. You are advised to treat them as P2's private mess (found inside: fat, obscure XML documents). As you will see next, both these JAR files can and should be generated from a slightly modified version of &lt;code&gt;site.xml&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;The plug-in .jar.pack.gz files are also generated - each from the original plug-in JAR found of the classic update site. They are essentially JARs recursively compressed with the &lt;code&gt;pack200&lt;/code&gt; tool first introduced in Java 1.5.&lt;/p&gt;
&lt;p&gt;The old &lt;code&gt;site.xml&lt;/code&gt; and old plug-in JAR files are not ever accessed by the P2 update manager, but if you wish to stay backwards-compatible, you should keep them around in your update site as well.&lt;/p&gt;
&lt;p&gt;If you read the &lt;a href="http://wiki.eclipse.org/Update_Site_Optimization"&gt;Update Site Optimization&lt;/a&gt; article, you might be wondering whether a "digest" file (&lt;code&gt;digest.zip&lt;/code&gt;) is also needed, where to put it, and what for. I have never observed P2 trying to access this file and suspect that it has been superseded by the &lt;code&gt;content.jar&lt;/code&gt; and &lt;code&gt;artifacts.jar&lt;/code&gt; duo (which the original article fails to mention). However, it is worth noting that the official Eclipse Ganymede update site does contain a &lt;code&gt;digest.zip&lt;/code&gt; in the update site directory and an attribute &lt;code&gt;digestURL="http://download.eclipse.org/releases/ganymede/"&lt;/code&gt; on the &lt;code&gt;site&lt;/code&gt; element in &lt;code&gt;site.xml&lt;/code&gt; (the value of this attribute points to the update site, i.e. location of &lt;code&gt;digest.zip&lt;/code&gt;). We'll see next how to generate &lt;code&gt;digest.zip&lt;/code&gt; just in case.&lt;/p&gt;
&lt;h2&gt;How to upgrade to a P2 update site&lt;/h2&gt;
&lt;p&gt;In essence, the following steps are required:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Add a new attribute &lt;code&gt;pack200="true"&lt;/code&gt; to the &lt;code&gt;site&lt;/code&gt; element in &lt;code&gt;site.xml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Add a new attribute &lt;code&gt;digestURL="http://your/update/site/url/"&lt;/code&gt; to the &lt;code&gt;site&lt;/code&gt; element in &lt;code&gt;site.xml&lt;/code&gt;.&lt;/li&gt;
&lt;li&gt;Ensure that each of your feature JARs in the &lt;code&gt;features&lt;/code&gt; directory contains a &lt;code&gt;feature.properties&lt;/code&gt; file (which may be empty). (&lt;a href="/2009/05/solution-for-no-output-from.html"&gt;Here is why.&lt;/a&gt;)&lt;/li&gt;
&lt;li&gt;Generate a .jar.pack.gz file from each plug-in JAR file in the &lt;code&gt;plugins&lt;/code&gt; subdirectory.&lt;/li&gt;
&lt;li&gt;Generate &lt;code&gt;digest.zip&lt;/code&gt; based on the classic update site (including &lt;code&gt;site.xml&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Generate &lt;code&gt;content.jar&lt;/code&gt; and &lt;code&gt;artifacts.jar&lt;/code&gt; based on the classic update site (including &lt;code&gt;site.xml&lt;/code&gt;).&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Steps 1, 2, 3 do not require any sophistication and thus won't be further elaborated. Steps 4 and 5 are (surprisingly) intertwined, as described below. Step 6 is also described in the last section.&lt;/p&gt;
&lt;h3&gt;How to generate .jar.pack.gz files (Step 4) and &lt;code&gt;digest.zip&lt;/code&gt; (Step 5)&lt;/h3&gt;
&lt;p&gt;The generating .jar.pack.gz files step actually consists of two parts:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;For each JAR file in question, "condition" or "repack" the JAR file to prepare it for the second part.&lt;/li&gt;
&lt;li&gt;Generate .jar.pack.gz files from a set of conditioned JAR files. Also generate &lt;code&gt;digest.zip&lt;/code&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;In order to condition (repack) a JAR file, run:&lt;/p&gt;&lt;pre&gt;
$JAVA_HOME/bin/java \
    -jar $launcher \
    -application org.eclipse.update.core.siteOptimizer \
    -jarProcessor -verbose -processAll -repack -outputDir $output_dir/plugins \
    $input_jar_file
&lt;/pre&gt;
&lt;p&gt;The above invocation contains some variables, to be replaced as follows:&lt;/p&gt;
&lt;table border="1"&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;JAVA_HOME&lt;/code&gt;&lt;/td&gt;&lt;td&gt;path to where Java 1.5 (or newer) is installed&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;launcher&lt;/code&gt;&lt;/td&gt;&lt;td&gt;path to &lt;code&gt;plugins/org.eclipse.equinox.launcher_1.0.101.R34x_v20080819.jar&lt;/code&gt; from your Eclipse 3.4 (or newer) installation (the version number in the JAR file name may vary)&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;output_dir&lt;/code&gt;&lt;/td&gt;&lt;td&gt;path to where the conditioned JAR file should be written - the &lt;code&gt;plugins&lt;/code&gt; directory of the upgraded update site&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;&lt;code&gt;input_file&lt;/code&gt;&lt;/td&gt;&lt;td&gt;path to the input plug-in JAR file&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Sadly, you will have to run the above tool for each JAR file individually. Observe that
the conditioned JARs contain &lt;code&gt;META-INF/eclipse.inf&lt;/code&gt;, absent in unconditioned ones.
Finally, copy &lt;code&gt;site.xml&lt;/code&gt; and the &lt;code&gt;features&lt;/code&gt; subdirectory of your original
update site to &lt;code&gt;$output_dir&lt;/code&gt;. This completes part 1.&lt;/p&gt;
&lt;p&gt;For part 2, run the following command. Unlike part 1, this processes all the conditioned JARs
it can find through &lt;code&gt;site.xml&lt;/code&gt;:&lt;/p&gt;&lt;pre&gt;
$JAVA_HOME/bin/java \
    -jar $launcher \
    -application org.eclipse.update.core.siteOptimizer \
    -digestBuilder \
    -digestOutputDir=$output_dir \
    -siteXML=$output_dir/site.xml \\
    -jarProcessor -pack -outputDir $output_dir $output_dir
&lt;/pre&gt;
&lt;p&gt;You should now have &lt;code&gt;$output_dir/plugins&lt;/code&gt; full of conditioned JARs and a corresponding .pack.jar.gz for each of them. You should also have &lt;code&gt;$output_dir/digest.zip&lt;/code&gt;. If not, maybe you forgot to take care of &lt;code&gt;feature.properties&lt;/code&gt; in Step 3 mentioned earlier.&lt;/p&gt;
&lt;h3&gt;How to generate &lt;code&gt;content.jar&lt;/code&gt; and &lt;code&gt;artifacts.jar&lt;/code&gt; (Step 6)&lt;/h3&gt;
&lt;p&gt;Here is how (set a human readable &lt;code&gt;$project_name&lt;/code&gt; first):&lt;/p&gt;&lt;pre&gt;
$JAVA_HOME/bin/java -jar $launcher \
    -application org.eclipse.equinox.p2.metadata.generator.EclipseGenerator \
    -updateSite ${output_dir}/ \
    -site file:${output_dir}/site.xml \
    -metadataRepository file:${output_dir}/ \
    -metadataRepositoryName "${project_name} Update Site" \
    -artifactRepository file:${output_dir}/ \
    -artifactRepositoryName "${project_name} Artifacts" \
    -compress \
    -reusePack200Files \
    -noDefaultIUs \
    -vmargs -Xmx256M
&lt;/pre&gt;
&lt;h2&gt;The final result&lt;/h2&gt;
&lt;p&gt;Here is an example P2-upgraded, backwards compatible update site with 1 feature version:&lt;p&gt;
&lt;pre&gt;
.
|-- site.xml
|-- artifacts.jar
|-- content.jar
|-- digest.zip
|-- features
|   `-- org.epic.feature.main_0.6.34.jar
`-- plugins
    |-- org.epic.debug_0.6.27.jar
    |-- org.epic.debug_0.6.27.jar.pack.gz
    |-- org.epic.doc_0.6.2.jar
    |-- org.epic.doc_0.6.2.jar.pack.gz
    |-- org.epic.lib_0.6.1.jar
    |-- org.epic.lib_0.6.1.jar.pack.gz
    |-- org.epic.perleditor_0.6.23.jar
    |-- org.epic.perleditor_0.6.23.jar.pack.gz
    |-- org.epic.regexp_0.6.1.jar
    |-- org.epic.regexp_0.6.1.jar.pack.gz
    |-- org.epic.source_0.6.34.jar
    `-- org.epic.source_0.6.34.jar.pack.gz
&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;site.xml&lt;/code&gt; contains the two new attributes, &lt;code&gt;pack200&lt;/code&gt; and &lt;code&gt;digestURL&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;P2 will access &lt;code&gt;content.jar&lt;/code&gt;, &lt;code&gt;artifacts.jar&lt;/code&gt;, &lt;code&gt;org.epic.feature.main_0.6.34.jar&lt;/code&gt; and the plug-in  &lt;code&gt;.jar.pack.gz&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;The classic update manager will access &lt;code&gt;site.xml&lt;/code&gt;, &lt;code&gt;org.epic.feature.main_0.6.34.jar&lt;/code&gt; and the plug-in &lt;code&gt;.jar&lt;/code&gt; files.&lt;/p&gt;
&lt;p&gt;Automation of the above process using Ant scripts, predefined Eclipse tasks or some such is left as an exercise for the reader. Also note another post with hints on &lt;a href="/2009/05/debugging-eclipse-helper-applications.html"&gt;how to debug helper applications started by the Eclipse launcher&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-442098124378620886?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/442098124378620886/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/05/migrating-eclipse-update-sites-to-p2.html#comment-form' title='8 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/442098124378620886'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/442098124378620886'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/05/migrating-eclipse-update-sites-to-p2.html' title='Migrating Eclipse update sites to P2'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>8</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-259159017497342467</id><published>2009-05-07T14:34:00.001+02:00</published><updated>2009-05-07T14:37:09.738+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='startup.jar'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><title type='text'>Debugging Eclipse helper applications</title><content type='html'>&lt;p&gt;Eclipse includes some command-line tools for platform developers, which can be launched by providing the &lt;code&gt;-application&lt;/code&gt; option to the Equinox launcher JAR (&lt;a href="/2009/05/solution-for-missing-startupjar-in.html"&gt;formerly &lt;code&gt;startup.jar&lt;/code&gt;&lt;/a&gt;). A need may arise to debug one of those helper applications. Proceed as follows:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Import the plug-in &lt;code&gt;org.eclipse.equinox.launcher&lt;/code&gt; into workspace (with source code).&lt;/li&gt;
&lt;li&gt;Import the plug-in containing the helper application into workspace (with source code). For example: &lt;code&gt;org.eclipse.update.core&lt;/code&gt; contains the siteOptimizer application.&lt;/li&gt;
&lt;li&gt;Create a debug configuration for the Java class &lt;code&gt;org.eclipse.equinox.launcher.Main&lt;/code&gt;. Specify &lt;code&gt;-Dosgi.install.area=&lt;em&gt;/path/to/eclipse/installation/dir&lt;/em&gt;&lt;/code&gt; as a VM argument. Specify the remaining command-line arguments (e.g. &lt;code&gt;-application ...&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;Set a breakpoint at the entry point to the helper application (likely a method called &lt;code&gt;run&lt;/code&gt; or &lt;code&gt;start&lt;/code&gt;).&lt;/li&gt;
&lt;li&gt;If the debugger asks for a source code location, point it to the workspace project.&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-259159017497342467?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/259159017497342467/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/05/debugging-eclipse-helper-applications.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/259159017497342467'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/259159017497342467'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/05/debugging-eclipse-helper-applications.html' title='Debugging Eclipse helper applications'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-3442320951377991241</id><published>2009-05-07T14:32:00.001+02:00</published><updated>2009-05-07T14:33:31.994+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='startup.jar'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><title type='text'>Solution for missing startup.jar in Eclipse</title><content type='html'>&lt;p&gt;Some articles, for example &lt;a href="http://wiki.eclipse.org/Update_Site_Optimization"&gt;Update Site Optimization&lt;/a&gt;, mention a file &lt;code&gt;startup.jar&lt;/code&gt; used for launching Eclipse's helper command-line applications. However, this file no longer exists in newer versions of Eclipse (such as 3.4).&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; just replace references to the missing &lt;code&gt;startup.jar&lt;/code&gt; with references to &lt;code&gt;$ECLIPSE_HOME/plugins/org.eclipse.equinox.launcher_&lt;em&gt;version&lt;/em&gt;.jar&lt;/code&gt;
(Insert the actual &lt;em&gt;version&lt;/em&gt; found in your &lt;code&gt;plugins&lt;/code&gt; directory).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-3442320951377991241?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/3442320951377991241/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/05/solution-for-missing-startupjar-in.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3442320951377991241'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3442320951377991241'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/05/solution-for-missing-startupjar-in.html' title='Solution for missing startup.jar in Eclipse'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-8111007740982545939</id><published>2009-05-07T14:08:00.005+02:00</published><updated>2009-05-22T22:48:32.699+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='siteOptimizer'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='update site'/><category scheme='http://www.blogger.com/atom/ns#' term='digestBuilder'/><title type='text'>Solution for no output from digestBuilder in Eclipse siteOptimizer</title><content type='html'>&lt;p&gt;The article &lt;a href="http://wiki.eclipse.org/Update_Site_Optimization"&gt;Update Site Optimization&lt;/a&gt; provides instructions for creating optimized update sites in Eclipse to speed up downloads. While following those instructions, you may run into a problem that the digestBuilder component seems to run (displaying lines such as "Extracting locales from &lt;em&gt;feature jar&lt;/em&gt;" and "Processing... &lt;em&gt;feature jar&lt;/em&gt;"), but no output is ever written to the specified &lt;code&gt;digestOutputDir&lt;/code&gt;. Alternatively, &lt;code&gt;digest.zip&lt;/code&gt; might be created,
but missing some features.&lt;/p&gt;
&lt;p&gt;&lt;strong&gt;Solution:&lt;/strong&gt; make sure that each of your feature JAR files contains a file named &lt;code&gt;feature.properties&lt;/code&gt; (even an empty one). As of Eclipse 3.4, features without this file are skipped altogether by the digestBuilder.&lt;/p&gt;
&lt;p&gt;Further reading: &lt;a href="/2009/05/debugging-eclipse-helper-applications.html"&gt;Debugging Eclipse helper applications&lt;/a&gt; and the &lt;a href="/2009/05/migrating-eclipse-update-sites-to-p2.html"&gt;tutorial on how to optimize an Eclipse update site for P2&lt;/a&gt;.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-8111007740982545939?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/8111007740982545939/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/05/solution-for-no-output-from.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8111007740982545939'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/8111007740982545939'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/05/solution-for-no-output-from.html' title='Solution for no output from digestBuilder in Eclipse siteOptimizer'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-6300503045672785451</id><published>2009-04-23T21:34:00.010+02:00</published><updated>2009-05-22T11:07:03.916+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='charset'/><category scheme='http://www.blogger.com/atom/ns#' term='utf8'/><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='encoding'/><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><title type='text'>Viewing internal representation of strings in Perl</title><content type='html'>&lt;p&gt;This article explains how to debug encoding problems arising from a wrong interpretation of the byte sequence stored as the internal representation of a string variable in Perl 5.8.x. Such problems can manifest themselves as invalid characters being printed or stored to a database under a variety of circumstances. The &lt;a href="http://perldoc.perl.org/utf8.html"&gt;official Perl utf8 documentation&lt;/a&gt; explains how Perl handles string values behind the scenes, and experts advise to &lt;a href="http://use.perl.org/~miyagawa/journal/35700#"&gt;disregard the internal representation&lt;/a&gt; and just trust Perl to do the right thing (see also these &lt;a href="http://juerd.nl/site.plp/perluniadvice"&gt;recommendations on dealing with Unicode in Perl&lt;/a&gt;). While founded on good premises of early prevention, the ideal approach falls short when you have to diagnose real-life problems.&lt;/p&gt;&lt;p&gt;Here is what you need to know:&lt;/p&gt;&lt;ul&gt;&lt;li&gt;The value of every string variable is internally stored by Perl in a data structure with two fields: a sequence of bytes (called the "internal representation"), and a flag (called the "utf8 flag") indicating how the internal representation should be interpreted by functions that deal with &lt;em&gt;characters&lt;/em&gt; (such as &lt;code&gt;print&lt;/code&gt;, &lt;code&gt;substr&lt;/code&gt;, and &lt;code&gt;length&lt;/code&gt;).&lt;/li&gt;&lt;li&gt;If the flag is set, the internal representation is interpreted as a UTF-8 encoded string. If the flag is not set, the same byte sequence is interpreted as an ISO-8859-1 encoded (Latin1) string.&lt;/li&gt;&lt;li&gt;Problems arise when the flag for some reason does not match the actual encoding used in the internal representation.&lt;/li&gt;&lt;/ul&gt;&lt;p&gt;The official documentation explains how to determine the flag's current value: &lt;code&gt;utf8::is_utf8($string)&lt;/code&gt; or, if you want to remain compatible with Perl 5.8.0, &lt;code&gt;Encode::is_utf8($string)&lt;/code&gt;. But it does not reveal how to view the internal representation. There are several ways that &lt;em&gt;do not work&lt;/em&gt;, such as &lt;code&gt;Data::Dumper&lt;/code&gt; and a straightforward &lt;code&gt;unpack('C*', $string)&lt;/code&gt;. The solution is a combination of &lt;code&gt;use bytes&lt;/code&gt; and &lt;code&gt;unpack&lt;/code&gt;, as illustrated by the following sample code:&lt;/p&gt;&lt;pre&gt;
use Encode;

my $hash = { key1 =&amp;gt; 'ä', key2 =&amp;gt; 'ä' };

# Convert the internal representation from latin1 to utf8.
# This also turns on the utf8 flag on the value:
utf8::upgrade($hash-&amp;gt;{key2});

print "str1: " . hexdump($hash-&amp;gt;{key1}) . "\n";
print "str2: " . hexdump($hash-&amp;gt;{key2}) . "\n";

if ($str1 eq $str2)
{
    print "str1 eq str2\n";
}

# Note that the output of Data::Dumper is not helpful:
use Data::Dumper;
print Dumper($hash);

# For a given string parameter, returns a string which shows
# whether the utf8 flag is enabled and a byte-by-byte view
# of the internal representation.
#
sub hexdump
{
    my $str = shift;
    my $flag = Encode::is_utf8($str) ? 1 : 0;
&lt;b&gt;    use bytes; # this tells unpack to deal with raw bytes
    my @internal_rep_bytes = unpack('C*', $str);&lt;/b&gt;
    return
        $flag
        . '('
        . join(' ', map { sprintf("%02x", $_) } @internal_rep_bytes)
        . ')';
}
&lt;/pre&gt;&lt;p&gt;The correct encoding for the Unicode character No. 0x00E4 "latin small letter a with diaeresis", also known as "a umlaut" (&amp;auml;) happens to be 0xE4 in ISO-8859-1 and 0xC3 0xA4 in UTF-8. Note that &lt;code&gt;Data::Dumper&lt;/code&gt; displays the &lt;em&gt;Unicode character number&lt;/em&gt; "\x{e4}" (which coincides with ISO-8859-1) rather than the internal &lt;em&gt;byte&lt;/em&gt; representation. The program's output is shown below:&lt;/p&gt;&lt;pre&gt;
str1: 0(e4)
str2: 1(c3 a4)
str1 eq str2
$VAR1 = {
          'key1' =&amp;gt; 'ä'
          'key2' =&amp;gt; "\x{e4}",
        };
&lt;/pre&gt;&lt;p&gt;Once you determine that the utf8 flag is set incorrectly (mismatching the internal representation) your next questions are surely going to be:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;Why is the flag set the wrong way?&lt;/li&gt;
&lt;li&gt;How do I fix the flag - &lt;em&gt;without also modifying the bytes of the internal representation&lt;/em&gt;?&lt;/li&gt;&lt;/ol&gt;&lt;p&gt;You should first attempt to answer the first question, by examining your code (and very likely also the code of used modules). The second question essentially asks for "quick and dirty" workarounds, given that the various functions provided by the &lt;code&gt;utf8&lt;/code&gt; and &lt;code&gt;Encode&lt;/code&gt; modules &lt;em&gt;do&lt;/em&gt; alter the internal representation as their intended effect. Nothing is impossible in Perl, however:&lt;/p&gt;&lt;dl&gt;
&lt;dt&gt;Unset the utf8 flag&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;{ use bytes; $str = pack('C*', unpack('C*', $str); }&lt;/code&gt;&lt;/dd&gt;
&lt;dt&gt;Set the utf8 flag&lt;/dt&gt;
&lt;dd&gt;&lt;code&gt;$str = pack("U0C*", unpack("C*", $str));&lt;/code&gt;&lt;/dd&gt;
&lt;/dl&gt;
&lt;p&gt;Checking that the internal representation and the value of the utf8 flag are synchronized is the basic first step in troubleshooting encoding problems in Perl. It ensures the consistency of data manipulated in memory. New pitfalls lie where the data has to leave the process, being subject to character encoding conversions in input/output operations (e.g. communicating with a database). However, that's another can of worms...&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-6300503045672785451?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/6300503045672785451/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/04/viewing-internal-representation-of.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6300503045672785451'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6300503045672785451'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/04/viewing-internal-representation-of.html' title='Viewing internal representation of strings in Perl'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-3486268656886540960</id><published>2009-04-15T23:24:00.005+02:00</published><updated>2009-04-27T23:33:51.926+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mod_perl'/><category scheme='http://www.blogger.com/atom/ns#' term='configuration'/><category scheme='http://www.blogger.com/atom/ns#' term='howto'/><category scheme='http://www.blogger.com/atom/ns#' term='tutorial'/><category scheme='http://www.blogger.com/atom/ns#' term='EPIC'/><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><title type='text'>Debugging mod_perl with EPIC</title><content type='html'>&lt;p&gt;This article describes how to debug mod_perl applications using &lt;a href="http://www.epic-ide.org"&gt;EPIC&lt;/a&gt; and explains some of the current shortcomings. The functionality was tested with EPIC version 0.6.33, &lt;a href="http://perl.apache.org"&gt;mod_perl&lt;/a&gt; version 2.0.4, and &lt;a href="http://search.cpan.org/~fwiles/Apache-DB-0.14/DB.pm"&gt;Apache::DB&lt;/a&gt; version 0.14. It is important to note that while the EPIC debugger frontend is fairly stable and debugging mod_perl applications in principle is not very different from debugging remote Perl scripts, at the time of writing there is little experience with EPIC and mod_perl in particular. Furthermore, the procedures described here will appear a little kludgy, as they overload the already existing remote debugging functionality to serve mod_perl debugging. You should expect to invest some time into troubleshooting before you arrive at a working configuration.&lt;/p&gt;
&lt;h2&gt;Set up Apache::DB without EPIC&lt;/h2&gt;
&lt;p&gt;The first step is to make sure that the interactive command-line debugger works as expected (without EPIC). You can find a more detailed description of &lt;code&gt;Apache::DB&lt;/code&gt; in CPAN and in mod_perl documentation. In the following, I assume that you have an example Apache site configured as follows:&lt;/p&gt;&lt;pre&gt;
Alias /modperl/ /home/jpl/modperl/
    
PerlModule ModPerl::Registry
&amp;lt;IfDefine PERLDB&amp;gt;
    PerlRequire /home/jpl/modperl/db.pl
&amp;lt;/IfDefine&amp;gt;
PerlPostConfigRequire /home/jpl/modperl/startup.pl

&amp;lt;Location /modperl/&amp;gt;
    SetHandler perl-script
    PerlHandler ModPerl::Registry
    &amp;lt;IfDefine PERLDB&amp;gt;
        PerlFixupHandler Apache::DB
    &amp;lt;/IfDefine&amp;gt;
    Options +ExecCGI
    PerlSendHeader On
    allow from 127.0.0.1
    deny from all
&amp;lt;/Location&amp;gt;
&lt;/pre&gt;&lt;p&gt;The file &lt;code&gt;/home/jpl/modperl/db.pl&lt;/code&gt; referenced in the above configuration contains:&lt;/p&gt;&lt;pre&gt;
use APR::Pool (); 
use Apache::DB (); 
Apache::DB-&amp;gt;init();
&lt;/pre&gt;&lt;p&gt;The file &lt;code&gt;/home/jpl/modperl/startup.pl&lt;/code&gt; contains whatever is needed to initialize the environment for your mod_perl scripts. For example, we should define @INC there:&lt;/p&gt;&lt;pre&gt;
use lib qw(/home/jpl/modperl/lib);
1;
&lt;/pre&gt;&lt;p&gt;Apache::DB requires that Apache is started with the -X (single-process) option. I use &lt;code&gt;apache2ctl -k start -X -DPERLDB&lt;/code&gt;, which leaves the Apache process running in the foreground and enables the debugging-related directives by defining the &lt;code&gt;PERLDB&lt;/code&gt; token. Whenever a script &lt;code&gt;/modperl/*.pl&lt;/code&gt; is loaded in the browser, the terminal running &lt;code&gt;apache2ctl&lt;/code&gt; gets an interactive debugger prompt. So far, this is all more or less standard mod_perl configuration, no EPIC involved.&lt;/p&gt;                                                                                                                                                                      &lt;h2&gt;Create a "Perl Remote" debug configuration in EPIC&lt;/h2&gt;                                                                                                                                            &lt;p&gt;In Eclipse, open the dialog Run/Debug Configurations... to create a launch configuration of type "Perl Remote" with the following settings:&lt;/p&gt;                                                     &lt;table&gt;
&lt;tr&gt;&lt;td&gt;Project&lt;/td&gt;&lt;td&gt;The Perl project containing your scripts and modules.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;File to execute&lt;/td&gt;&lt;td&gt;It doesn't matter - choose any script from the project.&lt;/td&gt;&lt;/tr&gt;                                                                                                      &lt;tr&gt;&lt;td&gt;Local Host IP&lt;/td&gt;&lt;td&gt;It doesn't matter - enter 127.0.0.1.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Target Host Project Installation Path&lt;/td&gt;&lt;td&gt;Enter the file system path on the remote machine under which your Perl project is deployed (eg. &lt;code&gt;/home/jpl/modperl&lt;/code&gt;). If you are running Apache on the same machine as Eclipse, enter the path displayed in Project Properties as "Location". If you notice that breakpoints are ignored, this is a key setting to tweak, more on that later.&lt;/td&gt;&lt;/tr&gt; 
&lt;tr&gt;&lt;td&gt;Port&lt;/td&gt;&lt;td&gt;This is the port on which EPIC is going to listen for a connection from the debugger running on the target Apache host. Enter a port number that is not firewalled (in case of doubt, test connectivity with netcat or the like!). Default is 5000.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Capture Output&lt;/td&gt;&lt;td&gt;It has to be unchecked, as you don't want EPIC to attempt hijacking of STDOUT and STDERR of your scripts!&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Create Debug Package&lt;/td&gt;&lt;td&gt;It should be checked on the first invocation (see below). It may be unchecked later on.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td&gt;Debug Package File Path&lt;/td&gt;&lt;td&gt;Enter the path to a new file that EPIC will create locally when the debugging session is started, for example: &lt;code&gt;/tmp/epicdb.zip&lt;/code&gt;. This ZIP file will contain a custom version of &lt;code&gt;perl5db.pl&lt;/code&gt;, which must be used instead of the &lt;code&gt;Apache/perl5db.pl&lt;/code&gt; in order to support setting breakpoints (it refers to EPIC's &lt;code&gt;epic_breakpoints.pm&lt;/code&gt; module, also included in the ZIP file). More on that in the next section.&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;Run the debug configuration. You should see a Remote Perl Script item in the Debug view (switch to the Debug perspective if necessary). Also, on disk, the ZIP archive mentioned above should now be available. Terminate the debugger session now, as we are not done with adjusting the configuration yet.&lt;/p&gt;
&lt;h2&gt;Install the EPIC helper modules on the target host&lt;/h2&gt;
&lt;p&gt;Copy the ZIP archive created in the previous step to the Apache host, create a new directory there and unpack the ZIP archive into it. You will notice that all project files are contained in it. However, you should only leave
&lt;code&gt;*epic*.pm&lt;/code&gt; and &lt;code&gt;perl5db.pl&lt;/code&gt; in the target directory - remove all other files and subdirectories.&lt;/p&gt;
&lt;h2&gt;Redirect Apache::DB to contact EPIC&lt;/h2&gt;
&lt;p&gt;Edit your Apache configuration like so:&lt;/p&gt;
&lt;pre&gt;
Alias /modperl/ /home/jpl/modperl/

PerlModule ModPerl::Registry
&amp;lt;IfDefine PERLDB&amp;gt;
    &lt;b&gt;PerlSetEnv PERLDB_OPTS "RemotePort=127.0.0.1:5000 DumpReused ReadLine=0 PrintRet=0"
    PerlSetEnv PERL5DB "BEGIN { $DB::CreateTTY=0; require '/path/to/EPIC/provided/perl5db.pl'; }"&lt;/b&gt;
    PerlRequire /home/jpl/modperl/db.pl
&amp;lt;/IfDefine&amp;gt;
PerlPostConfigRequire /home/jpl/modperl/startup.pl

&amp;lt;Location /modperl/&amp;gt;
    SetHandler perl-script
    PerlHandler ModPerl::Registry
    &amp;lt;IfDefine PERLDB&amp;gt;
        PerlFixupHandler Apache::DB
    &amp;lt;/IfDefine&amp;gt;
    Options +ExecCGI
    PerlSendHeader On
    allow from 127.0.0.1
    deny from all
&amp;lt;/Location&amp;gt;
&lt;/pre&gt;
&lt;p&gt;&lt;code&gt;/path/to/EPIC/provided/perl5db.pl&lt;/code&gt; should be adjusted to point to where you unpacked the ZIP archive.&lt;/p&gt;
&lt;p&gt;&lt;code&gt;RemotePort&lt;/code&gt; should be adjusted to include the IP address and port where Eclipse/EPIC are listening for debugger connections.&lt;/p&gt;
&lt;h2&gt;Edit db.pl to include EPIC helper modules&lt;/h2&gt;
&lt;p&gt;Add the directory in which &lt;code&gt;perl5db.pl&lt;/code&gt; and &lt;code&gt;epic_breakpoints.pm&lt;/code&gt; are located to @INC. I suggest that you edit the file &lt;code&gt;db.pl&lt;/code&gt;, which was mentioned above:&lt;/p&gt;&lt;pre&gt;
&lt;b&gt;use lib qw(/path/to/EPIC/provided);&lt;/b&gt;
use APR::Pool ();
use Apache::DB ();
Apache::DB-&amp;gt;init();
&lt;/pre&gt;
&lt;h2&gt;Test debugging without breakpoints&lt;/h2&gt;
&lt;p&gt;In EPIC Preferences, enable the "suspend at first line" option. Launch the Remote Perl debug configuration created earlier. On the target host, restart Apache in single-process mode. Load one of your scripts in the browser. You should see in EPIC that the debugger suspends in one of the handler modules (eg. &lt;code&gt;Apache::Registry&lt;/code&gt;). From here on, you could step through the code to eventually enter your scripts and modules. Resume execution, try reloading the script or loading another script. EPIC should suspend again. If this doesn't work, there is no sense of going further. For troubleshooting, you can enable Perl debugger console in EPIC Preferences.&lt;/p&gt;
&lt;p&gt;The proper way to terminate the debugging session is stop the Apache process. EPIC should detect it and terminate the session automatically. If you attempt terminating the session from EPIC, you may get into trouble and may have to kill the Apache process with &lt;code&gt;kill -9&lt;/code&gt; later. In case of problems, make sure that you have no runaway Apache processes. The relative order of starting the EPIC debugging session and the Apache process doesn't matter. Obviously, before you run a script to be debugged by making a request in the browser, both the EPIC debugging session and the Apache process must be running.&lt;/p&gt;
&lt;h2&gt;Test breakpoints in your own modules&lt;/h2&gt;
&lt;p&gt;Do not set breakpoints in scripts just yet. Set a breakpoint in one of your own modules and check whether EPIC properly suspends on it. If the breakpoint is ignored, check the file &lt;code&gt;workspace/metadata/.log&lt;/code&gt; for warnings like "Could not map local path ... to a remote path". If they appear for the file in which you set breakpoints, you should try adjusting "Target Host Project Installation Path" in the Perl Remote launch configuration. Watch out for possible path variations due to symlinks. Essentially, the path entered in the launch configuration should correspond to the path used by Perl internally to refer to files on the remote host. You can see those internal paths if you enable debugger console in EPIC preferences and step through your code. For example, I had trouble with breakpoints being ignored due to the Perl debugger referring to files as &lt;code&gt;/home/jpl/modperl/hello.pl&lt;/code&gt;, while the location displayed in file properties in EPIC was &lt;code&gt;/mnt/data/home/jpl/modperl/hello.pl&lt;/code&gt;. The project's include path configured in EPIC and the actual include path on the remote host are also used in mappings. As a general rule, the less the remote host differs from the development machine on which EPIC is running, the better.&lt;/p&gt;
&lt;h2&gt;Test breakpoints in scripts&lt;/h2&gt;
&lt;p&gt;Breakpoints in scripts don't work out-of-the-box with mod_perl and EPIC. The reason is that EPIC gets no opportunity to actually set breakpoints in scripts, which are loaded by mod_perl in a custom way and converted dynamically into packages. Fortunately, there is a little trick which allows suspending in scripts. Edit the target script to include &lt;code&gt;$DB::single = 1;&lt;/code&gt; immediately before the line on which you want to suspend. Also don't forget to set an EPIC breakpoint as usual, like so:&lt;/p&gt;&lt;pre&gt;&lt;b&gt;$DB::single = 1;&lt;/b&gt;
print "suspend before print\n"; # set EPIC breakpoint on this line!
&lt;/pre&gt;&lt;p&gt;The debugger should now suspend on the correct line.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-3486268656886540960?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/3486268656886540960/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/04/debugging-modperl-applications-with.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3486268656886540960'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3486268656886540960'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/04/debugging-modperl-applications-with.html' title='Debugging mod_perl with EPIC'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-7718378344862516480</id><published>2009-03-15T15:04:00.001+01:00</published><updated>2009-03-15T15:07:18.247+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='open source'/><category scheme='http://www.blogger.com/atom/ns#' term='footer'/><category scheme='http://www.blogger.com/atom/ns#' term='work attitude'/><category scheme='http://www.blogger.com/atom/ns#' term='mailman'/><category scheme='http://www.blogger.com/atom/ns#' term='attachment'/><title type='text'>Workaround for Mailman list footer sent as attachment</title><content type='html'>&lt;p&gt;Summary: The popular open source mailing list software GNU Mailman does not play well with the popular mail client Thunderbird running under the German version of the popular operating system Windows. When you send a message to a Mailman-managed list, the list footer may be included as an attachment ("MIME part") rather than appended to the message. Mailman developers deny responsibility for (solving) the problem.&lt;/p&gt;
&lt;p&gt;The short story is that even if your mail client sends a &lt;em&gt;plain text&lt;/em&gt; message, but it happens to be in a different character encoding than the ISO-8859-1 encoding expected by Mailman, the footer will be attached improperly. For German Thunderbird/Windows, you can change the default encoding for outgoing messages from ISO-8859-15 (German with Euro) to ISO-8859-1 (German) to work around this problem. The option that you should change is "mailnews.send_default_charset"
in Advanced preferences (also accessible through about:config).&lt;/p&gt;
&lt;p&gt;The problem is not solved once and for all by the above workaround, as it depends on the configuration of each sender's mail client (but not each recipient's mail client). However, the workaround is sufficient if you use the mailing list for one-to-many communication.&lt;/p&gt;
&lt;p&gt;I'll spend the rest of this article dissecting the problem on a more general level, as it seems like a nice example for discussing some implicit differences between the worlds of professional and amateur software development (aka work vs. play).&lt;/p&gt; 
&lt;p&gt;The problem is a special case of an old and well-understood issue, &lt;a href="http://wiki.list.org/pages/viewpage.action?pageId=4030707"&gt;elaborated at length in the FAQ by the Mailman developers&lt;/a&gt;. Unfortunately, the four solutions proposed there are all unsatisfactory (my comments in italic):&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Configure Mailman to remove the msg_header/msg_footer. &lt;em&gt;But I want this footer to appear! Essential feature!&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Configure Mailman to strip all HTML and MIME formatting and send out all messages as text-only (text/plain). &lt;em&gt;I've already done that, and it didn't help.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Get everyone to change the MUA they use to one that is more HTML/MIME-aware. &lt;em&gt;Yeah, dream on.&lt;/em&gt;&lt;/li&gt;
&lt;li&gt;Live with the problem. &lt;em&gt;Come on guys, is attaching a few lines of ASCII text to a text-only email really so difficult?&lt;/em&gt;&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;The answer to the last question might be: "Yes, if you want to make it work universally". But users don't care about what works universally, they care what works for them, out of the box. The developers' standpoint is an example of a more general behavioral pattern common among open source programmers (as I know well from my own experience as the volunteer maintainer of &lt;a href="http://www.epic-ide.org"&gt;EPIC&lt;/a&gt;). What we, the developers, are saying is, in order
of increasing detail:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Don't bother me.&lt;/li&gt;
&lt;li&gt;Works as designed!&lt;/li&gt;
&lt;li&gt;You have no idea how complicated this code is and how much time we spent on it &lt;em&gt;already&lt;/em&gt;.&lt;/li&gt;
&lt;li&gt;Solving this "little" problem of yours would break &lt;i&gt;n&lt;/i&gt; different exotic use cases of which you're completely unaware.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;Of course it would be more up-front to say:&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;Oops, we screwed up. But we are lazy/not in mood to fix it for &lt;em&gt;you&lt;/em&gt;. If you don't like it, find a better product/service. Sorry.&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;...which is quite a legitimate reply for a development-is-my-hobby-and-you-are-not-my-customer type of project.&lt;/p&gt;
&lt;p&gt;In a professional setting, the developers answer to the paying client. Either a fix or a &lt;em&gt;satisfactory&lt;/em&gt; workaround &lt;em&gt;must&lt;/em&gt; be delivered on time, no matter whether it seems convenient and aesthetically pleasing to the developers and whether the blame for the problem lies in our code or elsewhere. We are hired to shield our clients from this kind of deliberation and the only factor that we might call to our rescue is whether the client's request seems to be a valid business requirement without conflict with her other priorities. Being able to send mails using Thunderbird and see footers properly attached by Malman certainly meets these criteria.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-7718378344862516480?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/7718378344862516480/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/03/workaround-for-mailman-list-footer-sent.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7718378344862516480'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7718378344862516480'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/03/workaround-for-mailman-list-footer-sent.html' title='Workaround for Mailman list footer sent as attachment'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-4595713963470682474</id><published>2009-03-05T15:07:00.002+01:00</published><updated>2009-05-07T22:56:18.076+02:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='SOAP::Lite'/><category scheme='http://www.blogger.com/atom/ns#' term='SOAP'/><category scheme='http://www.blogger.com/atom/ns#' term='Perl'/><category scheme='http://www.blogger.com/atom/ns#' term='serialization'/><category scheme='http://www.blogger.com/atom/ns#' term='programming style'/><category scheme='http://www.blogger.com/atom/ns#' term='debugging'/><title type='text'>Debugging serialization in SOAP::Lite</title><content type='html'>&lt;p&gt;Scenario: you are using the Perl &lt;code&gt;SOAP::Lite&lt;/code&gt; module as a SOAP client, and want to invoke a web service operation which accepts a complex data type. You provide SOAP::Lite with an appropriately structured SOAP::Data object that represents the value of your type. However, on the server side you notice that xsi:nil is transmitted instead of the value.&lt;/p&gt;
&lt;p&gt;The solution is to define a seralizer method for your complex type, like so:&lt;/p&gt;&lt;pre&gt;
sub SOAP::Serializer::as_name_of_your_complex_type
{
    my $self = shift;
    my $value = shift;
    my $name = shift;
    my $type = shift;
    my $attr = shift;

    return [$name, { 'xsi:type' =&amp;gt; 'tns:name_of_your_complex_type', %$attr }, $value];
}&lt;/pre&gt;
&lt;p&gt;The rest of this post elaborates how one would arrive at this solution and provides a short study of SOAP::Lite internals.&lt;/p&gt;
&lt;h2&gt;The perils of understanding SOAP::Lite&lt;/h2&gt;
&lt;p&gt;Debugging SOAP::Lite is not easy. In fact, I bet that the author of SOAP::Lite does not use a debugger himself. Apparently, he didn't spend a lot of time pondering the cruelty to his readers inflicted by a whole arsenal of Perl tricks. Let me enumerate several issues:&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;The code is long. This reduces your chances of finding a portion relevant to your problem by the sheer luck of skimming over it.&lt;/li&gt;
&lt;li&gt;It heavily relies on Perl automatic (predefined) variables. Your memory will be severely strained if you try to read this code. The author possibly assumes that Perl is your only language and mother tongue, too.&lt;/li&gt;
&lt;li&gt;It uses generated code and late initialization to great extent. Of course, you can't set breakpoints in the generated code. It's also rather difficult to SEE the generated code in all its glory, for this requires you to first establish the numerous points of its generation. You will also get dizzy from the frequent back-and-forth transfers of control between subroutines that implement actual functionality and generic "internal plumbing" subroutines that have little to do with the main thread of execution. Forget stepping through SOAP::Lite code. At best, you can stumble through it.&lt;/li&gt;
&lt;li&gt;It uses nested expressions a lot - long live functional programming! Too bad that it defeats the idea of setting line breakpoints.&lt;/li&gt;
&lt;li&gt;It follows the "self-documenting genius code" style. Natural language explanations of the code are unnecessary. Even worse, it doesn't contain any specifications (parameter/return value/contracts) for the subroutines. You have to infer each subroutine's intent and valid invocation scenarios solely based on its implementation. Surely, there is a method to this madness, but it's up to you to find it.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;All that said, one must admit that SOAP::Lite has been around for a very long time, works very well (until it ceases working, that is), it is the
only such comprehensive SOAP module around, and it is of course free. These factors mean that we have to adjust to its author's preferred programming style rather than vice versa (and also thank him for sparing us the effort of implementing the SOAP protocol ourselves).&lt;/p&gt;
&lt;h2&gt;A walkthrough toward generated code&lt;/h2&gt;
&lt;p&gt;There are plenty of ways to interact with SOAP::Lite. For the following descriptions, assume that you use something like that:&lt;pre&gt;
my $service = SOAP::Lite        
        -&amp;gt;service("$url/wsdl/inventory.wsdl")
        -&amp;gt;proxy("$url/cgi-bin/soap_server.pl");

my $result = $service-&amp;gt;dispatch_to(
        $test_user,
        $test_password,
        'inventory_update',
        $args
        );
&lt;/pre&gt;
&lt;p&gt;...where $test_user, $test_password are strings and $args is a SOAP::Data object containing the value of a complex type.&lt;/p&gt;
&lt;p&gt;You begin your interaction with SOAP::Lite by invoking the static &lt;code&gt;service&lt;/code&gt; method. This method is a factory method - it invokes the SOAP::Lite constructor to create a new SOAP::Lite object. You don't get a SOAP::Lite instance from it, though. Rather, the object is blessed into a (generated) subclass whose name is based on the &lt;code&gt;name&lt;/code&gt; attribute of the &lt;code&gt;wsdl:service&lt;/code&gt; element in the WSDL you specified as parameter. In our example case, the subclass is called 'dispatch_to_service'.&lt;/p&gt;
&lt;p&gt;Two noteworthy attributes of the newly created object are _serializer and _deserializer, of type SOAP::Serializer and SOAP::Deserializer, respectively. Other attributes' names also start with an underscore.&lt;/p&gt;
&lt;p&gt;The created service object at first appears to have no methods of its own. Various methods are added to this object at various points of execution, though. For example, when you attempt to invoke the method &lt;code&gt;proxy&lt;/code&gt;, the code generator kicks in. In this case, a subroutine (?) named SOAP::Lite::BEGIN generates non-underscore-named accessor (get/set) methods for all (most of?) the underscore-named attributes. Also generated is the 'proxy' method, which delegates to the the same-named method on the SOAP::Transport object stored in the &lt;code&gt;_transport&lt;/code&gt; attribute. This one in turn creates a &lt;code&gt;SOAP::Transport::HTTP::Client&lt;/code&gt; object, which is stored in the &lt;code&gt;_proxy&lt;/code&gt; attribute of the service object and henceforth returned whenever you call &lt;code&gt;$service-&amp;gt;proxy&lt;/code&gt;.&lt;/p&gt;
&lt;p&gt;Within your invocation of &lt;code&gt;SOAP::Lite::service&lt;/code&gt;, the generation of stubs for wsdl:operations of the service (that is, the actual business logic methods) also occurs. More precisely, the following line from Lite.pm is responsible for invoking the generator:&lt;/p&gt;&lt;pre&gt;
my %services = %{$self-&amp;gt;schema-&amp;gt;parse(@_)-&amp;gt;load-&amp;gt;services};
&lt;/pre&gt;
&lt;p&gt;The subroutine &lt;code&gt;SOAP::Schema::load&lt;/code&gt; iterates over all the operation names and invokes &lt;code&gt;generate_stub&lt;/code&gt; for each operation. The generated stub code is stored in the &lt;code&gt;_stub&lt;/code&gt; attribute of a &lt;code&gt;SOAP::Schema&lt;/code&gt; object. Actually, the operations are not even placed as normal methods in the generated stub code, but rather (again) generated using the AUTOLOAD mechanism by the stub code. Here is an example of what it looks like in all its generated glory. Actually getting to the point of seeing this code took me a few hours debugging:&lt;/p&gt;&lt;pre&gt;
package dispatch_to_service;
# Generated by SOAP::Lite (v0.710.08) for Perl -- soaplite.com
# Copyright (C) 2000-2006 Paul Kulchenko, Byrne Reese
# -- generated at [Thu Mar  5 14:05:39 2009]
# -- generated from http://localhost/wsdl/inventory.wsdl
my %methods = (
dispatch_to =&amp;gt; {
    endpoint =&amp;gt; 'http://localhost/cgi-bin/soap_server.pl',
    soapaction =&amp;gt; 'http://www.plosquare.com/wsdl/WebServices#dispatch_to',
    namespace =&amp;gt; 'http://www.plosquare.com/wsdl/WebServices',
    parameters =&amp;gt; [
      SOAP::Data-&amp;gt;new(name =&amp;gt; 'user', type =&amp;gt; 'xsd:string', attr =&amp;gt; {}),
      SOAP::Data-&amp;gt;new(name =&amp;gt; 'password', type =&amp;gt; 'xsd:string', attr =&amp;gt; {}),
      SOAP::Data-&amp;gt;new(name =&amp;gt; 'function', type =&amp;gt; 'xsd:string', attr =&amp;gt; {}),
      SOAP::Data-&amp;gt;new(name =&amp;gt; 'args', type =&amp;gt; 'tns:inventory_argument_type', attr =&amp;gt; {}),
    ], # end parameters
  }, # end dispatch_to
); # end my %methods

use SOAP::Lite;
use Exporter;
use Carp ();

use vars qw(@ISA $AUTOLOAD @EXPORT_OK %EXPORT_TAGS);
@ISA = qw(Exporter SOAP::Lite);
@EXPORT_OK = (keys %methods);
%EXPORT_TAGS = ('all' =&amp;gt; [@EXPORT_OK]);

sub _call {
    my ($self, $method) = (shift, shift);
    my $name = UNIVERSAL::isa($method =&amp;gt; 'SOAP::Data') ? $method-&amp;gt;name : $method;
    my %method = %{$methods{$name}};
    $self-&amp;gt;proxy($method{endpoint} || Carp::croak "No server address (proxy) specified")
        unless $self-&amp;gt;proxy;
    my @templates = @{$method{parameters}};
    my @parameters = ();
    foreach my $param (@_) {
        if (@templates) {
            my $template = shift @templates;
            my ($prefix,$typename) = SOAP::Utils::splitqname($template-&amp;gt;type);
            my $method = 'as_'.$typename;
            # TODO - if can('as_'.$typename) {...}
            my $result = $self-&amp;gt;serializer-&amp;gt;$method($param, $template-&amp;gt;name, $template-&amp;gt;type, $template-&amp;gt;attr);
            push(@parameters, $template-&amp;gt;value($result-&amp;gt;[2]));
        }
        else {
            push(@parameters, $param);
        }
    }
    $self-&amp;gt;endpoint($method{endpoint})
       -&amp;gt;ns($method{namespace})
       -&amp;gt;on_action(sub{qq!"$method{soapaction}"!});
  $self-&amp;gt;serializer-&amp;gt;register_ns("http://schemas.xmlsoap.org/wsdl/","wsdl");
  $self-&amp;gt;serializer-&amp;gt;register_ns("http://www.w3.org/2001/XMLSchema-instance","xsi");
  $self-&amp;gt;serializer-&amp;gt;register_ns("http://schemas.xmlsoap.org/soap/encoding/","soapenc");
  $self-&amp;gt;serializer-&amp;gt;register_ns("http://www.w3.org/2001/XMLSchema","xsd");
  $self-&amp;gt;serializer-&amp;gt;register_ns("http://www.plosquare.com/wsdl/WebServices","tns");
  $self-&amp;gt;serializer-&amp;gt;register_ns("http://schemas.xmlsoap.org/wsdl/soap/","soapbind");
    my $som = $self-&amp;gt;SUPER::call($method =&amp;gt; @parameters);
    if ($self-&amp;gt;want_som) {
        return $som;
    }
    UNIVERSAL::isa($som =&amp;gt; 'SOAP::SOM') ? wantarray ? $som-&amp;gt;paramsall : $som-&amp;gt;result : $som;
}

sub BEGIN {
    no strict 'refs';
    for my $method (qw(want_som)) {
        my $field = '_' . $method;
        *$method = sub {
            my $self = shift-&amp;gt;new;
            @_ ? ($self-&amp;gt;{$field} = shift, return $self) : return $self-&amp;gt;{$field};
        }
    }
}
no strict 'refs';
for my $method (@EXPORT_OK) {
    my %method = %{$methods{$method}};
    *$method = sub {
        my $self = UNIVERSAL::isa($_[0] =&amp;gt; __PACKAGE__)
            ? ref $_[0]
                ? shift # OBJECT
                # CLASS, either get self or create new and assign to self
                : (shift-&amp;gt;self || __PACKAGE__-&amp;gt;self(__PACKAGE__-&amp;gt;new))
            # function call, either get self or create new and assign to self
            : (__PACKAGE__-&amp;gt;self || __PACKAGE__-&amp;gt;self(__PACKAGE__-&amp;gt;new));
        $self-&amp;gt;_call($method, @_);
    }
}

sub AUTOLOAD {
    my $method = substr($AUTOLOAD, rindex($AUTOLOAD, '::') + 2);
    return if $method eq 'DESTROY' || $method eq 'want_som';
    die "Unrecognized method '$method'. List of available method(s): @EXPORT_OK\n";
}

1;
&lt;/pre&gt;
&lt;p&gt;Accordingly, if you want to debug your stubs, you should incorporate your debugging output in the generator routine &lt;code&gt;SOAP::Schema::generate_stub&lt;/code&gt;. Back in the beginning of the post, the problem was that a complex data structure was being serialized into xsi:nil rather than into the provided value. Inserting debugging output would have shown that the line&lt;/p&gt;&lt;pre&gt;
my $result = $self-&amp;gt;serializer-&amp;gt;$method($param, $template-&amp;gt;name, $template-&amp;gt;type, $template-&amp;gt;attr);
&lt;/pre&gt;
&lt;p&gt;in the generated stub doesn't yield the expected value for the 'args' parameter and would provide a clue to the custom serialization method as a method of solving the problem.&lt;/p&gt;
&lt;p&gt;Hopefully you enjoyed this little trip into SOAP::Lite internals as much as I did making it on my own (that is to say, just a bit ;-). Seriously, this post serves to capture several facts about SOAP::Lite that might aid debugging if (when) the next such necessity arrives.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-4595713963470682474?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/4595713963470682474/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/03/debugging-serialization-in-soaplite.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4595713963470682474'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/4595713963470682474'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/03/debugging-serialization-in-soaplite.html' title='Debugging serialization in SOAP::Lite'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-1978674990672157213</id><published>2009-03-04T10:32:00.003+01:00</published><updated>2009-03-04T10:36:41.703+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='performance'/><category scheme='http://www.blogger.com/atom/ns#' term='MySQL'/><category scheme='http://www.blogger.com/atom/ns#' term='integration'/><category scheme='http://www.blogger.com/atom/ns#' term='Access'/><category scheme='http://www.blogger.com/atom/ns#' term='pipe'/><category scheme='http://www.blogger.com/atom/ns#' term='UDF'/><category scheme='http://www.blogger.com/atom/ns#' term='FIFO'/><category scheme='http://www.blogger.com/atom/ns#' term='trigger'/><category scheme='http://www.blogger.com/atom/ns#' term='linked table'/><title type='text'>Exporting data from Microsoft Access to MySQL using a linked table, trigger, UDF</title><content type='html'>&lt;p&gt;This article describes a simple, but effective integration solution for transferring data on user request from an MS Access database to a MySQL database hosted on a remote web server. The data transfer occurs over an encrypted connection through the Internet, which creates some performance challenges. On the receiving side, the data has to be post-processed, which is achieved using a MySQL trigger and a user-defined function to notify a background daemon process.&lt;/p&gt;&lt;p&gt;The solution is outlined below:&lt;/p&gt;&lt;ol&gt;
&lt;li&gt;First off, ensure ODBC connectivity between the MySQL Access database and the MySQL database on the remote server. Install &lt;a href="http://nemesis2.qx.net/pages/MyEnTunnel"&gt;MyEnTunnel&lt;/a&gt; on the Windows machine to tunnel port 3306 from the local machine to the remote server over an encrypted (ssh) link. Configure MyEnTunnel to keep the connection open at all times. Set up an ODBC data source on the Windows machine to connect to the appropriate database at localhost:3306 (now tunneled to the remote server), using appropriate credentials. Note: I had some trouble configuring MyEnTunnel as a Windows service on Vista (though it worked ok on Windows XP). Adding it as a normal autostart program was a viable workaround.&lt;/li&gt;
&lt;li&gt;Based on the connection from the previous step, you can already create a linked table in Access which is mapped to a pre-existing MySQL table. Inserting rows into that table will insert them into MySQL. However, if you try inserting &amp;gt;1000 rows through a DSL connection, you will notice that the whole setup is unbearably slow (several minutes). Same applies to deleting rows. Some suggest setting up pass-through queries to work around the problem. I have not seriously tried this. Instead, I chose to reduce the number of INSERTs necessary by using a single linked table which acts as a "mailbox" on the MySQL side for receiving a "message" (long string) from MS Access. This message is deciphered by a remote daemon process.&lt;/li&gt;
&lt;li&gt;A naive approach would be to have the mailbox table contain a single column of type "text" and accept a single row. This won't work, MS Access will truncate everything below 64 KB or even below. You have to split the message into chunks. I chose to have 32 KB chunks. The layout of the mailbox table is as follows:&lt;pre&gt;
CREATE TABLE `mailbox` (
  `id` int(4) NOT NULL auto_increment,
  `msg` text,
  `_ts` timestamp NOT NULL default CURRENT_TIMESTAMP on update CURRENT_TIMESTAMP,
  `last_row` int(1) default '0',
  PRIMARY KEY(`id`)
)
&lt;/pre&gt;
To transmit the chunked message, first a &lt;code&gt;DELETE FROM mailbox&lt;/code&gt; is executed, then a series of rows, each containing up to 32 KB &lt;code&gt;msg&lt;/code&gt; payload are inserted, and all but the last row have &lt;code&gt;last_row=0&lt;/code&gt;. The chunking is done by a VBA macro, which basically executes a query in Access, concatenates the rows (and columns) into a string, finally divides up the string into appropriately sized chunks.  
&lt;/li&gt;
&lt;li&gt;The following steps will make the server do something in response to the received data. Generally, you can react to database changes using triggers (provided that your MySQL version is recent enough). What we'd like to do is to have a trigger wake up a daemon process, which can then read the mailbox table, stitch together the received message chunks, and process the message. For that, we'll to add three more pieces to the puzzle: a dummy table, a trigger, and a user-defined function (UDF).&lt;/li&gt;
&lt;li&gt;The dummy table looks as follows:&lt;pre&gt;
CREATE TABLE `dbg_tmp_dummy` (
  `dummy` varchar(256) default NULL
)
&lt;/pre&gt;
It should contain a single row, value doesn't matter. It will become apparent next why the dummy table is required.
&lt;/li&gt;
&lt;li&gt;In order to notify an external process, I chose to use the tried and true UNIX named pipe mechanism (signals could be used just as well, but you'd then have to run the daemon process under MySQL identity). MySQL is supposed to write a character to a FIFO file, which the daemon process is reading from. MySQL doesn't support writing to files out-of-the-box without overwriting the file (the built-in export function will complain if the target file already exists). To work around this, a little &lt;a href="http://dev.mysql.com/doc/refman/5.0/en/adding-functions.html"&gt;MySQL user-defined function&lt;/a&gt; has to be implemented in C, so that the following incantation is possible: &lt;code&gt;SELECT writefile('/tmp/fifo.pipe', 'x');&lt;/code&gt; with the effect of a single byte 'x' being appended to &lt;code&gt;/tmp/fifo.pipe&lt;/code&gt;. Given that we are dealing with a named pipe, and that the daemon process might not be listening, the write has to be a non-blocking one. I will skip the implementation details here. Rest assured that the UDF is a simple one. If you are new to UDFs, read the MySQL manual and have a look at &lt;code&gt;udf_example.c&lt;/code&gt; shipped with the MySQL source code. Essentially, you compile the C file into a .so library, place the library in a directory searched by MySQL, use &lt;code&gt;CREATE FUNCTION&lt;/code&gt; to register the function in your database.
&lt;/li&gt;
&lt;li&gt;Finally comes the trigger part. To define the trigger, connect to the target database using the command-line MySQL client and enter the following:&lt;pre&gt;
delimiter |
CREATE TRIGGER mailbox_trigger AFTER INSERT ON mailbox
FOR EACH ROW BEGIN
UPDATE mailbox SET dummy=writefile('/tmp/fifo.pipe', 'x') WHERE NEW.last_row=1;
END;
|
&lt;/pre&gt;
The above code is quite self-explanatory. The important things to note is that you have to use the special delimiter, you can't just enter the trigger definition having the standard semicolon as the delimiter (you'd get a syntax error). It is now apparent why the dummy table is needed. It is impossible to execute a SELECT statement within a trigger which is not tied to an INSERT/UPDATE. Finally, note how the &lt;code&gt;last_row&lt;/code&gt; attribute is used to only send the notification after the full message has been received.
&lt;/li&gt;
&lt;li&gt;The daemon process might be a Perl script which reads from the pipe:&lt;pre&gt;
sub wait_for_notification
{
    if (! -p $pipe_path) # create pipe if necessary
    {
        unlink($pipe_path);
        my $prev_umask = umask(077); # rw for owner (mysql)
        system("mknod $pipe_path p", 1, 1) || die "mknod: $!";
        umask($prev_umask);
    }
    open(FL, "&amp;lt;$pipe_path") || die "open: $!";
    for (;;)
    {
        my $buf = '';
        my $ret = sysread(FL, $buf, 1);
    
        if (!defined($ret))
        {
            die "sysread $pipe_path: $!";
        }
        elsif (!$ret) # EOF: we read something
        {
            close(FL) || die "close $pipe_path: $!";
            return;
        }
    }
}
&lt;/pre&gt;
Beware that notifications will be lost if MySQL writes to the pipe while the daemon is not reading it. For my particular use case, this is not an issue, as the daemon can also periodically poll the database and the notification volume is very low. You could modify the solution to use shared memory with appropriate locking instead of pipes to make sure that no race conditions are possible.   
&lt;/li&gt;
&lt;/ol&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-1978674990672157213?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/1978674990672157213/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/03/exporting-data-from-microsoft-access-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1978674990672157213'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1978674990672157213'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/03/exporting-data-from-microsoft-access-to.html' title='Exporting data from Microsoft Access to MySQL using a linked table, trigger, UDF'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-6252627595575367772</id><published>2009-02-16T22:34:00.003+01:00</published><updated>2009-02-16T22:40:30.393+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='merge'/><category scheme='http://www.blogger.com/atom/ns#' term='CVS'/><category scheme='http://www.blogger.com/atom/ns#' term='problem'/><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='conflict'/><title type='text'>Solution for Eclipse CVS merge conflicts</title><content type='html'>&lt;p&gt;Problem: Eclipse's CVS merge feature (despite all your efforts) keeps showing tons of pseudo-conflicts (red conflict symbol with a superimposed white plus or cross sign, whatever it might mean). Solution: don't use Eclipse (for merging). Instead, fall back on the good old command-line CVS client; it's not that difficult.&lt;/p&gt;
&lt;p&gt;Change to your project directory and issue a command like:&lt;/p&gt;
&lt;pre&gt;cvs -q update -d \
    -j "VERSION_2_2:2008-11-24" \
    -j VERSION_2_2 &amp;&gt; /tmp/merge.log
&lt;/pre&gt;
&lt;p&gt;Here VERSION_2_2 is your source branch, from which the merging
should occur. The first -j also contains a start tag (or date).&lt;/p&gt;
&lt;p&gt;Inspect merge.log for mentions of "warning" and "conflict". Inspect the reported files and merge manually around the standard CVS conflict markers, i.e. &amp;gt;&amp;gt;&amp;gt;&amp;gt;, ==== and &amp;lt;&amp;lt;&amp;lt;&amp;lt;). Finally, delete all files whose names match the pattern .#* (created by the CVS client to indicate which files were affected by merge).&lt;/p&gt;
&lt;p&gt;There is one catch here - the &lt;code&gt;extssh&lt;/code&gt; access mode routinely used by Eclipse is not recognized by the command-line CVS client, so you will have to replace &lt;code&gt;:extssh:&lt;/code&gt;  strings by &lt;code&gt;:ext:&lt;/code&gt; in all &lt;code&gt;CVS/Root&lt;/code&gt; files within your project and set the environment variable &lt;code&gt;CVS_RSH=/usr/bin/ssh&lt;/code&gt; to emulate Eclipse's behavior (after you're
done revert the replacements).&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-6252627595575367772?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/6252627595575367772/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/02/solution-for-eclipse-cvs-merge.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6252627595575367772'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/6252627595575367772'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/02/solution-for-eclipse-cvs-merge.html' title='Solution for Eclipse CVS merge conflicts'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-1767337813990414264</id><published>2009-01-22T11:17:00.002+01:00</published><updated>2009-01-22T11:33:44.764+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='strace'/><category scheme='http://www.blogger.com/atom/ns#' term='apt-get'/><category scheme='http://www.blogger.com/atom/ns#' term='dpkg'/><category scheme='http://www.blogger.com/atom/ns#' term='observability'/><title type='text'>Subprocess post-installation script returned error exit status 1</title><content type='html'>&lt;p&gt;Scenario: while installing a package in Debian, we get into the following situation:&lt;/p&gt;
&lt;pre&gt;debian:~# apt-get install bugzilla
Reading Package Lists... Done
Building Dependency Tree... Done
bugzilla is already the newest version.
0 upgraded, 0 newly installed, 0 to remove and 1272 not upgraded.
1 not fully installed or removed.
Need to get 0B of archives.
After unpacking 0B of additional disk space will be used.
Setting up bugzilla (2.22.1-2) ...
dbconfig-common: writing config to /etc/dbconfig-common/bugzilla.conf
dpkg: error processing bugzilla (--configure):
 subprocess post-installation script returned error exit status 1
Errors were encountered while processing:
 bugzilla
E: Sub-process /usr/bin/dpkg returned an error code (1)
&lt;/pre&gt;
&lt;p&gt;This is yet another "observability" problem, as clearly too little information is provided to determine the problem's cause.&lt;/p&gt;
&lt;p&gt;The correct way to proceed is to trace the execution of the failed post-installation script, but first we need to find out where it is and how it is called. Note that the &lt;code&gt;apt-get install&lt;/code&gt; internally runs &lt;code&gt;dpkg --install&lt;/code&gt; and, as we can learn from dpkg's man page, &lt;code&gt;dpkg --configure&lt;/code&gt;. The post-installation script is invoked by &lt;code&gt;dpkg --configure&lt;/code&gt;. To see how it is done, we use strace:&lt;/p&gt;
&lt;pre&gt;debian:~# ( strace -f dpkg --configure bugzilla 2&gt;&amp;1 ) \
| grep execve | grep post
[pid  6289] execve("/var/lib/dpkg/info/bugzilla.postinst",
["/var/lib/dpkg/info/bugzilla.post"..., "configure", "2.20.1-1"],
[/* 47 vars */] &lt;unfinished ...&gt;
[pid  6289] execve("/usr/share/debconf/frontend",
["/usr/share/debconf/frontend", "/var/lib/dpkg/info/bugzilla.post"...,
"configure", "2.20.1-1"], [/* 45 vars */]) = 0
[pid  6310] execve("/var/lib/dpkg/info/bugzilla.postinst",
["/var/lib/dpkg/info/bugzilla.post"..., "configure", "2.20.1-1"],
[/* 46 vars */]) = 0
&lt;/pre&gt;
&lt;p&gt;Based on the first logged execve, we try the following command:&lt;/p&gt;
&lt;pre&gt;/var/lib/dpkg/info/bugzilla.postinst configure 2.20.1-1; echo $?&lt;/pre&gt;
&lt;p&gt;and we see that it in fact reproduces the error reported by &lt;code&gt;dpkg --configure&lt;/code&gt;. From here we can rely on generic shell script tracing or debugging techniques to determine the cause.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-1767337813990414264?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/1767337813990414264/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/01/subprocess-post-installation-script.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1767337813990414264'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1767337813990414264'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/01/subprocess-post-installation-script.html' title='Subprocess post-installation script returned error exit status 1'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-7357829258063900095</id><published>2009-01-18T12:41:00.006+01:00</published><updated>2009-01-18T12:51:14.967+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='rt73'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='wlan0'/><category scheme='http://www.blogger.com/atom/ns#' term='udev'/><category scheme='http://www.blogger.com/atom/ns#' term='rausb0'/><title type='text'>Renaming rausb0 to wlan0</title><content type='html'>&lt;table&gt;
&lt;tr&gt;&lt;td valign="top"&gt;&lt;b&gt;Problem:&lt;/b&gt;&lt;/td&gt;&lt;td&gt;When a USB wireless device using the rt73 driver is plugged in under Linux, it appears (e.g. in ifconfig) as rausb0 rather than the more familiar device name wlan0.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td valign="top"&gt;&lt;b&gt;Solution:&lt;/b&gt;&lt;/td&gt;&lt;td&gt;Add the following line to your /etc/udev/rules.d/70-persistent-net.rules in order to rename the device:&lt;pre&gt;
ACTION=="add", KERNEL=="rausb0", NAME="wlan0"
&lt;/pre&gt;&lt;/td&gt;&lt;/tr&gt;
&lt;/table&gt;
&lt;p&gt;To diagnose this sort of problems, launch &lt;code&gt;udevadm monitor&lt;/code&gt; before running &lt;code&gt;modprobe rt73&lt;/code&gt;. The correct output of &lt;code&gt;udevadm monitor&lt;/code&gt; while the renaming rule is in effect should look as follows:&lt;pre&gt;
UEVENT[1232279287.492254] add      
/module/rt73 (module)
UEVENT[1232279287.494837] add      
/bus/usb/drivers/rt73 (drivers)
UEVENT[1232279287.496842] add      
/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1:1.0/rausb0 (net)
UEVENT[1232279287.501910] add      
/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1 (firmware)
UDEV  [1232279287.505345] add      
/module/rt73 (module)
UDEV  [1232279287.508952] add      
/bus/usb/drivers/rt73 (drivers)
UDEV  [1232279287.534015] add      
/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1:1.0/wlan0 (net)
UEVENT[1232279287.555346] remove   
/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1 (firmware)
UDEV  [1232279287.558466] remove   
/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1 (firmware)
UDEV  [1232279287.561163] add      
/devices/pci0000:00/0000:00:1d.0/usb1/1-1/1-1 (firmware)
&lt;/pre&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-7357829258063900095?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/7357829258063900095/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2009/01/renaming-rausb0-to-wlan0.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7357829258063900095'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/7357829258063900095'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2009/01/renaming-rausb0-to-wlan0.html' title='Renaming rausb0 to wlan0'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-148294745140288399</id><published>2008-12-30T13:44:00.005+01:00</published><updated>2008-12-30T14:34:02.676+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='resolution'/><category scheme='http://www.blogger.com/atom/ns#' term='x11'/><category scheme='http://www.blogger.com/atom/ns#' term='modeline'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='xorg'/><category scheme='http://www.blogger.com/atom/ns#' term='observability'/><title type='text'>ModeLine ignored by xorg display driver</title><content type='html'>&lt;p&gt;In the recent past, I used to switch screen resolutions in XFree/X.org by pressing the CTRL-ALT-minus or CTRL-ALT-plus keys. For each supported mode, there used to be a ModeLine entry in xorg.conf and those entries were referenced by the "Screen" section. As often happens when technology "advances", on my new Dell D830 notebook (using xorg 1:7.3+18 from Debian 'testing', GM965/GM960 graphics controller, 'intel' display driver), this tried and true method no longer works. The magic key combo is ignored altogether, as is adding ModeLines to xorg.conf. No warnings in /var/log/Xorg.0.log nor anywhere else are recorded, AFAICS. The standard xorg.conf seems almost empty, which in itself is a good thing (less configuration means less opportunity for mistakes). The bad thing, obviously, is that invalid or deprecated (?) configuration entries are silently skipped without a clue for the user about what is going on inside the black box, whether its behavior is normal or not, and which alternatives might be available. This is an example of what I like to call an "observability" problem. Sometimes a careless implementation is to blame, but such problems may also have their roots in software designers' inability to correctly foresee and specify the possible variance in implementations.&lt;/p&gt;
&lt;p&gt;Fortunately, there is an easy solution to ignored ModeLines. xorg includes a program called &lt;code&gt;xrandr&lt;/code&gt;, which can be used to test and programmatically set a resolution, as described in the &lt;a href="https://wiki.ubuntu.com/X/Config/Resolution"&gt;Ubuntu Wiki article X/Config/Resolution&lt;/a&gt;. This seems to be a substitute for the traditional ModeLines in xorg.conf. Although the Wiki article mentions that xorg.conf can still be used for specifying resolutions, this is simply not the case in my setup.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-148294745140288399?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/148294745140288399/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2008/12/modeline-ignored-by-xorg-display-driver.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/148294745140288399'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/148294745140288399'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2008/12/modeline-ignored-by-xorg-display-driver.html' title='ModeLine ignored by xorg display driver'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-3775174820530569083</id><published>2008-12-30T00:11:00.010+01:00</published><updated>2008-12-30T01:51:15.437+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='booting'/><category scheme='http://www.blogger.com/atom/ns#' term='grub'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='easybcd'/><category scheme='http://www.blogger.com/atom/ns#' term='vista'/><category scheme='http://www.blogger.com/atom/ns#' term='xp'/><title type='text'>Multi-boot configuration for Vista, Windows XP, Linux</title><content type='html'>&lt;p&gt;I bought my (Dell) notebook with Windows Vista preinstalled. Because some of my customers stay loyal to Windows XP and I prefer Linux for flexibility, I wanted to also install both of these OSes on the same disk, so as to have a choice of each operating system through the GRUB boot menu (Linux being the dominant OS and the other two secondary choices). This article contains a record of my installation with some warnings and explanations mixed in, unlike those "easy" HOWTOs that omit the gory details and leave you wanting if anything goes wrong in the process or your preconditions differ slightly.&lt;/p&gt;
&lt;p&gt;First of all, it is important to note that the final configuration which I settled upon does not include all three OSes within a single GRUB boot menu. Instead, the menu contains two categories of items: Linux (one item per kernel configuration) and Windows (just one item for booting either Windows XP or Vista). The Windows menu item instructs GRUB to delegate control to Vista's own boot loader, which then offers a second choice between Vista and XP.&lt;/p&gt;
&lt;p&gt;The entry for the Windows menu item in /boot/grub/menu.lst is trivial:&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;
title           Windows Vista/XP
root            (hd0,2)
savedefault
makeactive
chainloader     +1
&lt;/pre&gt;&lt;/p&gt;
&lt;p&gt;The partition table in the final configuration looks as follows - (hd0,2) in GRUB-speak refers to /dev/sda3:&lt;/p&gt;
&lt;p&gt;&lt;pre&gt;
  Device Boot      Start         End      Blocks   Id  System
/dev/sda1               1          23      184716   de  Dell Utility
/dev/sda2              24        1069     8401995    7  HPFS/NTFS
/dev/sda3   *        1070        7494    51602432    7  HPFS/NTFS
/dev/sda4            7495       29863   179678992+   5  Extended
/dev/sda5   *        7495        9925    19526976   83  Linux
/dev/sda6            9926       10411     3903763+  82  Linux swap / Solaris
/dev/sda7           10412       29863   156248158+  83  Linux
&lt;/pre&gt;&lt;/p&gt;
&lt;table border="1" cellspacing="0" cellpadding="3"&gt;
&lt;tbody&gt;&lt;tr&gt;&lt;td valign="top"&gt;/dev/sda1&lt;/td&gt;&lt;td&gt;"Diagnostics" partition; this is a small FAT16 partition preinstalled by Dell. It contains programs for testing memory etc.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td valign="top"&gt;/dev/sda2&lt;/td&gt;&lt;td&gt;The Windows XP partition; originally the same block range  used to hold a ~10 GB "recovery" partition, which was apparently too small for Vista for its intended purpose.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td valign="top"&gt;/dev/sda3&lt;/td&gt;&lt;td&gt;The Windows Vista partition; originally it occupied the rest of disk and had to be shrunk to make room for Linux.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td valign="top"&gt;/dev/sda4&lt;/td&gt;&lt;td&gt;The extended partition which holds /dev/sda5, /dev/sda6, /dev/sda7. It is very important to create this partition because any disk can only hold 4 primary partitions. If you forget this little quirk, you will likely end up with large amounts of unpartitioned (that is, unusable) disk space.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td valign="top"&gt;/dev/sda5&lt;/td&gt;&lt;td&gt;The Linux boot/root partition.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td valign="top"&gt;/dev/sda6&lt;/td&gt;&lt;td&gt;The Linux swap partition.&lt;/td&gt;&lt;/tr&gt;
&lt;tr&gt;&lt;td valign="top"&gt;/dev/sda7&lt;/td&gt;&lt;td&gt;The Linux "data" partition, which in my case holds an encrypted file system for /home and /usr/local (not quite relevant)&lt;/td&gt;&lt;/tr&gt;
&lt;/tbody&gt;&lt;/table&gt;
&lt;p&gt;The non-trivial part of the setup concerns the order of installation and the steps required to make both boot managers work correctly. The same approach might be used to install in a different order. The key piece of information to keep in mind is which step overwrites which boot records.&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;Before installing anything, I made snapshots of the Master Boot Record (/dev/sda) as well as of each partition's initial sectors using a &lt;a href="http://www.knoppix.net/"&gt;Knoppix CD&lt;/a&gt;. After booting from the CD, I issued commands like &lt;code&gt;dd if=/dev/sda1 of=/path/to/sda1/backup/file bs=512 count=32&lt;/code&gt; for each partition and the main MBR. To store the backup files, I used the /dev/sda1 "diagnostics" partition. A USB stick could be used just as well. I heuristically chose to back up the first 32 sectors of each partition, not being sure how many of them really need to be protected. This number has later turned out sufficient.&lt;/li&gt;
&lt;li&gt;I began by resizing Vista's own partition using Vista's &lt;a href="http://www.tweakvista.com/articles/38991/resize-partitions-with-vista-disk-management/"&gt;disk management&lt;/a&gt; tool. It didn't allow me to go below 50 GB. I used the same tool to delete the /dev/sda2 "recovery" partition in order to create a gap for installing Windows XP. (By the way, an attempt to use the Partition Magic tool to scale down Windows XP below 40 GB and move it to the beginning of disk resulted in an unbootable system; now I believe PM can't deal at all with Vista.)&lt;/li&gt;
&lt;li&gt;I installed Windows XP, creating a new partition for it during the installation process. Actually, I had to first create a custom Windows XP installation CD based on the original one, in order to include some missing SATA disk drivers. The Windows XP installer did not just alter the MBR, but &lt;em&gt;it also overwrote the beginning of the Vista's /dev/sda3 partition&lt;/em&gt;. Accordingly, I was unable to boot Vista after this step (instead Windows XP booted itself).&lt;/li&gt;
&lt;li&gt;Using the Knoppix CD, I created another set of boot sector backup files (just in case; those were not used later on).&lt;/li&gt;
&lt;li&gt;I proceeded to install Linux (Debian), taking care to use the "expert mode" to create an extended partition. (It later turned out that fdisk is too dumb to expand this partition in order to accommodate new logical partitions; I recommend cfdisk instead.)&lt;/li&gt;
&lt;li&gt;At this point, I had a configuration which booted Linux, and (if I recall correctly) Windows XP, based on an entry in GRUB's menu.lst created by the installer - the same entry I pasted in the beginning of this post.&lt;/li&gt;
&lt;li&gt;Having booted into Linux, I created another set of boot sector backup files (again, just in case).&lt;/li&gt;
&lt;li&gt;The really interesting part begins now. To "revive" Vista's boot loader, I restored with dd the 16KB backup of Vista's partition I made in Step 1. This left me being able to boot Linux and Vista, but not Windows XP. (It is noteworthy that partition numbering changes when you delete a partition. For example, /dev/sda1, /dev/sda2, /dev/sda3 became /dev/sda1, /dev/sda2 after deleting the "recovery" /dev/sda2 partition. The original ordering was restored after installing Windows XP into /dev/sda2, though.)&lt;/li&gt;
&lt;li&gt;In Vista, I installed the free &lt;a href="http://neosmart.net/dl.php?id=1"&gt;EasyBCD&lt;/a&gt; tool, which lets you edit Vista's own boot loader entries. Here I added a "Windows XP" menu entry and pointed it to the partition with Windows XP (which is called D: from Vista's own perspective).&lt;/li&gt;
&lt;li&gt;At this stage, my attempts to boot into Windows XP using Vista's boot loader failed with an error message about missing NTLDR. To fix this, following advice from the EasyBCD forum, I booted into Vista and copied c:\ntldr.dll to Windows XP's d:\ntldr.dll (where it was, in fact, missing). In order to see the file c:\ntldr.dll in Vista, I had to tweak some settings regarding displaying hidden and system files.&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;Having completed all of the above steps, I was able to boot all three OSes - Linux directly using GRUB, and the two versions of Windows by redirecting GRUB into Vista's boot loader. It might not be the nicest possible configuration (requires a few extra key strokes during the boot process), but it works.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-3775174820530569083?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/3775174820530569083/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2008/12/multi-boot-configuration-for-vista.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3775174820530569083'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3775174820530569083'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2008/12/multi-boot-configuration-for-vista.html' title='Multi-boot configuration for Vista, Windows XP, Linux'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-1527122046927525817</id><published>2008-12-29T21:34:00.007+01:00</published><updated>2008-12-29T22:08:19.318+01:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='troubleshooting'/><category scheme='http://www.blogger.com/atom/ns#' term='gtk'/><category scheme='http://www.blogger.com/atom/ns#' term='linux'/><category scheme='http://www.blogger.com/atom/ns#' term='library'/><category scheme='http://www.blogger.com/atom/ns#' term='error'/><category scheme='http://www.blogger.com/atom/ns#' term='64 bit'/><title type='text'>Solution for Gtk-WARNING **: ... : wrong ELF class: ELFCLASS64</title><content type='html'>&lt;p&gt;&lt;code&gt;(eclipse:13576): Gtk-WARNING **: /usr/lib/gtk-2.0/2.10.0/engines/libbluecurve.so: wrong ELF class: ELFCLASS64
&lt;/code&gt;&lt;/p&gt;&lt;p&gt;If you get the above error message in a 64 bit Linux system while trying to run a 32 bit application using GTK 2 (like Eclipse), it means that GTK is picking up the wrong (64 bit) version of a plugin library. The first step is, of course, to check that the 32 bit version of the library is available (example location in Debian/Ubuntu; I first extracted the library from &lt;a href="http://packages.debian.org/lenny/i386/gtk2-engines-wonderland/download"&gt;the i386 package&lt;/a&gt; and moved it to this location):&lt;/p&gt;&lt;p&gt;&lt;code&gt;jpl@debian:~$ file /emul/ia32-linux/usr/lib/gtk-2.0/2.10.0/engines/libbluecurve.so 
/emul/ia32-linux/usr/lib/gtk-2.0/2.10.0/engines/libbluecurve.so: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, stripped
&lt;/code&gt;&lt;/p&gt;&lt;p&gt;Even though the 32 bit version exists and is available in the library search path used by the dynamic linker, GTK insists on using the 64 bit version. &lt;p&gt;Changing &lt;code&gt;LD_LIBRARY_PATH&lt;/code&gt;, adding the particular library to &lt;code&gt;LD_PRELOAD&lt;/code&gt;, changing &lt;code&gt;-Djava.library.path&lt;/code&gt; do &lt;i&gt;not&lt;/i&gt; fix the problem. However, a similar easy solution exists - override the search path by setting the &lt;code&gt;GTK_PATH&lt;/code&gt; environment variable in a wrapper script for the 32 bit application in question:&lt;/p&gt;&lt;p&gt;&lt;code&gt;export GTK_PATH=/emul/ia32-linux/usr/lib/gtk-2.0
&lt;/code&gt;&lt;/p&gt;&lt;p&gt;This issue has been reported as &lt;a href="http://bugs.debian.org/cgi-bin/bugreport.cgi?bug=490947"&gt;Debian bug 490947&lt;/a&gt; and &lt;a href="https://bugs.launchpad.net/ubuntu/+source/ia32-libs/+bug/190227"&gt;Ubuntu bug 190227&lt;/a&gt;. Reports elsewhere linked it to RealPlayer, however this appears to be a problem with the (packaging of) the GTK2 library, so various applications are affected.&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-1527122046927525817?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://plosquare.blogspot.com/feeds/1527122046927525817/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://plosquare.blogspot.com/2008/12/gtk-warning-wrong-elf-class-elfclass64.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1527122046927525817'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/1527122046927525817'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2008/12/gtk-warning-wrong-elf-class-elfclass64.html' title='Solution for Gtk-WARNING **: ... : wrong ELF class: ELFCLASS64'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-112080040011486548.post-3516166533899446604</id><published>2008-12-29T20:05:00.012+01:00</published><updated>2008-12-29T21:14:05.796+01:00</updated><title type='text'>About This Blog</title><content type='html'>&lt;p&gt;Dear Visitor,&lt;/p&gt;&lt;p&gt;Welcome to my "blog", associated with &lt;a href="http://www.plosquare.com/"&gt;my professional web site&lt;/a&gt;. It is unlikely to become anything resembling a traditional "web-based diary". Rather, I created it as a simple venue for promoting my services by helping other people solve their technical problems and giving away some free advice.
&lt;/p&gt;&lt;p&gt;If you are like me, you rely heavily on search engines to find solutions to technical issues and for general troubleshooting inspiration, before attempting a deeper (and more time-intensive) analysis "from first principles". When troubleshooting software, a well-targeted keyword-based search yields fair results &lt;i&gt;most&lt;/i&gt; of the time, given a troubleshooter with sufficient expertise. The other times, we learn something new. In a broad sense, we perform successful "applied research", although due to the narrow scope of our problems, there is no room for its results in scientific journals or conference proceedings. Nonetheless, these results carry significant external value measured in time and effort saved by someone else because of their availability. For the troubleshooter himself, writing up a case together with its solution helps gain finer insight into a particular system and commit those heuristics that worked well to memory. Last but not least, it becomes an exercise in successful communication. Making it available to everyone else is then just a matter of courtesy, which in this Internet age may be paid back in the same manner thousandfold.&lt;/p&gt;&lt;p&gt;I hope that you found here what you were looking for. If not, it only takes a few clicks and an email to find out whether I could assist you.
&lt;/p&gt;&lt;p&gt;Best regards,
Jan Ploski, &lt;a href="http://www.plosquare.com/"&gt;plosquare.com&lt;/a&gt;&lt;/p&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/112080040011486548-3516166533899446604?l=plosquare.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3516166533899446604'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/112080040011486548/posts/default/3516166533899446604'/><link rel='alternate' type='text/html' href='http://plosquare.blogspot.com/2008/12/about-this-blog.html' title='About This Blog'/><author><name>jpl</name><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='16' height='16' src='http://1.bp.blogspot.com/_o_c25p_Zq5Y/S2ih0zb7JjI/AAAAAAAAAB8/hN2pm_mGr7k/S220/favicon.gif'/></author></entry></feed>
