<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
    <title>Mark S. Kolich</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/" />
    <link rel="self" type="application/atom+xml" href="http://mark.koli.ch/atom.xml" />
    <id>tag:,2008-10-25:/1</id>
    <updated>2010-01-21T03:03:44Z</updated>
    <subtitle>Engineer, Entrepreneur, Consultant</subtitle>
    <generator uri="http://www.sixapart.com/movabletype/">Movable Type 4.21-en</generator>

<entry>
    <title>Clok: A new way to view time</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2010/01/clok-a-new-way-to-view-time.html?rss" />
    <id>tag:mark.koli.ch,2010://1.223</id>

    <published>2010-01-21T01:45:00Z</published>
    <updated>2010-01-21T03:03:44Z</updated>

    <summary><![CDATA[Clok is a "very fuzzy" world-clock I built, modeled after Caskey Dickson's idea and Java Clok implementation.&nbsp; Clok is not built to give you the exact time in every time zone; for that, get another, more accurate, world clock.&nbsp; Instead,...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="clok" label="clok" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="css" label="css" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="javascript" label="javascript" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="php" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<a href="http://clok.koli.ch/">Clok</a> is a "very fuzzy" world-clock I built, modeled after <a href="http://technocage.com/%7Ecaskey/clok/">Caskey Dickson's idea and Java Clok implementation</a>.&nbsp; Clok is not built to give you the exact time in every time zone; for that, get <a href="http://www.timeanddate.com/worldclock/">another, more accurate, world clock</a>.&nbsp; Instead, my version of Clok is used to help me answer basic time related questions, like:<br /><br /><ul><li>Several of my co-workers are based in London; can I email them at the office, or are they at home?<br /></li><li>I need to call my girlfriend, but she's in Manila visiting family.&nbsp; Is it daytime there?</li><li>Some interesting world news just broke in Dubai; roughly what time is there?</li></ul><br />These questions are all a slight variation of the timeless (no pun intended), "what time is it?"&nbsp; The latest version of Clok can be found at <a href="http://clok.koli.ch/">http://clok.koli.ch</a>.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">1 - How Do I Read this Clok?</span><br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="clok-main-snap.jpg" src="http://mark.koli.ch/2010/01/20/clok-main-snap.jpg" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="134" width="200" /></span>Each column represents a single hour, in a 24-hour day.&nbsp; Each hour is color coded, according to the various pieces, or periods, of a typical day.&nbsp; As <a href="http://technocage.com/%7Ecaskey/clok/">explained here</a>, "sleeping is obviously the least productive and so that is represented in black. The morning is the time between sleeping and lunch, lunch is a time of recovery and planning for the rest of the day. Then there is the afternoon, which for many is the time when the majority of their work gets done. Finally comes evening, which is personal time, time spent with family, or time taking care of those responsibilities that have to do with running our lives and not earning a paycheck."&nbsp; The colors of each hour in the day directly correspond to these periods.<br /><br />Each row is a different time zone, modeled after <a href="http://en.wikipedia.org/wiki/Time_zone#Standard_time_zones">this list on Wikipedia</a>.&nbsp; And finally, the <span style="color: red; font-weight: bold;">red line</span> is the approximate current time in that time zone.<br /><br />Clok automatically updates itself every minute while open; no browser refresh is required.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">2 - The User Interface</span><br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="clok-mode-snap.jpg" src="http://mark.koli.ch/2010/01/20/clok-mode-snap.jpg" class="mt-image-right" style="margin: 0px 0px 10px 10px; float: right;" height="71" width="124" /></span>You may have noticed that when you mouseover each row on the Clok, four tiny buttons appear on the far right of each time zone.&nbsp; These represent the various modes: H for human, K for hacker/developer/coder, S for server, and R for raccoon (nocturnal).&nbsp; When clicked, these buttons change the mode of Clok.&nbsp; If you're a relatively normal individual with decent sleeping habits, you will find Human mode most useful.&nbsp; If you, or a friend across the world participates in a more nocturnal lifestyle, then you might enjoy Raccoon mode.<br /><br />If you find the time zone annotations annoying, and would like to hide them, click anywhere in the orange header bar and the name/UTC ID of each time zone should disappear.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">3 - Technical Details</span><br /><br />My version of Clok is pure CSS, JavaScript, and PHP (no Flash!).&nbsp; When the DOM is ready, AJAX is used behind the scenes to call a PHP controller on the server, which actually does the work of computing the time in every requested time zone.&nbsp; This controller returns a block of JSON that maps a time zone to the position of its red "current time" bar.&nbsp; The JavaScript loops over these JSON objects in the response, and uses <a href="http://docs.jquery.com/Effects/animate">jQuery to move (animate)</a> the red bars into position.&nbsp; To stay current, an interval fires once every 60,000 ms (1 minute) which triggers Clok to refresh itself.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">4 - Where Can I Find this Clok?</span><br /><br />Try <a href="http://clok.koli.ch/">http://clok.koli.ch</a>.<br /><br />Enjoy.]]>
        
    </content>
</entry>

<entry>
    <title>Java: JumpToLine, Jump or Seek to a Line in a File</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2010/01/java-jumptoline-jump-to-a-line-in-a-file.html?rss" />
    <id>tag:mark.koli.ch,2010://1.222</id>

    <published>2010-01-18T20:13:00Z</published>
    <updated>2010-01-18T20:15:20Z</updated>

    <summary><![CDATA[Seeking to a line number in a text file isn't too hard to implement in Java if you use a few common and trusted API's like Apache's Commons I/O library.&nbsp; Recently I needed some Java that could automatically seek to...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="howto" label="howto" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="java" label="java" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[Seeking to a line number in a text file isn't too hard to implement in Java if you use a few common and trusted API's like <a href="http://commons.apache.org/io/">Apache's Commons I/O library</a>.&nbsp; Recently I needed some Java that could automatically seek to a given line in a file and then remember the line number of the last line read.&nbsp; The next time I open the log reader, it should automatically seek itself to the last line read, and let me read any subsequent lines added by another user or process.&nbsp; This is perfect for log file monitoring: the first invocation of the reader would read lines 1 through X and the next invocation would read lines X+1 through Y, and so on.&nbsp; Using Apache's Commons I/O API, this isn't difficult at all.&nbsp; You may or may not know that the Commons I/O API contains a very convenient <a href="http://commons.apache.org/io/api-release/org/apache/commons/io/LineIterator.html">LineIterator class</a>, which lets the developer iterate over lines in a file using a <a href="http://java.sun.com/javase/6/docs/api/java/io/Reader.html">Reader</a>.<br /><br />With that in mind, <a href="http://mark.koli.ch/2010/01/18/JumpToLine.java">meet JumpToLine</a>, a somewhat hackish class I wrote that wraps Apache's Commons I/O LineIterator in a way that lets you <b>seek ahead</b> to a specific line in a file, and is smart enough to remember the last line read (so that you don't read the same line twice).]]>
        <![CDATA[<br /><span style="font-size: 1.1em; font-weight: bold;">Example #1</span><br /><br />Here's how you might use <b>JumpToLine to seek to line 10 in mylog.log</b>, then read that line and every line after it:<br /><br /><pre class="prettyprint">final JumpToLine jtl = new JumpToLine(new File("mylog.log"));<br /><br />try {<br />  // Open the underlying reader and LineIterator.<br />  jtl.open();<br />  // Seek to line 10; will throw a NoSuchElementException if<br />  // out of range.<br />  jtl.seek(10);<br />  // While there are any lines after and including line 10,<br />  // read them.<br />  while(jtl.hasNext()) {<br />    final String line = jtl.readLine();<br />    System.out.println(line);<br />  }<br />} catch (Exception e) {<br />  e.printStackTrace(System.err);<br />} finally {<br />  // Close the underlying reader and LineIterator.<br />  jtl.close();<br />}</pre><br /><span style="font-size: 1.1em; font-weight: bold;">Example #2</span><br /><br />Here's how you might use <b>JumpToLine to seek to the last line read in mylog.log</b>, and then read any subsequent lines added to the file since it was last read:<br /><br /><pre class="prettyprint">final JumpToLine jtl = new JumpToLine(new File("mylog.log"));<br /><br />try {<br />  // Open the underlying reader and LineIterator.<br />  jtl.open();<br />  // Seek to the last line read since we last tried to<br />  // read any lines from this file.<br />  jtl.seek();<br />  // While there are any more lines to read from the last<br />  // line read position, then read them.<br />  while(jtl.hasNext()) {<br />    final String line = jtl.readLine();<br />    System.out.println(line);<br />  }<br />  // For grins, what is the last line number we read?<br />  System.out.println("Last line number read: " +<br />      jtl.getLastLineRead());<br />} catch (Exception e) {<br />  e.printStackTrace(System.err);<br />} finally {<br />  // Close the underlying reader and LineIterator.<br />  jtl.close();<br />}</pre><br />Download <a href="http://mark.koli.ch/2010/01/18/JumpToLine.java">JumpToLine here</a>.&nbsp; Note that <b>JumpToLine is dependent on Apache's Commons I/O</b> API which you <a href="http://commons.apache.org/downloads/download_io.cgi">can download here</a>.<br />]]>
    </content>
</entry>

<entry>
    <title>Happy New Year&apos;s</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/12/happy-new-years-1.html?rss" />
    <id>tag:mark.koli.ch,2009://1.221</id>

    <published>2009-12-31T15:55:00Z</published>
    <updated>2009-12-31T16:10:22Z</updated>

    <summary><![CDATA[Last year I wrote up a quick blog post to ring in the New Year, highlighting some of my accomplishments and failures of 2008.&nbsp; In that spirit, keeping the tradition alive, here's my 2009 in a nutshell:I kicked off 2009...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="2010" label="2010" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="Thumbnail image for shanghai-fireworks-new.year.jpg" src="http://mark.koli.ch/assets_c/2008/12/shanghai-fireworks-new.year-thumb-180x240.jpg" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="240" width="180" /></span>Last year I <a href="http://mark.koli.ch/2008/12/happy-new-years.html">wrote up a quick blog post to ring in the New Year</a>, highlighting some of my accomplishments and failures of 2008.&nbsp; In that spirit, keeping the tradition alive, here's my 2009 in a nutshell:<br /><br />I kicked off 2009 <a href="http://mark.koli.ch/2009/01/howto-combating-movable-type-trackback-spam.html">figuring out how to block Trackback Spam</a>, and wrote up an <a href="http://mark.koli.ch/2009/01/four-more-reasons-why-i-dislike-facebook.html">opinionated piece re: why I dislike and therefore don't use, Facebook</a>.&nbsp; <a href="http://mark.koli.ch/2009/01/kolichcom-is-not-mycougarlandcom-classic-failure-to-properly-update-dns-records.html">MyCougarLand.com failed to update their DNS records</a>, which generated a lot of traffic (not the kind you would expect) to my blog.&nbsp; In late January, I discovered that I <a href="http://mark.koli.ch/2009/01/jury-acquits-calif-ex-sheriff-mike-carona-of-all-but-1-count-and-all-i-got-was-this-bottle-of-wine.html">own a bottle of wine from a convicted felon</a>.&nbsp; Ala February, I <a href="http://mark.koli.ch/2009/02/finally-joined-twitter-twittercommarkkolich.html">joined Twitter</a>, and heard that <a href="http://mark.koli.ch/2009/03/my-whois-firefox-plugin-featured-by-network-solutions-labs.html">Network Solutions featured my WHOIS Firefox Plugin on their homepage</a>.&nbsp; I bought a <a href="http://mark.koli.ch/2009/03/apc-br1500lcd-review.html">new battery backup UPS for my home data center</a>, after a little physics exercise to <a href="http://mark.koli.ch/2009/03/picking-the-right-ups-battery-backup-and-figuring-out-how-much-it-costs-to-self-host.html">discover the exact type of UPS I needed</a>.&nbsp; I launched <a href="http://mark.koli.ch/2009/03/howto-make-your-own-tiny-url-shortener-service.html">kolich.cc</a>, but then later built and released <a href="http://onyx.koli.ch/">Onyx</a>.&nbsp; With summer approaching, I made my own <a href="http://mark.koli.ch/2009/04/howto-make-your-own-solar-shield.html">solar shield out of cardboard and tinfoil</a> while thinking up <a href="http://mark.koli.ch/2009/04/10-awesome-htaccess-hacks-for-movable-type.html">ten awesome .htaccess hacks for Movable Type</a>.&nbsp; Continuing the awesomeness, I launched a <a href="http://mark.koli.ch/2009/04/kolichcom-is-now-mobile-device-ready-at-kolichmobi.html">mobile version of my blog at kolich.mobi</a> for all of my readers on mobile devices.&nbsp; Like any good year, a few of my <a href="http://mark.koli.ch/2009/05/howto-whole-disk-backups-with-dd-gzip-and-p7zip.html">systems crashed, then recovered</a> but I <a href="http://mark.koli.ch/2009/05/a-little-office-fun-with-hp-laserjet-printers.html">had fun with HP LaserJet printers at the office</a>.&nbsp; I registered <a href="http://mark.koli.ch/2009/05/httpkolichtel-an-interesting-experiment-whois-crossed-with-dns.html">kolich.tel</a>, <a href="http://mark.koli.ch/2009/06/whoa-whats-with-the-green-face-avatar-green4iran.html">went green for Iran</a>, and figured out how to <a href="http://mark.koli.ch/2009/07/howto-include-binary-image-data-in-cascading-style-sheets-css.html">include base-64 encoded binary data in CSS</a>.&nbsp; And, of course, I released <a href="http://mark.koli.ch/2009/07/gagawa-php-12-beta-released.html">Gagawa PHP 1.2</a> before <a href="http://mark.koli.ch/2009/08/mount-san-gorgonio-hike-to-the-summit-and-back-in-a-single-day.html">hiking Mount San Gorgonio</a> in the San Bernardino National Forest, but not before I <a href="http://mark.koli.ch/2009/09/remember-kids-an-http-content-length-is-the-number-of-bytes-not-the-number-of-characters.html">wasted several days at the office on a stupid bug</a>.&nbsp; I <a href="http://mark.koli.ch/2009/09/recently-acquired-kolich-blog-moving-soon.html">registered koli.ch</a> (thank you Network Solutions!), and <a href="http://mark.koli.ch/2009/10/even-better-a-big-5000-x-5000px-animated-gif-for-hot-linkers.html">gave hot linkers a nice big 5000x5000px animated GIF to chew on</a>.&nbsp; And to cap it all off, I <a href="http://mark.koli.ch/2009/11/the-twitter-abacus-silently-logging-every-link-you-click-on-twittercomabacus.html">blew the whistle on Twitter</a> (they're spying on us) and then dove into some C++ to <a href="http://mark.koli.ch/2009/12/uac-prompt-from-java-createprocess-error740-the-requested-operation-requires-elevation.html">discover how Windows UAC works</a>, which by the way, reminded me how much I hate Windows.<br /><br />2009, the end.]]>
        
    </content>
</entry>

<entry>
    <title>Experimenting with More Domains, and Fun with Http 302 Found</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/12/experimenting-with-more-domains.html?rss" />
    <id>tag:mark.koli.ch,2009://1.220</id>

    <published>2009-12-29T00:10:00Z</published>
    <updated>2009-12-29T00:11:30Z</updated>

    <summary><![CDATA[My somewhat narcissistic obsession with domain names involving my last name has recently driven me to purchase markkolich.com.&nbsp; The saga leading up to this acquisition started when I snagged koli.ch, and saw my ranking in a few search engine results...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="domains" label="domains" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="http" label="http" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="kolich" label="kolich" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="netsol" label="netsol" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="seo" label="seo" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[My somewhat <a href="http://mark.koli.ch/2009/11/contemplating-cctlds-vs-traditional-dot-com-domains.html">narcissistic obsession</a> with <a href="http://mark.koli.ch/2009/09/recently-acquired-kolich-blog-moving-soon.html">domain names involving my last name</a> has recently driven me to purchase <a href="http://markkolich.com/">markkolich.com</a>.&nbsp; The saga leading up to this acquisition started when I snagged <a href="http://koli.ch/">koli.ch</a>, and saw my ranking in a few search engine results absolutely plummet.&nbsp; I kicked off my own investigation and realized that Google doesn't seem to understand a custom domain hack of my last name.&nbsp; For the record, Yahoo! does.&nbsp; I suppose that's one thing Yahoo! actually does better than Google: properly interpreting domain hacks and other URL trickery.<br /><br />In any event, I setup the <a href="http://markkolich.com/">markkolich.com</a> VirtualHost to redirect to <a href="http://mark.koli.ch/">mark.koli.ch</a> via a <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.3">302 Found</a>.&nbsp; It seems that when Google and Yahoo encounter an <b>HTTP 302 Found</b>, they gracefully redirect their spiders to the destination but maintain the redirector's address.&nbsp; For example, this URL <a href="https://onyx.koli.ch/x2fr">https://onyx.koli.ch/x2fr</a> redirects to <a href="http://plugins.jquery.com/project/Timer">http://plugins.jquery.com/project/Timer</a> using a 302 Found.&nbsp; When spiders encounter <a href="https://onyx.koli.ch/x2fr">https://onyx.koli.ch/x2fr</a> they are forwarded to the destination, but add <a href="http://plugins.jquery.com/project/Timer">http://plugins.jquery.com/project/Timer</a> under <a href="https://onyx.koli.ch/x2fr">https://onyx.koli.ch/x2fr</a> to their index.&nbsp; In other words, the jQuery Timer plugin page will be listed under the URL <a href="https://onyx.koli.ch/x2fr">https://onyx.koli.ch/x2fr</a> in the search results!&nbsp; Interestingly enough, HTTP 301 Moved Permanently does not exhibit this behavior.<br /><br />For the time being, I'm going to see how the new domain stacks up against koli.ch.&nbsp; My expectation is that spiders will index <b>markkolich.com</b>, see a 302 Found, and add it to their index as is without worrying about the destination address.<br /><br />Stay tuned.]]>
        
    </content>
</entry>

<entry>
    <title>A Better Way to Include JavaScript and CSS in your Web-Apps (Break Proxy and Browser Caching too!)</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/12/a-better-way-to-include-javascript-and-css-in-your-web-apps-break-proxy-caching-too.html?rss" />
    <id>tag:mark.koli.ch,2009://1.219</id>

    <published>2009-12-23T02:30:00Z</published>
    <updated>2009-12-23T02:42:44Z</updated>

    <summary><![CDATA[Most web-apps include a number of JavaScript and CSS files, that in most cases, need to be included/sourced on every page of the application.&nbsp; Doing so can bring up a few, while trivial, often annoying problems:Dependencies between scripts.&nbsp; For example,...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="css" label="css" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="gagawa" label="gagawa" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="javascript" label="javascript" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="php" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[Most web-apps include a number of JavaScript and CSS files, that in most cases, need to be included/sourced on every page of the application.&nbsp; Doing so can bring up a few, while trivial, often annoying problems:<br /><br /><ul><li>Dependencies between scripts.&nbsp; For example, if you attempt to use a jQuery plugin before the base jQuery library has loaded into the browser you'll obviously see an error.&nbsp; Working out these dependencies before hand can save you a few headaches.<br /><br /></li><li>Web-browsers and web-proxies cache your JavaScript and CSS files too often.&nbsp; I can't count the number of times on two hands when I made a small change to a JavaScript file, saved the change, refreshed my browser, and .... nothing.&nbsp; The web-browser didn't load the changed file; instead, it loaded the JavaScript from cache.&nbsp; Using a mechanism to always force the web-browser or web-proxy to reload your JavaScript and CSS can save you time.<br /></li></ul>Meet <a href="http://code.google.com/p/gagawa/">Gagawa</a>, an open source object-oriented HTML generation engine written in Java and PHP.&nbsp; You probably wouldn't build an entire site with Gagawa, but it's absolutely perfect for small HTML tasks, like solving this irritating dependency and caching problem.]]>
        <![CDATA[<br />Using Gagawa PHP, problem solved:<br /><br /><pre class="prettyprint">&lt;?php<br /><br />require_once("gagawa.php");<br /><br />$js = array(<br />  // The base jQuery library, needed by all other<br />  // jQuery plugins and your jQuery code.<br />  "/js/jquery-1.3.2.min.js",<br /><br />  // jQuery plugins; jQuery base needs to be loaded<br />  // before you source these.<br />  "/js/jquery.ui.min.js",<br />  "/js/jquery.hotkeys.min.js",<br /><br />  // Some other, unrelated JavaScript library like<br />  // underscore JS.<br />  "/js/underscore.min.js",<br /><br />  // Your JavaScript, with dependencies on jQuery, the<br />  // jQuery plugins and Underscore JS, should be loaded<br />  // last.<br />  "/js/yourjs.min.js"<br />  );<br /><br />$css = array(<br />  "/css/othercss.min.css",<br />  "/css/yourcss.css"<br />  );<br /><br />// Build &lt;script ...&gt;&lt;/script&gt; for each JavaScript<br />// we need, in order. Use time() to break browser<br />// caching.<br />foreach ($js as $url) {<br />  $script = new Script("text/javascript");<br />  echo $script-&gt;setSrc($url."?".time())-&gt;write();<br />  }<br /><br />// Build &lt;link ...&gt;&lt;/link&gt; for each CSS file we need,<br />// in order.  Use time() to break browser caching.<br />foreach ($css as $url) {<br />  $link = new Link();<br />  $link-&gt;setHref($url."?".time())-&gt;setRel("stylesheet")-&gt;setType("text/css");<br />  echo $link-&gt;write();<br />  }<br /><br />?&gt;</pre><br />This code snippet generates some solid output that fits nicely into the &lt;head&gt; of your HTML:<br /><br /><pre class="prettyprint">&lt;script type="text/javascript" src="/js/jquery-1.3.2.min.js?1261531930"&gt;&lt;/script&gt;<br />&lt;script type="text/javascript" src="/js/jquery.ui.min.js?1261531930"&gt;&lt;/script&gt;<br />&lt;script type="text/javascript" src="/js/jquery.hotkeys.min.js?1261531930"&gt;&lt;/script&gt;<br />&lt;script type="text/javascript" src="/js/underscore.min.js?1261531930"&gt;&lt;/script&gt;<br />&lt;script type="text/javascript" src="/js/yourjs.js?1261531930"&gt;&lt;/script&gt;<br /><br />&lt;link href="/css/yourcss.css?1261531930" rel="stylesheet" type="text/css"&gt;&lt;/link&gt;</pre><br />Note that each JavaScript and CSS file is loaded and added to your HTML, in order as desired, eliminating any stray dependency issues.&nbsp; Also, note that I'm appending "?" and the current <a href="http://en.wikipedia.org/wiki/Unix_time">Epoch timestamp</a> to the end of the JavaScript source URL, and CSS Href.&nbsp; PHP's <a href="http://php.net/manual/en/function.time.php">time()</a> function works nicely here because I'll get a different timestamp on each page load.&nbsp; And as far as the browser (or a web-proxy) is concerned, "/js/yourjs.min.js?<b>1261534594</b>" is different than "/js/yourjs.min.js?<b>1234567890</b>" and will be treated as such.&nbsp; With this mechanism, every time you refresh the page, the browser will be forced to reload and reevaluate your scripts.&nbsp; Say goodbye to browsers caching and not recognizing your changes!<br /><br />You can download the latest version of Gagawa at <a href="http://code.google.com/p/gagawa/downloads/list">http://code.google.com/p/gagawa/downloads/list</a>.&nbsp; Also, if you're interested, other Gagawa examples can be found on the <a href="http://code.google.com/p/gagawa/">Gagawa homepage</a>.<br /><br />If you're looking for a real, live, example of this technique in action <a href="https://onyx.koli.ch/">check out Onyx</a>.<br /><br />Merry Christmas.<br />]]>
    </content>
</entry>

<entry>
    <title>Writing Your Own Animation Loop with javax.swing.Timer</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/12/writing-your-own-animation-loop-with-javaxswingtimer.html?rss" />
    <id>tag:mark.koli.ch,2009://1.218</id>

    <published>2009-12-20T20:55:00Z</published>
    <updated>2009-12-21T00:03:17Z</updated>

    <summary><![CDATA[In the last week or so, while working on some Java Swing code for a project at work, I hit Swing bug #4480705.&nbsp; When the user clicked a button to start an "activation", I changed the ImageIcon on a JButton...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="gui" label="gui" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="howto" label="howto" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="java" label="java" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="swing" label="swing" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/12/20/javax-swing-timer-demo-screencap.jpg"><img alt="javax-swing-timer-demo-screencap.jpg" src="http://mark.koli.ch/assets_c/2009/12/javax-swing-timer-demo-screencap-thumb-180x301.jpg" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="301" width="180" /></a></span>In the last week or so, while working on some Java Swing code for a project at work, I hit <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4480705">Swing bug #4480705</a>.&nbsp; When the user clicked a button to start an "activation", I changed the ImageIcon on a JButton to an animated GIF to indicate activity (e.g., "we're doing something, please wait").&nbsp; So, while the application was busy, I disabled the JButton and set its ImageIcon to an <a href="http://www.ajaxload.info/">animated spinner</a>.&nbsp; Loading this animated GIF and setting it on the JButton using setIcon() caused the Java Swing EventQueue threads to deadlock, <a href="http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=4480705">as described here</a>, and my entire application hung.&nbsp; Well technically speaking, not the entire application, but just the Swing portion of the JVM.&nbsp; Clearly, Swing doesn't like animated GIF's as much as the Sun documentation claims.<br /><br />The solution to this deadlock is to avoid using animated GIF's all together, and write your own animate loop using <a href="http://java.sun.com/javase/6/docs/api/javax/swing/Timer.html">javax.swing.Timer</a>.&nbsp; That's exactly what I did, and it works great.<br /><br />For your viewing pleasure, I <a href="http://mark.koli.ch/2009/12/20/SwingTimerDemo.java">wrote up a quick demo/example</a> (see Beavis and Butthead screencap) which demonstrates the use of <b>javax.swing.Timer</b> and how you can integrate it into your Swing application.<br /><br />Download <a href="http://mark.koli.ch/2009/12/20/SwingTimerDemo.java">just the source</a>, or <a href="http://mark.koli.ch/2009/12/20/javax-swing-timer-example.zip">the full Eclipse project</a>.<br /><br />Rock on.]]>
        
    </content>
</entry>

<entry>
    <title>UAC Prompt From Java: CreateProcess error=740, The requested operation requires elevation (ShellExecuteEx Runas Example)</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/12/uac-prompt-from-java-createprocess-error740-the-requested-operation-requires-elevation.html?rss" />
    <id>tag:mark.koli.ch,2009://1.217</id>

    <published>2009-12-18T17:35:00Z</published>
    <updated>2009-12-18T18:30:48Z</updated>

    <summary><![CDATA[I just finished an absolutely monstrous project at work that involved quite a bit of Java and a little Visual C++.&nbsp; The latter part of this project involved writing some VC++ that interactively upgraded a piece of Java based software...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="howto" label="howto" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="java" label="java" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="microsoft" label="microsoft" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="uac" label="uac" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="vista" label="vista" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="windows" label="windows" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/12/11/uac-prompt-sucks.jpg"><img alt="uac-prompt-sucks.jpg" src="http://mark.koli.ch/assets_c/2009/12/uac-prompt-sucks-thumb-200x163.jpg" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="163" width="200" /></a></span>I just finished an absolutely monstrous project at work that involved quite a bit of Java and a little Visual C++.&nbsp; The latter part of this project involved writing some VC++ that interactively upgraded a piece of Java based software installed on a PC.&nbsp; This sounds relatively mundane, but frankly, it wasn't.&nbsp; I ended up spending almost of week of engineering effort, writing code that gracefully understands how to deal with <a href="http://en.wikipedia.org/wiki/User_Account_Control">Window's User Account Control (UAC)</a>.&nbsp; If you're not familiar with UAC, it's that incredibly annoying security mechanism in Vista, and Win7, that prompts the user for confirmation if a piece of software attempts to make any changes to a protected location on the computer.&nbsp; I appreciate what Microsoft was trying to accomplish with UAC, but it couldn't be more painful for a developer to work with.&nbsp; Not to mention I couldn't find any decent documentation from Microsoft that discussed how to properly integrate your software with UAC.&nbsp; I found a lot of marketing type documents and other nonsense through <a href="http://msdn.microsoft.com/en-us/default.aspx">MSDN</a>, and eventually gave up in disgust.&nbsp; All I wanted was a simple document, ideally one titled "this is how to open a UAC prompt in your application."<br /><br />In the end, I figured out how to deal with UAC by studying the source code of the <b>Mozilla Firefox Updater</b> (knowing that Firefox is open-source, so I can look at its code, and it always seems to update itself just fine on my development Win 7 and Vista boxes).&nbsp; This post is an attempt to document how I built an application that understands, and gracefully handles UAC.<br />]]>
        <![CDATA[So, you need to modify a file or directory in a protected location on the file system, eh?&nbsp; Like "Program Files"?&nbsp; Or, maybe you need to register a DLL or other library as an Administrator?&nbsp; In Vista, and Windows 7, your application <b>can't do any of these things without elevating itself to an Administrator</b>, or privileged user.&nbsp; Your application running as a normal user, must programmatically elevate itself to an Administrator before it has permission to run these privileged operations.&nbsp; Your application needs to trigger Windows to display a UAC prompt.&nbsp; Welcome to the painful world of Windows <b>User Account Control</b>.<br /><br />Here are several important points to remember about UAC (things I learned the hard way):<br /><br /><ul><li>Unprivileged applications running as normal users, cannot simply fork another process to trigger a UAC prompt.&nbsp; Even if the "<b>requestedExecutionLevel</b>" of your application manifest is "<b>requireAdministrator</b>".&nbsp; Using _spawnv(), exec(), etc. with a binary that has "requireAdministrator" set in its manifest will NOT work.&nbsp; For example, a Java app running as a normal user cannot spawn a process with privileged access; even in the process Java is trying to spawn has the correct "requireAdministrator" manifest property.<br /><br /></li><li>A unprivileged application can only trigger a UAC prompt using the <a href="http://msdn.microsoft.com/en-us/library/bb762153%28VS.85%29.aspx">ShellExecute</a> or <a href="http://msdn.microsoft.com/en-us/library/bb762154%28VS.85%29.aspx">ShellExecuteEx</a> shell functions, provided via <b>shell32.lib</b>.<br /><br /></li><li>Setting <b>requireAdministrator</b> in your application manifest only appears to open a UAC prompt when a user double clicks on your executable in Windows Explorer.<br /></li></ul><br /><br /><span style="font-size: 1.1em; font-weight: bold;">1 - The Application Manifest</span><br /><br />Several resources claim you can trigger a UAC by simply inserting an <a href="https://onyx.koli.ch/get/3101/uac-manifest-xml.txt">application manifest</a> into the assembly of your application binary.&nbsp; An application manifest that triggers a UAC looks something like this:<br /><br /><pre class="prettyprint">&lt;assembly xmlns="urn:schemas-microsoft-com:asm.v1" manifestVersion="1.0"&gt;<br />  &lt;assemblyIdentity version="1.0.0.0" processorArchitecture="X86"<br />       name="yourapp.exe" type="win32"&gt;<br />  &lt;/assemblyIdentity&gt;<br />  &lt;description&gt;Some Application Description&lt;/description&gt;<br />  &lt;dependency&gt;<br />    &lt;dependentAssembly&gt;<br />      &lt;assemblyIdentity type="win32"<br />         name="Microsoft.Windows.Common-Controls"<br />         version="6.0.0.0" processorArchitecture="*"<br />         publicKeyToken="6595b64144ccf1df" language="*" /&gt;<br />    &lt;/dependentAssembly&gt;<br />  &lt;/dependency&gt;<br />  &lt;ms_asmv3:trustInfo xmlns:ms_asmv3="urn:schemas-microsoft-com:asm.v3"&gt;<br />    &lt;ms_asmv3:security&gt;<br />      &lt;ms_asmv3:requestedPrivileges&gt;<br />        &lt;ms_asmv3:requestedExecutionLevel level="requireAdministrator"<br />            uiAccess="false"&gt;<br />        &lt;/ms_asmv3:requestedExecutionLevel&gt;<br />      &lt;/ms_asmv3:requestedPrivileges&gt;<br />    &lt;/ms_asmv3:security&gt;<br />  &lt;/ms_asmv3:trustInfo&gt;<br />&lt;/assembly&gt;<br /></pre><br />Note the "level=requireAdministrator" attribute in this XML that I alluded to earlier.&nbsp; This works, but only when the user double-clicks your executable, or launches it from the Start Menu.&nbsp; This is hardly sufficient for an application that needs to become an Administrator when updating itself.&nbsp; You'll need something more.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">2 - Basic UAC Flow</span><br /><br />Ok, so before you dig into it, I thought it might be helpful to explain the basic flow of a UAC aware application and how everything fits together.&nbsp; Normally, your application runs as an unprivileged user.&nbsp; But, sometimes it needs to be an Administrator (to do whatever).&nbsp; So, here's the basic idea, in <b>pseudo code</b>:<br /><br /><pre class="prettyprint">int main (int argc, char **argv) {<br /><br />  HRESULT operation = tryToDoSomethingPrivileged();<br /><br />  if (operation == ACCESS_DENIED &amp;&amp; !alreadyElevated) {<br /><br />    // Spawn a copy of ourselves, via ShellExecuteEx().<br />    // The "runas" verb is important because that's what<br />    // internally triggers Windows to open up a UAC prompt.<br />    HANDLE child = ShellExecuteEx(argc, argv, "runas");<br /><br />    if (child) {<br />      // User accepted UAC prompt (gave permission).<br />      // The unprivileged parent should wait for<br />      // the privileged child to finish.<br />      WaitForSingleObject(child, INFINITE);<br />      CloseHandle(pid);<br />    }<br />    else {<br />      // User rejected UAC prompt.<br />      return FAILURE;<br />    }<br /><br />    return SUCCESS;<br /><br />  }<br />  <br />  return SUCCESS;  <br /><br />}</pre><br />On Windows XP, or other versions of Windows with UAC disabled, note that the user will simply run right through the code fragment without any prompt.&nbsp; However, if UAC is enabled, the privileged operation will be rejected meaning the application needs to spawn a copy of itself using <a href="http://msdn.microsoft.com/en-us/library/bb762154%28VS.85%29.aspx">ShellExecuteEx</a>.<br /><br />Here's a quick code snippet showing the usage of ShellExecuteEx to open a UAC prompt:<br /><br /><pre class="prettyprint">SHELLEXECUTEINFO sinfo;<br />memset(&amp;sinfo, 0, sizeof(SHELLEXECUTEINFO));<br />sinfo.cbSize       = sizeof(SHELLEXECUTEINFO);<br />sinfo.fMask        = SEE_MASK_FLAG_DDEWAIT |<br />	               SEE_MASK_NOCLOSEPROCESS;<br />sinfo.hwnd         = NULL;<br />sinfo.lpFile       = argv[0];<br />sinfo.lpParameters = spawnCmdLine;<br />sinfo.lpVerb       = L"runas"; // &lt;&lt;-- this is what makes a UAC prompt show up<br />sinfo.nShow        = SW_SHOWMAXIMIZED;<br /><br />// The only way to get a UAC prompt to show up<br />// is by calling ShellExecuteEx() with the correct<br />// SHELLEXECUTEINFO struct.  Non privlidged applications<br />// cannot open/start a UAC prompt by simply spawning<br />// a process that has the correct XML manifest.<br />BOOL result = ShellExecuteEx(&amp;sinfo);<br /></pre><br />Note the "runas" lpVerb in the <a href="http://msdn.microsoft.com/en-us/library/bb759784%28VS.85%29.aspx">SHELLEXECUTEINFO struct</a>; this is the verb that triggers windows to "shell execute" your application via UAC. <br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">3 - An Example</span><br /><br />I whipped up a quick yet complete UAC example in Visual C++.&nbsp; You can download the <a href="http://mark.koli.ch/2009/12/18/uac-example.zip">entire VC++ project here</a>.&nbsp; Or, download just the <a href="http://mark.koli.ch/2009/12/18/uac-example-bin.zip">pre-compiled release binary</a> if you want to experiment.&nbsp; Or, perhaps you'd <a href="http://mark.koli.ch/2009/12/18/uac-example.cpp">prefer just the CPP source code</a>.&nbsp; My VC++ example code was built using <a href="http://www.microsoft.com/Express/VC/">Visual C++ 2008 Express Edition, which you can download for free here</a>.<br /><br />The UAC demo, aptly named "uac-example", should be run from a command prompt with a single argument:<br /><br /><pre class="prettyprint">C:\&gt; uac-example.exe &lt;working directory&gt;</pre><br />My UAC example works by attempting to create an empty file in the working directory specified by the command line argument.&nbsp; If the working directory happens to be a Windows protected location on the file system, like "C:\Program Files", the example app will initially fail to create this empty file.&nbsp; In that case, it will re-spawn a copy of itself via a UAC prompt and try again as an Administrator.&nbsp; If the app successfully created the file when elevated, you will see the following success message:<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="worked-with-uac.png" src="http://mark.koli.ch/2009/12/18/worked-with-uac.png" class="mt-image-none" style="" height="169" width="224" /></span><br /><br />If you reject the UAC prompt (clicked Deny), you'll see this:<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="rejected-uac-prompt.png" src="http://mark.koli.ch/2009/12/18/rejected-uac-prompt.png" class="mt-image-none" style="" height="169" width="259" /></span><br /><br />If your working directory isn't a Windows protected directory, like %APPDATA% or your Desktop, you'll immediately see this without a UAC prompt:<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="worked-no-uac-req.png" src="http://mark.koli.ch/2009/12/18/worked-no-uac-req.png" class="mt-image-none" style="" height="169" width="263" /></span><br /><br />Here are some example working directories you might like to try:<br /><br /><pre class="prettyprint">C:\&gt; uac-example.exe "C:\Program Files"<br />C:\&gt; uac-example.exe "%APPDATA%"<br />C:\&gt; uac-example.exe .<br /></pre><br />Please note that if you're using a UAC capable computer, but UAC is turned off, no matter what directory you give to my example app it will always say "Worked (no UAC required)!"&nbsp; This is obviously because if UAC is turned off, Windows isn't actively protecting any locations on the file system, so any running process can pretty much do whatever it wants.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">4 - Opening a UAC Prompt From Java</span><br /><br />If you need to open a UAC prompt from Java, you should know that there is no way to do so without writing your own native app.  Your Java code should call your UAC enabled native app to do "whatever it needs to do" in a privileged mode.&nbsp; Here's an example using my uac-example app with Java's ProcessBuilder:<br /><br /><pre class="prettyprint">import java.io.File;<br />import java.util.ArrayList;<br />import java.util.List;<br /><br />public class UACTester {<br />  <br />  private static final String UAC_EXAMPLE_EXE = "uac-example.exe";<br /><br />  public static void main(String[] args) {<br /><br />    final File uacExample = new File(UAC_EXAMPLE_EXE);<br />    File workingDir;<br />    <br />    try {<br />      workingDir = new File(args[0]);<br />    } catch ( Exception e ) {<br />      workingDir = new File(".");<br />    }<br />    <br />    try {<br />      // Build the command list to be given to the ProcessBuilder<br />      final List&lt;String&gt; cmdArgs = new ArrayList&lt;String&gt;();<br />      cmdArgs.add(uacExample.getAbsolutePath());<br />      cmdArgs.add(workingDir.getAbsolutePath());<br /><br />      // Create a process, and start it.<br />      final ProcessBuilder p = new ProcessBuilder(cmdArgs);<br />      p.directory(new File("."));<br />      p.start();<br />    } catch (Throwable t) {<br />      t.printStackTrace(System.out);<br />    }<br />    <br />  }<br /><br />}<br /></pre><br />Note that if your Java app attempts to spawn a process as an Administrator, you'll most likely see an exception like this:<br /><br /><pre class="prettyprint">CreateProcess error=740, The requested operation requires elevation<br />...<br /></pre><br />If the argument given to my sample Java app is a UAC protected directory, the native executable it calls will need to open a UAC prompt.<br /><br />Yea. <br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">5 - Links and Other Resources</span><br /><br />Here are a few links and other resources I gathered while writing this blog post and integrating UAC support into a project at work:<br /><br /><ul><li><a href="http://blogs.msdn.com/cheller/archive/2006/08/24/how-to-embed-a-manifest-in-an-assembly-let-me-count-the-ways.aspx">How to Embed a Manifest into an Assembly in Visual Studio</a></li><li><a href="http://msdn.microsoft.com/en-us/magazine/cc163486.aspx#S4">MSDN: Designing Apps that Require Administrator Privileges</a></li><li><a href="http://www.geektieguy.com/2007/08/25/getting-vista-uac-elevation-to-work-for-web-deployed-clickonce-applications/">Getting Vista UAC elevation to work for web deployed ClickOnce applications</a></li><li><a href="https://onyx.koli.ch/x2e3">Onyx: UAC Resources (my personal UAC resources collection)</a></li></ul><br />That's it!&nbsp; Hope this helps someone else out there struggling with UAC.<br />]]>
    </content>
</entry>

<entry>
    <title>SEO: Contemplating ccTLD&apos;s vs. Traditional Dot-Com Domains</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/11/contemplating-cctlds-vs-traditional-dot-com-domains.html?rss" />
    <id>tag:mark.koli.ch,2009://1.216</id>

    <published>2009-12-01T07:13:00Z</published>
    <updated>2009-12-01T07:41:30Z</updated>

    <summary><![CDATA[You're reading this blog, so you may have noticed that I'm operating under mark.koli.ch; a domain hack of my name using the .ch Swiss ccTLD.&nbsp; The previously retired kolich.com sits idle, serving up HTTP 410 Gone's when appropriate.&nbsp; Interestingly enough,...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="cctld" label="cctld" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="domains" label="domains" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="seo" label="seo" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/30/delicious-vs-kolich-domain-hack-google.png"><img alt="delicious-vs-kolich-domain-hack-google.png" src="http://mark.koli.ch/assets_c/2009/11/delicious-vs-kolich-domain-hack-google-thumb-200x207.png" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="207" width="200" /></a></span>You're reading this blog, so you may have noticed that I'm operating under <b>mark.koli.ch</b>; a domain hack of my name using the <a href="http://en.wikipedia.org/wiki/Country_code_top-level_domain">.ch Swiss ccTLD</a>.&nbsp; The previously retired <b>kolich.com</b> sits idle, serving up <a href="http://mark.koli.ch/2009/11/rediscovering-http-410-gone.html">HTTP 410 Gone's</a> when appropriate.&nbsp; Interestingly enough, however, since making the switch to <b>koli.ch</b> I've noticed a significant drop-off of in traffic to my blog.&nbsp; I suspect that the switch to a non-traditional ccTLD, like .ch, is to blame.&nbsp; Since the switch to a dot-ch domain incoming traffic to my blog, directly from Google, has fallen off significantly.&nbsp; It seems that Google's ranking algorithms consider dot-com domains "more valuable" or relevant, than sites hosted under various ccTLD's.&nbsp; Equally annoying is that Google, and even Yahoo, fail to properly understand my <b>koli.ch</b> domain hack at all.&nbsp; In other words, when someone issues a search for "kolich blog", Google and Yahoo fail to properly interpret "koli.ch" as "kolich".&nbsp; Therefore, search results with "kolich" as a whole-word in the URL appear higher in the search results than others with "koli.ch".&nbsp; Google must understand some domain-hacks though, given that they seem to <a href="http://mark.koli.ch/2009/11/30/delicious-vs-kolich-domain-hack-google.png">recgonize "del.icio.us"</a> as "delicious" in their search results.&nbsp; I'm still wondering how to tell Google that "koli.ch" should be interpreted as "kolich", a whole word.<br /><br />It's funny though because I've often criticized and poked fun at folks touting various SEO (search engine optimization) techniques.&nbsp; My opinion has been that as long as you build a great site, with decent information on it, "they" (visitors) will come.&nbsp; I still live by that mantra but it's clear to me now that Google, and other search engines, really do pay careful attention to your domain.&nbsp; Even so, I have no plans to switch back to <b>kolich.com</b>, but if you are considering a switch to a domain hack like <b>mark.koli.ch</b> you should expect or at least be aware of the changes this move might bring to your page rank in various search results.<br />]]>
        
    </content>
</entry>

<entry>
    <title>Apache Tip: Deny TRACE and TRACK Requests with mod_rewrite</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/11/apache-tip-deny-trace-and-track-requests-with-mod-rewrite.html?rss" />
    <id>tag:mark.koli.ch,2009://1.215</id>

    <published>2009-11-14T18:41:46Z</published>
    <updated>2009-11-15T06:20:08Z</updated>

    <summary><![CDATA[It's long been rumored that exposing the HTTP TRACE and TRACK methods on your web-server can open the door to a number of miscellaneous vulnerabilities, including cookie thefts and other cross-site tracing attacks.&nbsp; Many resources out there claim you should...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="apache" label="apache" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="howto" label="howto" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="mod_rewrite" label="mod_rewrite" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/14/curl-reject-trace-method.png"><img alt="curl-reject-trace-method.png" src="http://mark.koli.ch/assets_c/2009/11/curl-reject-trace-method-thumb-200x155.png" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="155" width="200" /></a></span>It's long been rumored that exposing the HTTP <a href="http://www.cgisecurity.com/questions/httptrace.shtml">TRACE</a> and <a href="http://osvdb.org/5648">TRACK</a> methods on your web-server can open the door to a number of miscellaneous vulnerabilities, including <a href="http://osvdb.org/5648">cookie thefts</a> and other <a href="http://www.karakas-online.de/forum/viewtopic.php?t=341">cross-site tracing attacks</a>.&nbsp; Many resources out there claim you should configure you web-server to flat-out reject TRACE and TRACK requests, and I agree with them.&nbsp; Generally speaking, there's really no good need (that I've found) that would require or make use of TRACE or TRACK.&nbsp; With that said, if you're running Apache, it's fairly easy to reject TRACE and TRACK using <a href="http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html">mod_rewrite</a>:<br /><br /><pre class="prettyprint">RewriteCond %{REQUEST_METHOD} ^TRACE [NC,OR]<br />RewriteCond %{REQUEST_METHOD} ^TRACK [NC]<br />RewriteRule ^/(.*)$ - [F,L]</pre><br />You can prove to yourself that this works, by using a tool like <b>curl</b> to issue an HTTP TRACE and TRACK to your newly secured web-server.&nbsp; Use the <b>-X option with curl</b> to specify the HTTP request type:<br /><br /><pre class="prettyprint">#/&gt; curl -v -A "Curl" -X TRACE mark.koli.ch<br />* About to connect() to mark.koli.ch port 80 (#0)<br />*   Trying 24.130.215.240... connected<br />* Connected to mark.koli.ch (24.130.215.240) port 80 (#0)<br />&gt; TRACE / HTTP/1.1<br />&gt; User-Agent: Curl<br />&gt; Host: mark.koli.ch<br />&gt; Accept: */*<br />&gt;<br />&lt; HTTP/1.1 403 Forbidden<br />&lt; Date: Sat, 14 Nov 2009 18:53:06 GMT<br />&lt; Server: Apache<br />&lt; Content-Length: 202<br />&lt; Connection: close<br />&lt; Content-Type: text/html; charset=iso-8859-1<br />&lt;<br />&lt;!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"&gt;<br />&lt;html&gt;&lt;head&gt;<br />&lt;title&gt;403 Forbidden&lt;/title&gt;<br />&lt;/head&gt;&lt;body&gt;<br />&lt;h1&gt;Forbidden&lt;/h1&gt;<br />&lt;p&gt;You don't have permission to access /<br />on this server.&lt;/p&gt;<br />&lt;/body&gt;&lt;/html&gt;<br />* Closing connection #0<br /></pre><br />Yep, works nicely.&nbsp; One thing that slightly annoys me though is that the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec9.html#sec9.2">HTTP OPTIONS method</a> still reports <a href="http://mark.koli.ch/2009/11/14/options-still-shows-trace-method.png">that my server supports TRACE, even though I clearly don't anymore</a>.&nbsp; A quick Google search reports that many other folks have had the same concern, with no clear resolution.]]>
        
    </content>
</entry>

<entry>
    <title>The Twitter Abacus: Silently Logging Every Link You Click On (twitter.com/abacus)</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/11/the-twitter-abacus-silently-logging-every-link-you-click-on-twittercomabacus.html?rss" />
    <id>tag:mark.koli.ch,2009://1.214</id>

    <published>2009-11-13T23:27:00Z</published>
    <updated>2009-11-13T23:32:58Z</updated>

    <summary><![CDATA[This afternoon, I was using the HttpFox Firefox extension to analyze some web-traffic for a work related project.&nbsp; With HttpFox still running in the background (I forgot I left it running), I opened another tab and navigated over to my...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="commonsense" label="commonsense" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="geterdone" label="geterdone" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="javascript" label="javascript" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="twitter" label="twitter" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="whatcouldpossiblygowrong" label="whatcouldpossiblygowrong" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[This afternoon, I was using the <a href="https://addons.mozilla.org/en-US/firefox/addon/6647">HttpFox Firefox extension</a> to analyze some web-traffic for a work related project.&nbsp; With HttpFox still running in the background (I forgot I left it running), I opened another tab and navigated over to <a href="http://twitter.com/markkolich">my Twitter page</a> to check out a few things.&nbsp; I clicked a few links, replied to a few folks, etc.&nbsp; Switching back to my work project, I closed Twitter and re-opened HttpFox.&nbsp; Well well well, <a href="http://mark.koli.ch/2009/11/13/twitter-abacus-tracking-clicked-links.png">what do we have here</a>.&nbsp; I discovered that Twitter silently rolled out some JavaScript that actively tracks every link I click on.&nbsp; Any link, in any Tweet, that you click on is silently reported back to Twitter behind the scenes.&nbsp; Looking at the output of HttpFox pretty much proves it:<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/13/twitter-abacus-tracking-clicked-links.png"><img alt="twitter-abacus-tracking-clicked-links.png" src="http://mark.koli.ch/assets_c/2009/11/twitter-abacus-tracking-clicked-links-thumb-400x260.png" class="mt-image-none" style="" height="260" width="400" /></a></span><br /><br />Looks like <a href="http://twitter.com/abacus">twitter.com/abacus</a> is some type of web-service used by Twitter to log what links we're all clicking on.&nbsp; I'm curious why Twitter cares what links we're clicking on.<br /><br />BTW, Twitter, if you're reading this, the HTTP Content-Type in your responses from /abacus are incorrect.&nbsp; You're phoning home by creating a new Image() in <a href="http://a3.twimg.com/a/1258070043/javascripts/twitter.js">your core JavaScript</a> like so:<br /><br /><pre class="prettyprint">(new Image()).src="/abacus?"+$.param(A);</pre><br />But your Content-Type from this request is <b>text/html</b>, which could cause problems in a few browsers.&nbsp; If you're going to use an Image(), the returned Content-Type from your /abacus web-service should be that of an image: image/jpeg, image/png, image/gif, etc.<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/13/twitter-abacus-tracking-clicked-links-wrong-content-type.png"><img alt="twitter-abacus-tracking-clicked-links-wrong-content-type.png" src="http://mark.koli.ch/assets_c/2009/11/twitter-abacus-tracking-clicked-links-wrong-content-type-thumb-400x102.png" class="mt-image-none" style="" height="102" width="400" /></a></span><br /><div><br />Cheers.<br /></div>]]>
        
    </content>
</entry>

<entry>
    <title>Rediscovering HTTP 410 Gone</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/11/rediscovering-http-410-gone.html?rss" />
    <id>tag:mark.koli.ch,2009://1.213</id>

    <published>2009-11-11T05:47:00Z</published>
    <updated>2009-11-11T05:58:29Z</updated>

    <summary><![CDATA[This evening, I accomplished another important milestone in the kolich.com migration process.&nbsp; Since acquiring koli.ch, I've been slowly migrating my blog and all of its resources to my new online home under mark.koli.ch.&nbsp; This migration began back in September 2009,...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="http" label="http" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="whatcouldpossiblygowrong" label="whatcouldpossiblygowrong" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/10/http-410-gone.png"><img alt="http-410-gone.png" src="http://mark.koli.ch/assets_c/2009/11/http-410-gone-thumb-200x127.png" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="127" width="200" /></a></span>This evening, I accomplished another important milestone in the <b>kolich.com</b> migration process.&nbsp; Since acquiring <b>koli.ch</b>, I've been slowly migrating my blog and all of its resources to my new online home under <b>mark.koli.ch</b>.&nbsp; This migration began back in September 2009, as I <a href="http://mark.koli.ch/2009/09/recently-acquired-kolich-blog-moving-soon.html">aptly described here</a> and <a href="http://mark.koli.ch/2009/10/successfully-moved-my-blog-to-kolich.html">here</a>.&nbsp; Only problem is, I still see a ton of bots and other users requesting resources under <b>kolich.com</b>, even though as far as I'm concerned, it's shut down and there's nothing to see there.<br /><br />For a month or so, I've been gracefully redirecting traffic with an <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.3.2">HTTP 301 Moved Permanently</a>.&nbsp; Even so, it appears that Google and other crawlers are still hitting kolich.com looking for stuff that simply doesn't exist there anymore, even though I've been telling them for a solid month to "go look somewhere else."&nbsp; Time to pull out the big guns.&nbsp; A quick flip through my handy copy of RFC 2616, that's the HTTP 1.1 spec, lead me to rediscover <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.11">HTTP 410 Gone</a>.&nbsp; If you haven't met HTTP 410, it's the forgotten step child of <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec10.html#sec10.4.5">HTTP 404 Not Found</a>.&nbsp; As <a href="http://diveintomark.org/archives/2003/03/27/http_error_410_gone">described here</a>, "Error 410 means 'Resource gone', as in, a resource used to exist at this location, but now it's gone. Not only is it gone, but I don't know (or I don't want to tell you) where it went. If I knew where it went, and I wanted to tell you, I would use error 301 ('Permanent redirect') and any smart client would simply redirect to the new address. But 410 means 'Resource gone, no forwarding address'. Train gone sorry."<br /><br />Looks great, that's exactly what I need.&nbsp; Time to serve up some 410's.&nbsp; I configured my local <b>mark.kolich.com</b> Apache 2.2.3 virtual host with <a href="http://httpd.apache.org/docs/2.2/mod/mod_rewrite.html">mod_rewrite</a> to return an HTTP 410 Gone for most resources:<br /><br /><pre class="prettyprint">RewriteCond %{REQUEST_URI} !\.(html?)$ [NC]<br />RewriteCond %{REQUEST_URI} !^/$ [NC]<br />RewriteRule ^/(.*)$ - [G,L]</pre><br />Note the [G,L] on the RewriteRule directive.&nbsp; G, meaning Gone, and L meaning the last rule in the chain to apply to this request.&nbsp; In this case, any request for a resource that doesn't end in .html (or .htm) and isn't aimed at the server root, I immediately respond with an HTTP 410 Gone.&nbsp; Here's <a href="http://mark.kolich.com/favicon.ico">a nice example</a>.&nbsp; I'm handling HTML pages a little differently.&nbsp; Requests for an actual blog entry itself (a resource that ends in .html), are <a href="http://mark.kolich.com/2009/03/using-the-twitter-api-cache-your-tweets-in-php.html">caught an handled a little more gracefully as shown here</a>.&nbsp; I haven't yet decided when to phase out this graceful catch.<br /><br />In any event, let's see if an HTTP 410 gets the attention of those pesky crawlers and RSS feed readers.&nbsp; To be continued ...]]>
        
    </content>
</entry>

<entry>
    <title>Let Movable Type Generate Your XML Sitemap with a Custom Index Template</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/11/let-movable-type-generate-your-xml-sitemap.html?rss" />
    <id>tag:mark.koli.ch,2009://1.212</id>

    <published>2009-11-07T08:45:00Z</published>
    <updated>2009-11-09T16:37:03Z</updated>

    <summary><![CDATA[Many sites are moving towards dynamic XML sitemaps.&nbsp; These sitemaps let you tell Google, Yahoo, Bing, and Ask.com which pages on your site they should index, how often, and when they were last modified.&nbsp; You can even assign a priority...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="mt" label="mt" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="xml" label="xml" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[Many sites are moving towards dynamic XML sitemaps.&nbsp; These sitemaps let you tell Google, Yahoo, Bing, and Ask.com which pages on your site they should index, how often, and when they were last modified.&nbsp; You can even assign a priority to each page in the sitemap, which serves as an indication of how important a specific page is in relation to others.<br /><br />The sitemap protocol is <a href="http://www.sitemaps.org/protocol.php">well defined here at sitemaps.org</a>.<br /><br />Yesterday, I configured Movable Type, my blog publishing platform, to automatically generate <a href="http://mark.koli.ch/sitemap.xml">my own sitemap.xml</a> when I publish a new page or blog entry.&nbsp; I added a custom Movable Type Index Template that would automatically generate a complete <a href="http://mark.koli.ch/sitemap.xml">sitemap.xml</a> for me, and place it under the root of my blog at <a href="http://mark.koli.ch/sitemap.xml">http://mark.koli.ch/sitemap.xml</a>.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">1- My Sitemap XML Index Template</span><br /><br />My custom sitemap XML Index Template is relatively straightforward.&nbsp; In my Movable Type control panel, I clicked "Create index template" on the Blog Templates screen.&nbsp; I named my template "XML Sitemap" and used the following configuration:<br /><br /><pre class="prettyprint">&lt;?xml version="1.0" encoding="&lt;$mt:PublishCharset$&gt;"?&gt;<br />&lt;urlset xmlns="http://www.sitemaps.org/schemas/sitemap/0.9"&gt;<br /><br />&lt;!-- blog root --&gt;<br /> &lt;url&gt;<br />  &lt;loc&gt;&lt;$mt:BlogURL encode_xml="1"$&gt;&lt;/loc&gt;<br />  &lt;lastmod&gt;<br />    &lt;mt:Entries lastn="1"&gt;<br />      &lt;$mt:EntryModifiedDate utc="1" format="%Y-%m-%dT%H:%M:%S+00:00"$&gt;<br />    &lt;/mt:Entries&gt;<br />  &lt;/lastmod&gt;<br />  &lt;changefreq&gt;weekly&lt;/changefreq&gt;<br />  &lt;priority&gt;1.0&lt;/priority&gt;<br /> &lt;/url&gt;<br /><br />&lt;!-- pages --&gt;<br />&lt;mt:Pages lastn="0"&gt;<br /> &lt;url&gt;<br />  &lt;loc&gt;&lt;$mt:PagePermalink$&gt;&lt;/loc&gt;<br />  &lt;lastmod&gt;&lt;mt:PageModifiedDate utc="1" format="%Y-%m-%dT%H:%M:%S+00:00"$&gt;&lt;/lastmod&gt;<br />  &lt;priority&gt;0.8&lt;/priority&gt;<br /> &lt;/url&gt;<br />&lt;/mt:Pages&gt;<br /><br />&lt;!-- entries --&gt;<br />&lt;mt:Entries lastn="0"&gt;<br /> &lt;url&gt;<br />  &lt;loc&gt;&lt;$mt:EntryPermalink encode_xml="1"$&gt;&lt;/loc&gt;<br />  &lt;lastmod&gt;&lt;$mt:EntryDate utc="1" format="%Y-%m-%dT%H:%M:%S+00:00"$&gt;&lt;/lastmod&gt;<br />  &lt;changefreq&gt;never&lt;/changefreq&gt;<br />  &lt;priority&gt;0.6&lt;/priority&gt;<br /> &lt;/url&gt;<br />&lt;/mt:Entries&gt;<br /><br />&lt;!-- archives --&gt;<br />&lt;mt:ArchiveList archive_type="Monthly"&gt;<br /> &lt;url&gt;<br />  &lt;loc&gt;&lt;mt:ArchiveLink&gt;&lt;/loc&gt;<br />  &lt;priority&gt;0.4&lt;/priority&gt;<br />  &lt;changefreq&gt;never&lt;/changefreq&gt;<br /> &lt;/url&gt;<br />&lt;/mt:ArchiveList&gt;<br /><br />&lt;/urlset&gt;<br /></pre><br />Using the <a href="http://www.sitemaps.org/protocol.php">sitemap XML protocol defined at sitemaps.org</a>, I configured this template to include my blog root, all pages, all entries, and all archives in the sitemap.&nbsp; I assigned a higher priority to my blog root and individual pages versus the entries and archives.&nbsp; Note that I also omitted the &lt;changefreq&gt; tag under each "page", because I have no idea how often those pages will actually change.&nbsp; Also, I intentionally omitted the &lt;lastmod&gt; tag under each archive page, since again, there's not point in defining the last modified date on an archive.<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/07/edit-template-xml-sitemap.png"><img alt="edit-template-xml-sitemap.png" src="http://mark.koli.ch/assets_c/2009/11/edit-template-xml-sitemap-thumb-450x552.png" class="mt-image-none" style="" height="552" width="450" /></a></span><br /><br />Of course, you're free to change this template as you see fit as long as it adheres to the sitemap standard.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">2- Submit Your Sitemap XML (Submission URL's)</span><br /><br />Once you publish your sitemap with Movable Type, you'll probably want to alert Google, Bing, Yahoo and Ask.com that you've got a new sitemap.xml available for your blog.&nbsp; As <a href="http://www.sitemaps.org/protocol.php#informing%5C">described here in the sitemap protocol</a>, you can "ping" these web crawlers to alert them of the change.&nbsp; To do so, copy and paste these URL's into a web-browser, and replace &lt;sitemap URL&gt; with the full URL to your new sitemap:<br /><br /><pre class="prettyprint">http://www.google.com/ping?sitemap=&lt;sitemap URL&gt;<br />http://search.yahooapis.com/SiteExplorerService/V1/ping?sitemap=&lt;sitemap URL&gt;<br />http://submissions.ask.com/ping?sitemap=&lt;sitemap URL&gt;<br />http://www.bing.com/webmaster/ping.aspx?siteMap=&lt;sitemap URL&gt;</pre><br />Example:<br /><br /><pre class="prettyprint">http://www.google.com/ping?sitemap=http://mark.koli.ch/sitemap.xml</pre><br />On each submission, you should see some type of successful (HTTP 200 OK) response indicating that your submission was successful.&nbsp; Here's what Google's looked like:<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/07/google-added-sitemap.png"><img alt="google-added-sitemap.png" src="http://mark.koli.ch/assets_c/2009/11/google-added-sitemap-thumb-450x121.png" class="mt-image-none" style="" height="121" width="450" /></a></span><br /><br />Enjoy!]]>
        
    </content>
</entry>

<entry>
    <title>Onyx (beta)</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/11/onyx-beta.html?rss" />
    <id>tag:mark.koli.ch,2009://1.211</id>

    <published>2009-11-06T20:15:00Z</published>
    <updated>2009-11-06T20:23:30Z</updated>

    <summary><![CDATA[If you follow me on Twitter, you may have noticed I silently launched Onyx a few weeks ago.&nbsp; In a nutshell, as described on the Onyx homepage ..."Onyx is a social file management tool I built to help me keep...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="beta" label="beta" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="onyx" label="onyx" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="php" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><img alt="onyx-logo.jpg" src="http://mark.koli.ch/2009/10/16/onyx-logo.jpg" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="115" width="108" /></span>If you follow <a href="http://twitter.com/markkolich">me on Twitter</a>, you may have noticed I silently launched <a href="https://onyx.koli.ch/">Onyx</a> a few weeks ago.&nbsp; In a nutshell, as described on the <a href="https://onyx.koli.ch/">Onyx homepage</a> ...<br /><br /><i>"Onyx is a social file management tool I built to help me keep track of,
organize, and share my digital archive. While browsing the web, I tend
to accumulate a lot of junk; if I like something, I save it. If I see a
cool application of some sort, I'll take a screen shot. If I find a
cool song, I'll snag it for later. Or, if I have an important document
I need to archive, I'll store it. All of this digital content was
sitting around in a relatively unorganized and unsearchable set of
files and directories on a local file system. Onyx is my solution to
this digital content clutter problem. Files and bookmarks uploaded into
Onyx can be protected, searched, organized and shared much easier than
a set of files and directories on my local disk."</i><br /><br />Yep.<br /><br />Onyx was a chance for me to "cross off" an important task on my digital
TODO list that's been hanging over my head for a while: organize and
archive all of my digital crap.&nbsp; It also gave me a chance to play with
some new technologies I've been wanting to integrate into a real
project for quite a while, like jQuery UI's <a href="http://jqueryui.com/demos/draggable/">draggable</a> and <a href="http://jqueryui.com/demos/droppable/">droppable</a>.&nbsp; I also <a href="http://mark.koli.ch/2009/10/base36-encoding-for-tiny-urls-with-php.html">learned how to base-36 encode numbers for a tiny URL</a>, and solved a <a href="http://mark.koli.ch/2009/10/internet-explorer-cant-open-file-via-https-try-removing-the-pragma-header.html">very annoying problem using HTTPS with Internet Explorer</a>.<br /><br />In the last 24-hours, I finished uploading all of my personal, and public, digital content into Onyx <a href="https://onyx.koli.ch/get/1/mark">which you can browse here</a> from my Onyx home directory.&nbsp; Of course, like any good file management solution, my personal/private files are protected.&nbsp; What you'll see in my home directory are files and folders I've allowed the public to view.<br /><br />For the curious software engineer, Onyx is written entirely in PHP running on Apache 2.2.3.&nbsp; I'm also using a <a href="http://mark.koli.ch/2009/08/howto-implement-a-url-pattern-interpreter-similar-to-django-and-ror-in-php.html">clever little mod_rewrite hack in Apache to drop the .php on each Onyx URL</a>.&nbsp; Dropping the .php makes my URL's look a little cleaner; hipster Django and RoR can suck on that one.&nbsp; You may also ask why I named this project "Onyx".&nbsp; As <a href="http://en.wikipedia.org/wiki/Onyx">described here on Wikipedia</a>, Onyx is a type of colorful layered quartz which contains bands of almost every color.&nbsp; This colorful layering reminded me of the layered structure of a file system: files, folders, bookmarks, etc. all mashed together.&nbsp; Hence, Onyx.<br /><br />If you'd like to read a little more about my Onyx project, you might find <a href="http://mark.koli.ch/2009/10/onyx-my-custom-solution-to-the-digital-clutter-problem.html">this post interesting</a>.&nbsp; Thoughts and feedback are always welcome.<br /><br />Rock on.<br />]]>
        
    </content>
</entry>

<entry>
    <title>Google Dashboard Fail, and Blocking Google Tracking Cookies in Firefox</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/11/google-dashboard-fail-and-blocking-the-google-tracking-cookie-in-firefox.html?rss" />
    <id>tag:mark.koli.ch,2009://1.210</id>

    <published>2009-11-06T05:02:00Z</published>
    <updated>2009-11-07T20:26:02Z</updated>

    <summary><![CDATA[Today, the spoiled elitists at Google announced the release of Google Dashboard.&nbsp; It's a way for you to see, in reasonable detail, what Google knows about you based on the Google services you use. Their blog claims, "in an effort...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="commonsense" label="commonsense" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="goodluckwiththat" label="goodluckwiththat" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="google" label="google" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/05/google-cookie-block.png"><img alt="google-cookie-block.png" src="http://mark.koli.ch/assets_c/2009/11/google-cookie-block-thumb-200x221.png" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="221" width="200" /></a></span>Today, the spoiled elitists at Google announced the release of <a href="http://googleblog.blogspot.com/2009/11/transparency-choice-and-control-now.html">Google Dashboard</a>.&nbsp; It's a way for you to see, in reasonable detail, what Google knows about you based on the Google services you use. Their blog claims, "in an effort to provide you with greater
transparency and control over their own data, we've built the Google
Dashboard." Clever.<br /><br />What's bothersome to me is that I need a Google Account to see what Google supposedly knows about me.&nbsp; Well, what about those cute little <b>.google.com</b> cookies they <a href="http://mark.koli.ch/2009/11/05/google-cookie-block.png">shove into my browser when I use their search engine</a>?&nbsp; IMHO, Google Dashboard is missing one key feature: the ability to clearly show me what Google knows about me and my web-search history, <b>anonymously</b>, based on the already unique ID tracking numbers in those cookies.&nbsp; Google, why do I need an account to see what you've learned about me based on my "anonymous" web-history?<br /><br />There's probably only a few realistic explanations for why Google wouldn't let you see this information:<br /><br /><ol><li>Their cookies aren't actually used for tracking of web-searches and user habits.&nbsp; I suppose this is a possibility.<br /></li><li>Or, more likely, analyzing your web-search traffic is where the real bacon is.&nbsp; And, not surprisingly, Google doesn't want to show us the real underlying data their advertising engine uses to show us ads, which is their primary revenue stream.&nbsp; I guess I don't blame them.&nbsp; After all, they are just another public corporation with shareholder responsibilities.<br /></li></ol><br /><b>I'm awfully tired of the world bending over and blindly accepting everything Google throws at us as the greatest thing since sliced bread.&nbsp; If you really understand how Google makes their money, you should also try to understand what Google is not showing us, or not telling us, and why.</b><br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">Blocking Google Cookies in Firefox</span><br /><br />For the most part, I've given up on Google.&nbsp; Their web-search is fine, but I don't particularly enjoy the fact that my web-search and browsing history is "anonymously" tracked behind my back.&nbsp; If you'd like to permanently, or temporarily, block Google from inserting their nosy tracking cookies into your browser you can easily do so by setting a "cookie exception" in Firefox (assuming you use Firefox):<br /><br /><ol><li>Click the Tools menu, and select "Options...".</li><li>Click the Privacy tab.</li><li>Click the "Exceptions..." button.</li><li>In the "Address of web site:" box, enter ".google.com" no quotes and click Block to add the google.com domain to your blocked list.<br /></li></ol><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/11/05/google-block-cookie-howto.png"><img alt="google-block-cookie-howto.png" src="http://mark.koli.ch/assets_c/2009/11/google-block-cookie-howto-thumb-400x395.png" class="mt-image-none" style="" height="395" width="400" /></a></span><br /><br /><b>A few blog readers astutely pointed out that if you block cookies from .google.com, you won't be able to login to any Google services.&nbsp; Yes, I know that.&nbsp; And for the record, I don't use Gmail or any other Google Account that would require me to login on a regular basis.&nbsp; When I need to login to my Google Code account, I temporary unblock .google.com, and login.</b><br />]]>
        
    </content>
</entry>

<entry>
    <title>Controlling When and Where Java Writes Out its Permgen and Heap to Disk</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/10/controlling-when-and-where-java-writes-out-its-permgen-and-heap-to-disk.html?rss" />
    <id>tag:mark.koli.ch,2009://1.209</id>

    <published>2009-10-30T17:10:00Z</published>
    <updated>2009-10-30T17:35:14Z</updated>

    <summary><![CDATA[A blog reader recently contacted me with an interesting question: can you explicitly tell Java when and where to flush its permgen and heap to disk?&nbsp; The answer, based on what I understand about Java and operating system fundamentals, is...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="java" label="java" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="tips" label="tips" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[A blog reader recently contacted me with an interesting question: can you explicitly tell Java when and where to flush its <a href="http://blogs.sun.com/jonthecollector/entry/presenting_the_permanent_generation">permgen</a> and heap to disk?&nbsp; The answer, based on what I understand about Java and operating system fundamentals, <b>is no</b>.<br /><br />I can say with much certainty that you can't control where Java saves its heap and permgen (either on disk or in memory).&nbsp; Java itself doesn't know about paging stuff out to disk.&nbsp; It simply asks the operating system for the memory it needs and if the OS can't give it, then either the OS has to fail the request or make room by paging out unused chunks of memory to swap.&nbsp; In other words, Java relies on the host OS to handle this type of stuff.<br /><br />But what if you're dealing with millions of Java objects on a standard computer and you don't have room to keep all of those objects in physical memory?&nbsp; In this case, your only real option is to write code that manually swaps objects in/out of the disk.&nbsp; Of course, this requires that you implement your own swapping mechanism, which isn't too bad.&nbsp; When your Java application needs a set of objects, it loads what it needs into memory from disk, does some stuff with the objects, then writes them back out to disk.<br /><br />Meet <b>java.io.Serializable</b>:<br /><a href="http://java.sun.com/javase/6/docs/api/java/io/Serializable.html">http://java.sun.com/javase/6/docs/api/java/io/Serializable.html</a><br /><br />Here's an example:<br /><br /><pre class="prettyprint">import java.io.ByteArrayOutputStream;<br />import java.io.ObjectOutputStream;<br />import java.io.OutputStream;<br />import java.io.Serializable;<br />import java.util.ArrayList;<br />import java.util.List;<br /><br />public class Dog implements Serializable {<br /><br />  private static final long serialVersionUID = -4367737315167700936L;<br /><br />  private String name_;<br />  private String breed_;<br />  <br />  public Dog (String name, String breed) {<br />    this.name_ = name;<br />    this.breed_ = breed;<br />  }<br />  <br />  @Override<br />  public String toString(){<br />    return String.format("%s:%s", this.name_, this.breed_);<br />  }<br />  <br />  public static void main (String [] args) {<br />    <br />    final List&lt;Dog&gt; dogs = new ArrayList&lt;Dog&gt;();<br />    dogs.add( new Dog("Fido", "mutt") );<br />    dogs.add( new Dog("Clifford", "big red dog") );<br />    <br />    ByteArrayOutputStream os = null;<br />    ObjectOutputStream out = null;<br />    for( Dog d : dogs ){<br />      try {<br /><br />        // To write the dogs out to a file, you'll of course<br />        // need to use a FileOutputStream instead of a<br />        // ByteArrayOutputStream<br />        os = new ByteArrayOutputStream();<br />        out = new ObjectOutputStream(os);<br />        out.writeObject(d);<br />        <br />        // Print the serialized version of Dog<br />        final String serialized = os.toString();<br />        System.out.println(d.toString() + " serialized is: " +<br />              serialized);<br />&nbsp;<br />      } catch (Exception e) {<br />        e.printStackTrace(System.err);<br />      }<br />      finally {<br />        closeQuietly(os);<br />        closeQuietly(out);<br />      }<br />    }<br />    <br />  }<br />  <br />  private static final void closeQuietly(final OutputStream os){<br />    try {<br />      os.close();<br />    } catch (Exception e) {  }<br />  }<br />  <br />}<br /></pre><br />Each of the objects you wish to save to disk will have to implement java.io.Serializable.&nbsp; This will let you convert a Java object into something that can be written out to disk.&nbsp; From there, you will have to write some type of queue or stack control mechanism that will know when, from where, and how to page these objects in and out of the disk.]]>
        
    </content>
</entry>

<entry>
    <title>Base36 Encoding for Tiny URLs With PHP</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/10/base36-encoding-for-tiny-urls-with-php.html?rss" />
    <id>tag:mark.koli.ch,2009://1.207</id>

    <published>2009-10-29T00:15:00Z</published>
    <updated>2009-10-29T06:45:38Z</updated>

    <summary><![CDATA[While adding permalink and Twitter support to Onyx last week, I realized that PHP can easily convert a number between two arbitrary bases using its built in base_convert function.&nbsp; This was crucial for me because it meant that I could...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="base36" label="base36" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="onyx" label="onyx" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="php" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[While adding <a href="http://en.wikipedia.org/wiki/Permalink">permalink</a> and Twitter support to <a href="https://onyx.koli.ch/">Onyx</a> last week, I realized that PHP can easily convert a number between two arbitrary bases using its built in <a href="http://php.net/manual/en/function.base-convert.php">base_convert function</a>.&nbsp; This was crucial for me because it meant that I could quickly generate a base-36 encoded string from a standard base-10 number:<br /><br /><pre class="prettyprint">$tiny = base_convert($key, 10, 36);<br /></pre><br />As <a href="http://en.wikipedia.org/wiki/Base_36">Wikipedia explains</a>, "...the choice of 36 is convenient in that the digits can be represented using the Arabic numerals 0-9 and the Latin letters A-Z.&nbsp; Base 36 is therefore the most compact case-insensitive alphanumeric numeral system using ASCII characters...".&nbsp; Not to mention, it's the highest radix most languages support by default.<br /><br />I built <a href="https://onyx.koli.ch/">Onyx</a> to maintain a list of files (as inode's) in a MySQL database table.&nbsp; Luckily, the inode's are stored in this table as auto incrementing BIGINT(20)'s, which are of course, base-10 encoded numbers:<br /><br /><pre class="prettyprint">CREATE TABLE files (<br /><br />  f_inode BIGINT(20) NOT NULL AUTO_INCREMENT,<br />  ...<br /><br />  INDEX inode_index ( f_inode ),<br />  PRIMARY KEY ( f_inode ),<br />  ...<br /><br />) TYPE=InnoDB;<br /></pre><br />Base-36 encoding my inode's is key because this means that I can easily, for example, convert base-10 inode 3,032 into "2c8" base-36 for a tiny URL.&nbsp; From there, it's simply a matter of using the base-36 encoded string in
a tiny/shortened permalink URL to refer to the correct file/inode on
Onyx.&nbsp; Yes, this is a poor example because I'm only saving 1 character in a supposedly shorter URL ("3032" vs "2c8").&nbsp; However, this makes a lot more sense with larger base-10 numbers like 105,621,983 which condenses nicely into "1qvufz" base-36.&nbsp; Personally, I'd rather see /1qvufz on the end of a "tiny" URL than /105621983.<br /><br />Enjoy.<br />]]>
        
    </content>
</entry>

<entry>
    <title>Bundle Java (the JRE) and Launch a Java App with 7zip SFX! ... (Convert Java Apps to an Executable, sort of)</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/10/bundle-java-the-jre-and-launch-a-java-app-with-7zip-sfx.html?rss" />
    <id>tag:mark.koli.ch,2009://1.206</id>

    <published>2009-10-27T04:00:00Z</published>
    <updated>2009-10-27T07:15:18Z</updated>

    <summary><![CDATA[I've been playing around with a lot of installer type stuff recently.&nbsp; I discovered that Mozilla Firefox uses the 7zip SFX install launcher (if that's what you call it?) to kick off the Firefox installation process.&nbsp; I started playing around...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="7zip" label="7zip" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="installers" label="installers" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="java" label="java" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="sfx" label="sfx" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[<span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/10/26/7zip-sfx-extracting-shot.png"><img alt="7zip-sfx-extracting-shot.png" src="http://mark.koli.ch/assets_c/2009/10/7zip-sfx-extracting-shot-thumb-200x102.png" class="mt-image-left" style="margin: 0pt 20px 20px 0pt; float: left;" height="102" width="200" /></a></span>I've been playing around with a lot of installer type stuff recently.&nbsp; I discovered that <a href="http://howto.gumph.org/content/customize-firefox-installer/">Mozilla Firefox uses the 7zip SFX install launcher</a> (if that's what you call it?) to kick off the Firefox installation process.&nbsp; I started playing around with <a href="http://7zsfx.solta.ru/en/">7zip SFX, and realized that you can do some pretty cool stuff with it</a>.&nbsp; In fact, I discovered that you can actually bundle a Java app and the Java Runtime Environment (JRE) into your own little 7zip SFX launcher.&nbsp; Naturally, this means you can write a Java app and then let your users start it by double clicking a native Win32 .exe.&nbsp; And best of all, because your launcher contains the Java Runtime Environment, the user does not have to have a JRE installed on their system to run your application!&nbsp; The launcher extracts the JRE and your app to a temporary directory, then launches it using that freshly extracted JRE.<br /><br />Continue reading for the details ...]]>
        <![CDATA[<br /><span style="font-size: 1.1em; font-weight: bold;">1 - Why Is This Useful</span><br /><br />Java is fantastic for its write once, run anywhere methodology.&nbsp; Only problem is, unlike a native Windows app, you need a JVM/JRE to run a Java application.&nbsp; Most vendors who sell software written in Java tell their users or customers that they need to install a JRE first before they can run the app.&nbsp; This makes sense, but it's a slight (err, huge) inconvenience; Sun's Java installer is bulky and often cumbersome.&nbsp; Wouldn't it be nice if you could avoid that forced installation step, and simply ship a supported Java runtime with your Java application?&nbsp; This way, the user simply double clicks an .exe, a launcher extracts a supported JRE, and starts.&nbsp; In short, the user doesn't have to install a JRE  at all, but rather the JRE they need is simply extracted to a temporary directory and your application starts using that freshly extracted JRE.&nbsp; Further, when the user exits the application, the temporary JRE directory your app launcher created is automatically cleaned up, and all is well.<br /><br />Not surprisingly, this is completely doable using <a href="http://7zsfx.solta.ru/en/">7zip SFX</a>.&nbsp; However, note that if you choose to ship the JRE with your launcher, you can
expect your executable to be <b>approximately 16MB larger</b> than it would be
without the JRE.&nbsp; IMHO, 16MB is a small price to pay for the added convenience of not having to install another piece of bloated software.&nbsp; Plus you know that the JRE your launcher extracts and starts your application with fully supports your Java app; you don't have to worry about the Java updater updating the JRE on the user's system behind your back which might break your app.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">2 - Getting Started</span><br /><br />Before you start packaging up your app with 7zip, you'll probably want to <a href="http://mark.koli.ch/2009/10/26/7zipsfx-launch-java-example-pack.zip">download my complete example pack</a>.&nbsp; This ZIP file contains everything you'll need to get started, including a ready to ship JRE (Java 6 Update 16) and an Ant build file.&nbsp; Note that you do not need to install 7zip; I've packed the necessary 7zip.exe to create the archive with the <a href="http://mark.koli.ch/2009/10/26/7zipsfx-launch-java-example-pack.zip">example pack</a>.&nbsp; However, if you want to install 7zip, can <a href="http://www.7-zip.org/download.html">download the installer here</a> or <a href="https://onyx.koli.ch/x2v">from my mirror on Onyx</a>.&nbsp; This sample pack is also an Eclipse project.&nbsp; If you work out of Eclipse, you can import the .project inside of the example pack into your Eclipse IDE.<br /><br />Or, if you want to see the <b>7zSD.sfx launcher in action</b>, <a href="http://mark.koli.ch/2009/10/26/7zipsfx-launch-java-example-exe.zip">download  the pre-built demo</a> launcher.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">3 - Fundamentals</span><br /><br />Here's how this all works.&nbsp; 7zip (and other ZIP installer type packages) provide SFX launchers.&nbsp; These launchers are essentially native Windows executables that understand how to extract an archive to a temporary directory, and launch an application (usually another installer).&nbsp; This is how the <a href="http://howto.gumph.org/content/customize-firefox-installer/">Mozilla Firefox installer works</a>: when you launch the "installer" the extracting files dialog that opens is actually the 7zip SFX launcher extracting the real "setup.exe" to a temporary directory.&nbsp; Once done, it starts setup.exe to complete the installation process.<br /><br />In this case, the basic principle is the same, except I'm using the 7zip SFX launcher to extract my application and required JRE components to a temporary directory, and then start it.&nbsp; Producing a native Windows SFX launcher is quite easy; you need to binary concatenate three files together: the SFX launcher, an app.tag configuration file, and a 7zip archive.&nbsp; In Windows, using the <b>copy</b> command, this looks something like:<br /><br /><pre class="prettyprint">C:\&gt; copy /b 7zSD.sfx + app.tag + app.7z start.exe</pre><br />This produces start.exe, a portable native Windows app that contains everything your Java application needs to run in a single executable!&nbsp; When run, start.exe will use 7zSD.sfx to extract the contents of app.7z to a temporary directory, and launch whatever application you've defined in app.tag.&nbsp; Pretty freakin' sweet if I say so myself.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">4 - My Sample Java App</span><br /><br />My example Java app is very straightforward.&nbsp; Yours will, of course, be more complicated.&nbsp; My sample app simply opens a <a href="http://java.sun.com/javase/6/docs/api/javax/swing/JOptionPane.html">JOptionPane</a> to display the current "working directory" (where the SFX launcher was started from) and the "temporary directory" (the temp directory where the SFX launcher extracted the JRE and application files to).<br /><br /><pre class="prettyprint">package com.kolich.sevenzip.example;<br /><br />import java.io.File;<br />import java.io.IOException;<br /><br />import javax.swing.JFrame;<br />import javax.swing.JOptionPane;<br />import javax.swing.SwingUtilities;<br /><br />public class StartHere {<br />  <br />  private File workingDir_;<br />  private File tempDir_;<br />  <br />  public StartHere(File root, File temp){<br />    this.workingDir_ = root;<br />    this.tempDir_ = temp;<br />  }<br />  <br />  /**<br />   * The working directory, where the application was<br />   * started from.<br />   * @return<br />   */<br />  public File getWorkingDir(){<br />    return this.workingDir_;<br />  }<br />  <br />  /**<br />   * The temp directory, where the launcher extracted<br />   * your app and JRE to on the users' system.<br />   * @return<br />   */<br />  public File getTempDir(){<br />    return this.tempDir_;<br />  }<br /><br />  public static void main(String[] args)<br />    throws Exception {<br />    <br />    File root;<br />    try {<br />      root = new File(args[0]);<br />    } catch ( Exception e ) {<br />      root = new File(".");<br />    }<br />    <br />    File temp;<br />    try {<br />      temp = new File(args[1]);<br />    } catch ( Exception e ) {<br />      temp = new File(".");<br />    }<br />    <br />    final StartHere sh = new StartHere(root, temp);    <br />    Runnable worker = new Runnable() {<br />        public void run() {<br />          showMessageDialog(sh);<br />          System.exit(0);<br />        }<br />    };<br />    SwingUtilities.invokeLater(worker);<br />    <br />  }<br />  <br />  private static void showMessageDialog(StartHere sh) {<br />    try {<br />      JOptionPane.showMessageDialog(new JFrame(),<br />        "A java app launched by 7zip SFX!\n\n" +<br />        "My working directory is:\n" +<br />        sh.getWorkingDir().getCanonicalPath() +<br />        "\n\nAnd I've been extracted to temp directory:\n" +<br />        sh.getTempDir().getCanonicalPath() );<br />    } catch (IOException e) {<br />      e.printStackTrace( System.err );<br />    }<br /><br />  }<br />  <br />}</pre><br />Here's a screen shot:<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/10/26/7zip-sfx-extracting-shot2.png"><img alt="7zip-sfx-extracting-shot2.png" src="http://mark.koli.ch/assets_c/2009/10/7zip-sfx-extracting-shot2-thumb-400x226.png" class="mt-image-none" style="" height="226" width="400" /></a></span><br /><br />The Ant build script in my example pack compiles this app and creates app.jar, a runnable JAR file.<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">5 - The App.tag Configuration File</span><br /><br />I'm using the 7zSD.sfx launcher by Oleg Scherbakov at <a href="http://7zsfx.solta.ru/en/">http://7zsfx.solta.ru/en/</a>.&nbsp; There are a ton of configuration options as <a href="http://7zsfx.solta.ru/en/configinfo.html">described here</a> on Oleg's web-site.&nbsp; In the example, my app.tag configuration file is as follows:<br /><br /><pre class="prettyprint">;!@Install@!UTF-8!<br />Title="7ZIP Java Launcher Example"<br />ExtractDialogText="Extracting ..."<br />GUIFlags="32"<br />ExtractTitle="Extracting"<br />FinishMessage="Application stopped."<br />RunProgram="launcher\jre\bin\javaw.exe -jar launcher\app.jar \"%%S\" \"%%T\""<br />;!@InstallEnd@!<br /></pre><br />There's nothing too complicated about the configuration file.&nbsp; In this example, I'm simply extracting the 7zip file included with the native SFX launcher and starting launcher\jre\bin\javaw.exe, which is the JRE packaged with the launcher (found under launcher\jre in the example pack).&nbsp; The %%S property in the configuration file is the directory that contains the SFX executable (where the user started it from).&nbsp; The %%T property is the temporary directory where the SFX launcher placed the extracted JRE and application files.&nbsp; Note that when the Java application exits, the SFX launcher will automatically delete/cleanup this temporary directory.<br /><br />This example simply asks 7zSD.sfx to extract and then start launcher\app.jar using launcher\jre\bin\javaw.exe. <br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">6 - The Ant Build File</span><br /><br />My ant build file does a few things.&nbsp; First, it compiles the Java app and packages it into a runnable JAR file.&nbsp; From there, it uses 7zip to compress the JRE and the resulting JAR file into app.7z.&nbsp; Finally, it uses <a href="http://ant.apache.org/manual/CoreTasks/concat.html">Ant's concat task</a> to binary concatenate 7zSD.sfx, app.tag and the app.7z file together.&nbsp; The result is start.exe, a native self-contained Windows executable that contains the JRE and Java app itself!<br /><br />Note that the JRE is 7zip'ed inside of app.7z.&nbsp; This is how the JRE is shipped/included with the launcher.<br /><br /><pre class="prettyprint">&lt;project name="7zipexample" default="package.7zipexample"&gt;<br />  <br />  &lt;property name="src.dir" location="${basedir}/src/com/kolich"/&gt;<br />  &lt;property name="build.dir" location="${basedir}/build"/&gt;<br />  &lt;property name="launcher.dir" location="${basedir}/launcher"/&gt;<br />  &lt;property name="7zip.exe.dir" location="${basedir}/7zip"/&gt;<br />  &lt;property name="sfx.dir" location="${basedir}/sfx"/&gt;<br />  &lt;property name="dist.dir" location="${basedir}/dist"/&gt;<br />  <br />  &lt;target name="clean.7zipexample" depends="clean.build.7zipexample,<br />      clean.dist.7zipexample" /&gt;<br />  <br />  &lt;target name="clean.build.7zipexample"&gt;<br />    &lt;delete includeemptydirs="true"&gt;<br />    &lt;fileset dir="${build.dir}" includes="**/*" /&gt;<br />    &lt;/delete&gt;<br />  &lt;/target&gt;<br />  <br />  &lt;target name="clean.dist.7zipexample"&gt;<br />    &lt;delete includeemptydirs="true"&gt;<br />    &lt;fileset dir="${dist.dir}" includes="**/*" /&gt;<br />      &lt;fileset dir="${launcher.dir}" includes="app.jar" /&gt;<br />      &lt;fileset dir="${launcher.dir}" includes="app.7z" /&gt;<br />    &lt;/delete&gt;<br />  &lt;/target&gt;<br /> <br />  &lt;target name="package.7zipexample" depends="clean.7zipexample"&gt;<br />    <br />    &lt;!-- compile the source --&gt;<br />    &lt;javac destdir="${build.dir}" srcdir="${src.dir}"&gt;<br />      &lt;include name="**/*.java"/&gt;<br />    &lt;/javac&gt;<br />    <br />    &lt;!-- create a runnable jar --&gt;<br />    &lt;jar destfile="${launcher.dir}/app.jar" manifest="Manifest.mf"&gt;<br />      &lt;fileset dir="${build.dir}"&gt;<br />        &lt;include name="**/*.class" /&gt;<br />      &lt;/fileset&gt;<br />    &lt;/jar&gt;<br />    <br />    &lt;!-- compress all of the files we need to down with 7zip --&gt;<br />    &lt;exec executable="${7zip.exe.dir}/7z.exe" failonerror="true"&gt;<br />      &lt;arg value="a" /&gt;<br />      &lt;arg value="-t7z" /&gt;<br />      &lt;arg value="-r" /&gt;<br />      &lt;arg value="${launcher.dir}\app.7z" /&gt;<br />      &lt;arg value="${launcher.dir}" /&gt;<br />    &lt;/exec&gt;<br />    <br />    &lt;!-- concat the files we need together to produce a binary<br />        launcher --&gt;<br />    &lt;concat destfile="${dist.dir}/start.exe" binary="yes"&gt;<br />      &lt;fileset file="${sfx.dir}/7zSD.sfx" /&gt;<br />      &lt;fileset file="${sfx.dir}/app.tag" /&gt;<br />      &lt;fileset file="${launcher.dir}/app.7z" /&gt;<br />    &lt;/concat&gt;<br />    <br />  &lt;/target&gt;<br />  <br />&lt;/project&gt;<br /></pre><br />You can always manually build the installer package yourself, but why bother if you have an Ant build file ready to do the work for you?&nbsp; When you run the <b>package.7zipexample</b> build target in the example build file, the resulting ready to launch executable can be found at dist\start.exe.&nbsp; Start.exe is your shippable application.&nbsp; Again, no pre-installed Java Runtime Environment required!<br /><br /><br /><span style="font-size: 1.1em; font-weight: bold;">7 - Changing the Icons and Version Information</span><br /><br />If you use Oleg's 7zSD.sfx launcher as is, you'll notice the icon attached to the resulting .exe is quite poor.&nbsp; In all likelihood, you'll want to replace the icon with one for your application.&nbsp; Doing so is quite easy with <a href="http://www.angusj.com/resourcehacker/">Resource Hacker</a>, a freeware utility to view, modify, rename, add, delete and extract resources in 32bit Windows executables and resource files.&nbsp; Detailed instructions on <a href="http://7zsfx.solta.ru/en/icon.html">how to replace the icon can be found here on the 7zSD.sfx web-site</a>.&nbsp; Note that you can also use Resource Hacker to edit the version and copyright details included in the resulting executable as shown below.<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/10/26/7zip-sfx-change-resources.png"><img alt="7zip-sfx-change-resources.png" src="http://mark.koli.ch/assets_c/2009/10/7zip-sfx-change-resources-thumb-400x284.png" class="mt-image-none" style="" height="284" width="400" /></a></span><br /><br />&nbsp; <br />In summary, it's fairly straightforward to bundle and ship the Java Runtime Environment with your Java application using 7zip SFX.&nbsp; Heck,  Sun allows and even tells you how to redistribute the JRE with your applications (just <a href="http://java.sun.com/j2se/1.5.0/jre/README">read the LICENSE file provided</a> with any JRE installation).&nbsp; In the near future, I plan on converting <a href="http://mark.koli.ch/2009/09/a-really-simple-web-server-using-restlet.html">Cappuccino</a>, a really simple web-server powered by Restlet, to a 7zip SFX enabled app.<br /><br />Good luck, and don't hesitate to contact me if you have any questions or concerns.&nbsp; Enjoy!<br />]]>
    </content>
</entry>

<entry>
    <title>Internet Explorer Can&apos;t Open Files Via HTTPS: Try Removing The Pragma Header</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/10/internet-explorer-cant-open-file-via-https-try-removing-the-pragma-header.html?rss" />
    <id>tag:mark.koli.ch,2009://1.205</id>

    <published>2009-10-23T05:50:00Z</published>
    <updated>2009-10-23T06:25:28Z</updated>

    <summary><![CDATA[While wrapping up a few bugs on one of my latest projects, Onyx, I encountered one of the most annoying and irritating IE nuisances to date.&nbsp; Let me reiterate my hatred for Internet Explorer.&nbsp; So here's the deal.&nbsp; In PHP,...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="fail" label="fail" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="http" label="http" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="iefail" label="iefail" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="php" label="php" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="ssl" label="ssl" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[While wrapping up a few bugs on one of my latest projects, <a href="https://onyx.koli.ch/">Onyx</a>, I encountered one of the most annoying and irritating IE nuisances to date.&nbsp; Let me reiterate my hatred for Internet Explorer.&nbsp; So here's the deal.&nbsp; In PHP, I was serving up a few static files nestled away on the web-server.&nbsp; The user would click a link, my PHP would <a href="http://php.net/manual/en/function.fopen.php">fopen</a> a file, and send it to the browser.&nbsp; Simple enough, and this worked just fine on HTTP.&nbsp; Then, I moved my new web-application to a production environment under HTTPS.&nbsp; Suddenly, some downloads failed on IE with the following error:<br /><br /><b>"Internet Explorer cannot download [file] from [server]. Internet Explorer was not able to open this Internet site. The requested site is either unavailable or cannot be found. Please try again later."</b><br /><br />Here's a screen shot of the error from IE8:<br /><br /><span class="mt-enclosure mt-enclosure-image" style="display: inline;"><a href="http://mark.koli.ch/2009/10/22/ie-cannot-open-file-https.jpg"><img alt="ie-cannot-open-file-https.jpg" src="http://mark.koli.ch/assets_c/2009/10/ie-cannot-open-file-https-thumb-400x197.jpg" class="mt-image-none" style="" height="197" width="400" /></a></span><br /><br /><br />With a little digging, I tracked down the following support pages on <b>microsoft.com</b> that explain the problem:<br /><br /><ul><li><a href="http://support.microsoft.com/kb/316431">http://support.microsoft.com/kb/316431</a> -- Internet Explorer is unable to open Office documents from an SSL Web site.</li><li><a href="http://support.microsoft.com/kb/812935">http://support.microsoft.com/kb/812935</a> -- "Internet Explorer Cannot Download" Error Message When You Use an HTTPS URL to Open an Office Document or PDF File</li></ul>It appears that Internet Explorer gets confused when it sees a Pragma and Cache-Control header together in the same response.&nbsp; And, it has difficulties interpreting these headers properly over HTTPS.&nbsp; As described on <b>microsoft.com</b>, "when Internet Explorer communicates with
a secure Web site through SSL, Internet Explorer enforces any no-cache
request. If the header or headers are present, Internet Explorer does
not cache the file. Consequently, Office cannot open the file."&nbsp; And, when you're using sessions in PHP, the PHP engine automatically by default inserts the Expire, Cache-Control, <b>and</b> Pragma headers in your responses as <a href="http://php.net/manual/en/function.session-cache-limiter.php">explained here</a>. <br /><br />In my case, the <b>solution to this issue was to remove</b> the <a href="http://www.w3.org/Protocols/rfc2616/rfc2616-sec14.html#sec14.32">Pragma header</a> in my HTTP response.&nbsp; In PHP, I forcefully unset the Pragma header with the following code-snippet:<br /><br /><pre class="prettyprint">header("Pragma: ");<br /></pre><br />This successfully clears the Pragma header in my responses, and all is well.&nbsp; With this tweak, IE7 and 8 correctly handle my file downloads.]]>
        
    </content>
</entry>

<entry>
    <title>Reliably Checking Windows Bitness (32-bit or 64-bit) With a Tiny C++ App</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/10/reliably-checking-os-bitness-32-or-64-bit-on-windows-with-a-tiny-c-app.html?rss" />
    <id>tag:mark.koli.ch,2009://1.204</id>

    <published>2009-10-21T22:04:00Z</published>
    <updated>2009-11-13T17:53:37Z</updated>

    <summary><![CDATA[I threw myself right into the bitness fire this afternoon, trying to figure out how to reliably determine if a Windows OS is 32-bit or native 64-bit.&nbsp; I tried all sorts of things, everything from a VB Script, to a...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="bitness" label="bitness" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="code" label="code" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="cpp" label="cpp" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="fail" label="fail" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="microsoft" label="microsoft" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[I threw myself right into the bitness fire this afternoon, trying to figure out how to reliably determine if a Windows OS is 32-bit or native 64-bit.&nbsp; I tried all sorts of things, everything from a VB Script, to a few tiny C++ programs built to issue <a href="http://en.wikipedia.org/wiki/Windows_Management_Instrumentation">WMI queries</a>.&nbsp; I tried using WMI to read the OSArchitecture property from its "SELECT * FROM Win32_OperatingSystem" output, but that failed miserably on Windows XP.&nbsp; As it turns out, the <b>OSArchitecture property you can read via WMI wasn't added until Vista</b>.&nbsp; Nice.&nbsp; So how do we check for 64-bit Windows XP?<br /><br />After about an hour or so of searching, I stumbled across <a href="http://www.tech-archive.net/Archive/DotNet/microsoft.public.dotnet.languages.csharp/2007-06/msg03188.html">this post</a> which described in reasonable detail what I needed to:<br /><br />"In your code, you first need to check the size of IntPtr, if it returns 8 then you are running on a 64-bit OS. If it returns 4, you are running a 32 bit application, so now you need to know whether you are running natively or under WOW64. To get this information you will need to call kernel32.dll API "IsWow64Process" using PInvoke, this API returns a Boolean 'true' if you are running under WOW64, that means you are running 32 bit application on a 64-bit Windows system. Be careful however to check the OS version before calling this API, only XP SP2 and implements this one."<br /><br />How could Microsoft make something that should be so easy, so complicated?&nbsp; What a FAIL.<br /><br />I then found <a href="http://www.neowin.net/forum/lofiversion/index.php/t773130.html">this post</a>, that offered up a nice little 32-bit C++ app one can compile and run to check the real, actual bitness of the OS.&nbsp; For the most part, it does exactly what the first post said I needed to do, with the exception of checking the size of IntPtr's.&nbsp; I cleaned it up a little bit, and <b>successfully compiled it on 32-bit Windows XP with Microsoft Visual C++ 2005</b> (Version 8.0.50).&nbsp; This app checks the bitness of the OS by discovering if it's running under WOW64, or natively as a 32-bit app.&nbsp; <a href="http://en.wikipedia.org/wiki/WoW64">WOW64 stands for Windows-on-Windows</a>, it's the 64-bit only kernel subsystem that lets 32-bit apps run on 64-bit Windows.&nbsp; The bitness checker starts and then asks the Windows kernel if it's running under WOW64.&nbsp; If it is running under WOW64, that clearly means it's a 32-bit app (as compiled) running on a 64-bit OS.&nbsp; If it's not running in WOW64, then we're on a 32-bit OS ...<br /><br /><pre class="prettyprint">#include "stdafx.h"<br />#include &lt;iostream&gt;<br />#include "comutil.h"<br /><br />#define RESPONSE_32_BIT "32"<br />#define RESPONSE_64_BIT "64"<br /><br />using namespace std;<br /><br />typedef BOOL (WINAPI *IW64PFP)(HANDLE, BOOL *);<br /><br />int main(int argc, char **argv){<br /><br />  BOOL res = FALSE;<br /><br />  // When this application is compiled as a 32-bit app,<br />  // and run on a native 64-bit system, Windows will run<br />  // this application under WOW64.  WOW64 is the Windows-<br />  // on-Windows subsystem that lets native 32-bit applications<br />  // run in 64-bit land.  This calls the kernel32.dll<br />  // API to see if this process is running under WOW64.<br />  // If it is running under WOW64, then that clearly means<br />  // this 32-bit application is running on a 64-bit OS,<br />  // and IsWow64Process will return true.<br />  IW64PFP IW64P = (IW64PFP)GetProcAddress(<br />            GetModuleHandle(L"kernel32"), "IsWow64Process");<br /><br />  if(IW64P != NULL){<br />    IW64P(GetCurrentProcess(), &amp;res);<br />  }<br /><br />  cout &lt;&lt; ((res) ? RESPONSE_64_BIT : RESPONSE_32_BIT) &lt;&lt; endl;<br /><br />  return 0;<br />  <br />}<br /></pre><br />Will output "32" on 32-bit Windows and "64" on 64-bit Windows.&nbsp; Download the <a href="http://mark.koli.ch/2009/10/21/bitness-checker.zip">pre-built .exe and .cpp source here</a>.<br /><br /><ul><li><b>Tested and worked on:</b>&nbsp; 32-bit Windows XP Professional SP3, 32-bit Windows Vista Enterprise SP2, 64-bit Windows Vista Enterprise, 32-Bit Windows 7 Home Premium, 64-bit Windows 7 Home Premium.<br /><br /></li><li><strike><b>Did NOT test on:</b> 64-bit Windows XP. I don't have a 64-bit Windows XP box lying around. If you have one, and you can check this code for me, please let me know and I'll be happy to update this post and give you credit for testing it for me.</strike> <a href="http://twitter.com/cmsimike">@cmsimike</a> confirmed on 10/27/09 that my bitness checker also works perfectly on 64-bit Windows XP.<br /><br /></li><li>Roger confirmed on 11/12/09 that my bitness checker also works on 32-bit Windows Server 2003 and 64-bit Windows Server 2008.&nbsp; Thanks, Roger!<br /></li></ul><br />Cheers.<br />]]>
        
    </content>
</entry>

<entry>
    <title>Java&apos;s &quot;os.arch&quot; System Property is the Bitness of the JRE, NOT the Operating System</title>
    <link rel="alternate" type="text/html" href="http://mark.koli.ch/2009/10/javas-osarch-system-property-is-the-bitness-of-the-jre-not-the-operating-system.html?rss" />
    <id>tag:mark.koli.ch,2009://1.203</id>

    <published>2009-10-19T18:15:00Z</published>
    <updated>2009-10-27T17:47:32Z</updated>

    <summary><![CDATA[If you ever use Java to check if a system is 32 or 64-bit, you should know that Java's "os.arch" system property returns the bitness of the JRE, not the OS itself.&nbsp; Sites like this are WRONG; any resource that...]]></summary>
    <author>
        <name>Mark Kolich</name>
        <uri>http://mark.koli.ch</uri>
    </author>
    
    <category term="bitness" label="bitness" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="java" label="java" scheme="http://www.sixapart.com/ns/types#tag" />
    <category term="lame" label="lame" scheme="http://www.sixapart.com/ns/types#tag" />
    
    <content type="html" xml:lang="en-US" xml:base="http://mark.koli.ch/">
        <![CDATA[If you ever use Java to check if a system is 32 or 64-bit, you should know that Java's "os.arch" system property returns the bitness of the JRE, not the OS itself.&nbsp; Sites <a href="http://www.roseindia.net/java/beginners/OSInformation.shtml">like this are WRONG</a>; any resource that claims Java's "os.arch" property returns the real "architecture of the OS" is lying.&nbsp; Case in point, I recently ran this tiny program on a <b>64-bit Windows 7 machine</b>, with a 32-bit JRE:<br /><br /><pre class="prettyprint">import com.sun.servicetag.SystemEnvironment;<br /><br />public class OSArchLies {<br />  <br />  public static void main(String[] args) {<br />    <br />    // Will say "x86" even on a 64-bit machine<br />    // using a 32-bit Java runtime<br />    SystemEnvironment env =<br />        SystemEnvironment.getSystemEnvironment();<br />    final String envArch = env.getOsArchitecture();<br />    <br />    // The os.arch property will also say "x86" on a<br />    // 64-bit machine using a 32-bit runtime<br />    final String propArch = System.getProperty("os.arch");<br />    <br />    System.out.println( "getOsArchitecture() says =&gt; " + envArch );<br />    System.out.println( "getProperty() says =&gt; " + propArch );<br />    <br />  }<br /><br />}<br /></pre><br />The output from this tiny app on a 64-bit box:<br /><br /><pre class="prettyprint">#/&gt; java OSArchLies<br />getOsArchitecture() says =&gt; x86<br />getProperty() says =&gt; x86</pre><br />In this case, one would expect to see something like "x86_64" or "amd64" instead of just "x86".&nbsp; Bottom line, don't believe what you read online about "os.arch" and other Java system properties.&nbsp; They are usually properties of the <b>JRE/JDK itself, and not necessarily the real properties of the underlying OS or architecture</b>.&nbsp; If you need to check if a system is actually 32 or 64-bit, you should look elsewhere in the system registry or <a href="http://mark.koli.ch/2009/10/reliably-checking-os-bitness-32-or-64-bit-on-windows-with-a-tiny-c-app.html">write your own native app and call it from Java</a>.<br />]]>
        
    </content>
</entry>

</feed>
