<?xml version='1.0' encoding='UTF-8'?><?xml-stylesheet href="http://www.blogger.com/styles/atom.css" type="text/css"?><feed xmlns='http://www.w3.org/2005/Atom' xmlns:openSearch='http://a9.com/-/spec/opensearchrss/1.0/' xmlns:georss='http://www.georss.org/georss' xmlns:gd='http://schemas.google.com/g/2005' xmlns:thr='http://purl.org/syndication/thread/1.0'><id>tag:blogger.com,1999:blog-2582624793655573268</id><updated>2011-12-02T18:47:34.168-08:00</updated><category term='Mobile'/><category term='Mediafly'/><category term='2009'/><category term='JavaEE'/><category term='introduction'/><category term='javascript'/><category term='mug'/><category term='tsql'/><category term='usb'/><category term='Music'/><category term='ICEFaces'/><category term='Opera'/><category term='jasmine'/><category term='Boredom'/><category term='tomcat'/><category term='Session-Management'/><category term='youtube'/><category term='memory'/><category term='greader'/><category term='codinghorror'/><category term='House'/><category term='DamagePlan'/><category term='Pantera'/><category term='Web'/><category term='c#'/><category term='firefox'/><category term='JavaME'/><category term='portable'/><category term='charging'/><category term='sqlserver'/><category term='Browser'/><category term='blogger'/><category term='Blackberry'/><category term='add-ons'/><category term='sql'/><category term='Dimebag Darrell'/><category term='syntaxhighlighting'/><category term='Career'/><category term='.net'/><category term='JSF'/><category term='eclipse'/><category term='Documentation'/><category term='bdd'/><category term='gmail'/><category term='Servlet'/><title type='text'>Symphonic Code</title><subtitle type='html'>My offering to the coding gods</subtitle><link rel='http://schemas.google.com/g/2005#feed' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/posts/default'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default?max-results=100'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/'/><link rel='hub' href='http://pubsubhubbub.appspot.com/'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><generator version='7.00' uri='http://www.blogger.com'>Blogger</generator><openSearch:totalResults>21</openSearch:totalResults><openSearch:startIndex>1</openSearch:startIndex><openSearch:itemsPerPage>100</openSearch:itemsPerPage><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-6281061763199090984</id><published>2011-12-02T18:40:00.001-08:00</published><updated>2011-12-02T18:47:34.181-08:00</updated><title type='text'>How do I download ImgBurn?</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-OHNzwMxWzgs/TtmNr3Hwu2I/AAAAAAAAAGM/q8hQ6dT4A5E/s1600/ImgBurnBullshit.png"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 400px; height: 318px;" src="http://3.bp.blogspot.com/-OHNzwMxWzgs/TtmNr3Hwu2I/AAAAAAAAAGM/q8hQ6dT4A5E/s400/ImgBurnBullshit.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5681728189574986594" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Which one of these gets me ImgBurn? All I want to do is burn a DVD. Why is this so hard?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-6281061763199090984?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/6281061763199090984/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2011/12/how-do-i-download-imgburn.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/6281061763199090984'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/6281061763199090984'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2011/12/how-do-i-download-imgburn.html' title='How do I download ImgBurn?'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/-OHNzwMxWzgs/TtmNr3Hwu2I/AAAAAAAAAGM/q8hQ6dT4A5E/s72-c/ImgBurnBullshit.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-8522800618344764385</id><published>2011-05-20T14:37:00.000-07:00</published><updated>2011-05-20T15:58:21.931-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='bdd'/><category scheme='http://www.blogger.com/atom/ns#' term='jasmine'/><title type='text'>Exploring Jasmine BDD Framework for Javascript</title><content type='html'>It has been AGES since I've put anything down on paper. I remember when I was at least writing something here (...anything) that my communication skills only improved. I can tell in the past year my skills have deteriorated a bit as I have not been required to write as much. This must be remedied, and I've found a great candidate that I think both is personally applicable and hopefully will benefit someone out there as well. There will hopefully be a seperate string of posts outlining my new experiences and journeys in the start-up world, but that can wait for the time being.&lt;br /&gt;&lt;br /&gt;And let's get at it!&lt;br /&gt;&lt;br /&gt;I recently have been doing a lot of Javascript development at work. After speaking with some coworkers and developer friends of mine, there seems to be a an impetus to better organize Javascript source files. I know from my experience that I've been in situations, both professionally and in my own side-projects, where my JS files just grow and grow like a wildebeast. Other times, I have a difficult time validating that my logic has been correctly implemented. On the server side it's usually a lot easier to validate behavior, either through proper testing or looking at values in a database or many other means. With Javascript, usually you have to fire up the browser and physically navigate through your page in order to visually validate the behavior you are expecting. &lt;br /&gt;&lt;br /&gt;This technique stinks. I know I always forget to check something or leave something off or, worse, introduce regression bugs into my scripts. Bad, Karl.&lt;br /&gt;&lt;br /&gt;Jasmine (&lt;a href="http://pivotal.github.com/jasmine"&gt;http://pivotal.github.com/jasmine&lt;/a&gt;) appears to at least help facilitate BDD when writing Javascript. I've played around with it a little bit and have found it to be quite nice for designing Javascript, and hopefully will give me newfound confidence in the scripts I write.&lt;br /&gt;&lt;br /&gt;Judge for yourself, though. I am but one developer who has had a decent introductory experience. Try the tool out for yourself and see if it helps your workflow.&lt;br /&gt;&lt;br /&gt;For discussion sake I created a very simple spec. Let's say we want to have an image editor, with very simple requirements.&lt;br /&gt;&lt;br /&gt;ImageEditor&lt;br /&gt;-----------&lt;br /&gt;- Should be able to open an image from URL&lt;br /&gt;- When an image is opened, Should show controls for editing the image&lt;br /&gt;- When an image is opened, Should be able to close the image&lt;br /&gt;- When no image is opened, throw an exception if try to close&lt;br /&gt;&lt;br /&gt;So, super simple spec, but a spec nonetheless. We're given four requirements to implement in our Javascript code.&lt;br /&gt;&lt;br /&gt;To start, I'm going to download the latest standalone release of jasmine (as of May 20th, 2011, that was 1.0.2) here (&lt;a href="http://pivotal.github.com/jasmine/download.html"&gt;http://pivotal.github.com/jasmine/download.html&lt;/a&gt;). If you have a Ruby project you will be working with there is a Jasmine Gem, but to keep things language agnostic I'm not going to make any assumptions.&lt;br /&gt;&lt;br /&gt;The zip comes with some example specs and .js files, which I basically followed along with in this post. Nothing fancy, just a "Hello, world", which is basically what I'm showing you here.&lt;br /&gt;&lt;br /&gt;First thing to take a look at is spec/SpecRunner.html.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-ioInaqIPLQE/Tdbf2tj_2EI/AAAAAAAAADs/jykKlNSOeQ8/s1600/1.PNG"&gt;&lt;img style="text-align: center;cursor:pointer; cursor:hand;width: 320px; height: 41px;" src="http://2.bp.blogspot.com/-ioInaqIPLQE/Tdbf2tj_2EI/AAAAAAAAADs/jykKlNSOeQ8/s320/1.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608916516973041730" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;When you first load the page, it runs your tests and shows you the results, either green if they all pass or red for any failures. Standard test fare. You can click "Show passed" checkbox to drill down into each individual test.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-Cn1PEwtUd74/TdbgD3yg4PI/AAAAAAAAAD0/4lVGKwo9XuI/s1600/2.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 154px;" src="http://4.bp.blogspot.com/-Cn1PEwtUd74/TdbgD3yg4PI/AAAAAAAAAD0/4lVGKwo9XuI/s320/2.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608916743056580850" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Nice and clean. &lt;br /&gt;&lt;br /&gt;Ok, so let's start coding some Javascript!&lt;br /&gt;&lt;br /&gt;Our first spec is "Should be able to open an image from URL". So let's write an ImageSpec.js spec first:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;ImageSpec.js&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;describe("Image", function() {&lt;br /&gt; var image;&lt;br /&gt; &lt;br /&gt; beforeEach(function() {&lt;br /&gt;  image = new Image();&lt;br /&gt; });&lt;br /&gt; &lt;br /&gt; it("should be able to set an image url", function() {&lt;br /&gt;  var url = "http://www.google.com/images/logo_sm.gif";&lt;br /&gt;  image.load(url);&lt;br /&gt;  expect(image.url).toEqual(url);&lt;br /&gt; });&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Notice the language that Jasmine uses. The &lt;b&gt;describe&lt;/b&gt; function is used to encapsulate a suite of tests. The &lt;b&gt;it&lt;/b&gt; function specifies your test. This makes your test code read VERY similarly to your spec.&lt;br /&gt; &lt;br /&gt;We're going to add a reference to SpecRunner.html for our new ImageSpec.js file. It looks like this:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;SpecRunner.html&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"&lt;br /&gt;  "http://www.w3.org/TR/html4/loose.dtd"&amp;gt;&lt;br /&gt;&amp;lt;html&amp;gt;&lt;br /&gt;&amp;lt;head&amp;gt;&lt;br /&gt;  &amp;lt;title&amp;gt;Jasmine Test Runner&amp;lt;/title&amp;gt;&lt;br /&gt;  &amp;lt;link rel="stylesheet" type="text/css" href="lib/jasmine-1.0.2/jasmine.css"&amp;gt;&lt;br /&gt;  &amp;lt;script type="text/javascript" src="lib/jasmine-1.0.2/jasmine.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;  &amp;lt;script type="text/javascript" src="lib/jasmine-1.0.2/jasmine-html.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;!-- include source files here... --&amp;gt;&lt;br /&gt;&lt;br /&gt;  &amp;lt;!-- include spec files here... --&amp;gt;&lt;br /&gt;  &amp;lt;script type="text/javascript" src="spec/SpecHelper.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;  &amp;lt;script type="text/javascript" src="spec/ImageSpec.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/head&amp;gt;&lt;br /&gt;&amp;lt;body&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;  jasmine.getEnv().addReporter(new jasmine.TrivialReporter());&lt;br /&gt;  jasmine.getEnv().execute();&lt;br /&gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;br /&gt;&amp;lt;/body&amp;gt;&lt;br /&gt;&amp;lt;/html&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Running this spec we should see 1 failure:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-8OqnoEJ91Mc/TdbiAaKd7XI/AAAAAAAAAD8/YL09PJ2Ib7g/s1600/3.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 116px;" src="http://3.bp.blogspot.com/-8OqnoEJ91Mc/TdbiAaKd7XI/AAAAAAAAAD8/YL09PJ2Ib7g/s320/3.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608918882587635058" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Which we do. Ok, now let's write the code to make this pass.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;Image.js&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;function Image() {&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;Image.prototype.load = function(url) {&lt;br /&gt; this.url = url;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, now we have to add the following line to our SpecRunner.html in order to load our script:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;SpecRunner.html&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="src/Image.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And this is where my first gripe with Jasmine comes in: in a large project, I can have MANY Javascript files. This means I'm going to be editing this SpecRunner.html file A LOT while I'm developing. Weak. Apparently, the Ruby Gem eliminates this problem, and I'm willing to bet there are plugins out there that will alleviate this pain on agnostic projects. For the time being though it's just something to keep in mind that I thought was annoying.&lt;br /&gt;&lt;br /&gt;After we run our test now we should see green:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-w6wX8PPHE1Y/TdbiYjASH7I/AAAAAAAAAEE/udTnjS8XPjM/s1600/4.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 64px;" src="http://1.bp.blogspot.com/-w6wX8PPHE1Y/TdbiYjASH7I/AAAAAAAAAEE/udTnjS8XPjM/s320/4.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608919297277697970" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;And it passes. Alright, let's move onto the ImageEditor.&lt;br /&gt;&lt;br /&gt;Our next spec states: When an image is opened, should show controls for editing the image.&lt;br /&gt;&lt;br /&gt;Ok, let's write a test. &lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;ImageEditorSpec.js&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;describe("ImageEditor", function() {&lt;br /&gt; var imageEditor;&lt;br /&gt; var image;&lt;br /&gt; &lt;br /&gt; beforeEach(function() {&lt;br /&gt;  imageEditor = new ImageEditor();&lt;br /&gt;  image = new Image();&lt;br /&gt; });&lt;br /&gt; &lt;br /&gt; it("should be able to open an image from URL", function() {&lt;br /&gt;  imageEditor.open(image);&lt;br /&gt;  expect(imageEditor.currentImage).toEqual(image);&lt;br /&gt; });&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we need to add the following line to our SpecRunner.html (somewhat annoyed with that yet? You might be...):&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;SpecRunner.html&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="spec/ImageEditorSpec.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Run the tests, and should have a failure:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-8-7GB3JUQrM/TdbinVILp5I/AAAAAAAAAEM/3wENaA-hNe8/s1600/5.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 198px;" src="http://1.bp.blogspot.com/-8-7GB3JUQrM/TdbinVILp5I/AAAAAAAAAEM/3wENaA-hNe8/s320/5.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608919551250769810" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Ok, let's pass this test.&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;ImageEditor.js&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;function ImageEditor() {&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;ImageEditor.prototype.open = function(image) {&lt;br /&gt; this.currentImage = image;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now we run the test:&lt;br /&gt;&lt;br /&gt;Still fails. D'oh! We forgot to add the ImageEditor.js file to our SpecRunner.html file! Are you really annoyed with that yet? (I know I am...)&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;SpecRunner.html&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;script type="text/javascript" src="src/ImageEditor.js"&amp;gt;&amp;lt;/script&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Ok, now let's run the test:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/-2yGcBYJ7HOc/Tdbi2lKiDzI/AAAAAAAAAEU/_aa-wT7oViQ/s1600/6.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 94px;" src="http://1.bp.blogspot.com/-2yGcBYJ7HOc/Tdbi2lKiDzI/AAAAAAAAAEU/_aa-wT7oViQ/s320/6.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608919813253631794" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Green. Passed. &lt;br /&gt;&lt;br /&gt;Ok, so we've opened the image. But there's still part of the spec that isn't implemented: should show controls for editing the image. &lt;br /&gt;&lt;br /&gt;Ok, let's use Jasmine's spyOn function to monitor when functions are called.&lt;br /&gt;&lt;br /&gt;Here's our test:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;ImageEditorSpec.js&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;it("should show controls when an image is opened", function() {&lt;br /&gt; spyOn(imageEditor, 'showControls');&lt;br /&gt; &lt;br /&gt; imageEditor.open(image);&lt;br /&gt; &lt;br /&gt; expect(imageEditor.showControls).toHaveBeenCalledWith(true);&lt;br /&gt;}); &lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;To me, I relate this to something similar to a Stub in Rhino.Mocks. I just want to make sure my function gets called when the image is opened. This will of course fail until we implement.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-FkGVBo7qBcA/TdbjAohhkPI/AAAAAAAAAEc/GVOqFN3BxfA/s1600/7.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 162px;" src="http://4.bp.blogspot.com/-FkGVBo7qBcA/TdbjAohhkPI/AAAAAAAAAEc/GVOqFN3BxfA/s320/7.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608919985954066674" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;So let's implement this behavior:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;ImageEditor.js&lt;/b&gt;&lt;/i&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;ImageEditor.prototype.open = function(image) {&lt;br /&gt; this.currentImage = image;&lt;br /&gt; this.showControls(true);&lt;br /&gt;}&lt;br /&gt;&lt;br /&gt;ImageEditor.prototype.showControls = function(showControls) {&lt;br /&gt; this.isShowControls = showControls;&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, the open function now calls showControls, which our test will check for. In true BDD I should write a test for showControls as well, but for the sake of this blog post I will omit that. Now we run our tests and:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/-a6puWzPVpAk/TdbrMTCZN_I/AAAAAAAAAEk/XzfQQRIYmzk/s1600/8.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 110px;" src="http://3.bp.blogspot.com/-a6puWzPVpAk/TdbrMTCZN_I/AAAAAAAAAEk/XzfQQRIYmzk/s320/8.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608928982437804018" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Tada, green.&lt;br /&gt;&lt;br /&gt;Ok, for our last spec, let's use Jasmine's expect function to test if an exception is thrown:&lt;br /&gt;&lt;br /&gt;- When no image is opened, throw an exception if try to close&lt;br /&gt;&lt;br /&gt;Here's our test, where we are explicitly checking for an exception:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;ImageEditorSpec.js&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;describe("#close", function() {&lt;br /&gt;    it("should throw an exception if image editor already closed", function() {&lt;br /&gt; expect(function() {&lt;br /&gt;  imageEditor.close();&lt;br /&gt; }).toThrow("image editor already closed");&lt;br /&gt;    });&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I love the way this reads. It's pure Javascript, but to me it's extremely straightforward and doesn't require much in the way of picking up and going. &lt;br /&gt;&lt;br /&gt;Running the tests should show a failure:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/-XUQsxheTazA/TdbrMkinPkI/AAAAAAAAAEs/Q7VAdM1lLjc/s1600/9.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 198px;" src="http://2.bp.blogspot.com/-XUQsxheTazA/TdbrMkinPkI/AAAAAAAAAEs/Q7VAdM1lLjc/s320/9.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608928987136343618" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Which it does fail. Let's write the code to make it pass:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;ImageEditor.js&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;ImageEditor.prototype.close = function() {&lt;br /&gt;    if(!this.currentImage) {&lt;br /&gt;        throw new Error('image editor already closed');&lt;br /&gt;    }&lt;br /&gt; &lt;br /&gt;    this.currentImage = null;&lt;br /&gt;    this.showControls(false);&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-rAJbqdujyT4/TdbrNJEvjWI/AAAAAAAAAE0/MjYWL78YPQA/s1600/10.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 139px;" src="http://4.bp.blogspot.com/-rAJbqdujyT4/TdbrNJEvjWI/AAAAAAAAAE0/MjYWL78YPQA/s320/10.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608928996943170914" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Pass. As you can see in my code, there are a couple more functions that should be tested. Here is the spec I used for testing those:&lt;br /&gt;&lt;br /&gt;&lt;b&gt;&lt;i&gt;ImageEditorSpec.js&lt;/i&gt;&lt;/b&gt;&lt;br /&gt;&lt;pre name="code" class="JScript"&gt;&lt;br /&gt;describe("when an image is open", function() {&lt;br /&gt;    beforeEach(function() {&lt;br /&gt;        imageEditor.open(image);&lt;br /&gt;    });&lt;br /&gt; &lt;br /&gt;    it("should show the editing controls", function() {&lt;br /&gt;        expect(imageEditor.isShowControls).toBeTruthy();&lt;br /&gt;    });&lt;br /&gt; &lt;br /&gt;    it("should be able to close the image", function() {&lt;br /&gt;        imageEditor.close();&lt;br /&gt;        expect(imageEditor.currentImage).toEqual(null);&lt;br /&gt;    });&lt;br /&gt;   &lt;br /&gt;    it("should hide controls when an image is closed", function() {&lt;br /&gt;        spyOn(imageEditor, 'showControls');&lt;br /&gt;  &lt;br /&gt;        imageEditor.close();&lt;br /&gt;  &lt;br /&gt;        expect(imageEditor.showControls).toHaveBeenCalledWith(false);&lt;br /&gt;    });&lt;br /&gt;});&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;And these all pass...&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/-a64714YCLQE/TdbrNS6i9uI/AAAAAAAAAE8/uyWhzLAWVIk/s1600/11.PNG"&gt;&lt;img style="display:block; margin:0px auto 10px; text-align:center;cursor:pointer; cursor:hand;width: 320px; height: 198px;" src="http://4.bp.blogspot.com/-a64714YCLQE/TdbrNS6i9uI/AAAAAAAAAE8/uyWhzLAWVIk/s320/11.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5608928999584757474" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;If this framework improves my code even a little I will be thankful. I don't consider myself a Javascript expert by any means, but I think this tool will at least give me the confidence I need to break new ground and learn even more going forward, as well as being a lot more confident with refactorings.&lt;br /&gt;&lt;br /&gt;In the future I hope to expand on this post with more advanced testing and implementing JQuery into my tests, as we use that library in production.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-8522800618344764385?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/8522800618344764385/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2011/05/exploring-jasmine-bdd-framework-for.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/8522800618344764385'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/8522800618344764385'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2011/05/exploring-jasmine-bdd-framework-for.html' title='Exploring Jasmine BDD Framework for Javascript'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/-ioInaqIPLQE/Tdbf2tj_2EI/AAAAAAAAADs/jykKlNSOeQ8/s72-c/1.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-7720373050209252402</id><published>2010-03-02T09:59:00.000-08:00</published><updated>2010-03-02T15:21:53.692-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='.net'/><category scheme='http://www.blogger.com/atom/ns#' term='c#'/><category scheme='http://www.blogger.com/atom/ns#' term='youtube'/><title type='text'>Programmatically uploading videos to YouTube using C#</title><content type='html'>Recently I spent some time investigating the &lt;a href="http://code.google.com/apis/gdata/articles/dotnet_client_lib.html"&gt;Google Data .NET Client library&lt;/a&gt;. Specifically, I was interested in the &lt;a href="http://code.google.com/apis/youtube/2.0/developers_guide_dotnet.html"&gt;YouTube Data API&lt;/a&gt;. What I wanted to do was programmatically upload a video file to my YouTube account. I ran into a couple of (minor) speed bumps along the way, and noticed there were a few things that weren't as clear as they should have been. Hopefully I can clarify the problems I encountered, in case future developers run into the same trip ups. Let's get started.&lt;br /&gt;&lt;br /&gt;For the context of this post, I should explain my development environment. I am using the following:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Windows 7 Professional (32-bit)&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Visual Studio 2008 Professional SP1&lt;/li&gt;&lt;br /&gt;&lt;li&gt;.NET 3.5 SP1&lt;/li&gt;&lt;br /&gt;&lt;li&gt;ASP.NET MVC 1.0&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;OK, so we're going to set up a very basic ASP.NET MVC web site that will basically do two things: &lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;Provide a link to use for authenticating a Google Account&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Provide a form to direct upload a video (including metadata)&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;First things first: download the most up to date version of the &lt;a href="http://code.google.com/apis/gdata/articles/dotnet_client_lib.html"&gt;Google Data .NET Client library&lt;/a&gt; and follow the instructions for installing and setting it up.&lt;br /&gt;&lt;br /&gt;Next, ensure you have a YouTube developer API key attached to your YouTube account. If you have not done this yet, go &lt;a href="http://code.google.com/apis/youtube/dashboard/gwt/index.html"&gt;here&lt;/a&gt; and associate a Developer ID with your account. Take note of this ID (it's pretty long).&lt;br /&gt;&lt;br /&gt;Now, let's set up a new ASP.NET MVC project. We're going to use the Visual Studio defaults here, and just name our project "YouTubeUploader". &lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_n_tMTNWlgdQ/S41clHM1dtI/AAAAAAAAACU/uPEoXxkzljI/s1600-h/ASP.NET+MVC+Template.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 215px;" src="http://1.bp.blogspot.com/_n_tMTNWlgdQ/S41clHM1dtI/AAAAAAAAACU/uPEoXxkzljI/s320/ASP.NET+MVC+Template.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444109317218596562" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Next, we need to add some references to the Google APIs. When you install the Google Data API, there should be a solution at All Programs -&gt; Google Data API SDK -&gt; Google Data API SDK.sln that the &lt;a href="http://code.google.com/apis/gdata/articles/dotnet_client_lib.html"&gt;setup guide&lt;/a&gt; tells you to open and build. Once you have done this, you can select these binaries as a reference in your current project, which is what we do here.&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_n_tMTNWlgdQ/S41csHOkt5I/AAAAAAAAACc/a8VZeg_kZ8Q/s1600-h/Add+Google+References.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 318px; height: 320px;" src="http://4.bp.blogspot.com/_n_tMTNWlgdQ/S41csHOkt5I/AAAAAAAAACc/a8VZeg_kZ8Q/s320/Add+Google+References.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444109437484971922" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Next, we're going to create a ViewModel to encapsulate all the inputs required to pass to our video uploader. This is going to be a very basic ViewModel, with nothing more than properties for retrieving inputs for our video. Here's what the code looks like:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_n_tMTNWlgdQ/S41c5ZznLLI/AAAAAAAAACk/BIOR0PPVlWg/s1600-h/Add+UploadViewModel+class.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 192px;" src="http://4.bp.blogspot.com/_n_tMTNWlgdQ/S41c5ZznLLI/AAAAAAAAACk/BIOR0PPVlWg/s320/Add+UploadViewModel+class.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444109665810459826" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre name="code" class="csharp"&gt;&lt;br /&gt;    namespace YouTubeUploader.Models&lt;br /&gt;    {&lt;br /&gt;        public class UploadViewModel&lt;br /&gt;        {&lt;br /&gt;            public string Title { get; set; }&lt;br /&gt;            public string Keywords { get; set; }&lt;br /&gt;            public string Description { get; set; }&lt;br /&gt;            public bool Private { get; set; }&lt;br /&gt;            public string VideoTags { get; set; }&lt;br /&gt;            public double Latitude { get; set; }&lt;br /&gt;            public double Longitude { get; set; }&lt;br /&gt;            public string Path { get; set; }&lt;br /&gt;            public string Type { get; set; }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Like I said, very basic ViewModel here.&lt;br /&gt;&lt;br /&gt;Next, we need to add a controller method to handle our Login logic. For simplicity sake, we're going to use the HomeController for all of our methods here. In your production situation, however, this logic might be split apart into different modules. We're just going for the basic "Hello, World" functionality here. In order to successfully make YouTube API calls (or any Google Data API, for that matter) you must retrieve an authenticated session token from the Google servers. This can be accomplished a number of different ways. Since we're trying to make a web site here, we're going to go with the &lt;a href="http://code.google.com/apis/gdata/articles/authsub_dotnet.html"&gt;AuthSub method&lt;/a&gt; of Google authentication. Here, we're going to provide a link to our user where they can go and authenticate themselves with the Google servers, send back a session token, and then finally re-direct the user back to a page of our choosing. This token is returned as part of the request query string, which we can handle in a number of different ways. For our purposes, we are going to use a string parameter on one of our controller methods to take the parameter and use it to create an authenticated session token in memory. The method will look like this: &lt;br /&gt;&lt;i&gt;(NOTE: throughout the post, I reference "http://localhost:50555/" as my development server. I am just running my site through Visual Studio 2008 and am taking the default server address provided. This may vary in your environment, so please replace this address for what your environment requires.)&lt;/i&gt;&lt;br /&gt;&lt;pre name="code" class="csharp"&gt;&lt;br /&gt; public ActionResult Login()&lt;br /&gt;        {&lt;br /&gt;            Session["authSubUrl"] = AuthSubUtil.getRequestUrl("http://localhost:50555/Home/Upload", "http://gdata.youtube.com", false, true);&lt;br /&gt;&lt;br /&gt;            return View();&lt;br /&gt;        }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;What we're doing here is using a Google utility (AuthSubUtil.getRequestUrl) to generate the text for our link to provide to our users. getRequestUrl takes the following parameters:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;continueUrl: Where the user will be redirected after authenticating. For our example, I used my local development server (http://localhost:50555/Home/Upload) since I want to pass my authenticated session token into my Upload GET method...more on that next.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;scope: for YouTube API calls we use http://gdata.youtube.com&lt;/li&gt;&lt;br /&gt;&lt;li&gt;secure: If you have &lt;a href="http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html"&gt;registered your app with Google&lt;/a&gt; with the appropriate security credentials, you can set this to true to ensure that your API requests do not show the "Warning: Access Consent" verbiage after authenticating. Also, some API calls are not allowed unless your app is registered. For our testing, we send in false.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;session: Whether the authenticated token should persist over multiple API calls or just be a "one-time-only" shot. This becomes very clear when we actually create our YouTubeRequest object.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt; &lt;br /&gt;Next we add a view for our Login page. It's going to be a very generic view, with only one link on the page. Here is the whole view:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_n_tMTNWlgdQ/S41dHLMiS7I/AAAAAAAAACs/fEGA0Cfml1c/s1600-h/Create+Login+Empty+View.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 301px; height: 320px;" src="http://2.bp.blogspot.com/_n_tMTNWlgdQ/S41dHLMiS7I/AAAAAAAAACs/fEGA0Cfml1c/s320/Create+Login+Empty+View.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444109902406634418" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage" %&amp;gt;&lt;br /&gt;    &amp;lt;asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"&amp;gt;&lt;br /&gt;     Login&lt;br /&gt;    &amp;lt;/asp:Content&amp;gt;&lt;br /&gt;    &amp;lt;asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"&amp;gt;&lt;br /&gt;        &amp;lt;h2&amp;gt;Login&amp;lt;/h2&amp;gt;&lt;br /&gt;        &amp;lt;a href="&amp;lt;%= Session["authSubUrl"] %&amp;gt;"&amp;gt;Click here to login&amp;lt;/a&amp;gt;&lt;br /&gt;&amp;lt;/asp:Content&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Notice how we are retrieving the URL text from Session["authSubUrl"], which we set in our Login() method. You could just as easily encapsulate this value into a ViewModel, however, I felt for the type of exercise we're performing here, this was sufficient.&lt;br /&gt;&lt;br /&gt;Let's compile our project now and run our website. What you see when you navigate to http://localhost:&lt;yourPortNumberHere&gt;/Home/Login is similar to the following:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_n_tMTNWlgdQ/S41dQgCWuBI/AAAAAAAAAC0/zySQJiNX-Uc/s1600-h/Google+Authentication+Link+status.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://4.bp.blogspot.com/_n_tMTNWlgdQ/S41dQgCWuBI/AAAAAAAAAC0/zySQJiNX-Uc/s320/Google+Authentication+Link+status.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444110062619899922" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;The link brings us to a very familiar page to anyone with a Google account:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_n_tMTNWlgdQ/S41dYLNndJI/AAAAAAAAAC8/YoHzjySZewo/s1600-h/Youtube+authentication+1.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://3.bp.blogspot.com/_n_tMTNWlgdQ/S41dYLNndJI/AAAAAAAAAC8/YoHzjySZewo/s320/Youtube+authentication+1.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444110194468942994" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Once the user has entered their credentials, the following screen shows up:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://4.bp.blogspot.com/_n_tMTNWlgdQ/S41dilbK76I/AAAAAAAAADE/uCR6pU2Fxa8/s1600-h/Youtube+Authentication+Authorize+Access+screen.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://4.bp.blogspot.com/_n_tMTNWlgdQ/S41dilbK76I/AAAAAAAAADE/uCR6pU2Fxa8/s320/Youtube+Authentication+Authorize+Access+screen.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444110373303807906" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;This is the warning I mentioned previously about a secure application. If you secure your site with Google, the verbiage here (according to the &lt;a href="http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html"&gt;documentation&lt;/a&gt;) is omitted. I have not yet secured a site with Google yet, so I have not experienced this difference.&lt;br /&gt;&lt;br /&gt;After clicking on "Allow Access", we're presented with the following screen:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_n_tMTNWlgdQ/S41dyEVCb9I/AAAAAAAAADM/1RkDbgzMC8g/s1600-h/Redirected+Authentication+without+Upload+view.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 200px;" src="http://2.bp.blogspot.com/_n_tMTNWlgdQ/S41dyEVCb9I/AAAAAAAAADM/1RkDbgzMC8g/s320/Redirected+Authentication+without+Upload+view.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444110639297621970" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;D'oh! We don't have an Upload view or controller method yet to handle this redirect! This is what we will create next. Take a look at the URL that Google navigated to post-login. &lt;b&gt;http://localhost:50555/Home/Upload?token=CPvdxbuhGRDovLiXBw&lt;/b&gt; That looks awfully similar to what we specified in our Login() method, doesn't it? And you can see the authenticated token in the QueryString at the end of our URL. &lt;br /&gt;&lt;br /&gt;Next we add a controller method to handle GET requests to our Upload page. This is where we are going to handle binding our session token into a YouTubeRequestSettings object, and we'll use that to build a YouTubeRequest object, which is how we'll interact with the YouTube Data API. The method looks like this:&lt;br /&gt;&lt;pre name="code" class="csharp"&gt;&lt;br /&gt;    public ActionResult Upload(string token)&lt;br /&gt;    {&lt;br /&gt;        Session["token"] = AuthSubUtil.exchangeForSessionToken(token, null);&lt;br /&gt;&lt;br /&gt;        return View();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ok, what we're doing here is handling the QueryString token we get back from Google as a part of the GET request by making sure our method has a string parameter (which we call token). The method then uses a method on AuthSubUtil called exchangeForSessionToken which takes a string and an AsymmetricAlgorithm and returns a token good for an entire user session. This way we only have to authenticate the user once per session and they can make as many API calls as the system allows. Since we are not using a secured certificate for authentication we are leaving this as a null parameter. However, if you choose to use this functionality in a production environment I highly suggest taking a look at &lt;a href="http://code.google.com/apis/accounts/docs/RegistrationForWebAppsAuto.html"&gt;the documentation&lt;/a&gt; on registering your app with Google to take advantage of the heightened security. As this is a simple exercise, we are omitting this.&lt;br /&gt;&lt;br /&gt;Next we add a strongly typed view (Create) for our Upload logic (UploadViewModel). We are going to choose "Create" template from the dropdown, and our view comes out like so:&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_n_tMTNWlgdQ/S41d--SIazI/AAAAAAAAADU/1lPZ9zBLZMk/s1600-h/Create+Strongly+Type+Upload+View+Create.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 302px; height: 320px;" src="http://3.bp.blogspot.com/_n_tMTNWlgdQ/S41d--SIazI/AAAAAAAAADU/1lPZ9zBLZMk/s320/Create+Strongly+Type+Upload+View+Create.png" border="0" alt=""id="BLOGGER_PHOTO_ID_5444110861013117746" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;%@ Page Title="" Language="C#" MasterPageFile="~/Views/Shared/Site.Master" Inherits="System.Web.Mvc.ViewPage&amp;lt;YouTubeUploader.Models.UploadViewModel&amp;gt;" %&amp;gt;&lt;br /&gt;    &amp;lt;asp:Content ID="Content1" ContentPlaceHolderID="TitleContent" runat="server"&amp;gt;&lt;br /&gt;     Upload&lt;br /&gt;    &amp;lt;/asp:Content&amp;gt;&lt;br /&gt;    &amp;lt;asp:Content ID="Content2" ContentPlaceHolderID="MainContent" runat="server"&amp;gt;&lt;br /&gt;        &amp;lt;h2&amp;gt;Upload&amp;lt;/h2&amp;gt;&lt;br /&gt;        &amp;lt;%= Html.ValidationSummary("Create was unsuccessful. Please correct the errors and try again.") %&amp;gt;&lt;br /&gt;        &amp;lt;% using (Html.BeginForm()) {%&amp;gt;&lt;br /&gt;            &amp;lt;fieldset&amp;gt;&lt;br /&gt;                &amp;lt;legend&amp;gt;Fields&amp;lt;/legend&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="Title"&amp;gt;Title:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("Title") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("Title", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="Keywords"&amp;gt;Keywords:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("Keywords") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("Keywords", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="Description"&amp;gt;Description:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("Description") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("Description", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="Private"&amp;gt;Private:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("Private") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("Private", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="VideoTags"&amp;gt;VideoTags:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("VideoTags") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("VideoTags", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="Latitude"&amp;gt;Latitude:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("Latitude") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("Latitude", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="Longitude"&amp;gt;Longitude:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("Longitude") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("Longitude", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="Path"&amp;gt;Path:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("Path") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("Path", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;label for="Type"&amp;gt;Type:&amp;lt;/label&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.TextBox("Type") %&amp;gt;&lt;br /&gt;                    &amp;lt;%= Html.ValidationMessage("Type", "*") %&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;input type="submit" value="Create" /&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;&lt;br /&gt;            &amp;lt;/fieldset&amp;gt;&lt;br /&gt;        &amp;lt;% } %&amp;gt;&lt;br /&gt; &amp;lt;div&amp;gt;&lt;br /&gt;  &amp;lt;%=Html.ActionLink("Back to List", "Index") %&amp;gt;&lt;br /&gt; &amp;lt;/div&amp;gt;&lt;br /&gt;    &amp;lt;/asp:Content&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This is a very limited view. What we are doing is adding a field for every property on our UpdateViewModel. This allows the user to specify what kind of video they want to upload.&lt;br /&gt;&lt;br /&gt;Next we add a controller method to handle the POST request for our Upload page (i.e. what happens when we click "Create"). This is where the bulk of our logic will reside. Here's what the code looks like:&lt;br /&gt;&lt;pre name="code" class="csharp"&gt;&lt;br /&gt;    [AcceptVerbs(HttpVerbs.Post)]&lt;br /&gt;    public ActionResult Upload(UploadViewModel uploadViewModel)&lt;br /&gt;    {&lt;br /&gt;        const string developerKey = "THIS_IS_WHERE_YOUR_REALLY_LONG_DEVELOPER_API_KEY_GOES";&lt;br /&gt;        const string applicationName = "THIS_IS_WHERE_YOUR_APP_NAME_GOES";            &lt;br /&gt;&lt;br /&gt;        _settings = new YouTubeRequestSettings(applicationName, "ThisCanSeriouslyBeAnyString_It'sBeenDeprecated", developerKey, (string) Session["token"]);&lt;br /&gt;        _request = new YouTubeRequest(_settings);&lt;br /&gt;&lt;br /&gt;        var newVideo = new Video();&lt;br /&gt;&lt;br /&gt;        newVideo.Title = uploadViewModel.Title;&lt;br /&gt;        newVideo.Keywords = uploadViewModel.Keywords;&lt;br /&gt;        newVideo.Description = uploadViewModel.Description;&lt;br /&gt;        newVideo.YouTubeEntry.Private = uploadViewModel.Private;&lt;br /&gt;&lt;br /&gt;        newVideo.YouTubeEntry.Location = new GeoRssWhere(uploadViewModel.Latitude, uploadViewModel.Longitude);&lt;br /&gt;&lt;br /&gt;  newVideo.Tags.Add(new MediaCategory(uploadViewModel.VideoTags, YouTubeNameTable.DeveloperTagSchema));&lt;br /&gt;&lt;br /&gt;        newVideo.YouTubeEntry.MediaSource = new MediaFileSource(uploadViewModel.Path, uploadViewModel.Type);&lt;br /&gt;        var createdVideo = _request.Upload(newVideo);&lt;br /&gt;&lt;br /&gt;        return View();&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Ok, so it was this method where I ran into the gotcha's that prompted me to write this post in the first place. Once again, in a production environment, you will probably have the developerKey and applicationName stored in some kind of configuration file / object or a database. For our example, we're just setting some hard-coded strings inside our method. These are used to create our YouTubeRequestSettings object. As you can see, the method takes 4 parameters, and &lt;b&gt;this is the method call that was a pain to debug&lt;/b&gt;. The 4 parameters are:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;applicationName: The name of our application, as specified in our YouTube Account screen, to the left of our developer api key.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;client: If you look on your YouTube account screen (as of February 24th, 2010) you'll notice there &lt;i&gt;is not a client id&lt;/i&gt; on your screen. In fact, there is verbiage stating that they are no long required. &lt;b&gt;Use &lt;i&gt;any&lt;/i&gt; string you want here&lt;/b&gt;. Anything. I used "ThisIsMyRidiculouslyLongClientIdStringThatWillWorkJustBecause" and that is fine. It can be anything. I don't know why this hasn't been deprecated yet, but hopefully in the future it does to reduce confusion.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;developerKey: This is your developer key from your YouTube account page. It's really long, so be sure when you copy / paste it in that you grabbed everything.&lt;/li&gt;&lt;br /&gt;&lt;li&gt;authSubToken: This is the string version of the AuthSub session token we created in our Login() method.&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;Once you understand the functionality in setting up your YouTubeRequestSettings object the rest is a walk in the park. The YouTubeRequest object itself takes a YouTubeRequestSettings object as a parameter, so you just new() up one of those with the YouTubeRequestSettings object we just created. Then, we create a new Video() object and set the properties on it equal to the values in our UploadViewModel. This is an ideal situation for &lt;a href="http://www.codeplex.com/AutoMapper"&gt;AutoMapper&lt;/a&gt; in that all we're doing is basically mapping properties from one object to another. However, for this example we are just going to set them explicity ourselves. Then we create a new MediaFileSource object as a property on our Video object. Be sure to escape '\' in your path, if you are using a local path (i.e. instead of C:\MyCode\Project you need C:\\MyCode\\Project). Also, for the Type property, you need the &lt;a href="http://en.wikipedia.org/wiki/MIME"&gt;MIME type&lt;/a&gt; of the video you are uploading. For example, for Windows Media Video files, you want to use "video/x-ms-wmv" as your type.&lt;br /&gt;&lt;br /&gt;And that's it! Let's run the web site now and see our results.&lt;br /&gt;&lt;br /&gt;To make this more robust (and actually &lt;b&gt;usable&lt;/b&gt;) you'll want to provide some kind of feedback mechanism to notify your user whether the upload failed or was successful. For this example I chose to just prove how to upload the files. &lt;br /&gt;&lt;br /&gt;I hope this eases someone's pain and eliminates the 45 minutes - 1 hour I lost trying to figure out why my API calls weren't being correctly authenticated. Take some time and experiment with the rest of the APIs, which allows you to do pretty much &lt;i&gt;anything&lt;/i&gt; you can do on the web site.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-7720373050209252402?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/7720373050209252402/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2010/03/programmatically-uploading-videos-to.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/7720373050209252402'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/7720373050209252402'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2010/03/programmatically-uploading-videos-to.html' title='Programmatically uploading videos to YouTube using C#'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_n_tMTNWlgdQ/S41clHM1dtI/AAAAAAAAACU/uPEoXxkzljI/s72-c/ASP.NET+MVC+Template.png' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-9102938979981295274</id><published>2010-02-08T22:12:00.000-08:00</published><updated>2010-02-10T06:11:00.158-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Mediafly'/><category scheme='http://www.blogger.com/atom/ns#' term='Career'/><title type='text'>Here's to new beginnings...</title><content type='html'>&lt;center&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://www.pocketberry.com/wp-content/uploads/2009/06/img_0025.png"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 304px; height: 341px;" src="http://www.pocketberry.com/wp-content/uploads/2009/06/img_0025.png" border="0" alt="" /&gt;&lt;/a&gt;&lt;/center&gt;&lt;br /&gt;&lt;br /&gt;Today I started a new chapter in my life. Today was my first day as a Senior Software Engineer for &lt;a href="http://www.mediafly.com"&gt;Mediafly&lt;/a&gt; in &lt;a href="http://maps.google.com/maps?q=10+w.+hubbard+chicago+il&amp;ie=UTF8&amp;hq=&amp;hnear=10+W+Hubbard+St,+Chicago,+Cook,+Illinois+60654&amp;gl=us&amp;ei=2udwS8iqBozoM5TX3f4J&amp;ved=0CAgQ8gEwAA&amp;z=16"&gt;Chicago, IL&lt;/a&gt;!&lt;br /&gt;&lt;br /&gt;I don't believe I'm at liberty to discuss details of the underlying architecture out of my respect to my colleagues, but in short I will be working on a C# content distribution system that allows users to receive audio &amp; video content anywhere (there are mobile applications for &lt;b&gt;&lt;i&gt;almost&lt;/i&gt;&lt;/b&gt; every major phone platform, among others).&lt;br /&gt;&lt;br /&gt;Mediafly is exactly the type of shop that I wanted to go to after my time spent behind a corporate firewall. Even in the one day I've spent in their office I realized just how stuck in time my previous employer really is. The atmosphere in the office is full of vibrant life and enthusiasm, instead of the vapid sense of dread accompanying many days in the cubicle jungle. The amount of discussion and collaboration was --UNSEEN-- in any of my previous work environments. These 7 other individuals seem like they are as passionate for making a great product as I am. Awesome.&lt;br /&gt;&lt;br /&gt;It's only been one day so far, but I'm already getting a vibe that this is a move I should have made a long time ago. About a year ago I was told by my (now former) boss that I'd be getting a promotion if I wanted, but no monetary compensation. I (somewhat) politely declined, citing that, more or less, I "don't do mroe work for same money". Sorry. The joke was on me, though, in that I kept working, and ended up getting more responsibility piled on top of me anyway. So I was essentially devaluing myself, since I was doing more work for the same money. I should have been happy to at least have a job (which I was...and definitely still am, considering the times we live in) but being a rabidly passionate developer made me question whether I was in the right spot. I knew I wasn't. It just took a little push to get me in the right direction.&lt;br /&gt;&lt;br /&gt;That push was the news that our department at (former employer) was in jeopardy of being outsourced. Great. So I put in a year of work, only to now have --zero-- chance for my promised "promotion". That was the last straw. Nevermind the fact I was working on software that --nobody-- cared about, not even the client. Nevermind I was doing more than I should have been. My job was going overseas. Time to act.&lt;br /&gt;&lt;br /&gt;And I did. The day I found out about that (the morning, actually...) I had my updated resume in my Google Docs account ready to go. The feed for jobs.stackoverflow.com is in my reader, and I caught a listing from Mediafly. I filled out the form to the best of my abilities and linked to my &lt;a href="http://careers.stackoverflow.com/karlgrz"&gt; Stack Overflow Careers&lt;/a&gt; public CV, and had an email back later that evening. The rest is history. &lt;br /&gt;&lt;br /&gt;It's such a refreshing breath of air to be associated with a group of people dedicated to creating a quality product throughout, from initial data model to finished end result. Despite the fact that things seemed busy today there were no undertones of apathy. There were no signs of any wasted effort. Just a clean, efficient machine working towards a common goal.&lt;br /&gt;&lt;br /&gt;It's only been one day so far, but I'm already proud to be a member of the Mediafly family. I'm so excited to dive into kickstarting my development skills and working towards the same common goal my colleagues so passionately strived for today.&lt;br /&gt;&lt;br /&gt;Today I put together a &lt;a href="http://www.ikea.com/ca/en/catalog/products/S79857848"&gt;Galant IKEA desk&lt;/a&gt; that went pretty smoothly. When I was done, I had a dime-sized blister on my hand...I'll refrain from sharing a snapshot of that to save your lunches. That's what happens when a software developer uses his hands for something other than tickling a keyboard. Hah!&lt;br /&gt;&lt;br /&gt;After that I began setting up my temporary development machine. I'm using a Dell XPS desktop for the time being until my laptop, keyboard, &amp; mouse arrive (I'll update with details when the hardware arrives). It's kind of a double benefit, as the box I'm setting up will be a backup machine in the event of an emergency. &lt;br /&gt;&lt;br /&gt;By the end of the day I was able to check out the codebase for the public facing website and explore some of the major assemblies. &lt;br /&gt;&lt;br /&gt;Thanks for the warm welcome, guys!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-9102938979981295274?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/9102938979981295274/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2010/02/heres-to-new-beginnings.html#comment-form' title='7 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/9102938979981295274'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/9102938979981295274'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2010/02/heres-to-new-beginnings.html' title='Here&apos;s to new beginnings...'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>7</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-163098731288039616</id><published>2009-12-11T10:09:00.000-08:00</published><updated>2009-12-11T11:14:49.085-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Music'/><category scheme='http://www.blogger.com/atom/ns#' term='2009'/><title type='text'>Top 10 Albums of 2009</title><content type='html'>So, in the theme of the holidays (and, coincidentally, jockin' Kyle's style), here's my top 10 albums of 2009. These are in no particular order, and represent only the albums I've actually picked up and listened to. While I like to think I listen to a lot of albums, I know I've not listened to all of them. And here we go...&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Crack_the_Skye"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/4/4a/Cracktheskye.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Crack the Skye&lt;/span&gt; - Mastodon &lt;br /&gt;&lt;br /&gt;Wow. This album blew me away. I had heard some good things about this album before listening to it. I was not anticipating how awesome it would turn out. To me, when you look at Mastodon on paper, there shouldn't be anything special. A progressive metal band from Atlanta. Hmm...OK. But their approach to writing music is magical. &lt;span style="font-style:italic;"&gt;Crack the Skye&lt;/span&gt; feels like a hallucinogenic mindfreak of a trip to me. From start to finish, it's a voyage that has not let me down. A worthy successor to &lt;span style="font-style:italic;"&gt;Blood Mountain&lt;/span&gt;, in my mind. "The Czar" (all 11+ minutes of it) continues to melt my mind every time I listen to it, especially at the climax as the lyrics "Spiraling up through the crack in the sky". Breathtaking. Should have been nominated for a Grammy.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Deflorate"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/e/eb/BDMdeflorate.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Deflorate&lt;/span&gt; - The Black Dahlia Murder&lt;br /&gt;&lt;br /&gt;I saw these guys live for the first time at this summer's Mayhem Festival and they really impressed me. I liked &lt;span style="font-style:italic;"&gt;Unhallowed&lt;/span&gt;, and when I heard they had a new album coming out I was kind of excited. This album has not left my rotation since it came out. From start to finish these guys just plow through my face with riff after riff that defines death metal. Great stuff. I can't wait for the next one.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Wrath_%28album%29"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/f/ff/Lamb_of_God_-_Wrath.JPG" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Wrath&lt;/span&gt; - Lamb of God&lt;br /&gt;&lt;br /&gt;What can I say about Lamb of God? Just another metal band? Hardly. These guys dominate a live stage like nobody else, and their passion really shows through on every set I've seen. This album feels a lot more stripped down than the producer's wet dream that was &lt;span style="font-style:italic;"&gt;Sacrament&lt;/span&gt;, and for that I really appreciate it. But the songs stick with me. The guitars are glorious, and the way Randy changes up his vocals throughout the album is a welcome change of pace. Definitely worthy of the Grammy nomination.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/The_Pariah,_the_Parrot,_the_Delusion"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/0/0a/Dredg-the-pariah-low-res-2009.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;The Pariah, The Parrot, The Delusion&lt;/span&gt; - Dredg&lt;br /&gt;&lt;br /&gt;I saw these guys earlier in the year at The Bottom Lounge in Chicago. I love 'em. I'm convinced these guys approach music the same way a painter approaches a new canvas. &lt;span style="font-style:italic;"&gt;Catch Without Arms&lt;/span&gt; was a great album, but this album feels a lot more attuned with &lt;span style="font-style:italic;"&gt;El Cielo&lt;/span&gt; or &lt;span style="font-style:italic;"&gt;Leitmotif&lt;/span&gt; in terms of artistic integrity. "Pariah" gives me goosebumps every time I hear the "Dah dah da dah da da dahhhhh" at the beginning. Excellent album.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Relapse_%28album%29"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/4/42/Relapse_%28album%29.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Relapse&lt;/span&gt; - Eminem&lt;br /&gt;&lt;br /&gt;The only hip-hop album on my list. Wow. F***ing brilliant. Eminem goes on hiatus for years and comes out with this opus? I think it was all worth it. After reading all of the struggles he went through during his time away from the mixing board and then hearing him pour his heart out on this album it's easy to see why he has problems with substances. Every track on this record could be a candidate for the best hip-hop song of the year, in my opinion.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Hollow_Crown_%28album%29"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/1/18/Albumcoverhollowcrown.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Hollow Crown&lt;/span&gt; - Architects&lt;br /&gt;&lt;br /&gt;A UK metalcore band? Huh? Seriously awesome album. I have my homie Gordon to thank for this one. The first time I played this album I was blasting my stereo at home, and I immediately started head banging to the drums on "Early Grave". Totally surprised me in a good way. These guys got chops, and I'd love to see 'em live sometime soon.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/The_Eternal_Return_%28album%29"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/a/a3/DarkesthourETERNALRETURN.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;The Eternal Return&lt;/span&gt; - Darkest Hour&lt;br /&gt;&lt;br /&gt;Darkest Hour has been a staple in the metal community for much of the 2000's. This album further defines their sound. It seems like they almost stepped back a lot of their riffing on this album, but in a good way. Almost like they've done all the crazy stuff in the past, now they are carving out songs that kick you in the face and stick with you. Dialed back, if you will. Not to say the riffs aren't complex and intricate, it just feels like they scaled things back perfectly to reflect a more defined song writing approach. Well done. The lead off song "Devolution of Flesh" has one of the most memorable guitar riffs of theirs I've heard since &lt;span style="font-style:italic;"&gt;Hidden Hands of a Sadist Nation&lt;/span&gt;.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Killswitch_Engage_%282009_album%29"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/e/ec/Killswitchengage2009album.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Killswitch Engage&lt;/span&gt; - Killswitch Engage&lt;br /&gt;&lt;br /&gt;I am a big Adam Dutkiewicz mark. The guy is as talented as he is hilarious. When I heard they were putting out a new album and touring on Mayhem Festival, I was stoked. They didn't disappoint me. In a way, the band stayed true to form, and in a way they seem to have added a lot more aggresion than was present on &lt;span style="font-style:italic;"&gt;As Daylight Dies&lt;/span&gt;. A lot of metal heads don't appreciate the lyrical content and styling of Howard Jones...I think he's great. Awesome album. Plus the inclusion of live versions of "Rose of Sharyn", "My Curse", and "Holy Diver" make it even better. Hearing Adam call the crowd "douches" made me laugh in my car when I listened through it the first time. I can't wait to hear Adam's black metal side project with Jesse Leech.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Black_Gives_Way_to_Blue"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/5/58/AIC_FINAL_COVERsmall.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Black Gives Way to Blue&lt;/span&gt; - Alice in Chains&lt;br /&gt;&lt;br /&gt;No Layne Staley. Hmm. Concerns? How is this the same band? Why am I buying this album? Because William DuVall is a damn good vocalist, and he can play some mean guitar, too, that's why. I'm convinced Jerry Cantrell cannot make a bad tune. The guy is a machine. This album harkens back to the good ole' days, and it feels like an Alice in Chains record. Great rock album.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;a href="http://en.wikipedia.org/wiki/Hatebreed_%28album%29"&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/b/b6/Hatebreed_Artwork.jpg" height="300" width="300"&gt;&lt;/a&gt;&lt;br /&gt;&lt;span style="font-style:italic;"&gt;Hatebreed&lt;/span&gt; - Hatebreed&lt;br /&gt;&lt;br /&gt;Jamey Jasta is one hard working SOB. I saw Hatebreed last night at the House of Blues in Chicago, and man, he does bring it on stage. Fired up. Adrenaline. Awesome live show. Every one of their albums has been, from start to finish, like a live recording to me. They are not the most dynamic band, they are not the most original band, but what they are is consistent and in-your-face to a point where I get a rush just hearing them play. This album convinced me that Hatebreed can not possibly produce a bad album. And it features Jasta &lt;span style="font-style:italic;"&gt;SINGING&lt;/span&gt; a few passages!!! What?!?! That's unheard of! Great anthems as always.&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;That's that. I'd also like to give honorable mention to Every Time I Die - &lt;span style="font-style:italic;"&gt;The Big Dirty&lt;/span&gt;. I know it didn't come out in 2009, but this album (thanks to my buddy Gordon) has been repeating in my head since I first heard it. I first gave them a listen this year, and I'm really glad I did, and kind of sorry I havn't heard anything of theirs previously. I've heard so much good stuff about &lt;span style="font-style:italic;"&gt;New Junk Aesthetic&lt;/span&gt;, and I'm sure it's amazing, but I can't really include it because I have not listened to it in it's entirety yet. That being said, I'm looking forward to picking it up soon.&lt;br /&gt;&lt;br /&gt;That's my take on 2009. I thought it was a great year for metal and music in general. Good stuff came out, and there's a lot of stuff out there that I need to get my hands on still. Hopefully 2010 can keep up the trends and not disappoint.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-163098731288039616?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/163098731288039616/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/12/top-10-albums-of-2009.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/163098731288039616'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/163098731288039616'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/12/top-10-albums-of-2009.html' title='Top 10 Albums of 2009'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-2212015756908142751</id><published>2009-12-08T09:25:00.000-08:00</published><updated>2009-12-08T09:30:17.413-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Music'/><category scheme='http://www.blogger.com/atom/ns#' term='Dimebag Darrell'/><category scheme='http://www.blogger.com/atom/ns#' term='DamagePlan'/><category scheme='http://www.blogger.com/atom/ns#' term='Pantera'/><title type='text'>R.I.P. Dimebag Darrell 1966 - 2004</title><content type='html'>One of the greatest shredders to ever pick up an axe, &lt;a href="http://en.wikipedia.org/wiki/Dimebag_Darrell"&gt;Dimebag Darrell&lt;/a&gt; of &lt;a href="http://en.wikipedia.org/wiki/Pantera"&gt;Pantera&lt;/a&gt; and &lt;a href="http://en.wikipedia.org/wiki/Damageplan"&gt;DamagePlan&lt;/a&gt; fame, was shot and killed onstage in Columbus, Ohio 5 years ago today in a heinous act, along with others that tried to stop the killer, and the killer himself.&lt;br /&gt;&lt;br /&gt;Today, appreciate the people around you. Appreciate life. Appreciate music. I'll be blasting Pantera all day in memory of one of the greatest metal guitarist of our time. This one's for you, Darrell. You are missed.&lt;br /&gt;&lt;br /&gt;&lt;img src="http://upload.wikimedia.org/wikipedia/en/f/fb/In_memory.jpg"&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-2212015756908142751?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/2212015756908142751/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/12/rip-dimebag-darrell-1966-2004.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/2212015756908142751'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/2212015756908142751'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/12/rip-dimebag-darrell-1966-2004.html' title='R.I.P. Dimebag Darrell 1966 - 2004'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-3128739614468821873</id><published>2009-11-17T08:37:00.000-08:00</published><updated>2009-11-17T08:46:26.831-08:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='House'/><title type='text'>New house!</title><content type='html'>As of October 26th, 2009, I am a new homeowner!&lt;br /&gt;&lt;br /&gt;&lt;img src="http://farm1.static.flickr.com/53/183462290_5e794e9ab7.jpg"&gt;&lt;br /&gt;&lt;br /&gt;It's been a pretty rough couple of weeks getting things set up. The home I purchased is in &lt;a href="http://maps.google.com/maps?f=q&amp;source=s_q&amp;hl=en&amp;geocode=&amp;q=Elgin,+IL&amp;sll=37.0625,-95.677068&amp;sspn=49.223579,79.013672&amp;ie=UTF8&amp;hq=&amp;hnear=Elgin,+Kane,+Illinois&amp;z=12"&gt;Elgin, IL&lt;/a&gt;, a suburb of Chicago about 20 minutes from my office. Myself and my girlfriend, Karen, are currently living there. We got the whole house painted (there were some VERY BRIGHT yellows all over that place...) and mostly furnished, and now the few maintainance issues I have to address are on the task list. There is really nothing too major to handle (the last things I need to do are replace the leaky kitchen faucet, run the bathroom exhaust fan out of the side of the home, and patch up a capped-off pipe under the kitchen sink).&lt;br /&gt;&lt;br /&gt;The home I purchased was a foreclosure, but surprisingly the previous owners kept the place in pretty good condition. It is a very old home (almost 100 years old!) but has a lot of character and a HUGE backyard! Plus it has a full basement (partially finished), so I can set up all my music studio goodness down there.&lt;br /&gt;&lt;br /&gt;I have internet getting hooked up tomorrow afternoon, so I will definitely be back to my posting frequency once that is finished. I'm &lt;b&gt;LOVIN'&lt;/b&gt; this place so far! And I will probably love it even more around tax time when I get that sweet First Time Homebuyers tax credit :-)&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-3128739614468821873?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/3128739614468821873/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/11/new-house.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/3128739614468821873'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/3128739614468821873'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/11/new-house.html' title='New house!'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://farm1.static.flickr.com/53/183462290_5e794e9ab7_t.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-2430859288348183860</id><published>2009-10-02T10:31:00.000-07:00</published><updated>2009-10-02T10:36:49.819-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='charging'/><category scheme='http://www.blogger.com/atom/ns#' term='portable'/><category scheme='http://www.blogger.com/atom/ns#' term='usb'/><title type='text'>Griffin PowerJolt Dual Universal</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_n_tMTNWlgdQ/SsY5MDJsOaI/AAAAAAAAABY/Nu7PZJLlDjs/s1600-h/usb+car.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://1.bp.blogspot.com/_n_tMTNWlgdQ/SsY5MDJsOaI/AAAAAAAAABY/Nu7PZJLlDjs/s320/usb+car.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5388056883362544034" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;I just received this in the mail this morning! &lt;a href="http://www.griffintechnology.com/products/powerjolt-dual-universal"&gt;The Griffin PowerJolt Dual Universal&lt;/a&gt; gives you not one, but &lt;b&gt;TWO&lt;/b&gt; USB ports in your car's 12 volt accessory socket!&lt;br /&gt;&lt;br /&gt;I find myself taking hour long car trips a lot, which equates to two hours of total drive time. Since I use an iPod for all of my musical goodness in my car, if I start the trip with a low charge it might not make it. This little beauty solves that problem, and since there's two ports I can even charge my Storm if I need to. Good stuff, Griffin!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-2430859288348183860?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/2430859288348183860/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/10/griffin-powerjolt-dual-universal.html#comment-form' title='2 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/2430859288348183860'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/2430859288348183860'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/10/griffin-powerjolt-dual-universal.html' title='Griffin PowerJolt Dual Universal'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://1.bp.blogspot.com/_n_tMTNWlgdQ/SsY5MDJsOaI/AAAAAAAAABY/Nu7PZJLlDjs/s72-c/usb+car.jpg' height='72' width='72'/><thr:total>2</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-7276340085295881693</id><published>2009-09-22T08:36:00.000-07:00</published><updated>2009-09-22T08:46:28.318-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Web'/><category scheme='http://www.blogger.com/atom/ns#' term='Blackberry'/><category scheme='http://www.blogger.com/atom/ns#' term='Mobile'/><category scheme='http://www.blogger.com/atom/ns#' term='Browser'/><category scheme='http://www.blogger.com/atom/ns#' term='Opera'/><title type='text'>Blackberry Opera Mini Beta 5</title><content type='html'>I've been testing out &lt;a href="http://www.opera.com/mini/next/"&gt;Opera Mini Beta 5&lt;/a&gt; on my Blackberry Storm for about a week now, and I'm ready to ditch both the Blackberry default browser and Opera Mini 4. &lt;br /&gt;&lt;br /&gt;This build is FAST, and has really good tab support which works great on the nice Storm screen. &lt;br /&gt;&lt;br /&gt;It is integrating with the rest of my Blackberry links (any time I would see a "Get Link" button before, now I see "Open in Opera Mini") which is HUGE for me! I don't have to wait five years to open a blog post through Blackberry's crappy browser anymore.&lt;br /&gt;&lt;br /&gt;The speed dial is really slick, I especially like the little "preview" icon of each site. It's nice to see these "delighters" in a mobile browser that make you feel like you're still using your desktop.&lt;br /&gt;&lt;br /&gt;I used Opera Mini on my Storm before, but this new beta is a serious contender to Mobile Safari on the iPhone. Once it comes out of beta I will definitely put up a proper writeup, but I'm liking where this is going so far.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-7276340085295881693?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/7276340085295881693/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/09/blackberry-opera-mini-beta-5.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/7276340085295881693'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/7276340085295881693'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/09/blackberry-opera-mini-beta-5.html' title='Blackberry Opera Mini Beta 5'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-8561584729852379607</id><published>2009-09-19T11:58:00.001-07:00</published><updated>2009-09-19T12:00:12.220-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Blackberry'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaME'/><category scheme='http://www.blogger.com/atom/ns#' term='Mobile'/><title type='text'>Building a Blackberry application from scratch</title><content type='html'>Today I began work on designing my first Blackberry smartphone application. I plan on documenting all the happenings here. Stay tuned for something interesting, whether it's good or bad.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-8561584729852379607?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/8561584729852379607/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/09/building-blackberry-application-from.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/8561584729852379607'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/8561584729852379607'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/09/building-blackberry-application-from.html' title='Building a Blackberry application from scratch'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-3075195935409422945</id><published>2009-09-18T11:22:00.000-07:00</published><updated>2009-09-18T11:35:12.032-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Boredom'/><category scheme='http://www.blogger.com/atom/ns#' term='Documentation'/><title type='text'>Documentation...(un)necessary evil?!</title><content type='html'>I hate writing documentation. Loathe it like I loathe Oprah (need a seperate post for all the ranting involved in that one...). But it is necessary. Writing a piece of software without documentation is like having a movie that is just the ending. How'd you get to this point? Where do I start? How did all this come about? Why are you doing this?&lt;br /&gt;&lt;br /&gt;Documentation aims to answer all these questions for your fellow developers, especially in a team environment. Asking someone to extend a feature for an application you've written without providing them some kind of documentation should be considered a felony in the state I work in. There are legacy C++ and FORTRAN programs that I have to maintain that have &lt;span style="font-weight:bold;"&gt;ZERO&lt;/span&gt; documentation. No comments, not even a little readme.txt file that says "Hey, sorry I'm lazy...umm, I decided to write this code before you were born (&lt;span style="font-style:italic;"&gt;not kidding on that one...I had to update a piece of FORTRAN code a month ago that is &lt;span style="font-weight:bold;"&gt;10 YEARS OLDER THAN ME!!!&lt;/span&gt;)&lt;/span&gt; and it does something...you figure it out. And fix it. And...yea, good luck!" &lt;br /&gt;&lt;br /&gt;I'm in the middle of writing some design documents for a rather large system that has been actively developed over the past 2 years. I have been on the project for the duration of this time, and am just now getting together some of these documents. I have taken with me a crucial lesson: &lt;span style="font-style:italic;"&gt;write your documentation as you develop, or you will be in for a world of hurt for a few weeks/months.&lt;/span&gt; As a developer, I &lt;span style="font-weight:bold;"&gt;love&lt;/span&gt; coding. Love it. Writing is OK, except when it's describing the software I've developed. Then it becomes a chore in my mind and I get really bored really fast and want to jump back on developing something new. I find it really hard to stay on task for more than a half an hour at a time before I start hoping a service request comes in or a quick fix is needed somewhere. Anything to distract from writing documentation.&lt;br /&gt;&lt;br /&gt;But then I get really angry when I look at code that has &lt;span style="font-weight:bold;"&gt;ZERO&lt;/span&gt; documentation. Hypocritical? Oh yes. Very.&lt;br /&gt;&lt;br /&gt;I've decided I need to start being a lot more proactive in my documentation efforts as I am in the development process, instead of waiting until the end and fighting through writing 100+ pages of dribble that is of no interest to me. By doing the work a little at a time, my hope is my pseudo-ADD won't rear it's ugly head taunt me with the impending doom that "documentation" has done to me in the past.&lt;br /&gt;&lt;br /&gt;Sorry, back to your regularly scheduled programming...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-3075195935409422945?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/3075195935409422945/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/09/documentationunnecessary-evil.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/3075195935409422945'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/3075195935409422945'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/09/documentationunnecessary-evil.html' title='Documentation...(un)necessary evil?!'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-1106025607943931349</id><published>2009-09-17T14:45:00.000-07:00</published><updated>2009-09-17T13:26:15.483-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='Servlet'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaEE'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='ICEFaces'/><title type='text'>Stream an Excel workbook to a client</title><content type='html'>A short while ago I came to a realization that the &lt;a href="http://www.icefaces.org/docs/v1_8_0/tld/ice/dataExporter.html"&gt;ice:dataExporter&lt;/a&gt; component that I was using to export table data from my &lt;a href="http://www.icefaces.org"&gt;ICEFaces&lt;/a&gt; application was not behaving exactly how I liked it. I noticed that when I would refresh the table (which worked great on the UI) and then re-export the file, I would get the SAME data in my output workbook. After some &lt;a href="http://www.google.com/search?source=ig&amp;hl=en&amp;rlz=&amp;=&amp;q=Export+excel+from+JSF&amp;aq=f&amp;oq=&amp;aqi="&gt;Googling&lt;/a&gt; and &lt;a href="http://stackoverflow.com/search?q=Export+excel+jsf"&gt;stackoverflowing&lt;/a&gt;, I came up with a solution that appears to be working great for my needs. I'd like to refactor this at a later date to be more generic than this implementation, but the concept works beautifully for my particular situation.&lt;br /&gt;&lt;br /&gt;Let's start with a simple interface for our data model. We'll call it "Customer". Here's what the interface looks like:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;    package com.test;&lt;br /&gt;    &lt;br /&gt;    public interface Customer&lt;br /&gt;    {&lt;br /&gt;        public String getName();&lt;br /&gt;        public String getAddress();&lt;br /&gt;        public String getEmail();       &lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;OK, so a couple of properties, no big deal. Just an example.&lt;br /&gt;&lt;br /&gt;We also have a managed bean that will hold a List of these Customer objects that we want to display in a table. Here's the Customer bean:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;    package com.test;&lt;br /&gt;    &lt;br /&gt;    import java.util.List;&lt;br /&gt;    import java.util.ArrayList;&lt;br /&gt;    &lt;br /&gt;    public class Bean&lt;br /&gt;    {&lt;br /&gt;        private List&amp;lt;Customer&amp;gt; customerList;&lt;br /&gt;        &lt;br /&gt;        public List&amp;lt;Customer&amp;gt; getCustomerList()&lt;br /&gt;        { &lt;br /&gt;            return customerList;&lt;br /&gt;        }&lt;br /&gt;&lt;br /&gt;        public void setCustomerList(List&amp;lt;Customer&amp;gt; customerList) &lt;br /&gt;        {&lt;br /&gt;            this.customerList = customerList;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        public Bean()&lt;br /&gt;        {&lt;br /&gt;            // Initialize our customerList. &lt;br /&gt;            // We're only going to make 2 Customer&lt;br /&gt;            // objects and add them to our list, &lt;br /&gt;            // but you could be calling&lt;br /&gt;            // a web service or querying a database &lt;br /&gt;            // for your data.&lt;br /&gt;            defineCustomerList();&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        private List&amp;lt;Customer&amp;gt; defineCustomerList()&lt;br /&gt;        {&lt;br /&gt;            this.customerList = new ArrayList&amp;lt;Customer&amp;gt;();&lt;br /&gt;            customerList.add(new OnlineCustomer("Karl", "123 Main St.", "kg@go.com"));&lt;br /&gt;            customerList.add(new OnlineCustomer("Paul", "321 Abbey Rd.", "pg@go.com"));&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;Again, really simple implementation just for this example. &lt;span style="font-style:italic;"&gt;Note: OnlineCustomer is just a basic object that implements the Customer interface I spoke of earlier. I have omitted it's class structure for brevity.&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;We need to reference the managed bean in our faces-config.xml file for our application to be able to use and reference it. Here's the entries for that in our faces-config.xml:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;    &amp;lt;?xml version="1.0" encoding="UTF-8"?&amp;gt;&lt;br /&gt;    &lt;br /&gt;    &amp;lt;faces-config&lt;br /&gt;        xmlns="http://java.sun.com/xml/ns/javaee"&lt;br /&gt;        xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"&lt;br /&gt;        xsi:schemaLocation="http://java.sun.com/xml/ns/javaee http://java.sun.com/xml/ns/javaee/web-facesconfig_1_2.xsd"&lt;br /&gt;        version="1.2"&amp;gt;&lt;br /&gt;       &amp;lt;managed-bean&amp;gt;&lt;br /&gt;          &amp;lt;managed-bean-name&amp;gt;Bean&amp;lt;/managed-bean-name&amp;gt;&lt;br /&gt;          &amp;lt;managed-bean-class&amp;gt;com.test.Bean&amp;lt;/managed-bean-class&amp;gt;&lt;br /&gt;          &amp;lt;managed-bean-scope&amp;gt;session&amp;lt;/managed-bean-scope&amp;gt;&lt;br /&gt;       &amp;lt;/managed-bean&amp;gt;  &lt;br /&gt;    &amp;lt;/faces-config&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So we're going to have a simple .jspx that has an &lt;a href="http://facestutorials.icefaces.org/tutorial/dataTable-tutorial.html"&gt;ice:dataTable&lt;/a&gt; component, and a button to export the table to an Excel file. Here's the markup:&lt;br /&gt;&lt;pre name="code" class="html"&gt;&lt;br /&gt;    &amp;lt;jsp:root&lt;br /&gt;        jsfc="f:view"&lt;br /&gt;        xmlns:jsp="http://java.sun.com/JSP/Page"&lt;br /&gt;        xmlns:f="http://java.sun.com/jsf/core"&lt;br /&gt;        xmlns:h="http://java.sun.com/jsf/html"&lt;br /&gt;        xmlns:ice="http://www.icesoft.com/icefaces/component"&lt;br /&gt;        xmlns:ui="http://java.sun.com/jsf/facelets"&lt;br /&gt;        xmlns:c="http://java.sun.com/jstl/core"&lt;br /&gt;        xmlns:fn="http://java.sun.com/jsp/jstl/functions"&lt;br /&gt;    &amp;gt;&lt;br /&gt;        &amp;lt;ice:outputDeclaration&lt;br /&gt;            doctypeRoot="html"&lt;br /&gt;            doctypePublic="-//W3C//DTD XHTML 1.0 Transitional//EN"&lt;br /&gt;            doctypeSystem="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&lt;br /&gt;        /&amp;gt;&lt;br /&gt;        &amp;lt;html&amp;gt;&lt;br /&gt;            &amp;lt;head&amp;gt;&lt;br /&gt;                &amp;lt;title&amp;gt;Excel Output Example&amp;lt;/title&amp;gt;&lt;br /&gt;                &amp;lt;link&lt;br /&gt;                    rel="stylesheet"&lt;br /&gt;                    type="text/css"&lt;br /&gt;                    href="../xmlhttp/css/rime/rime.css"&lt;br /&gt;                /&amp;gt;&lt;br /&gt;            &amp;lt;/head&amp;gt;&lt;br /&gt;            &amp;lt;body&amp;gt;&lt;br /&gt;                &amp;lt;ice:form&lt;br /&gt;                    partialSubmit="true"&lt;br /&gt;                    rendered="true" &amp;gt;   &lt;br /&gt;                    &amp;lt;ice:dataTable id="customerTable"      &lt;br /&gt;                                      value="#{Bean.customerList}"&lt;br /&gt;                                      rows="20"&lt;br /&gt;                                      var="customer"&amp;gt;&lt;br /&gt;                        &amp;lt;ice:column&amp;gt;&lt;br /&gt;                            &amp;lt;f:facet name="header"&amp;gt;&lt;br /&gt;                                &amp;lt;ice:outputText id="column1" value="Name"/&amp;gt;&lt;br /&gt;                            &amp;lt;/f:facet&amp;gt;&lt;br /&gt;                            &amp;lt;ice:outputText id="name" value="#{customer.name}" /&amp;gt;                                    &lt;br /&gt;                        &amp;lt;/ice:column&amp;gt;&lt;br /&gt;                        &amp;lt;ice:column&amp;gt;&lt;br /&gt;                            &amp;lt;f:facet name="header"&amp;gt;&lt;br /&gt;                                &amp;lt;ice:outputText id="column2" value="Address"/&amp;gt;&lt;br /&gt;                            &amp;lt;/f:facet&amp;gt;&lt;br /&gt;                            &amp;lt;ice:outputText id="address" value="#{customer.address}" /&amp;gt;                             &lt;br /&gt;                        &amp;lt;/ice:column&amp;gt;&lt;br /&gt;                        &amp;lt;ice:column&amp;gt;&lt;br /&gt;                            &amp;lt;f:facet name="header"&amp;gt;&lt;br /&gt;                                &amp;lt;ice:outputText id="column3" value="E-Mail"/&amp;gt;&lt;br /&gt;                            &amp;lt;/f:facet&amp;gt;&lt;br /&gt;                            &amp;lt;ice:outputText id="email" value="#{customer.email}" /&amp;gt;&lt;br /&gt;                        &amp;lt;/ice:column&amp;gt;&lt;br /&gt;                    &amp;lt;/ice:dataTable&amp;gt;      &lt;br /&gt;                    &amp;lt;ice:outputLink value="./export" &amp;gt;&lt;br /&gt;                        &amp;lt;h:outputText value="Export To Excel" /&amp;gt;&lt;br /&gt;                    &amp;lt;/ice:outputLink&amp;gt;                                            &lt;br /&gt;            &amp;lt;/body&amp;gt;&lt;br /&gt;        &amp;lt;/html&amp;gt;&lt;br /&gt;    &amp;lt;/jsp:root&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;So we have a button that is just an output link to the URL /export which we are going to configure to control our Excel output. How, you ask? We're going to write a Servlet that will handle requests to that URL and will stream an Excel workbook to the client, with the familiar "Open/Save As" dialog box your users are comfortable with.&lt;br /&gt;&lt;br /&gt;To accomplish this we need to tell our application what to do when it navigates to the URL /export. We don't want another page, since we don't want to give the user any choices, just export every customer that is currently in the List&lt;Customer&gt;. The way we are writing this Excel class, it will output &lt;span style="font-weight:bold;"&gt;ANY&lt;/span&gt; class that implements the Customer interface (&lt;span style="font-style:italic;"&gt;Note: as I mentioned above I want to refactor this later to be a bit more generic, but for this example it works fine.)&lt;/span&gt;&lt;br /&gt;&lt;br /&gt;OK, so first step let's write our ExportToExcel servlet. For this servlet we are going to use the &lt;a href="http://poi.apache.org/"&gt;Apache POI library&lt;/a&gt; for writing our Excel workbook. Note that ICEFaces uses a different library (&lt;a href="http://jexcelapi.sourceforge.net/"&gt;jxl&lt;/a&gt;) which would work fine as a substitute for POI. However, I am more comfortable with POI, hence that's why I am using it here). Here's what our servlet looks like:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;    package com.test;&lt;br /&gt;    &lt;br /&gt;    import java.io.*;&lt;br /&gt;    import java.util.List;&lt;br /&gt;    &lt;br /&gt;    import javax.servlet.*;&lt;br /&gt;    import javax.servlet.http.*;&lt;br /&gt;    &lt;br /&gt;    import org.apache.poi.hssf.usermodel.HSSFRichTextString;&lt;br /&gt;    import org.apache.poi.hssf.usermodel.HSSFRow;&lt;br /&gt;    import org.apache.poi.hssf.usermodel.HSSFSheet;&lt;br /&gt;    import org.apache.poi.hssf.usermodel.HSSFWorkbook;&lt;br /&gt;    &lt;br /&gt;    public class ExportToExcel extends HttpServlet &lt;br /&gt;    {&lt;br /&gt;        private static final long serialVersionUID = 2595261807932102942L;&lt;br /&gt;    &lt;br /&gt;        protected void doGet(HttpServletRequest req, HttpServletResponse resp)&lt;br /&gt;        {&lt;br /&gt;            doPost(req, resp);&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        protected void doPost(HttpServletRequest req, HttpServletResponse resp)&lt;br /&gt;        {&lt;br /&gt;            // Get the current session for our ICEFaces application.&lt;br /&gt;            // This gives us access to the session scoped managed beans&lt;br /&gt;            HttpSession session = req.getSession(false);&lt;br /&gt;            if (session != null)&lt;br /&gt;            {&lt;br /&gt;               // Retrieve the current, data-filled customerList &lt;br /&gt;               // collection from our session.&lt;br /&gt;               // This will drive the data of our Excel workbook.&lt;br /&gt;               List&amp;lt;Customer&amp;gt; customers = &lt;br /&gt;                   ((Bean)req.getSession().getAttribute("Bean"))&lt;br /&gt;                    .getCustomerList(); &lt;br /&gt;               &lt;br /&gt;               // In order to get the browser-native dialog box &lt;br /&gt;               // ("Open / Save As") we need to set these values&lt;br /&gt;               // in the response object that this servlet will &lt;br /&gt;               // send back to the client.&lt;br /&gt;               resp.setContentType("application/vnd.ms-excel");  &lt;br /&gt;               resp.setHeader(&lt;br /&gt;                   "Content-disposition", "attachment;filename=Customers.xls");            &lt;br /&gt;                 &lt;br /&gt;               // Our Excel workbook instance we are going to &lt;br /&gt;               // manipulate and fill with our Customer data.&lt;br /&gt;               HSSFWorkbook wb = new HSSFWorkbook();  &lt;br /&gt;               HSSFSheet sheet = wb.createSheet("Customers");  &lt;br /&gt;               &lt;br /&gt;               // Instance for a Excel worksheet row.&lt;br /&gt;               HSSFRow row;  &lt;br /&gt;               &lt;br /&gt;               // rowNum will be used to specify which &lt;br /&gt;               // row number we are going to be manipulating&lt;br /&gt;               // within the worksheet.&lt;br /&gt;               int rowNum = 0;&lt;br /&gt;               &lt;br /&gt;               // Create a new row that represents a row in the worksheet.&lt;br /&gt;               row = sheet.createRow((short)rowNum);  &lt;br /&gt;               &lt;br /&gt;               // Here we're just creating our header row.&lt;br /&gt;               // createCell(#) allows us to reference specific &lt;br /&gt;               // cells within our worksheet.&lt;br /&gt;               row.createCell((short)0)&lt;br /&gt;                  .setCellValue(new HSSFRichTextString("Name"));  &lt;br /&gt;               row.createCell((short)1)&lt;br /&gt;                  .setCellValue(new HSSFRichTextString("Address"));&lt;br /&gt;               row.createCell((short)2)&lt;br /&gt;                  .setCellValue(new HSSFRichTextString("Email"));&lt;br /&gt;               &lt;br /&gt;               // Increment our row to start writing out customer data &lt;br /&gt;               // from our List&amp;lt;Customer&amp;gt; collection.&lt;br /&gt;               rowNum++;&lt;br /&gt;               &lt;br /&gt;               // Iterate over our List&amp;lt;Customer&amp;gt; to &lt;br /&gt;               // write all the data out.&lt;br /&gt;               for(Customer customer:customers)&lt;br /&gt;               {           &lt;br /&gt;                   // Create a new row that represents a row in the worksheet.&lt;br /&gt;                   row = sheet.createRow((short)rowNum);               &lt;br /&gt;                   &lt;br /&gt;                   // Write out the data for each Customer object.&lt;br /&gt;                   row.createCell((short)0)&lt;br /&gt;                      .setCellValue(new HSSFRichTextString(estimate.getName()));  &lt;br /&gt;                   row.createCell((short)1)&lt;br /&gt;                       .setCellValue(new HSSFRichTextString(estimate.getAddress()));&lt;br /&gt;                   row.createCell((short)2)&lt;br /&gt;                      .setCellValue(new HSSFRichTextString(estimate.getEmail()));&lt;br /&gt;                   &lt;br /&gt;                   // Increment our row to start continue writing customer data &lt;br /&gt;                   // from our List&amp;lt;Customer&amp;gt; collection.&lt;br /&gt;                   rowNum++;&lt;br /&gt;               }&lt;br /&gt;               &lt;br /&gt;               try &lt;br /&gt;               {  &lt;br /&gt;                    // Create an output stream to stream data to the client.&lt;br /&gt;                    ServletOutputStream out = resp.getOutputStream();  &lt;br /&gt;                    &lt;br /&gt;                    // POI has a write() method on a HSSFWorkbook object &lt;br /&gt;                    // that takes a ServletOutputStream as a parameter&lt;br /&gt;                    // and streams the contents to the client.&lt;br /&gt;                    wb.write(out);  &lt;br /&gt;                    &lt;br /&gt;                    out.flush();  &lt;br /&gt;                    out.close();  &lt;br /&gt;               } &lt;br /&gt;               catch (IOException e) &lt;br /&gt;               {   &lt;br /&gt;                    // Print any errors to stdout&lt;br /&gt;                    e.printStackTrace();  &lt;br /&gt;               }           &lt;br /&gt;            }&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;We call the doPost() method from our doGet() because we want the excel to be output regardless of how this page is navigated to (&lt;a href="http://www.icefaces.org/docs/latest/tld/ice/commandButton.html"&gt;commandButton&lt;/a&gt; and &lt;a href="http://www.icefaces.org/docs/v1_8_0/tld/ice/commandLink.html"&gt;commandLink&lt;/a&gt; use different submit methods to HTTP). Now we run the same logic regardless of how the URL is reached. Good.&lt;br /&gt;&lt;br /&gt;The last piece is to tell our application what code to run when we navigate to /export. This is done using a servlet definition and servlet URL mapping in web.xml. We will be adding the following entries:&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;    &amp;lt;servlet&amp;gt;&lt;br /&gt;        &amp;lt;servlet-name&amp;gt;exportServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;        &amp;lt;servlet-class&amp;gt;com.test.ExportToExcel&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;        &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;&lt;br /&gt;    &amp;lt;/servlet&amp;gt;&lt;br /&gt;    &amp;lt;servlet-mapping&amp;gt;&lt;br /&gt;        &amp;lt;servlet-name&amp;gt;exportServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;        &amp;lt;url-pattern&amp;gt;/export&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;    &amp;lt;/servlet-mapping&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;This tells my application to listen for all GET/POST requests at the URL /export, and to use the exportServlet (com.test.ExportToExcel) to process these requests.&lt;br /&gt;&lt;br /&gt;This implementation works great for streaming data from a List to an excel worksheet. I've tested this with as much as ~1,500 objects with 12 properties and found performance to be quite snappy, even with many users. Obviously this is &lt;span style="font-weight:bold;"&gt;very&lt;/span&gt; concrete code and should be refactored to allow for a more dynamic approach, but for this example I think it's perfect.&lt;br /&gt;&lt;br /&gt;Thoughts? Anyone have a (much) better way to implement this?&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-1106025607943931349?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/1106025607943931349/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/09/stream-excel-workbook-to-client.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/1106025607943931349'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/1106025607943931349'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/09/stream-excel-workbook-to-client.html' title='Stream an Excel workbook to a client'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>3</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-3633525659739415323</id><published>2009-08-31T13:35:00.000-07:00</published><updated>2009-08-31T13:37:47.910-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='mug'/><category scheme='http://www.blogger.com/atom/ns#' term='codinghorror'/><title type='text'>New cubicle productivity booster arrives!</title><content type='html'>&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_n_tMTNWlgdQ/Spw0fg2BmgI/AAAAAAAAABQ/8eeDvrGq8rY/s1600-h/IMG00011-20090831-1537.jpg"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 240px;" src="http://2.bp.blogspot.com/_n_tMTNWlgdQ/Spw0fg2BmgI/AAAAAAAAABQ/8eeDvrGq8rY/s320/IMG00011-20090831-1537.jpg" border="0" alt=""id="BLOGGER_PHOTO_ID_5376229771170650626" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;Isn't she pretty?!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-3633525659739415323?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/3633525659739415323/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/08/new-cubicle-productivity-booster.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/3633525659739415323'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/3633525659739415323'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/08/new-cubicle-productivity-booster.html' title='New cubicle productivity booster arrives!'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://2.bp.blogspot.com/_n_tMTNWlgdQ/Spw0fg2BmgI/AAAAAAAAABQ/8eeDvrGq8rY/s72-c/IMG00011-20090831-1537.jpg' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-7554172874016283436</id><published>2009-08-25T08:27:00.000-07:00</published><updated>2009-08-31T13:38:25.683-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='firefox'/><category scheme='http://www.blogger.com/atom/ns#' term='add-ons'/><category scheme='http://www.blogger.com/atom/ns#' term='gmail'/><category scheme='http://www.blogger.com/atom/ns#' term='greader'/><title type='text'>Icon-only PermaTabs Collection of Add-ons for Firefox</title><content type='html'>This might not be news to everyone (or anyone...) but the kind folks over at &lt;a href="http://lifehacker.com"&gt;Lifehacker&lt;/a&gt; have put together a &lt;a href="http://lifehacker.com/5344395/icon+only-permatabs-collection-streamlines-your-minimal-gmail-google-reader-tabs"&gt;great post&lt;/a&gt; that streamlines how to set up permanent tabs in Firefox and some other goodies for GMail &amp; Google Reader. I've been using this set up for only about an hour or so and I am already kicking myself for not installing Better GMail 2 &amp; Better GReader 2 before today (considering how much I use these applications). Kudos!&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-7554172874016283436?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/7554172874016283436/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/08/icon-only-permatabs-collection-of-add.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/7554172874016283436'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/7554172874016283436'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/08/icon-only-permatabs-collection-of-add.html' title='Icon-only PermaTabs Collection of Add-ons for Firefox'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-2016920357676649820</id><published>2009-08-20T11:42:00.000-07:00</published><updated>2010-08-04T18:07:39.515-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='javascript'/><category scheme='http://www.blogger.com/atom/ns#' term='JavaEE'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='ICEFaces'/><category scheme='http://www.blogger.com/atom/ns#' term='Session-Management'/><title type='text'>ICEFaces DisposableBean and cleaning up session resources</title><content type='html'>Here's the scenario:&lt;br /&gt;&lt;br /&gt;Your user has this web application that runs a data process. Say, power through a 2GB file and run some processing on each record. Fairly straightforward back end logic, maybe aggregate a column on the file and write the total to a SQL database somewhere. Whatever.&lt;br /&gt;&lt;br /&gt;Let's say this process takes upwards of 45 minutes - 1 hour to complete, depending on the input file. In order to assure that the process finishes to completion we want to set our session timeout to be something longer than that. Let's say 2 hours, to ensure we can get through a really large file, if need be.&lt;br /&gt;&lt;br /&gt;OK, great. Where's the problem?&lt;br /&gt;&lt;br /&gt;Let's say they only want to run a very tiny file, like 10 MB, and this only takes 5 minutes to run. Why do I need to wait for 2 hours to invalidate that session if the user is done and back to surfin' the net after 5 minutes?!&lt;br /&gt;&lt;br /&gt;I wanted to have a mechanism in place for cleanup code to executed in the following two cases:&lt;br /&gt;&lt;br /&gt;1. The user's session expires naturally.&lt;br /&gt;2. The user closes their internet browser.&lt;br /&gt;&lt;br /&gt;The first one is somewhat trivial, in that JSF manages a lot of that bean clean up for us. The second one, however, proved to be quite the task in nailing down so it worked to my satisfaction. After a morning or so of research, however, I have an implementation that I like and seems to be working.&lt;br /&gt;&lt;br /&gt;Here's what I did:&lt;br /&gt;&lt;br /&gt;I'm using JSF 1.2, ICEFaces 1.8, and Tomcat 6.0. Since ICEFaces 1.7, they have included an interface with their framework called DisposableBean. What this interfaces does is define one method (dispose()) that gets called when the session is invalidated, either naturally or by application code. &lt;br /&gt;&lt;br /&gt;So let's say I have a simple session-scoped managed bean as such:&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;    package com.beans;&lt;br /&gt;&lt;br /&gt;    public class Bean&lt;br /&gt;    {&lt;br /&gt;        public Bean()&lt;br /&gt;        {&lt;br /&gt;            defineDescriptorList();&lt;br /&gt;        }   &lt;br /&gt;        &lt;br /&gt;        private List&amp;lt;String&amp;gt; descriptorList;&lt;br /&gt;    &lt;br /&gt;        public List&amp;lt;String&amp;gt; getDescriptorList()&lt;br /&gt;        {&lt;br /&gt;            return descriptorList;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        public void setDescriptorList(List&amp;lt;String&amp;gt; descriptorList)&lt;br /&gt;        {&lt;br /&gt;            this.descriptorList = descriptorList;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        private String selectedDescriptor;&lt;br /&gt;    &lt;br /&gt;        public String getSelectedDescriptor()&lt;br /&gt;        {&lt;br /&gt;            return selectedDescriptor;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        public void setSelectedDescriptor(String selectedDescriptor)&lt;br /&gt;        {&lt;br /&gt;            this.selectedDescriptor = selectedDescriptor;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        private void defineDescriptorList()&lt;br /&gt;        {&lt;br /&gt;            descriptorList = ServiceToSetupDescriptorList(); // Return a List&amp;lt;String&amp;gt; of pertinent descriptors&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        public String executeProcess()&lt;br /&gt;        {&lt;br /&gt;            ServiceToProcessLongDataFile(getSelectedDescriptor()); // Run our data process using selectedDescriptor&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In my faces-config.xml, I have the following entry to configure the JSF framework to manage this bean instance:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;managed-bean&amp;gt;&lt;br /&gt;  &amp;lt;managed-bean-name&amp;gt;Bean&amp;lt;/managed-bean-name&amp;gt;&lt;br /&gt;  &amp;lt;managed-bean-class&amp;gt;com.beans.Bean&amp;lt;/managed-bean-class&amp;gt;&lt;br /&gt;  &amp;lt;managed-bean-scope&amp;gt;session&amp;lt;/managed-bean-scope&amp;gt;&lt;br /&gt;&amp;lt;/managed-bean&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;OK, so upon the first reference to Bean on the .jspx page, JSF will instantiate an instance of the Bean class using the no-argument constructor I have defined. The call to defineDescriptorList() will set up my descriptorList collection with values from a service (could be database, file, whatever...irrelevant to this conversation).&lt;br /&gt;&lt;br /&gt;OK, now, let's say that on my page I just want to fire off a huge data processing event using a parameter chosen from our descriptorList collection. This is a basic example, so I'm just storing the selected value into a string variable and using that in my bean to fire off the process. To do this I will use a selectOneMenu component. Here's my markup:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="html"&gt;&lt;br /&gt;&amp;lt;jsp:root&lt;br /&gt;    jsfc="f:view"&lt;br /&gt;    xmlns:jsp="http://java.sun.com/JSP/Page"&lt;br /&gt;    xmlns:f="http://java.sun.com/jsf/core"&lt;br /&gt;    xmlns:h="http://java.sun.com/jsf/html"&lt;br /&gt;    xmlns:ice="http://www.icesoft.com/icefaces/component"&lt;br /&gt;    xmlns:ui="http://java.sun.com/jsf/facelets"&lt;br /&gt;    xmlns:c="http://java.sun.com/jstl/core"&lt;br /&gt;    xmlns:fn="http://java.sun.com/jsp/jstl/functions"&lt;br /&gt;&amp;gt;&lt;br /&gt;    &amp;lt;ice:outputDeclaration&lt;br /&gt;        doctypeRoot="html"&lt;br /&gt;        doctypePublic="-//W3C//DTD XHTML 1.0 Transitional//EN"&lt;br /&gt;        doctypeSystem="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&lt;br /&gt;    /&amp;gt;&lt;br /&gt;    &amp;lt;html&amp;gt;&lt;br /&gt;        &amp;lt;head&amp;gt;&lt;br /&gt;            &amp;lt;title&amp;gt;Descriptor Lookup Tool&amp;lt;/title&amp;gt;&lt;br /&gt;            &amp;lt;link&lt;br /&gt;                rel="stylesheet"&lt;br /&gt;                type="text/css"&lt;br /&gt;                href="/xmlhttp/css/rime/rime.css"&lt;br /&gt;            /&amp;gt;&lt;br /&gt;        &amp;lt;/head&amp;gt;&lt;br /&gt;        &amp;lt;body&amp;gt;&lt;br /&gt;             &amp;lt;!-- content --&amp;gt;&lt;br /&gt;            &amp;lt;h1&amp;gt;Descriptor Lookup Tool&amp;lt;/h1&amp;gt;&lt;br /&gt;            &amp;lt;br /&amp;gt;&lt;br /&gt;            &amp;lt;ice:form&lt;br /&gt;                partialSubmit="true"&lt;br /&gt;                rendered="true" &amp;gt;   &lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;ice:panelGroup&amp;gt;       &lt;br /&gt;                        &amp;lt;ice:panelGrid columns="2"&amp;gt;&lt;br /&gt;                            &amp;lt;ice:outputText style="font-weight: bold;" value="Descriptors" /&amp;gt;                                                                            &lt;br /&gt;                            &amp;lt;ice:selectOneMenu id="ddlDescriptor" partialSubmit="true" value="#{Bean.selectedDescriptor}" &amp;gt;                        &lt;br /&gt;                                &amp;lt;f:selectItems id="ddlItems" value="#{Bean.descriptorList}"/&amp;gt;&lt;br /&gt;                            &amp;lt;/ice:selectOneMenu&amp;gt;&lt;br /&gt;                        &amp;lt;/ice:panelGrid&amp;gt;&lt;br /&gt;                    &amp;lt;/ice:panelGroup&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;    &lt;br /&gt;                    &lt;br /&gt;                &amp;lt;p&amp;gt;                                                      &lt;br /&gt;                    &amp;lt;ice:panelGrid columns="1"&amp;gt;  &lt;br /&gt;                        &amp;lt;ice:commandLink value="Process Data File" &lt;br /&gt;                                         action="#{Bean.executeProcess}" /&amp;gt;&lt;br /&gt;                    &amp;lt;/ice:panelGrid&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;                                &lt;br /&gt;                &lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;ice:panelGrid columns="2"&amp;gt;                            &lt;br /&gt;                        &amp;lt;ice:messages id="errMsgs" errorClass="error" infoClass="ss_text" style="font-size:14px;" layout="table"/&amp;gt;&lt;br /&gt;                        &amp;lt;ice:outputText id="txtErrors" style="font-size:14px;" value="" /&amp;gt;                        &lt;br /&gt;                    &amp;lt;/ice:panelGrid&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;                               &lt;br /&gt;            &amp;lt;/ice:form&amp;gt;&lt;br /&gt;        &amp;lt;/body&amp;gt;&lt;br /&gt;    &amp;lt;/html&amp;gt;&lt;br /&gt;&amp;lt;/jsp:root&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Great! This gives us our list for what we're trying to accomplish. However, after I run my process, which could take up to 2 hours, and I close my browser window, I'm left with a session object that is going to be laying around for potentially longer (depending on how long the session timeout is set for). When that browser window closes I wanted to run some clean up code. This is where DisposableBean interface comes in. Let's make the following changes to our Bean class:&lt;br /&gt;&lt;br /&gt;1. Implement the DisposableBean interface, which includes overriding the dispose() method and adding our logic to cleanup our bean there.&lt;br /&gt;2. Add a Logout servlet that will call the dispose() method on any or all of the managed beans we want to clean up.&lt;br /&gt;3. Add some JavaScript to our .jspx page to listen for the browser close event and navigate to the "Logout" URL, which will run our Logout servlet code.&lt;br /&gt;&lt;br /&gt;Sounds like a lot of work? It's actually not as bad as you would think, and gives us the power to ensure our resources are cleaned up if the browser is closed.&lt;br /&gt;&lt;br /&gt;Let's tackle these one at a time.&lt;br /&gt;&lt;br /&gt;First, we implement the DisposableBean interface (which is an ICEFaces interface that is included with the framework as of version 1.7) to our Bean class. Here's what our class will look like after:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;    package com.beans;&lt;br /&gt;&lt;br /&gt;    import com.icesoft.faces.context.DisposableBean;&lt;br /&gt;&lt;br /&gt;    public class Bean implements DisposableBean&lt;br /&gt;    {&lt;br /&gt;        public Bean()&lt;br /&gt;        {&lt;br /&gt;            defineDescriptorList();&lt;br /&gt;        }   &lt;br /&gt;        &lt;br /&gt;        private List&amp;lt;String&amp;gt; descriptorList;&lt;br /&gt;    &lt;br /&gt;        public List&amp;lt;String&amp;gt; getDescriptorList()&lt;br /&gt;        {&lt;br /&gt;            return descriptorList;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        public void setDescriptorList(List&amp;lt;String&amp;gt; descriptorList)&lt;br /&gt;        {&lt;br /&gt;            this.descriptorList = descriptorList;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        private String selectedDescriptor;&lt;br /&gt;    &lt;br /&gt;        public String getSelectedDescriptor()&lt;br /&gt;        {&lt;br /&gt;            return selectedDescriptor;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        public void setSelectedDescriptor(String selectedDescriptor)&lt;br /&gt;        {&lt;br /&gt;            this.selectedDescriptor = selectedDescriptor;&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        private void defineDescriptorList()&lt;br /&gt;        {&lt;br /&gt;            descriptorList = ServiceToSetupDescriptorList(); // Return a List&amp;lt;String&amp;gt; of pertinent descriptors&lt;br /&gt;        }&lt;br /&gt;    &lt;br /&gt;        public String executeProcess()&lt;br /&gt;        {&lt;br /&gt;            ServiceToProcessLongDataFile(getSelectedDescriptor()); // Run our data process using selectedDescriptor&lt;br /&gt;            return null;&lt;br /&gt;        }&lt;br /&gt;        &lt;br /&gt;        public void dispose() throws Exception&lt;br /&gt;        {&lt;br /&gt;            descriptorList.clear();&lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;As you can see, we're just going to execute the clear() method on our List of descriptors in our little example to prove the concept.&lt;br /&gt;&lt;br /&gt;Next we're going to add a servlet that will control how our clean up gets executed. Here's my implementation of the Logout servlet:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;package com.beans;&lt;br /&gt;&lt;br /&gt;import javax.servlet.http.HttpServlet;&lt;br /&gt;import javax.servlet.http.HttpServletRequest;&lt;br /&gt;import javax.servlet.http.HttpServletResponse;&lt;br /&gt;import javax.servlet.http.HttpSession;&lt;br /&gt;&lt;br /&gt;public class Logout extends HttpServlet &lt;br /&gt;{&lt;br /&gt;    protected void doGet(HttpServletRequest req, HttpServletResponse resp)&lt;br /&gt;    {&lt;br /&gt;        doPost(req, resp);&lt;br /&gt;    }&lt;br /&gt;&lt;br /&gt;    protected void doPost(HttpServletRequest req, HttpServletResponse resp)&lt;br /&gt;    {&lt;br /&gt;        HttpSession session = req.getSession(false);&lt;br /&gt;        if (session != null)&lt;br /&gt;        {            &lt;br /&gt;            try&lt;br /&gt;            {&lt;br /&gt;                Bean bean = (Bean)req.getSession().getAttribute("Bean");&lt;br /&gt;                if(bean != null)&lt;br /&gt;                {&lt;br /&gt;                    bean.dispose();    &lt;br /&gt;                }                                &lt;br /&gt;            }&lt;br /&gt;            catch (Exception e)&lt;br /&gt;            {&lt;br /&gt;                e.printStackTrace();&lt;br /&gt;            }                        &lt;br /&gt;        }&lt;br /&gt;    }&lt;br /&gt;}&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;In order for this code to ever be executed as is, we need to set up an instance of this Servlet in the web.xml, as well as a URL pattern to use. We do this by adding the following to web.xml:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;servlet-mapping&amp;gt;&lt;br /&gt;    &amp;lt;servlet-name&amp;gt;logoutServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;    &amp;lt;url-pattern&amp;gt;/logout&amp;lt;/url-pattern&amp;gt;&lt;br /&gt;&amp;lt;/servlet-mapping&amp;gt;&lt;br /&gt;&amp;lt;servlet&amp;gt;&lt;br /&gt;    &amp;lt;servlet-name&amp;gt;logoutServlet&amp;lt;/servlet-name&amp;gt;&lt;br /&gt;    &amp;lt;servlet-class&amp;gt;com.beans.Logout&amp;lt;/servlet-class&amp;gt;&lt;br /&gt;    &amp;lt;load-on-startup&amp;gt;1&amp;lt;/load-on-startup&amp;gt;&lt;br /&gt;&amp;lt;/servlet&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;What this does is tells our application to instantiate an instance of the Logout servlet, and listen to any POSTs or GETs to any URL matching /logout. If any request is made to that URL, the Logout servlet will handle it.&lt;br /&gt;&lt;br /&gt;The last thing we want to do is add the JavaScript to our .jspx page to navigate to the /logout URL if the user closes the browser. We end up with a .jspx file that looks like so:&lt;br /&gt;&lt;br /&gt;UPDATE: This code appears to only work using IE. Since the purpose of this piece of code was to support an IE-only application it works great. However, I would like to extend this to work cross-browser at some point.&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="html"&gt;&lt;br /&gt;&amp;lt;jsp:root&lt;br /&gt;    jsfc="f:view"&lt;br /&gt;    xmlns:jsp="http://java.sun.com/JSP/Page"&lt;br /&gt;    xmlns:f="http://java.sun.com/jsf/core"&lt;br /&gt;    xmlns:h="http://java.sun.com/jsf/html"&lt;br /&gt;    xmlns:ice="http://www.icesoft.com/icefaces/component"&lt;br /&gt;    xmlns:ui="http://java.sun.com/jsf/facelets"&lt;br /&gt;    xmlns:c="http://java.sun.com/jstl/core"&lt;br /&gt;    xmlns:fn="http://java.sun.com/jsp/jstl/functions"&lt;br /&gt;&amp;gt;&lt;br /&gt;    &amp;lt;ice:outputDeclaration&lt;br /&gt;        doctypeRoot="html"&lt;br /&gt;        doctypePublic="-//W3C//DTD XHTML 1.0 Transitional//EN"&lt;br /&gt;        doctypeSystem="http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd"&lt;br /&gt;    /&amp;gt;&lt;br /&gt;    &amp;lt;html&amp;gt;&lt;br /&gt;        &amp;lt;head&amp;gt;&lt;br /&gt;            &amp;lt;title&amp;gt;Descriptor Lookup Tool&amp;lt;/title&amp;gt;&lt;br /&gt;            &amp;lt;link&lt;br /&gt;                rel="stylesheet"&lt;br /&gt;                type="text/css"&lt;br /&gt;                href="/xmlhttp/css/rime/rime.css"&lt;br /&gt;            /&amp;gt;&lt;br /&gt;            &amp;lt;!-- This JavaScript code detects the user closing their  --&amp;gt;&lt;br /&gt;            &amp;lt;!-- browser window. It offers the user a choice to exit or cancel and stay on --&amp;gt;&lt;br /&gt;            &amp;lt;!-- the page. If the user clicks "OK" any managed beans that implement --&amp;gt;&lt;br /&gt;            &amp;lt;!-- DisposableBean will have their dispose() method called. --&amp;gt;&lt;br /&gt;            &amp;lt;script type="text/javascript"&amp;gt;&lt;br /&gt;               window.onbeforeunload = function (evt) &lt;br /&gt;                {&lt;br /&gt;                  var message = 'Are you sure you want to leave?';&lt;br /&gt;                  if (typeof evt == 'undefined') &lt;br /&gt;                  {&lt;br /&gt;                    evt = window.event;&lt;br /&gt;                  }&lt;br /&gt;                  if (evt) &lt;br /&gt;                  {&lt;br /&gt;                    evt.returnValue = message;&lt;br /&gt;                    window.navigate('./logout');&lt;br /&gt;                  }&lt;br /&gt;                  return message;&lt;br /&gt;                }&lt;br /&gt;            &amp;lt;/script&amp;gt;&lt;br /&gt;        &amp;lt;/head&amp;gt;&lt;br /&gt;        &amp;lt;body&amp;gt;&lt;br /&gt;             &amp;lt;!-- content --&amp;gt;&lt;br /&gt;            &amp;lt;h1&amp;gt;Descriptor Lookup Tool&amp;lt;/h1&amp;gt;&lt;br /&gt;            &amp;lt;br /&amp;gt;&lt;br /&gt;            &amp;lt;ice:form&lt;br /&gt;                partialSubmit="true"&lt;br /&gt;                rendered="true" &amp;gt;   &lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;ice:panelGroup&amp;gt;       &lt;br /&gt;                        &amp;lt;ice:panelGrid columns="2"&amp;gt;&lt;br /&gt;                            &amp;lt;ice:outputText style="font-weight: bold;" value="Descriptors" /&amp;gt;                                                                            &lt;br /&gt;                            &amp;lt;ice:selectOneMenu id="ddlDescriptor" partialSubmit="true" value="#{Bean.selectedDescriptor}" &amp;gt;                        &lt;br /&gt;                                &amp;lt;f:selectItems id="ddlItems" value="#{Bean.descriptorList}"/&amp;gt;&lt;br /&gt;                            &amp;lt;/ice:selectOneMenu&amp;gt;&lt;br /&gt;                        &amp;lt;/ice:panelGrid&amp;gt;&lt;br /&gt;                    &amp;lt;/ice:panelGroup&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;    &lt;br /&gt;                    &lt;br /&gt;                &amp;lt;p&amp;gt;                                                      &lt;br /&gt;                    &amp;lt;ice:panelGrid columns="1"&amp;gt;  &lt;br /&gt;                        &amp;lt;ice:commandLink value="Process Data File" &lt;br /&gt;                                         action="#{Bean.executeProcess}" /&amp;gt;&lt;br /&gt;                    &amp;lt;/ice:panelGrid&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;                                &lt;br /&gt;                &lt;br /&gt;                &amp;lt;p&amp;gt;&lt;br /&gt;                    &amp;lt;ice:panelGrid columns="2"&amp;gt;                            &lt;br /&gt;                        &amp;lt;ice:messages id="errMsgs" errorClass="error" infoClass="ss_text" style="font-size:14px;" layout="table"/&amp;gt;&lt;br /&gt;                        &amp;lt;ice:outputText id="txtErrors" style="font-size:14px;" value="" /&amp;gt;                        &lt;br /&gt;                    &amp;lt;/ice:panelGrid&amp;gt;&lt;br /&gt;                &amp;lt;/p&amp;gt;                               &lt;br /&gt;            &amp;lt;/ice:form&amp;gt;&lt;br /&gt;        &amp;lt;/body&amp;gt;&lt;br /&gt;    &amp;lt;/html&amp;gt;&lt;br /&gt;&amp;lt;/jsp:root&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, when the user clicks the red "X" to close their browser while our application is running, they will be greeted with a confirmation dialog (OK &amp; Cancel) asking if they are sure they want to leave the application. If they click OK, the application routes to /logout, which runs our code in the Logout servlet, disposing of our Bean instance. &lt;br /&gt;&lt;br /&gt;In applications that hold a lot of data in memory for processing between page requests, this appears to be another tool to fight memory leaks. The dispose() method will be called either on browser close &lt;span style="font-weight:bold;"&gt;or&lt;/span&gt; when the session times out naturally, which means we are doing our part to minimize the amount of unnecessary overhead our application is using.&lt;br /&gt;&lt;br /&gt;I'm open to suggestions for better methods, but for this specific example it seems to be working great.&lt;br /&gt;&lt;br /&gt;UPDATE: I've noticed that once the session expires naturally, ICEFaces calls the bean's dispose() method a second time, even if the user closed their browser to "close" the application. Something to look into...&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-2016920357676649820?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/2016920357676649820/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/08/icefaces-disposablebean-and-cleaning-up.html#comment-form' title='6 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/2016920357676649820'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/2016920357676649820'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/08/icefaces-disposablebean-and-cleaning-up.html' title='ICEFaces DisposableBean and cleaning up session resources'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>6</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-1175818764056561363</id><published>2009-08-17T14:07:00.000-07:00</published><updated>2009-08-31T13:39:15.225-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaEE'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='ICEFaces'/><title type='text'>ICEFaces is becoming my best friend...</title><content type='html'>Last week was a greatly productive week, in my opinion. I continued developing a new internal web application for our clients (a glorified query tool for analyzing their data warehouse). I originally wrote this application using a different set of components, and I was, to put it lightly, "un-impressed" with the reliability of the suite.&lt;br /&gt;&lt;br /&gt;So I decided to re-write the application using ICEFaces.&lt;br /&gt;&lt;br /&gt;And I couldn't be happier. ICEFaces appears to be a much more mature, reliable framework than &lt;span style="font-style:italic;"&gt;unreliable component suite that actually cost money for a license&lt;/span&gt;. &lt;br /&gt;&lt;br /&gt;All the functionality I required was ready to go out of the box with ICEFaces (popup dialogs, data tables, panel series repeaters, transition effects ala jQuery). And since ICEFaces supports AJAX calls in all their components I get a front end that is much more responsive and less headache inducing (full page refreshes give me seizures sometimes...). &lt;br /&gt;&lt;br /&gt;My only gripe seems to be the fact that the component suite appears to render it's styles on application generation. I.e. the component suite itself creates the default style sheets used by all the components, which makes referencing a custom style sheet a pain (at least in the short trial and error I tried last week). Perhaps this will be easier as I continue using the framework more in the coming months. Thus far, I am very impressed with the suite of components, and will probably be reaching for those .jar files in many a project to come.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-1175818764056561363?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/1175818764056561363/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/08/icefaces-is-becoming-my-best-friend.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/1175818764056561363'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/1175818764056561363'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/08/icefaces-is-becoming-my-best-friend.html' title='ICEFaces is becoming my best friend...'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-4493271992047903490</id><published>2009-08-04T11:57:00.000-07:00</published><updated>2009-08-31T13:39:30.244-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='JavaEE'/><category scheme='http://www.blogger.com/atom/ns#' term='JSF'/><category scheme='http://www.blogger.com/atom/ns#' term='ICEFaces'/><title type='text'>Dynamically generate ice:commandButton components</title><content type='html'>These images are being used for the following Stack Overflow question:&lt;br /&gt;&lt;br /&gt;&lt;a href="http://stackoverflow.com/questions/1229456/dynamically-generate-icecommandbutton-components"&gt;Dynamically generate ice:commandButton components&lt;/a&gt;&lt;br /&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_n_tMTNWlgdQ/Sni7R9BR2BI/AAAAAAAAABI/unU4vxlm_xk/s1600-h/allSelected.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 34px;" src="http://3.bp.blogspot.com/_n_tMTNWlgdQ/Sni7R9BR2BI/AAAAAAAAABI/unU4vxlm_xk/s320/allSelected.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5366244873124042770" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://2.bp.blogspot.com/_n_tMTNWlgdQ/SniLF7r4QbI/AAAAAAAAABA/pS7hhW-kB-I/s1600-h/badButtons.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 34px;" src="http://2.bp.blogspot.com/_n_tMTNWlgdQ/SniLF7r4QbI/AAAAAAAAABA/pS7hhW-kB-I/s320/badButtons.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5366191890049286578" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://3.bp.blogspot.com/_n_tMTNWlgdQ/SniHmmjWalI/AAAAAAAAAA4/xQDtx0y4P-Y/s1600-h/deselected.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 34px;" src="http://3.bp.blogspot.com/_n_tMTNWlgdQ/SniHmmjWalI/AAAAAAAAAA4/xQDtx0y4P-Y/s320/deselected.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5366188053265541714" /&gt;&lt;/a&gt;&lt;br /&gt;&lt;a onblur="try {parent.deselectBloggerImageGracefully();} catch(e) {}" href="http://1.bp.blogspot.com/_n_tMTNWlgdQ/SniHmRVhK2I/AAAAAAAAAAw/SSoOLEugUjc/s1600-h/selected.PNG"&gt;&lt;img style="cursor:pointer; cursor:hand;width: 320px; height: 34px;" src="http://1.bp.blogspot.com/_n_tMTNWlgdQ/SniHmRVhK2I/AAAAAAAAAAw/SSoOLEugUjc/s320/selected.PNG" border="0" alt=""id="BLOGGER_PHOTO_ID_5366188047570381666" /&gt;&lt;/a&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-4493271992047903490?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/4493271992047903490/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/08/dynamically-generate-icecommandbutton.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/4493271992047903490'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/4493271992047903490'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/08/dynamically-generate-icecommandbutton.html' title='Dynamically generate ice:commandButton components'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><media:thumbnail xmlns:media='http://search.yahoo.com/mrss/' url='http://3.bp.blogspot.com/_n_tMTNWlgdQ/Sni7R9BR2BI/AAAAAAAAABI/unU4vxlm_xk/s72-c/allSelected.PNG' height='72' width='72'/><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-9157689088730202222</id><published>2009-06-26T08:48:00.000-07:00</published><updated>2009-06-26T08:59:24.078-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='sql'/><category scheme='http://www.blogger.com/atom/ns#' term='tsql'/><category scheme='http://www.blogger.com/atom/ns#' term='sqlserver'/><title type='text'>User and Role Permissions Generation script for SQL Server 2005</title><content type='html'>I've come across many instances in the past where I have needed to duplicate permissions for a single user or role on SQL Server 2005 (usually when trying to version control permissions for a department or group of users, for example). This was a tedious task that I used to dread when I first started commanding more of the DBA responsibilities.&lt;br /&gt;&lt;br /&gt;I've been using a couple of scripts to help myself with these tasks, and thought I would share them.&lt;br /&gt;&lt;br /&gt;Enjoy!&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;ScriptUserPermissions&lt;/span&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;SET ANSI_NULLS ON&lt;br /&gt;GO&lt;br /&gt;SET QUOTED_IDENTIFIER ON&lt;br /&gt;GO&lt;br /&gt;/*&lt;br /&gt;==========================================================================&lt;br /&gt;Procedure Name:  ScriptUserPermissions&lt;br /&gt;Author:          Karl Grzeszczak&lt;br /&gt;&lt;br /&gt;Purpose:    Generate all permissions attached to a particular user for the database it is run in.&lt;br /&gt;            NOTE: This stored procedure can be run in ANY database.&lt;br /&gt;&lt;br /&gt;Inputs:     @userName  varchar(200)  Name of the user.&lt;br /&gt;&lt;br /&gt;Outputs:    .sql script to generate user's permissions.&lt;br /&gt;&lt;br /&gt;==========================================================================  &lt;br /&gt;*/&lt;br /&gt;CREATE PROCEDURE [dbo].[ScriptUserPermissions]&lt;br /&gt;(&lt;br /&gt;  @userName varchar(200)&lt;br /&gt;)&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;  DECLARE @DatabaseUserName [sysname]&lt;br /&gt;  SET @DatabaseUserName = @userName&lt;br /&gt;&lt;br /&gt;  SET NOCOUNT ON&lt;br /&gt;&lt;br /&gt;  DECLARE&lt;br /&gt;    @errStatement [varchar](8000),&lt;br /&gt;    @msgStatement [varchar](8000),&lt;br /&gt;    @DatabaseUserID [smallint],&lt;br /&gt;    @ServerUserName [sysname],&lt;br /&gt;    @RoleName [varchar](8000),&lt;br /&gt;    @ObjectID [int],&lt;br /&gt;    @ObjectName [varchar](261)&lt;br /&gt;&lt;br /&gt;  SELECT&lt;br /&gt;    @DatabaseUserID = [sysusers].[uid],&lt;br /&gt;    @ServerUserName = [master].[dbo].[syslogins].[loginname]&lt;br /&gt;  FROM &lt;br /&gt;    [dbo].[sysusers]&lt;br /&gt;      INNER JOIN [master].[dbo].[syslogins]&lt;br /&gt;      ON [sysusers].[sid] = [master].[dbo].[syslogins].[sid]&lt;br /&gt;  WHERE &lt;br /&gt;    [sysusers].[name] = @DatabaseUserName&lt;br /&gt;&lt;br /&gt;  IF @DatabaseUserID IS NULL&lt;br /&gt;  BEGIN&lt;br /&gt;    SET @errStatement = 'User ' + @DatabaseUserName + ' does not exist in ' + DB_NAME() + CHAR(13) +&lt;br /&gt;                  'Please provide the name of a current user in ' + DB_NAME() + ' you wish to script.'&lt;br /&gt;    RAISERROR(@errStatement, 16, 1)&lt;br /&gt;  END&lt;br /&gt;  ELSE&lt;br /&gt;  BEGIN&lt;br /&gt;    SET @msgStatement = '--Security creation script for user ' + @ServerUserName + CHAR(13) +&lt;br /&gt;                  '--Created At: ' + CONVERT(varchar, GETDATE(), 112) + REPLACE(CONVERT(varchar, GETDATE(), 108), ':', '') + CHAR(13) +&lt;br /&gt;                  '--Created By: ' + SUSER_NAME() + CHAR(13) +&lt;br /&gt;                  '--Add User To Database' + CHAR(13) +                  &lt;br /&gt;                  'EXEC [sp_grantdbaccess]' + CHAR(13) +&lt;br /&gt;                  CHAR(9) + '@loginame = ''' + @ServerUserName + ''',' + CHAR(13) +&lt;br /&gt;                  CHAR(9) + '@name_in_db = ''' + @DatabaseUserName + '''' + CHAR(13) +&lt;br /&gt;                  '--Add User To Roles'&lt;br /&gt;    PRINT @msgStatement&lt;br /&gt;    DECLARE _sysusers&lt;br /&gt;    CURSOR&lt;br /&gt;    LOCAL&lt;br /&gt;    FORWARD_ONLY&lt;br /&gt;    READ_ONLY&lt;br /&gt;    FOR&lt;br /&gt;    SELECT&lt;br /&gt;    [name]&lt;br /&gt;    FROM [dbo].[sysusers]&lt;br /&gt;    WHERE&lt;br /&gt;    [uid] IN&lt;br /&gt;    (&lt;br /&gt;    SELECT&lt;br /&gt;    [groupuid]&lt;br /&gt;    FROM [dbo].[sysmembers]&lt;br /&gt;    WHERE [memberuid] = @DatabaseUserID&lt;br /&gt;    )&lt;br /&gt;    OPEN _sysusers&lt;br /&gt;    FETCH&lt;br /&gt;    NEXT&lt;br /&gt;    FROM _sysusers&lt;br /&gt;    INTO @RoleName&lt;br /&gt;    WHILE @@FETCH_STATUS = 0&lt;br /&gt;    BEGIN&lt;br /&gt;      SET @msgStatement = 'EXEC [sp_addrolemember]' + CHAR(13) +&lt;br /&gt;                    CHAR(9) + '@rolename = ''' + @RoleName + ''',' + CHAR(13) +&lt;br /&gt;                    CHAR(9) + '@membername = ''' + @DatabaseUserName + ''''&lt;br /&gt;      PRINT @msgStatement&lt;br /&gt;      FETCH&lt;br /&gt;      NEXT&lt;br /&gt;      FROM _sysusers&lt;br /&gt;      INTO @RoleName&lt;br /&gt;    END&lt;br /&gt;&lt;br /&gt;  SET @msgStatement = '--Set Object Specific Permissions'&lt;br /&gt;  PRINT @msgStatement&lt;br /&gt;  &lt;br /&gt;  DECLARE _sysobjects&lt;br /&gt;  CURSOR&lt;br /&gt;  LOCAL&lt;br /&gt;  FORWARD_ONLY&lt;br /&gt;  READ_ONLY&lt;br /&gt;  FOR&lt;br /&gt;  SELECT&lt;br /&gt;  DISTINCT([sysobjects].[id]),&lt;br /&gt;  '[' + USER_NAME([sysobjects].[uid]) + '].[' + [sysobjects].[name] + ']'&lt;br /&gt;  FROM [dbo].[sysprotects]&lt;br /&gt;  INNER JOIN [dbo].[sysobjects]&lt;br /&gt;  ON [sysprotects].[id] = [sysobjects].[id]&lt;br /&gt;  WHERE [sysprotects].[uid] = @DatabaseUserID&lt;br /&gt;  OPEN _sysobjects&lt;br /&gt;  FETCH&lt;br /&gt;  NEXT&lt;br /&gt;  FROM _sysobjects&lt;br /&gt;  INTO&lt;br /&gt;  @ObjectID,&lt;br /&gt;  @ObjectName&lt;br /&gt;  WHILE @@FETCH_STATUS = 0&lt;br /&gt;  BEGIN&lt;br /&gt;    SET @msgStatement = ''&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 193 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'SELECT,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 195 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'INSERT,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 197 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'UPDATE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 196 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'DELETE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 224 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'EXECUTE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 26 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'REFERENCES,'&lt;br /&gt;    IF LEN(@msgStatement) &gt; 0&lt;br /&gt;    BEGIN&lt;br /&gt;      IF RIGHT(@msgStatement, 1) = ','&lt;br /&gt;        SET @msgStatement = LEFT(@msgStatement, LEN(@msgStatement) - 1)&lt;br /&gt;        SET @msgStatement = 'GRANT' + CHAR(13) +&lt;br /&gt;                      CHAR(9) + @msgStatement + CHAR(13) +&lt;br /&gt;                      CHAR(9) + 'ON ' + @ObjectName + CHAR(13) +&lt;br /&gt;                      CHAR(9) + 'TO ' + @DatabaseUserName&lt;br /&gt;        PRINT @msgStatement&lt;br /&gt;    END&lt;br /&gt;    SET @msgStatement = ''&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 193 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'SELECT,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 195 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'INSERT,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 197 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'UPDATE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 196 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'DELETE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 224 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'EXECUTE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseUserID AND [action] = 26 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'REFERENCES,'&lt;br /&gt;    IF LEN(@msgStatement) &gt; 0&lt;br /&gt;    BEGIN&lt;br /&gt;      IF RIGHT(@msgStatement, 1) = ','&lt;br /&gt;      SET @msgStatement = LEFT(@msgStatement, LEN(@msgStatement) - 1)&lt;br /&gt;      SET @msgStatement = 'DENY' + CHAR(13) +&lt;br /&gt;                    CHAR(9) + @msgStatement + CHAR(13) +&lt;br /&gt;                    CHAR(9) + 'ON ' + @ObjectName + CHAR(13) +&lt;br /&gt;                    CHAR(9) + 'TO ' + @DatabaseUserName&lt;br /&gt;      PRINT @msgStatement&lt;br /&gt;    END&lt;br /&gt;    FETCH&lt;br /&gt;    NEXT&lt;br /&gt;    FROM _sysobjects&lt;br /&gt;    INTO&lt;br /&gt;    @ObjectID,&lt;br /&gt;    @ObjectName&lt;br /&gt;  END&lt;br /&gt;  CLOSE _sysobjects&lt;br /&gt;  DEALLOCATE _sysobjects&lt;br /&gt;  END&lt;br /&gt;END&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;span style="font-weight:bold;"&gt;ScriptUserPermissions&lt;/span&gt;&lt;br /&gt;&lt;hr&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;SET ANSI_NULLS ON&lt;br /&gt;GO&lt;br /&gt;SET QUOTED_IDENTIFIER ON&lt;br /&gt;GO&lt;br /&gt;/*&lt;br /&gt;==========================================================================&lt;br /&gt;Procedure Name:  ScriptRolePermissions&lt;br /&gt;Author:          Karl Grzeszczak&lt;br /&gt;&lt;br /&gt;Purpose:    Generate all permissions attached to a particular role for the database it is run in.&lt;br /&gt;            NOTE: This stored procedure can be run in ANY database.&lt;br /&gt;&lt;br /&gt;Inputs:    @roleName  varchar(200)  Name of the role.&lt;br /&gt;&lt;br /&gt;Outputs:    .sql script to generate role's permissions.&lt;br /&gt;&lt;br /&gt;==========================================================================  &lt;br /&gt;*/&lt;br /&gt;CREATE PROCEDURE [dbo].[ScriptRolePermissions]&lt;br /&gt;(&lt;br /&gt;  @roleName varchar(200)&lt;br /&gt;)&lt;br /&gt;AS&lt;br /&gt;BEGIN&lt;br /&gt;  DECLARE @DatabaseRoleName [sysname]&lt;br /&gt;  SET @DatabaseRoleName = @roleName&lt;br /&gt;&lt;br /&gt;  SET NOCOUNT ON&lt;br /&gt;  DECLARE&lt;br /&gt;    @errStatement [varchar](8000),&lt;br /&gt;    @msgStatement [varchar](8000),&lt;br /&gt;    @DatabaseRoleID [smallint],&lt;br /&gt;    @IsApplicationRole [bit],&lt;br /&gt;    @ObjectID [int],&lt;br /&gt;    @ObjectName [sysname]&lt;br /&gt;&lt;br /&gt;  SELECT&lt;br /&gt;    @DatabaseRoleID = [uid],&lt;br /&gt;    @IsApplicationRole = CAST([isapprole] AS bit)&lt;br /&gt;  FROM &lt;br /&gt;    [dbo].[sysusers]&lt;br /&gt;  WHERE&lt;br /&gt;    [name] = @DatabaseRoleName AND&lt;br /&gt;    (&lt;br /&gt;      [issqlrole] = 1 OR [isapprole] = 1&lt;br /&gt;    ) AND &lt;br /&gt;    [name] NOT IN&lt;br /&gt;    (&lt;br /&gt;      'public',&lt;br /&gt;      'INFORMATION_SCHEMA',&lt;br /&gt;      'db_owner',&lt;br /&gt;      'db_accessadmin',&lt;br /&gt;      'db_securityadmin',&lt;br /&gt;      'db_ddladmin',&lt;br /&gt;      'db_backupoperator',&lt;br /&gt;      'db_datareader',&lt;br /&gt;      'db_datawriter',&lt;br /&gt;      'db_denydatareader',&lt;br /&gt;      'db_denydatawriter'&lt;br /&gt;    )&lt;br /&gt;&lt;br /&gt;  IF @DatabaseRoleID IS NULL&lt;br /&gt;  BEGIN&lt;br /&gt;    IF @DatabaseRoleName IN&lt;br /&gt;    (&lt;br /&gt;      'public',&lt;br /&gt;      'INFORMATION_SCHEMA',&lt;br /&gt;      'db_owner',&lt;br /&gt;      'db_accessadmin',&lt;br /&gt;      'db_securityadmin',&lt;br /&gt;      'db_ddladmin',&lt;br /&gt;      'db_backupoperator',&lt;br /&gt;      'db_datareader',&lt;br /&gt;      'db_datawriter',&lt;br /&gt;      'db_denydatareader',&lt;br /&gt;      'db_denydatawriter'&lt;br /&gt;    )&lt;br /&gt;    SET @errStatement = 'Role ' + @DatabaseRoleName + ' is a fixed database role and cannot be scripted.'&lt;br /&gt;    ELSE&lt;br /&gt;    SET @errStatement = 'Role ' + @DatabaseRoleName + ' does not exist in ' + DB_NAME() + '.' + CHAR(13) +&lt;br /&gt;                  'Please provide the name of a current role in ' + DB_NAME() + ' you wish to script.'&lt;br /&gt;    RAISERROR(@errStatement, 16, 1)&lt;br /&gt;  END&lt;br /&gt;  ELSE&lt;br /&gt;  BEGIN&lt;br /&gt;    SET @msgStatement = '--Security creation script for role ' + @DatabaseRoleName + CHAR(13) +&lt;br /&gt;                  '--Created At: ' + CONVERT(varchar, GETDATE(), 112) + REPLACE(CONVERT(varchar, GETDATE(), 108), ':', '') + CHAR(13) +&lt;br /&gt;                  '--Created By: ' + SUSER_NAME() + CHAR(13) +&lt;br /&gt;                  '--Add Role To Database' + CHAR(13)&lt;br /&gt;    IF @IsApplicationRole = 1&lt;br /&gt;    SET @msgStatement = @msgStatement + 'EXEC sp_addapprole' + CHAR(13) +&lt;br /&gt;                  CHAR(9) + '@rolename = ''' + @DatabaseRoleName + '''' + CHAR(13) +&lt;br /&gt;                  CHAR(9) + '@password = ''{Please provide the password here}''' + CHAR(13)&lt;br /&gt;    ELSE&lt;br /&gt;    BEGIN&lt;br /&gt;      SET @msgStatement = @msgStatement + 'EXEC sp_addrole' + CHAR(13) +&lt;br /&gt;                    CHAR(9) + '@rolename ''' + @DatabaseRoleName + '''' + CHAR(13)&lt;br /&gt;    END&lt;br /&gt;    SET @msgStatement = @msgStatement + '--Set Object Specific Permissions For Role'&lt;br /&gt;    PRINT @msgStatement&lt;br /&gt;    DECLARE _sysobjects&lt;br /&gt;    CURSOR&lt;br /&gt;    LOCAL&lt;br /&gt;    FORWARD_ONLY&lt;br /&gt;    READ_ONLY&lt;br /&gt;    FOR&lt;br /&gt;    SELECT&lt;br /&gt;    DISTINCT([sysobjects].[id]),&lt;br /&gt;    '[' + USER_NAME([sysobjects].[uid]) + '].[' + [sysobjects].[name] + ']'&lt;br /&gt;    FROM [dbo].[sysprotects]&lt;br /&gt;    INNER JOIN [dbo].[sysobjects]&lt;br /&gt;    ON [sysprotects].[id] = [sysobjects].[id]&lt;br /&gt;    WHERE [sysprotects].[uid] = @DatabaseRoleID&lt;br /&gt;    OPEN _sysobjects&lt;br /&gt;    FETCH&lt;br /&gt;    NEXT&lt;br /&gt;    FROM _sysobjects&lt;br /&gt;    INTO&lt;br /&gt;    @ObjectID,&lt;br /&gt;    @ObjectName&lt;br /&gt;  &lt;br /&gt;    WHILE @@FETCH_STATUS = 0&lt;br /&gt;    BEGIN&lt;br /&gt;    SET @msgStatement = ''&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 193 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'SELECT,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 195 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'INSERT,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 197 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'UPDATE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 196 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'DELETE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 224 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'EXECUTE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 26 AND [protecttype] = 205)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'REFERENCES,'&lt;br /&gt;    IF LEN(@msgStatement) &gt; 0&lt;br /&gt;    BEGIN&lt;br /&gt;      IF RIGHT(@msgStatement, 1) = ','&lt;br /&gt;        SET @msgStatement = LEFT(@msgStatement, LEN(@msgStatement) - 1)&lt;br /&gt;      SET @msgStatement = 'GRANT' + CHAR(13) +&lt;br /&gt;                    CHAR(9) + @msgStatement + CHAR(13) +&lt;br /&gt;                    CHAR(9) + 'ON ' + @ObjectName + CHAR(13) +&lt;br /&gt;                    CHAR(9) + 'TO ' + @DatabaseRoleName&lt;br /&gt;      PRINT @msgStatement&lt;br /&gt;    END&lt;br /&gt;    SET @msgStatement = ''&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 193 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'SELECT,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 195 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'INSERT,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 197 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'UPDATE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 196 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'DELETE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 224 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'EXECUTE,'&lt;br /&gt;    IF EXISTS(SELECT * FROM [dbo].[sysprotects] WHERE [id] = @ObjectID AND [uid] = @DatabaseRoleID AND [action] = 26 AND [protecttype] = 206)&lt;br /&gt;    SET @msgStatement = @msgStatement + 'REFERENCES,'&lt;br /&gt;    IF LEN(@msgStatement) &gt; 0&lt;br /&gt;    BEGIN&lt;br /&gt;      IF RIGHT(@msgStatement, 1) = ','&lt;br /&gt;        SET @msgStatement = LEFT(@msgStatement, LEN(@msgStatement) - 1)&lt;br /&gt;      SET @msgStatement = 'DENY' + CHAR(13) +&lt;br /&gt;                    CHAR(9) + @msgStatement + CHAR(13) +&lt;br /&gt;                    CHAR(9) + 'ON ' + @ObjectName + CHAR(13) +&lt;br /&gt;                    CHAR(9) + 'TO ' + @DatabaseRoleName&lt;br /&gt;      PRINT @msgStatement&lt;br /&gt;    END&lt;br /&gt;    FETCH&lt;br /&gt;    NEXT&lt;br /&gt;    FROM _sysobjects&lt;br /&gt;    INTO&lt;br /&gt;    @ObjectID,&lt;br /&gt;    @ObjectName&lt;br /&gt;    END&lt;br /&gt;    CLOSE _sysobjects&lt;br /&gt;    DEALLOCATE _sysobjects&lt;br /&gt;  END &lt;br /&gt;END&lt;br /&gt;&lt;/pre&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-9157689088730202222?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/9157689088730202222/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/06/user-and-role-permissions-generation.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/9157689088730202222'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/9157689088730202222'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/06/user-and-role-permissions-generation.html' title='User and Role Permissions Generation script for SQL Server 2005'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-4206081213384053538</id><published>2009-06-26T08:45:00.000-07:00</published><updated>2009-06-26T14:29:45.437-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='eclipse'/><category scheme='http://www.blogger.com/atom/ns#' term='memory'/><category scheme='http://www.blogger.com/atom/ns#' term='tomcat'/><title type='text'>Giving Tomcat more memory in an Eclipse development environment</title><content type='html'>Sometimes when running tomcat inside eclipse there are processes that throw OutOfMemory errors (creating an extremely large Excel file, for instance) that are not thrown on our production servers.&lt;br /&gt;&lt;br /&gt;By default, Eclipse runs tomcat with 64MB of maximum memory, even if you set up Eclipse to use more memory by editing the eclipse.ini file (I have mine set to 1024, and tomcat still runs at 64MB).&lt;br /&gt;&lt;br /&gt;Eclipse makes it really easy to pass command line arguments to your Tomcat instance every time it is started. I found this out after spending about 30 minutes testing different configurations in the catalina.bat file. To increase your Eclipse environment's Tomcat memory allocation:&lt;br /&gt;&lt;br /&gt;- Go to Window -&gt; Preferences -&gt; Java -&gt; Installed JRE's -&gt; and click "Edit" after highlighting your JRE.&lt;br /&gt;- Add the following line to the "Default VM Arguments" text box: -Xms1024m -Xmx1024m&lt;br /&gt;&lt;br /&gt;This starts up Tomcat each time with a 1024MB Heap, which we are also setting as the maximum heap size. Please note that you can set -Xms (starting) to a smaller value, and have the JVM increase the size as it needs it. However, this operation is expensive and will affect performance accordingly. Just keep that in mind.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-4206081213384053538?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/4206081213384053538/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/06/giving-tomcat-more-memory-in-eclipse.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/4206081213384053538'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/4206081213384053538'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/06/giving-tomcat-more-memory-in-eclipse.html' title='Giving Tomcat more memory in an Eclipse development environment'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-6479334928626851118</id><published>2009-06-09T20:16:00.000-07:00</published><updated>2009-06-09T22:09:18.510-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='blogger'/><category scheme='http://www.blogger.com/atom/ns#' term='syntaxhighlighting'/><title type='text'>Enabling Syntax Highlighting in Blogger</title><content type='html'>This evening I wanted to put a little effort into enabling syntax highlighting for code in my posts here on Blogger. The process started innocently enough, but there were a few trip ups along the way, so I decided to share them here.&lt;br /&gt;&lt;br /&gt;I chose to use &lt;a href="http://code.google.com/p/syntaxhighlighter/"&gt;syntaxhighlighter&lt;/a&gt;, which is hosted on Google Code. It is an all JavaScript solution to syntax highlighting that seems to play nicely with Blogger.&lt;br /&gt;&lt;br /&gt;We will link to the svn trunk for the necessary JavaScript &amp; CSS files. This eliminates the need to host these files from a seperate source, which is incredibly convenient if you do not have a host set up for your blog.&lt;br /&gt;&lt;br /&gt;First things first, head over to the "Layout" tab from the Blogger dashboard. This will bring you to a screen with with a graphical representation of your blog. Click on "Edit HTML".&lt;br /&gt;&lt;br /&gt;From this page click inside the "Edit Template" text box with all the HTML. Locate the &amp;lt;head&amp;gt; tag, and copy the following lines immediately after it:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="js"&gt;&lt;br /&gt;&amp;lt;link href='http://syntaxhighlighter.googlecode.com/svn/trunk/Styles/SyntaxHighlighter.css' rel='stylesheet' type='text/css'/&amp;gt;  &lt;br /&gt;&amp;lt;script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shCore.js'/&amp;gt;   &lt;br /&gt;&amp;lt;script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushJava.js'/&amp;gt;&lt;br /&gt;&amp;lt;script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushJScript.js'/&amp;gt;&lt;br /&gt;&amp;lt;script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushCss.js'/&amp;gt;&lt;br /&gt;&amp;lt;script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushSql.js'/&amp;gt;&lt;br /&gt;&amp;lt;script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushCSharp.js'/&amp;gt;&lt;br /&gt;&amp;lt;script language='javascript' src='http://syntaxhighlighter.googlecode.com/svn/trunk/Scripts/shBrushXml.js'/&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;This enables the following:&lt;br /&gt;&lt;ul&gt;&lt;br /&gt;&lt;li&gt;CSS styling for the code &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Styles/SyntaxHighlighter.css"&gt;(SyntaxHighlighter.css)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Core JavaScript libraries for rendering &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/shCore.js"&gt;(shCore.js)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;Java syntax highlighting support &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/shBrushJava.js"&gt;(shBrushJava.js)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;JavaScript syntax highlighting support &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/shBrushJScript.js"&gt;(shBrushJScript.js)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;CSS syntax highlighting support &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/shBrushCss.js"&gt;(shBrushCss.js)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;SQL syntax highlighting support &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/shBrushSql.js"&gt;(shBrushSql.js)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;C# syntax highlighting support &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/shBrushCSharp.js"&gt;(shBrushCSharp.js)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;li&gt;XML and HTML syntax highlighting support &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/trunk/Scripts/shBrushXml.js"&gt;(shBrushXml.js)&lt;/a&gt;&lt;/li&gt;&lt;br /&gt;&lt;/ul&gt;&lt;br /&gt;&lt;br /&gt;NOTE: You don't &lt;b&gt;need&lt;/b&gt; to import the same syntax support I did. These are simply the languages that I will most likely be posting about on my blog, so I have enabled them off the bat. Import what you need. For the most up to date listing of supported language parsers, see the &lt;a href="http://code.google.com/p/syntaxhighlighter/source/browse/#svn/trunk/Scripts"&gt;Script&lt;/a&gt; directory from the current svn trunk of &lt;a href="http://code.google.com/p/syntaxhighlighter/"&gt;syntaxhighlighter&lt;/a&gt;.&lt;br /&gt;&lt;br /&gt;Next, locate the &amp;lt;/body&amp;gt; tag, and paste the following code immediately before it:&lt;br /&gt;&lt;pre name="code" class="js"&gt;&lt;br /&gt;&lt;script language='javascript'&gt;&lt;br /&gt;    dp.SyntaxHighlighter.BloggerMode();  &lt;br /&gt;    dp.SyntaxHighlighter.HighlightAll(&amp;#39;code&amp;#39;);  &lt;br /&gt;  &lt;/script&gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;Now, if you are using some versions of Firefox (3.0.10 I know for certain has the issue, I've heard 3.0.5 also has the problem, but your mileage may vary) you won't see any highlighting even when the HTML is formatted correctly. This is due to a problem parsing the .css files since I am using the ones stored directly at Google Code (and not on my own host). A simple fix is to remove the Strict DOCTYPE tag at the top of the HTML template. I found this fixed the problem immediately.&lt;br /&gt;&lt;br /&gt;Click "Save Template" next, and you're all done! &lt;br /&gt;&lt;br /&gt;In order to activate syntax highlighting, use the following HTML in your next post:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="xml"&gt;&lt;br /&gt;&amp;lt;pre name="code" class"xml"&amp;gt;&lt;br /&gt;    ...content goes here...&lt;br /&gt;    ...remember to find/replace all instances of...&lt;br /&gt;    ...&amp;lt; with &amp;amp;lt; and &amp;gt; with &amp;amp;gt;&lt;br /&gt;    ...(regardless of language) prior to publishing...&lt;br /&gt;&amp;lt;/pre&amp;gt;&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;That's it!&lt;br /&gt;&lt;br /&gt;Here's some other examples:&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="java"&gt;&lt;br /&gt;    // Java&lt;br /&gt;    public static void main(String args[])&lt;br /&gt;    {&lt;br /&gt;        System.out.println("Hello World!");&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="sql"&gt;&lt;br /&gt;    -- SQL&lt;br /&gt;    SELECT&lt;br /&gt;        Id,&lt;br /&gt;        COUNT(Value) As Total&lt;br /&gt;    FROM&lt;br /&gt;        Test&lt;br /&gt;    WHERE&lt;br /&gt;        Id &lt;&gt; 0&lt;br /&gt;    GROUP BY&lt;br /&gt;        Id&lt;br /&gt;    ORDER BY&lt;br /&gt;        Id ASC&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="csharp"&gt;&lt;br /&gt;    // C#&lt;br /&gt;    class Test&lt;br /&gt;    {&lt;br /&gt;       public int Value {get; set;}&lt;br /&gt;    }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;&lt;pre name="code" class="css"&gt;&lt;br /&gt;    /* CSS */&lt;br /&gt;    p.first{ color: blue; }&lt;br /&gt;&lt;/pre&gt;&lt;br /&gt;&lt;br /&gt;I hope this saves somebody a little bit of headache.&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-6479334928626851118?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/6479334928626851118/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/06/test.html#comment-form' title='0 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/6479334928626851118'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/6479334928626851118'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/06/test.html' title='Enabling Syntax Highlighting in Blogger'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>0</thr:total></entry><entry><id>tag:blogger.com,1999:blog-2582624793655573268.post-6831452649105801586</id><published>2009-06-06T02:00:00.000-07:00</published><updated>2009-06-26T14:30:10.524-07:00</updated><category scheme='http://www.blogger.com/atom/ns#' term='introduction'/><title type='text'>I'm all in...</title><content type='html'>&lt;span style="font-family:arial;"&gt;I've been putting this off for such a long time.&lt;br /&gt;&lt;br /&gt;I remember back to a time that was not too long ago, before I had completed my undergraduate studies. I distinctly recall feeling like I had enough things running around inside my head to fill an entire library of novels. I remember imagining vast wastelands spanning half a globe, creatures from the dark void of space, and entire civilizations of beings that I had never hoped of ever meeting. I had such a spontaneously creative imagination. Nothing could squelch it, either. 24/7, continuously flowing ingenuity. Music was coming out of my fingers into my guitar at a scarily constant pace. I was doing well in school. I was doing as much outside of school in personal projects to whet my teeth on new coding techniques and languages as I could. I was working just enough to get by, and the rest of my time was devoted to kicking back and having fun. I loved life, and it sure as hell loved me back.&lt;br /&gt;&lt;br /&gt;Then I got a job in a cubicle.&lt;br /&gt;&lt;br /&gt;I can't believe how much different life is inside the walls of a an office building. It is a completely new ecosystem that has yet to be documented. Literally, because I swear&lt;span style="font-weight: bold;"&gt; &lt;/span&gt;next to &lt;span style="font-weight: bold;"&gt;NOTHING&lt;/span&gt; has been documented (stupid pun...sorry, but there will probably be more). It is an environment that doesn't care about too much more than a bottom line in the grand scheme of things. I mean, don't get me wrong. The people that are in immediate contact are all good natured people, and the company takes care of you, sure. Actually, they are one of the only reason to keep coming back in.&lt;br /&gt;&lt;br /&gt;But the whole idea of a &lt;span style="font-style: italic;"&gt;corporation&lt;/span&gt; just kind of hurts me inside a little bit.&lt;br /&gt;&lt;br /&gt;The entire mindset of "Hey, you're gonna do exactly what we tell you otherwise we're gonna find someone else who will" is kind of scary. I seem to have created a sense of standard for myself over the last 3 years of how I want to do my work, and I know it does not include that vision. It includes taking the time to get something right the first time. It involves following good practices, but not to the point where I'm trying to shoehorn an elephant into a movie seat. I find myself always striving to improve my codebase.&lt;br /&gt;&lt;br /&gt;Don't get me wrong. I want to get things done just as much as the next guy. Not delivering anything is kind of failing, in my opinion. Why work on something for so long and put so much of yourself into it if nobody is &lt;span style="font-weight: bold;"&gt;ever&lt;/span&gt; going to see or use it?&lt;br /&gt;&lt;br /&gt;The main motivating factor in my creations is the satisfaction I get from using something I made. Guilty as charged. I love writing code and seeing the outcome of what I have created. I have loved computers since I was just a wee lad and my dad brought home an old 286 with MS-DOS. I was barely able to get a game running on it at first, but I was drawn to them. Like a deer stuck in the headlights, I just couldn't shake the grasp. Computers had gotten ahold of me and have never really loosened their strangle on me.&lt;br /&gt;&lt;br /&gt;And it just seems to me that a lot of my peers don't have that kind of drive. They don't have that emotion in their voice when they talk about coding. They don't get excited when they finally discover a work around for the limitation of a third-party library they are using. I don't see any kind of fire in their eyes when they talk about an algorithm that took them the better part of an entire day to even get down on paper.&lt;br /&gt;&lt;br /&gt;And that's fine.&lt;br /&gt;&lt;br /&gt;I've got nothing against anyway in this profession for just wanting to get in, get paid, and go home. In fact, I applaud you for the ability to just turn it off and continuing on in reality.&lt;br /&gt;&lt;br /&gt;But I want to get better.&lt;br /&gt;&lt;br /&gt;I know I'm not the best coder around. I know I'm not Anders Hejlsberg or Scott Guthrie or Bjarne Stroustrap or &lt;span style="font-style: italic;"&gt;insert name of great coder here&lt;/span&gt;&lt;insert&gt;. Honestly, I'll probably never be that good. I just want to get better.&lt;br /&gt;&lt;br /&gt;I like my job. I like the people I work with. But I really dislike working on internal projects as a cost center. I really want to find out what it feels like to be on the front lines for a real software company, not just supporting another department.&lt;br /&gt;&lt;br /&gt;And I want to get better.&lt;br /&gt;&lt;br /&gt;I've been putting off recording my thoughts and ideas for far too long now. It seems like an eternity since I last kept any kind of development journal, and I hope to share my experiences with the world. Maybe some of it will be useful to someone out there. Maybe this will be my inspiration to push myself even farther. We seem to learn more as a teacher than a student, right? I consider myself a poor teacher, but I always seem to teach &lt;span style="font-style: italic;"&gt;myself&lt;/span&gt; the most when I am trying to help someone else. I hope to share my coding mishaps, questions, answers, and opinions with you all in hopes of bettering myself and learning something new.&lt;br /&gt;&lt;br /&gt;&lt;/insert&gt;&lt;/span&gt;&lt;span style="font-family:arial;"&gt;My views have definitely gone back to normal since I started working in the real world. My creativity seems to have come back. The music is sounding the way it should be. Yet I can't help but wish I was in a different place, making something that was truly worthwhile to everyone (or even just &lt;span style="font-style: italic;"&gt;someone&lt;/span&gt;).&lt;br /&gt;&lt;br /&gt;One of these days...&lt;/span&gt;&lt;span style="font-family:arial;"&gt;&lt;br /&gt;&lt;/span&gt;&lt;div class="blogger-post-footer"&gt;&lt;img width='1' height='1' src='https://blogger.googleusercontent.com/tracker/2582624793655573268-6831452649105801586?l=karlgrz.blogspot.com' alt='' /&gt;&lt;/div&gt;</content><link rel='replies' type='application/atom+xml' href='http://karlgrz.blogspot.com/feeds/6831452649105801586/comments/default' title='Post Comments'/><link rel='replies' type='text/html' href='http://karlgrz.blogspot.com/2009/06/im-all-in.html#comment-form' title='3 Comments'/><link rel='edit' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/6831452649105801586'/><link rel='self' type='application/atom+xml' href='http://www.blogger.com/feeds/2582624793655573268/posts/default/6831452649105801586'/><link rel='alternate' type='text/html' href='http://karlgrz.blogspot.com/2009/06/im-all-in.html' title='I&apos;m all in...'/><author><name>KG</name><uri>http://www.blogger.com/profile/18218228553305698840</uri><email>noreply@blogger.com</email><gd:image rel='http://schemas.google.com/g/2005#thumbnail' width='32' height='25' src='http://4.bp.blogspot.com/_n_tMTNWlgdQ/SiV-CkMKwLI/AAAAAAAAAAM/l4_O2NLGisI/S220/boondocks_karl.jpg'/></author><thr:total>3</thr:total></entry></feed>
