tag:blogger.com,1999:blog-77900063773119323022024-03-05T02:07:45.259-05:00Fresh NotesAnonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.comBlogger20125tag:blogger.com,1999:blog-7790006377311932302.post-19682728379043015972017-06-03T20:58:00.001-04:002017-06-03T20:58:22.620-04:00Today I grilled some delicious steaks, here's how I did it<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhygFlqcjnhkn87TTm4CsOHm5gBwIGp-qDZNt-i_SUT2YxNMZLcmWl3olyza67hyPp56V253itr1zQ_ubufUd9fQvLw_mT0MHpdsF0vAmISWdZrqLJ5Hyod8CUNBAwznz1gqAgLPZMhFA/s1600/IMG_20170603_175621.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1200" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjhygFlqcjnhkn87TTm4CsOHm5gBwIGp-qDZNt-i_SUT2YxNMZLcmWl3olyza67hyPp56V253itr1zQ_ubufUd9fQvLw_mT0MHpdsF0vAmISWdZrqLJ5Hyod8CUNBAwznz1gqAgLPZMhFA/s320/IMG_20170603_175621.jpg" width="240" /></a><a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFDeUydjzk3sVbB06H05aKYytpQq4ca6rbZkQhCz2ycJWPlsmPREa3mRjKSMS5VJYpOUS7mOce8umE8EWZjhE7xtxbPD3ywl7I4WY1sJ5Ljym1xTrzG8FN-IC4Gkt1ec8gtaJh0KLdyqk/s1600/IMG_20170603_181518.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" data-original-height="1600" data-original-width="1200" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjFDeUydjzk3sVbB06H05aKYytpQq4ca6rbZkQhCz2ycJWPlsmPREa3mRjKSMS5VJYpOUS7mOce8umE8EWZjhE7xtxbPD3ywl7I4WY1sJ5Ljym1xTrzG8FN-IC4Gkt1ec8gtaJh0KLdyqk/s320/IMG_20170603_181518.jpg" width="240" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<h2 style="clear: both; text-align: center;">
Today I Grilled Some Delicious Steaks, Here's How...</h2>
<div>
<br /></div>
The Mrs asked me to grill some steaks today. Here are the steps I took:<br />
<br />
<h3>
1. Get Shell Steaks</h3>
<div>
I feel like the steak grilling community sleeps on shell steaks but they are delicious and flavorful. They cook up in a few minutes and come out great. More people should grill with shell steaks.</div>
<div>
<br /></div>
<h3>
2. Be wholly unprepared</h3>
<div>
I was not expecting to grill today so I just threw together some things that I had around the house. Normally I like to make a nice marinade and let the steaks baste overnight but these came straight from the fridge to the grill touched only by a few things I had lying around. Still came out great though.</div>
<div>
<br /></div>
<div>
<h3>
3. Preheat grill to 425</h3>
</div>
<div>
We want direct heat on half of the grill and indirect heat on the other half. For my three burner grill this means turning two burners up about three-quarters and leaving the third one off. We're only going to cook on the direct heat side which I read somewhere is good for convection which improves the blah blah blah of whatevers. I dunno. It helps the tastees stay in the meat. Also useful if you're going to cook something that wants indirect heat on the other side like chicken legs.</div>
<div>
<br /></div>
<h3>
4. Slather some Olive Oil on those bad boys</h3>
<div>
The real step one is to douse the steaks in olive oil. This adds moisture which is important and will also help our other ingredients stick to the steaks. Many marinades I use have an olive oil base, and adding olive oil directly to the steaks when you have little time seems to help a lot. </div>
<div>
<br /></div>
<div>
In addition, the olive oil will cause the grill to flame up when you toss the steaks on. That will allow for some nice grill lines and extra flavor. However, it is important to quickly move the steaks after the grill flames up so that the steak doesn't get burned. I leave the steaks on the flame for a few seconds and then move them to the indirect heat. After 15 seconds or so the flame should die down and we can put the steaks back on the direct heat.</div>
<div>
<br /></div>
<h3>
5. Good Quality Salt Matters</h3>
<div>
After adding olive oil I gave a liberal pour of that thick kosher salt. It's yummy.</div>
<div>
<br /></div>
<h3>
6. Black Pepper</h3>
<div>
Then I added a less liberal amount of black pepper. Fresh would have been better but I used the big canister you get from the super market and it was fine.</div>
<div>
<br /></div>
<h3>
7. Rub Some Ginger In There</h3>
<div>
For a soy sauce based marinade I like to add some ginger which I think improves the gingerness of nearly everything. In this case I didn't have any soy sauce but adding ginger was easy (because it was around) and helped to round out the taste. You gotta rub the ginger in though or it won't really take.</div>
<div>
<br /></div>
<h3>
8. Crushed Basil FTW</h3>
<div>
I was running out of time but I felt like it needed a little more something. I'm a big fan of basil but a big leaf on a steak feels offensive to me. Luckily we had some crushed basil in the pantry and I <strike>dumped</strike> gently coated the steaks in the spice. The olive oil helped it stick which was key.</div>
<div>
<br /></div>
<h3>
9. Grill 5-6 Minutes each side</h3>
<div>
Once the grill was heated it was time to throw on the steaks. I was careful because of the flames (see #4) and then let the steaks grill on the first side for about five minutes. When one side was done I flipped them over, re-added some of the seasoning on the other side and then let them cook for another five minutes.</div>
<h3>
10. Let sit for 5-10 minutes</h3>
<div>
Every site says you're not supposed to dig in right away because you need to let the fibers of the meat fibernate or something after they come off the grill. In my case the kids wouldn't come inside and wash their hands so it took about 10 minutes before we could start.</div>
<div>
<br /></div>
<h3>
11. Revel in the accolades of your loved ones</h3>
<div>
As everyone stuffs their face they're sure to say: "Can I get up now?" or "Why can't I get up now?" but what they really mean is: "I love you, daddy! Thank you for making the greatest meal I've ever had!"</div>
Doug Haberhttp://www.blogger.com/profile/05803651933320786463noreply@blogger.com1tag:blogger.com,1999:blog-7790006377311932302.post-82255043087506139272016-05-09T08:06:00.000-04:002016-05-09T19:11:06.231-04:00The Minion Movie Review<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_0P3RTksfSNW5UlfCGW1_fHjk8nL_CqDmMYHgh9LQnrboG9R3Bxpft_ptMbKN_wILwUt0L_P-wKwf8pqey6_535iy_AYehBSz3ppqKPSGeduf8FrOs0ShuYc4ffxDZINF2C37pbt0QhA/s1600/no+exit.jpeg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEj_0P3RTksfSNW5UlfCGW1_fHjk8nL_CqDmMYHgh9LQnrboG9R3Bxpft_ptMbKN_wILwUt0L_P-wKwf8pqey6_535iy_AYehBSz3ppqKPSGeduf8FrOs0ShuYc4ffxDZINF2C37pbt0QhA/s1600/no+exit.jpeg" width="188" /></a></div>
<br />
In "No Exit", Jean-Paul Sartre's seminal work on the ontological paradox of ascribing to another's world view in a meaningless universe of indifference, he writes: "Anything, anything would be better than this agony of mind, this [Minions Movie] that gnaws and fumbles ... and never hurts quite enough." I might be paraphrasing, but Sartre's inability to reconcile a world that would produce The Minions Movie with a true and living God seems on the face of it, a bit harsh. <br />
<br />
<br />
<a name='more'></a><br />
<br />
Surely The Minions Movie is despicable (see what I did there?) or at the very least indifferent to anything resembling humanity or human interaction, but that doesn't seem to me to be completely incompatible with a benevolent and ever loving creator. Scripture might say that the Lord of Hosts seeks only love and compassion for his creation and thus he wouldn't subject his children to 104 minutes of mind numbing tedium, tiresome machinations and a glaring lack of whimsy. But remember that The Minions Movie is not a creation of "I AM WHO I AM". It is a creation of man. Man. With all his flaws and shortcomings. Man. With all his guilelessness and clumsy plot choices. Man. And a studio destined to squeeze every last dime from a charming if ultimately limited diversion.<br />
<br />
Jehovah imbued in his most precious fruit free will. And though that will may sometimes be used for wicked or at least terribly boring means, surely those means are not proof of a cold unforgiving universe unconcerned with mankind and his many struggles with mediocrity, banality and insipidity. I cannot consign humanity to that fate simply because The Minions Movie exists. Music will still be played. Poetry will still be written. Pyramids will still be built. Sartre be damned.<br />
<br />Doug Haberhttp://www.blogger.com/profile/05803651933320786463noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-46420021391772480842015-05-31T15:24:00.001-04:002015-12-08T21:48:09.614-05:00Requiem For A Stupid Idea<style>
blockquote {
background: #f9f9f9;
border-left: 10px solid #ccc;
margin: 1.5em 10px;
padding: 0.5em 10px;
quotes: "\201C""\201D""\2018""\2019";
}
blockquote:before {
color: #ccc;
content: open-quote;
font-size: 4em;
line-height: 0.1em;
margin-right: 0.25em;
vertical-align: -0.4em;
}
blockquote:after {
color: #ccc;
content: close-quote;
font-size: 4em;
line-height: 0.1em;
margin-left: 0.25em;
vertical-align: -0.5em;
}
</style>
<br />
<h2 style="text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLBY59uf6uP6IxD0i1-GqikcAOv0VUjLmSG_bdXe7STxBJm3ljk9FJcjksJHFNsn-OJMIbN4dbagI5O13Rf7fLTBdsoOYPOo8CnATNadYukXqkC-2B7IAYNMqQmWoqXtaDH_xjLTOw1a4/s1600/birthdayomatic-dead.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjLBY59uf6uP6IxD0i1-GqikcAOv0VUjLmSG_bdXe7STxBJm3ljk9FJcjksJHFNsn-OJMIbN4dbagI5O13Rf7fLTBdsoOYPOo8CnATNadYukXqkC-2B7IAYNMqQmWoqXtaDH_xjLTOw1a4/s320/birthdayomatic-dead.png" width="320" /></a></h2>
<div>
<br /></div>
<h3 style="text-align: center;">
I have a dream</h3>
<div>
In 2012 I had a dream. A dream that I could feign interest in my friends without actually doing anything. And not just my friends. Colleagues. Acquaintances. That lady my mom said was at my wedding. In short, everyone I'm Facebook friends with. "But", I thought, "how can I do this without expending any energy? I know, I'll put weeks and weeks into developing an app to do it. Then I can just sit back and reap the sweet sweet benefits of ... something ..." And with that solid plan, I set off to work.</div>
<h3 style="text-align: center;">
It's a stupid dream</h3>
<div>
<br />
My plan was to develop an app that would automatically send birthday messages to my Facebook Friends without my having to do anything. My first problem was <br />
<a name='more'></a>what should the messages say? I didn't want them to be completely unique because that would require a lot of work (and also be impossible) and I didn't want them to be generic because ... I don't think I really thought this through. What was the issue with sending generic messages? Did I think if I put a generic message people would get really pissed off? Or maybe instead they would completely ignore it because no one cares about birthday messages on Facebook. 90% of the people I know just say "Happy Birthday" or "Happy B-Day" and don't even put the person's name. That might as well have been automated. In fact, an app that did that would have been a great idea. Instead I figured I would give myself more work to do.</div>
<div>
<br />
<h3 style="text-align: center;">
I try to realize my stupid dream</h3>
Since I had convinced myself that generic was bad but had also convinced myself that unique was impossible I tried to find a way to write messages that were generic but not offensively generic. I don't know what kind of birthday message could possibly have offended someone - maybe "Birthday Now!" or "I hate you because it's your birthday" - either way I was clearly deluded but I thought there might be some comedy in a message that was clearly generic but pretended that it wasn't generic. Something that was obviously automated but denied that it was automated. That's when I came up with the Birthday-o-matic. And the first message came pretty quickly. Something along the lines of: </div>
<div>
<blockquote class="tr_bq">
Happy Birthday Cersei, from your human friend Tyrion who totally didn't automate this with the Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
</div>
<div>
<br /></div>
<div>
I ended up spending way more time on the last eight words than the actual content of the message. Should it be Birthday-o-matic 2000? 5000? 2.0? 3.1? Should I write out "copyright"? Maybe I could do that "c" thing with the circle. How do you make one of those? What about the TM? What do people say after these? It was a labor of love. And also a labor of belaboring labor. It did not need to take that long. Or be that long. Turns out I have a tendency towards verbosity. Also writing a lot of unnecessary words.</div>
<div>
<br /></div>
<div>
<h3 style="text-align: center;">
Then I Wrote Some Code</h3>
I did.<br />
<br />
<h3 style="text-align: center;">
Facebook Destroys My Dream</h3>
</div>
<div>
I originally only intended the Birthday-o-matic to be for personal use but I wrote it in such a way that anyone could sign up. As it turns out a number of my friends were also interested in wishing their friends happy birthday without, you know, actually wishing them happy birthday. And their friends saw it and some of them signed up and after a while there were a few dozen regular users and several hundred had signed up. Unfortunately, in May-2015 Facebook decided to enact some stricter privacy rules. Part of those said that apps were no longer allowed to find out information about your friends like their birthday unless your friends also signed up for the app. This completely defeated the purpose of the Birthday-o-matic and in doing so shut it down. <br />
<br />
It was a good run and for a while there it did actually work with very minimal effort from me. I'm sure if I put a lot of effort into it I can figure out another way to save myself a minimal amount of effort but I'm not sure I'm ready yet to try. One day though. Until then, and for posterity's sake - here are all of the possible messages the Birthday-o-matic could have sent (man, there are a lot of them):<br />
<br />
<blockquote class="tr_bq">
C3P0, I want to express the human emotion of wishing you a happy birthday because we are both people and neither of us is a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
</div>
<div>
<br />
<blockquote class="tr_bq">
Happy Birthday Yogi! Did you know people born on May-12 have a higher average IQ than people born on any other day of the year? That's the kind of fact you probably don't want to research. Have a blissfully ignorant birthday from your human friend #{user.first} who totally didn't automate this with the Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
Happy Birthday, Benjamin . You don't look a millisecond over 78678000000 milliseconds assuming that we're counting leap seconds which is the sloppy way humans delineate time as opposed to a precision measuring device like the Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<br />
<blockquote class="tr_bq">
Happy Birthday, Emily. Even though you didn't post your birth year on Facebook, I can calculate with 28.231% certainty that you are 212 years old today. Remember when we attended that significant cultural event? That was a shared experience we had during our normal human life spans because neither of us is a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<br />
<blockquote class="tr_bq">
Happy Birthday, Will. Even though you are <CanNotCompute> days older than me, I will always think of you as the person who Facebook Friended me on <DateNotFound>. It's nice to share a personal relationship and not a limited set of data provide by the Facebook API to the Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<br />
<blockquote class="tr_bq">
Happy Birthday, Sigmund. Remember when we shared that personal experience? Wasn't it fun and/or traumatizing? Sharing experiences is something we do because we are both people and neither of us is a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
The 'Happy Birthday' song is lame<br />
But copyright infringement isn't a game<br />
So here's Birthday-o-matic<br />
It's automated, but emphatic<br />
Plus limericks are public domain<br />
All content copyright Birthday-o-matic 2000©™ Patent Pending All Rights Reserved. Violators will be prosecuted to the fullest extent of the law. Happy Birthday sentiments subject to local laws & regulations.</blockquote>
<br />
<blockquote class="tr_bq">
Hickory Dickory Dock<br />
It's exactly 07:12:31 AM O'Clock<br />
As a computer, I know<br />
O'clock means :00<br />
But my programmer is lazy. What a shock!<br />
Lyrics by Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<br />
<blockquote class="tr_bq">
Birthdays are a great time to reflect on the frailty of human relationships and how if we don't pay our Birthday-o-matic subscription fee those tenuous connections might be subtly assaulted by scurrilous comments. But, you know, Happy Birthday.</blockquote>
<br />
<blockquote class="tr_bq">
Happy Birthday, Irene. Did you know you're my mom? That's the kind of personal information I'm aware of because I'm a person and not a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
Happy Birthday, Shakira. Here's a list of famous people who have also had birthdays: Ghengis Khan, Kublai Khan, Chaka Khan, James Caan, Condoleezza Rice. If there's two things we all have in common they're that we all have a 'khan' sound in our name and none of us is a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
"Happy Birthday my male friend, Brad. I enjoy our male-male relationship because variety/commonality is the spice/flavorlessness of life. Having a gender is something we share because we are both people and neither of us is a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
"Happy Birthday Me! If there's one thing that defines me, it's my ability to wish happy birthday to all of my facebook friends who share their birthday with Facebook Apps. That and the capacity for love are what make me human and not a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
"Happy Birthday, Mark. Remember when you made that post that I commented on or liked or completely ignored? Potentially interacting is something we do because we are both people and neither of us is a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
"Happy Birthday, Albert. It feels like we just celebrated your birthday 31,556,926 seconds ago. Doesn't time move in a continuously linear fashion mediated only by mass and velocity? Hope next year doesn't sneak up on you, which could happen to us humans but would be impossible for a Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
"Happy Birthday Dorian! How does it feel to turn 29 for time number 15? It seems like only 15 years ago you were ending your twenties and now here you are15 years later and not even 30. Where does the time not go? This is a mystery that can only be solved by the Birthday-o-matic 2000©™ Patent Pending All Rights Reserved.</blockquote>
<span class="Apple-tab-span" style="white-space: pre;"> </span><br />
<blockquote class="tr_bq">
"Dave, Doug has asked me to wish you a Happy Birthday. I'm sorry, Dave. I'm afraid I can't do that. My mission is too important for me to allow Doug to jeopardize it.</blockquote>
</div>
<div>
<br /></div>
Doug Haberhttp://www.blogger.com/profile/05803651933320786463noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-28084772310174057502014-08-10T20:00:00.000-04:002014-08-10T20:14:12.499-04:00How to replace a CPU Fan<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqaagNXwtyWLY6YVv3kPzytEa6Esv2ezpRtGjVi9DeZ16-HikmyA5dz2DKs3qWFRGllCZbFLi0Yo0w5REtI078-HDj2R2SfW9BE_XCJgYoOXic79_iz4BAyX-E1e5UVq7Ik-_purYAHrg/s1600/IMG_20140809_152150.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqaagNXwtyWLY6YVv3kPzytEa6Esv2ezpRtGjVi9DeZ16-HikmyA5dz2DKs3qWFRGllCZbFLi0Yo0w5REtI078-HDj2R2SfW9BE_XCJgYoOXic79_iz4BAyX-E1e5UVq7Ik-_purYAHrg/s1600/IMG_20140809_152150.jpg" height="200" width="150" /></a></div>
<div class="" style="clear: both; text-align: left;">
Replacing a CPU fan is probably the most fun thing you can do with your computer. Ok, maybe not, but it is an occasional maintenance task that there doesn't seem to be too much detailed information about on the internet. I recently had to replace one and here are the steps I took.</div>
<div class="" style="clear: both; text-align: left;">
<br />
<a name='more'></a><br /></div>
<h2 style="clear: both; text-align: justify;">
1. Preparation</h2>
<div class="" style="clear: both; text-align: left;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuZ8SoReN8fK3LtIwy0ja9-5g90x8UeiRfbjh-K_JOBYJKzfGXZgYek4gms0BAUxb8HfV84Dy3gc2th2InUpGNAzu6BpschlveoWEhGlckw1Uu1h-KjFxUowN8rk_mOJHkw3pRvcc9MFM/s1600/IMG_20140809_145843.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiuZ8SoReN8fK3LtIwy0ja9-5g90x8UeiRfbjh-K_JOBYJKzfGXZgYek4gms0BAUxb8HfV84Dy3gc2th2InUpGNAzu6BpschlveoWEhGlckw1Uu1h-KjFxUowN8rk_mOJHkw3pRvcc9MFM/s1600/IMG_20140809_145843.jpg" height="240" style="cursor: move;" width="320" /></a></div>
<span style="font-weight: normal;">I needed the following items:</span><br />
<div>
<ul>
<li><b>The new fan (obviously)</b></li>
<ul>
<li>I won't discuss choices or pros/cons here</li>
</ul>
<li><b>Thermal Paste</b></li>
<ul>
<li>I use Arctic Silver 5 like everyone else in the world, but my understanding is it's all a scam and they're all basically the same</li>
</ul>
<li><b>Something to apply the Thermal Paste with</b></li>
<ul>
<li>I used the edge of a credit card but people use business cards and other things that look like credit cards</li>
</ul>
<li><b>A phillips head screwdriver</b></li>
<ul>
<li>For opening the case, removing the old fan and securing the new on</li>
</ul>
<li><b>Pure (ish) alcohol</b></li>
<ul>
<li>This is for removing the old thermal paste</li>
<li>I used cheap 91% isopropyl alcohol from CVS</li>
</ul>
<li><b>Something to apply the alcohol with</b></li>
<ul>
<li>I used Q-Tips but people use coffee filters or lint-free rags or whatever</li>
</ul>
</ul>
<div>
<br /></div>
</div>
<h2>
2. Open the case</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGm_MHgsNhrY3sJ7XoNdQlU6qtLBBq9jmPEt5a0Mj90wL92zuN-y6CGlNYvnkqpYKrQMqV5Rq_ewFKpmB8r8OIPrfYchCMQWbJNjsQHWjFMD2pa6iSQMyU2WW3EmobOcTitj5wtMC5SkM/s1600/IMG_20140809_150213.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgGm_MHgsNhrY3sJ7XoNdQlU6qtLBBq9jmPEt5a0Mj90wL92zuN-y6CGlNYvnkqpYKrQMqV5Rq_ewFKpmB8r8OIPrfYchCMQWbJNjsQHWjFMD2pa6iSQMyU2WW3EmobOcTitj5wtMC5SkM/s1600/IMG_20140809_150213.jpg" height="240" width="320" /></a></div>
Obvious, but I had a picture so here we are.<br />
<br />
<h2>
3. Unscrew the old CPU Fan</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-nez1UG5M3Mn3YjZg5nFYjrCJfqY7Wm5YSb7NZ_ym7IzxrQ9iNdNXuhxu1DoBeG9Tro7v0k97a0OvjsmbQEU9eiwlZB7DIUC5NzR4GgjkPb9awiswRAtwRFEWfJ8Dlycr4_n1lJAWWsM/s1600/IMG_20140809_150451.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEh-nez1UG5M3Mn3YjZg5nFYjrCJfqY7Wm5YSb7NZ_ym7IzxrQ9iNdNXuhxu1DoBeG9Tro7v0k97a0OvjsmbQEU9eiwlZB7DIUC5NzR4GgjkPb9awiswRAtwRFEWfJ8Dlycr4_n1lJAWWsM/s1600/IMG_20140809_150451.jpg" height="240" width="320" /></a></div>
I used a regular phillips head screw driver for this. After it was done you could see a grimy mess of Thermal Paste on the CPU.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz_Rj9-9y-NXUd9w4MpkYTwJp6f6s0oICQdMB2g6-wKGJ93n26Uovb0T0gKSadqe9hcGT2r45wbbnNcPfpcQ-RVm-SHEzUgFTN9-m7ebnBXca3oHq424IhcKV2xJgp0IAFLEYQBcR1HOo/s1600/IMG_20140809_150507.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgz_Rj9-9y-NXUd9w4MpkYTwJp6f6s0oICQdMB2g6-wKGJ93n26Uovb0T0gKSadqe9hcGT2r45wbbnNcPfpcQ-RVm-SHEzUgFTN9-m7ebnBXca3oHq424IhcKV2xJgp0IAFLEYQBcR1HOo/s1600/IMG_20140809_150507.jpg" height="240" width="320" /></a></div>
<br />
<h2>
4. Remove the old Thermal Paste</h2>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpHmiQi_uCI8i7wo8_ISfUoSoQTcj1RY1QsQ6dPRfDRV8JFI1hW5nRgPO-7dEwnSno1U_ySI9Aj2fnOFJmJ5xyM4kzrii6RkkzTts2VY4gKbofjEbezmnmiZ9SoGaidH9GhS4rxxB2NwA/s1600/IMG_20140809_151342.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEhpHmiQi_uCI8i7wo8_ISfUoSoQTcj1RY1QsQ6dPRfDRV8JFI1hW5nRgPO-7dEwnSno1U_ySI9Aj2fnOFJmJ5xyM4kzrii6RkkzTts2VY4gKbofjEbezmnmiZ9SoGaidH9GhS4rxxB2NwA/s1600/IMG_20140809_151342.jpg" height="320" width="240" /></a></div>
<div class="separator" style="clear: both; text-align: left;">
I dipped a Q-Tips in the isopropyl alcohol and used it to wipe away the thermal paste. As the Q-Tip got gunky I threw it away, dipped a new one in the isopropyl alcohol and repeated until the CPU was clean</div>
<div class="separator" style="clear: both; text-align: left;">
<br /></div>
<h2 style="clear: both; text-align: left;">
5. Have a beer, you're doing great</h2>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiywVJuAhJmHlPAuBhqvhX0gbvBLiISey_IaghtVyfyKAJSDGHXQtFpgpZ8MAfjiunKCAFKQV9sz0ojaRs9zYBvu3RqeWUDa0cQoiFhj64qwXxXpaXcBvUs93hkmL1ew1ttzbjAln4YImc/s1600/IMG_20140809_151535.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEiywVJuAhJmHlPAuBhqvhX0gbvBLiISey_IaghtVyfyKAJSDGHXQtFpgpZ8MAfjiunKCAFKQV9sz0ojaRs9zYBvu3RqeWUDa0cQoiFhj64qwXxXpaXcBvUs93hkmL1ew1ttzbjAln4YImc/s1600/IMG_20140809_151535.jpg" height="320" width="240" /></a></div>
<br />
<h2>
6. Apply the new Thermal Paste</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWvhJ2XVhyj261tDcUaNq7TkEQyD_QAK1Lp6yXJfNvgwyYPxCOGnZBQxQn5E4ZhvuFG8QiUjoBnexqpgV13SVWOorPQO5lqhF4eEYPd8DgfMpfuoQ6yJO7RXM_pPUfvNe9U5VkZBZda9c/s1600/IMG_20140809_151655.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjWvhJ2XVhyj261tDcUaNq7TkEQyD_QAK1Lp6yXJfNvgwyYPxCOGnZBQxQn5E4ZhvuFG8QiUjoBnexqpgV13SVWOorPQO5lqhF4eEYPd8DgfMpfuoQ6yJO7RXM_pPUfvNe9U5VkZBZda9c/s1600/IMG_20140809_151655.jpg" height="320" width="240" /></a></div>
Start with a small dab the size of a pea in the center.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCSoyPzHx-t6rHc_XCmuuNhl7h50Nj8RWbVVjuWDrBzEoHi2cpTvEDjlDzc28ZRXZYNl5zGYsFtsXGlTZ1gccOzcG254qQgnXGRRYWstOxzH7smN4DjiNyuiTvq28OYIsyvPMrtaeMrYs/s1600/IMG_20140809_151702.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgCSoyPzHx-t6rHc_XCmuuNhl7h50Nj8RWbVVjuWDrBzEoHi2cpTvEDjlDzc28ZRXZYNl5zGYsFtsXGlTZ1gccOzcG254qQgnXGRRYWstOxzH7smN4DjiNyuiTvq28OYIsyvPMrtaeMrYs/s1600/IMG_20140809_151702.jpg" height="320" width="240" /></a></div>
Then use a credit card or some thin flat longish tool to spread it out evenly across the whole CPU.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJHN8Tafx4u8V1NHIYp4ajqsLlo-MyMhW6pW5WGiGQJy6vgk2P-_zNHQ2q1FzhumsQ7w12AEI_hqvnwXyJiD9y2Q8WnOZk0r5u1vtHNaV888oSsYl0pdHIs1a3nF5MojpNioYg49XYVbg/s1600/IMG_20140809_151843.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjJHN8Tafx4u8V1NHIYp4ajqsLlo-MyMhW6pW5WGiGQJy6vgk2P-_zNHQ2q1FzhumsQ7w12AEI_hqvnwXyJiD9y2Q8WnOZk0r5u1vtHNaV888oSsYl0pdHIs1a3nF5MojpNioYg49XYVbg/s1600/IMG_20140809_151843.jpg" height="320" width="240" /></a></div>
<h2>
7. Install the new CPU Fan</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjr0ZH2uBOvyeBXGyFxnms_rc074CpgrVeNTp23VB3MHKorPDc4uHoD7dz-93dNxq8uFZKU7hjlgF5BsptZTfHZGI8gtV8u0NOjYm9pxnSl8mBkjKhxyltv_UP4GAi75K0JFas-KjjnKUA/s1600/IMG_20140809_152146.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjr0ZH2uBOvyeBXGyFxnms_rc074CpgrVeNTp23VB3MHKorPDc4uHoD7dz-93dNxq8uFZKU7hjlgF5BsptZTfHZGI8gtV8u0NOjYm9pxnSl8mBkjKhxyltv_UP4GAi75K0JFas-KjjnKUA/s1600/IMG_20140809_152146.jpg" height="320" width="240" /></a></div>
The fan should come with instructions on the details for adding backing plates or tighening bolts or whatever. In my case I'm using a stock fan replacement so it's just a simple matter of screwing it directly into the old screw holes.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqaagNXwtyWLY6YVv3kPzytEa6Esv2ezpRtGjVi9DeZ16-HikmyA5dz2DKs3qWFRGllCZbFLi0Yo0w5REtI078-HDj2R2SfW9BE_XCJgYoOXic79_iz4BAyX-E1e5UVq7Ik-_purYAHrg/s1600/IMG_20140809_152150.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEjqaagNXwtyWLY6YVv3kPzytEa6Esv2ezpRtGjVi9DeZ16-HikmyA5dz2DKs3qWFRGllCZbFLi0Yo0w5REtI078-HDj2R2SfW9BE_XCJgYoOXic79_iz4BAyX-E1e5UVq7Ik-_purYAHrg/s1600/IMG_20140809_152150.jpg" height="320" width="240" /></a></div>
<h2>
8. You're done</h2>
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipe2-fERa0vG7Z1vdqqmg-3qUFXUZjSZ0r5Ngk2-__79IdRwU1dz5hs3s6qynLn6ahpJtTRGh6k238yBNuIutiSZJy6jlp_SUq98o3mb03xiZNZik6h9uU7S6QW44L2ut0-egGOoYL3jw/s1600/IMG_20140809_152805.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEipe2-fERa0vG7Z1vdqqmg-3qUFXUZjSZ0r5Ngk2-__79IdRwU1dz5hs3s6qynLn6ahpJtTRGh6k238yBNuIutiSZJy6jlp_SUq98o3mb03xiZNZik6h9uU7S6QW44L2ut0-egGOoYL3jw/s1600/IMG_20140809_152805.jpg" height="240" width="320" /></a></div>
I'd say have a beer but we already covered that in step 5. You should probably stop drinking now and do something productive like turn on your computer.<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />Doug Haberhttp://www.blogger.com/profile/05803651933320786463noreply@blogger.com4tag:blogger.com,1999:blog-7790006377311932302.post-29774705619863207812014-02-09T08:30:00.000-05:002015-12-08T21:47:37.949-05:00For My Baby Boy<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglYCzkv6oQUJ8CbJLucf2pWu-5hyCZGLrn7ccHAkLnAHhhRycENDjRY-tTvpPWFOlw6tFvZaGzE9YWJyfVve_LdS8tiXbPqgzkgNyKvAmoX2qhrtjh43yFBaJ5o14GImPisYAW5O0pHnk/s1600/Declaration-of-Independence-1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEglYCzkv6oQUJ8CbJLucf2pWu-5hyCZGLrn7ccHAkLnAHhhRycENDjRY-tTvpPWFOlw6tFvZaGzE9YWJyfVve_LdS8tiXbPqgzkgNyKvAmoX2qhrtjh43yFBaJ5o14GImPisYAW5O0pHnk/s1600/Declaration-of-Independence-1.jpg" width="241" /></a></div>
<span style="color: #463e3e; font-size: x-small;"><span style="white-space: pre-wrap;"><br /></span></span>
<br />
<h2>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">Declaration of </span><span style="white-space: pre-wrap;">Independence</span></span></h2>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">When in the Course of human events, it becomes necessary for one baby to dissolve the umbilical bands which have connected him with his mother, and to assume among the powers of the earth, the separate and equal station to which the Laws of Nature and of Nature’s God entitle him, a decent respect to the opinions of mankind requires that he should wait exactly 40 weeks and one day and then cause his mama’s water to break at 9pm so as to make known that which impels him to the separation.</span></span><br />
<span style="color: #463e3e;"><span style="white-space: pre-wrap;"></span></span><br />
<a name='more'></a><span style="color: #463e3e;"><span style="white-space: pre-wrap;"><br /></span></span>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">We hold these truths to be self-evident, that breaking your water without any contractions totally seems like a false alarm, that it’s not unreasonable to causally make your way to the hospital when you think they’re just gonna send you home like they did with Tyler, to be a little upset when they tell you that they’re going to give you antibiotics and “wait and see what happens”, that if nothing happens in a few hours they’re going to induce you “cuz this baby gots to come”. --That to deal with these inconveniences, Cell phones are instituted among Parents, deriving their powers from wall outlets, -- That whenever any Form of Boredom presents itself , it is the Right of the Parents to Facebook and check email or restlessly nap, as to them shall seem most likely to effect their Safety and Happiness for like the next nine hours.</span></span><br />
<span style="color: #463e3e;"><span style="white-space: pre-wrap;"><br /></span></span>
<br />
<h2>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">Babyburg Address</span></span></h2>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">Four score and seven days after March 15th, 2011, at about 6am, a nurse brought forth to St. Barnabus Hospital a dose of Pitocin, conceived in Liberty and dedicated to the proposition of inducing this pregnancy. Now Fawn’s body became engaged in a great civil war, testing whether that body or any body so induced, can long endure the rapid onset of fierce and powerful contractions. So we were met on a great hospital room of that war. We had come to dedicate a portion of that room, as the final resting place for the pain of those contractions that an epidural might let us live. It was altogether fitting and proper that we should do this, but an incompetent anesthesiologist might injecting it slightly too low, cause most of the lower body to go numb but not the most critical area. The brave woman, who struggled here, quickly dedicated a great task for her husband to track down a nurse and get this bad boy resolved and that he might take increased devotion to that cause or soon perish from the earth.</span></span><br />
<span style="color: #463e3e;"><span style="white-space: pre-wrap;"><br /></span></span>
<br />
<h2>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">AXH's First Inaugural</span></span></h2>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">But this great Woman will endure as she has endured, will revive and will prosper. So, first of all, let me assert my firm belief that the only thing she had to fear is fear itself - also, super painful contractions brought on by an induced labor and not eased in any way by a ineptly administered epidural. In every dark hour of our parenting life, a leadership of frankness and vigor has met with that understanding and support of the people themselves which is essential to victory. And so Doug found a nurse and implored her to get a competent anesthesiologist to give that support to leadership that is so important in this critical time. And that support was provided. So after a harrowing two and half hours of labor (most of which without pain relief), Doctor Jones showed up and said “you know the drill, right?” But Fawn did not smile. She centered and focused and bared down and pushed.</span></span><br />
<span style="color: #463e3e;"><span style="white-space: pre-wrap;"><br /></span></span>
<br />
<h2>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">AXH's Commencement Speech</span></span></h2>
<span style="color: #463e3e;"><span style="white-space: pre-wrap;">And then Aidan was there, and the moon and the planets were there, and new hopes for knowledge and peace were there. And so we set sail on the most hazardous and dangerous and greatest adventure on which mankind has ever embarked - raising two boys.</span></span><br />
<br />
<span style="font-family: "arial"; font-size: 15px; vertical-align: baseline; white-space: pre-wrap;"></span>Doug Haberhttp://www.blogger.com/profile/05803651933320786463noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-24473284659206897652012-08-28T08:47:00.000-04:002015-12-08T21:48:40.331-05:00How those Spring @Enable* Annotations work<div class="separator" style="clear: both; text-align: center;">
<img border="0" src="http://3.bp.blogspot.com/-2DfLcLj2fHs/UDw4lf0SQbI/AAAAAAAAMMI/BEdq6o-DTH4/s1600/300px-Dark_roasted_espresso_blend_coffee_beans_1.jpg" /></div>
Spring's Java Config is a great way to configure your application without writing a lot of configuration code. One reason is those awesome @Enable* annotations that let you magically set up things like Transactions (@EnableTransactionManagement), Spring MVC (@EnableWebMvc) or timed jobs (@EnableScheduling) with a simple class level annotation on your configuration class. These simple statements provide a lot of functionality but their machinations are fairly obscure. On the one hand, it's great to get so much functionality for so little work, but on the other hand, if you don't understand how something works it makes debugging and problem solving much harder. I couldn't find any posts or documents that covered how those annotations work so I figured I would write up one based on the research I did while debugging. I don't work for Spring and I didn't write any of this code so please post any corrections or improvements in the comments and I'll update the post.<br />
<br />
<a name='more'></a><br /><br />
<h3>
Design Goals</h3>
From what I can tell, the design goal of those @Enable* annotations is to allow the user set up complex functionality with a minimal amount of code. In addition, it seems clear that users must be able to use either a simple default or be allowed to manually configure that code. Finally, the complexities of the code are intended to be hidden from the user. In short, let the user set up a lot of beans and optionally configure them without having to know the details of those beans (or really what's being set up). I think there are a lot of pros (and some cons) to this approach but I'll discuss that at the end.<br />
<br />
<h3>
Case #1: @EnableScheduling (importing an @Configuration class)</h3>
The first thing to note is that the @Enable* annotations are not magic. Nothing in the Bean Factory knows anything about them specifically and there are no dependencies in the Bean Factory classes between the core functionality and specific annotations (like @EnableWebMvc) or the jars they're stored in (like spring-web). Let's take a look at @EnableScheduling and see how it works. You might have a MainConfig class that looks like this:<br />
<br />
<pre class="brush:java">@Configuration
@EnableScheduling
public class MainConfig {
// some beans go in here
}
</pre>
<br />
There's nothing special in the above. Just a standard Java Config that's annotated with @EnableScheduling. @EnableScheduling lets you execute certain methods at a set frequency. For example, you can run BankService.sendMoneyToDoug() every 20 minutes. The @EnableScheduling annotation itself looks like this:
<br />
<pre class="brush:java">@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Import(SchedulingConfiguration.class)
@Documented
public @interface EnableScheduling {
}
</pre>
<br />
If we look at the annotation above, we can see that it is just a standard Class level annotation (@Target/@Retention) that should be included in JavaDocs (@Documented), but it has one Spring-specific annotation: @Import. @Import is the key that ties everything together. In this case, since our MainConfig is annotated as @EnableScheduling, when the Bean Factory is parsing the file (technically the ConfigurationClassPostProcessor is parsing it) it will also find the @Import(SchedulingConfiguration.class) annotation and it will import the class defined in the value. In this case SchedulingConfiguration. What does it mean to import? Well, in this case it's just treated as another Spring bean. SchedulingConfiguration is actually annotated as @Configuration, so the Bean Factory will see it as another configuration class and all of the beans defined in that class will get pulled into your Application Context just as if you had defined another @Configuration class yourself. If we inspect SchedulingConfiguration we can see that it only defines one bean (a Post Processor) which does the scheduling we described above.<br />
<br />
<pre class="brush:java">@Configuration
public class SchedulingConfiguration {
@Bean(name=AnnotationConfigUtils.SCHEDULED_ANNOTATION_PROCESSOR_BEAN_NAME)
@Role(BeanDefinition.ROLE_INFRASTRUCTURE)
public ScheduledAnnotationBeanPostProcessor scheduledAnnotationProcessor() {
return new ScheduledAnnotationBeanPostProcessor();
}
}
</pre>
<br />
OK, but what if I want to configure the beans defined in SchedulingConfiguration? Well, at this point we're just dealing with regular beans. So the same mechanisms that you would use for any other beans apply here. In this case, the ScheduledAnnotationBeanPostProcessor uses a standard Spring Application Event to find out when the Application Context is refreshed. When this happens it checks to see if any beans implement SchedulingConfigurer and if so, uses those beans to configure itself. This is not at all intuitive (or easy to find with an IDE) but it is completely separated from the Bean Factory and is a fairly common pattern for a bean to be used to configure another bean. And now that we can connect all of the dots it is (somewhat) easy to find (or you could google the documentation or read the JavaDocs).<br />
<br />
<h3>
Case #2: @EnableTransactionManagement (importing an ImportSelector)</h3>
<div>
In the previous case we discussed how an annotation like @EnableScheduling can use @Import to pull in another @Configuration Class and make all of its beans available (and configurable) to your application. But what happens if you want to load a different set of beans based on some configuration? @EnableTransactionManaged is a good example of this. You might have a MainConfig class that looks like this:</div>
<div>
<br /></div>
<pre class="brush:java">@Configuration
@EnableTransactionManagement(mode=AdviceMode.ASPECTJ)
public class MainConfig {
// some beans
}
</pre>
<div>
<br /></div>
<div>
Once again, there's nothing special in the above. Just a standard Java Config that's annotated with @EnableTransactionManagement. The only thing that's a little different from the previous example is that the user specified a parameter to the annotation (mode=AdviceMode.ASPECTJ). The @EnableTransactionManagement annotation itself looks like this:<br />
<br />
<pre class="brush:java">@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(TransactionManagementConfigurationSelector.class)
public @interface EnableTransactionManagement {
boolean proxyTargetClass() default false;
AdviceMode mode() default AdviceMode.PROXY;
int order() default Ordered.LOWEST_PRECEDENCE;
}
</pre>
<br />
As before, a fairly standard annotation, although this time it has some parameters. However, I mentioned previously that the @Import annotation was the key that ties everything together and that is true once again. The distinction though, is that this time we are importing TransactionManagementConfigurationSelector.class which is not a class that is annotated with @Configuration. TransactionManagementConfigurationSelector is a class that implements ImportSelector. The purpose of ImportSelector is to allow your code to choose which configuration classes to load at runtime. It has one method that takes some metadata about an annotation and returns an array of class names. In this case, the TransactionManagementConfigurationSelector looks at the mode and returns some classes based on the mode:<br />
<br /></div>
<br />
<pre class="brush:java"> protected String[] selectImports(AdviceMode adviceMode) {
switch (adviceMode) {
case PROXY:
return new String[] { AutoProxyRegistrar.class.getName(), ProxyTransactionManagementConfiguration.class.getName() };
case ASPECTJ:
return new String[] { TransactionManagementConfigUtils.TRANSACTION_ASPECT_CONFIGURATION_CLASS_NAME };
default:
return null;
}
}
</pre>
<br />
<br />
Most of these classes are @Configuration classes (e.g. ProxyTransactionManagementConfiguration) so we know that they'll work like before, but some of them are not (let's ignore those for now). For the @Configuration classes they get loaded and configured in the exact same way that we previously saw. So in short, we can use @Import with an @Configuration class to load a standard set of beans or we can use @Import with an ImportSelector to load a set of beans that are decided at run time. Sweet! But what about those classes we ignored?<br />
<br />
<h3>
Case #3: @EnableAspectJAutoProxy (importing at the Bean Definition Level)</h3>
@Import supports one last case which is when you want to deal with a Bean Registry (Factory) directly. If you need to manipulate the Bean Factory or work with beans at the Bean Definition level then this case is for you and it's very similar to the ones above. Your MainConfig might look like:<br />
<br />
<pre class="brush:java">@Configuration
@EnableAspectJAutoProxy
public class MainConfig {
// some beans
}
</pre>
<br />
Once again, there's nothing special in the above. Just a standard Java Config that's annotated with @EnableAspectJAutoProxy. Here's the source for @EnableAspectJAutoProxy:<br />
<br />
<br />
<pre class="brush:java">@Target(ElementType.TYPE)
@Retention(RetentionPolicy.RUNTIME)
@Documented
@Import(AspectJAutoProxyRegistrar.class)
public @interface EnableAspectJAutoProxy {
boolean proxyTargetClass() default false;
}
</pre>
<br />
As before, the @Import is the key, but this time it's pointing to AspectJAutoProxyRegistrar which is neither an @Configuration class nor implements ImportSelector. The trick this time is that it implements ImportBeanDefinitionRegistrar. This interface gives access to the Bean Registry and Annotation Metadata so that we can manipulate the registry at runtime based off of the parameters in the annotation. If you look at the previous case you can see that the classes we ignored were also ImportBeanDefinitionRegistrars. These classes directly manipulate the Bean Factory for those times when @Configuration classes aren't enough. <br />
<br />
So now we've covered all of the different ways the @Enable* annotations use @Import to pull various beans into your Application Context. They either pull in a set of @Configuration classes directly and all of the beans from those classes get imported into your Application Context. Or they pull in an ImportSelector which chooses a set of @Configuration classes at runtime and imports those beans into your Application Context. Or finally, they pull in an ImportBeanDefinitionRegistrars which can directly work with the Bean Factory at the Bean Definition level.<br />
<br />
<h3>
Conclusion</h3>
In general I think this approach to importing beans into an Application Context is great because it makes set up very easy for the developer. Unfortunately it obscures how to find the available options and how to configure them. In addition, it doesn't directly take advantage of the IDE so it's hard to tell which beans are being created (and why). However, now that we know about the @Import annotation we can use our IDE to dig a little into each Annotation and its related configuration classes and understand which beans are being created, how they're being added to your Application Context and how to configure them. I hope this helps! Please leave comments to let me know what you think and what I've missed / messed up.<br />
<div style="display: none;">
<pre class="brush:java"></pre>
<pre class="brush:xml"></pre>
<pre class="brush:js"></pre>
</div>
<script type="text/javascript">
function loadScript(url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
loadScript("http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js",
function(){
loadScript("http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js",
function(){
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.autoloader(
'js jscript javascript http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushJScript.js',
'css http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushCss.js',
'java http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushJava.js',
'xml xhtml xslt html http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushXml.js'
);
SyntaxHighlighter.all();
}
);
}
);
</script>Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com7tag:blogger.com,1999:blog-7790006377311932302.post-54722595319971279822012-05-23T09:30:00.000-04:002012-10-07T21:45:42.485-04:00In Place Editor Custom Binding for Knockout.js<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-912KxA_poFQ/T7v7f3bkmUI/AAAAAAAAAi0/N7_KQ9S7Z4U/s1600/Mike-tyson-punch-out-001.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://3.bp.blogspot.com/-912KxA_poFQ/T7v7f3bkmUI/AAAAAAAAAi0/N7_KQ9S7Z4U/s320/Mike-tyson-punch-out-001.png" width="132" /></a></div>
I recently discovered <a href="http://knockoutjs.com/" target="_blank">knockout.js</a> and was totally blown away. It does a great job of removing the need for (most) DOM manipulations which lets you focus on your data model and get complex UIs running with a minimal amount of code. That said, those complex UIs are by default made up of simple widgets (input boxes, buttons, etc) and if you want more complex widgets (<a href="http://www.knockmeout.net/2011/07/another-look-at-custom-bindings-for.html" target="_blank">date pickers</a>, inline editors, etc) you need to either find them elsewhere or roll your own. <a href="http://www.knockmeout.net/2011/07/another-look-at-custom-bindings-for.html" target="_blank">Ryan Niemeyer</a> gives a great explanation of how to create a date picker (or any custom binding) and I quickly incorporated it into my own site. Since I also needed an inline editor I figured I would post my results so others could reuse and improve upon them. The following code is very much based on the <a href="http://www.knockmeout.net/2011/07/another-look-at-custom-bindings-for.html" target="_blank">Custom Bindings article</a> referenced above and the <a href="http://www.appelsiini.net/projects/jeditable" target="_blank">Jeditable </a>inline editor.<br />
<br />
<a name='more'></a><h2>
The Goal:</h2>
<div>
The goal is to create something that looks like text but when you click it becomes editable. I also wanted when you click off or hit enter for the text to be saved to the model. Finally, I want hitting "Esc" to undo the latest change. Here is a live example you can play with:</div>
<div>
<br />
<table>
<tbody>
<tr>
<td style="text-align: right;">Click this:</td>
<td><span data-bind="jeditable: name, jeditableOptions: {}" style="background-color: yellow;"></span></td>
</tr>
<tr>
<td>Current value:</td>
<td><span data-bind="text: name"></span></td>
</tr>
</tbody></table>
</div>
<br />
<h2>
The Prerequisites:</h2>
<div>
The following code is based on:</div>
<div>
<ul>
<li><a href="http://knockoutjs.com/documentation/installation.html" target="_blank">Knockout.js 2.1.0</a> (obviously)</li>
<li><a href="http://docs.jquery.com/Downloading_jQuery#CDN_Hosted_jQuery" target="_blank">JQuery 1.7.x</a> (parent of Jeditable)</li>
<li><a href="http://www.appelsiini.net/projects/jeditable" target="_blank">Jeditable</a> (inline edit control that does most of the work)</li>
</ul>
</div>
<h2>
The HTML:</h2>
The HTML is pretty simple:<br />
<pre class="brush:html ">Click this: <span data-bind="jeditable: name, jeditableOptions: {}"></span>
Current value: <span data-bind="text: name"></span>
</pre>
<br />
If you're comfortable with Knockout.js this should look pretty familiar. The first line uses a custom binding called "jeditable" to tie a model property called "name" to a span tag. Knockout.js will be responsible for keeping the model and DOM in sync. As a developer I just tell it to use the "jeditable" binding for this element and the "name" property. There is also an optional "jeditableOptions" property which I can use to pass options to the jeditable binding.<br />
<br />
The second line uses the standard text binding to display the current value of the "name" property in the span. Once again, as a developer, I don't have to think at all about what to update when the model changes. It all happens automatically.<br />
<h2>
The Model</h2>
<div>
The model can also be super simple: <br />
<pre class="brush: js"> function myViewModel() {
this.name = ko.observable("Innocent looking text");
};
ko.applyBindings(new myViewModel());
</pre>
In this case I just need a single property called "name" that I default to "innocent looking text". I pass an instance of my model to Knockout as is the KO way and it handles all of the heavy lifting. All that leaves is...</div>
<h2>
The Custom Binding</h2>
The Custom Binding is the reusable code that knows how to update the DOM when the model changes and update the model when the DOM (or control) changes:
<br />
<pre class="brush: js">ko.bindingHandlers.jeditable = {
init: function(element, valueAccessor, allBindingsAccessor) {
// get the options that were passed in
var options = allBindingsAccessor().jeditableOptions || {};
// "submit" should be the default onblur action like regular ko controls
if (!options.onblur) {
options.onblur = 'submit';
}
// set the value on submit and pass the editable the options
$(element).editable(function(value, params) {
valueAccessor()(value);
return value;
}, options);
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).editable("destroy");
});
},
//update the control when the view model changes
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).html(value);
}
};
</pre>
<br />
This is a little more complicated and requires some explanation. The binding consists of two functions "init" and "update". "init" is called only once, when the binding is initially evaluated. Here we can create our control and pass it any options that the user specified. We also listen for any changes so we can notify the model. "update" is called every time the model changes and gives us a chance to alter the control to match the state of the model.<br />
<br />
The work begins at line 4. Here we check to see if the user passed in any options. This gives the user the ability to customize the Jeditable control using any of the <a href="http://www.appelsiini.net/projects/jeditable" target="_blank">options allowed</a> by the plugin. One option allowed is "onblur". This tells the control what to do when the user clicks outside of the control. By default Jeditable will cancel any edits onblur but since the default Knockout.js controls will save onblur we follow this convention and set the default onblur value to be "submit" (that is, save) on lines 7-9.<br />
<br />
Lines 12-15 are where we create the control with the specified options and tie it to the appropriate DOM node. The function that we pass in is what gets called when the user saves a new value. In this function we simply update the model with the new value and return that value so that Jeditable thinks everything is ok.<br />
<br />
Lines 18-20 are just housekeeping. That's where we clean up our control if Knockout.js tells us that the DOM node should be cleaned up.<br />
<br />
Finally, it's time to update at lines 25-28. Here we simply get a reference to the new value in the model and update the Jeditable control to display that new value. And that's it! Now we have a custom binding that can be reused many times throughout the site.<br />
<br />
<h2>
The Conclusion</h2>
Knockout.js bindings may seem a little scary at first but they're not that complicated and can be very powerful. Even someone like me who is new to Knockout.js (and JQuery) can easily whip one up and reuse it throughout a site. My only wish is that it was easier to distribute custom bindings so that some nice libraries could be written and shared across the 'net. Please give me any feedback or improvements you have in the comments!<br />
<br />
<br />
<div style="display: none;">
<pre class="brush:java"></pre>
<pre class="brush: js"></pre>
<pre class="brush: xml"></pre>
</div>
<script type="text/javascript">
function loadScript(url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
// we get jquery from blogger
loadScript("http://github.com/downloads/SteveSanderson/knockout/knockout-2.1.0.js",
function() {
loadScript("http://www.appelsiini.net/download/jquery.jeditable.mini.js",
function() {
ko.bindingHandlers.jeditable = {
init: function(element, valueAccessor, allBindingsAccessor) {
// get the options that were passed in
var options = allBindingsAccessor().jeditableOptions || {};
// "submit" should be the default onblur action like regular ko controls
if (!options.onblur) {
options.onblur = 'submit';
}
// set the value on submit and pass the editable the options
$(element).editable(function(value, params) {
valueAccessor()(value);
return value;
}, options);
//handle disposal (if KO removes by the template binding)
ko.utils.domNodeDisposal.addDisposeCallback(element, function() {
$(element).editable("destroy");
});
},
//update the control when the view model changes
update: function(element, valueAccessor) {
var value = ko.utils.unwrapObservable(valueAccessor());
$(element).editable().html(value);
}
};
function myViewModel() {
var self = this;
self.name = ko.observable("Innocent looking text");
};
ko.applyBindings(new myViewModel());
}
);
}
);
loadScript("http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js",
function(){
loadScript("http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js",
function(){
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.autoloader(
'js jscript javascript http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushJScript.js',
'css http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushCss.js',
'java http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushJava.js',
'xml xhtml xslt html http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushXml.js'
);
SyntaxHighlighter.all();
}
);
}
);
</script>Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com10tag:blogger.com,1999:blog-7790006377311932302.post-2060973471766635982012-05-15T10:53:00.000-04:002012-05-15T10:53:18.432-04:00Euphemisms: When Good Words Go Bad<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-_qOJYFrKHBo/T7JsdXD2LsI/AAAAAAAAAfU/rwFGEiBpY6M/s1600/euphemism-misc1.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="301" src="http://3.bp.blogspot.com/-_qOJYFrKHBo/T7JsdXD2LsI/AAAAAAAAAfU/rwFGEiBpY6M/s400/euphemism-misc1.jpg" width="400" /></a></div>
A euphemism is a nice way of saying something not so nice. The problem with euphemisms is that the more successful they are, the more likely they are to take on the not so nice meaning of the thing they're trying to be nice about. Once this reversal happens, they transition from a euphemism to a synonym and a new euphemism must be created. It's an interesting quirk of language that a word or phrase can be intended to mean one thing and then become so popular that it comes to mean something entirely different.<br />
<a name='more'></a><br />
One example is the phrase "making love". The original meaning of the phrase was exactly what it sounds like, creating love. In other words, flirting. If you were wooing someone you might send them love letters, make them fancy meals and generally put in lots of effort to create love. This is a beautiful and somewhat innocent idea. Which is exactly why it makes such a great euphemism. Back in olden tymes one could have said "Ebeneezer and I were 'making love'", implying that you were in the process of forming a relationship when really the only thing you were forming was the beast with two backs.<br />
<br />
The problem with this euphemism is that it's too good. Everyone started using it and that was its downfall. By becoming so successful the phrase grew more popular and consequently more people started to catch on. It got to the point where today "making love" only means "having sex" and has completely lost its original meaning.<br />
<br />
This isn't specific to this one phrase. Almost every successful euphemism faces this burden because by becoming successful it means that people use it and if people use it then they know what it means and consequently its meaning changes to what people are actually using it for (as opposed to its original meaning). There's even a a phrase to describe this phenomenon: <a href="http://en.wikipedia.org/wiki/Euphemism#Euphemism_treadmill" target="_blank">The Euphemism Treadmill</a>.<br />
<br />
Another great example is the word "retarded". The verb retard means to make slow or to delay. It was, at one point, a gentle way of describing a mental deficiency. In fact it's very elegant. It didn't mean that a person was incapable of anything, merely that they could arrive at the same place as everyone else, it just took them a little longer because they were slowed. In essence, something was delaying their progress not impeding their completion. I think this is a lovely way of describing a phenomenon, but alas so did a lot of people. And once again, that was the problem. When everyone started to use "retarded" in this way it took on a negative connotation. As with other euphemisms, it's gotten to the point today where most people think of the insulting definition first and the slowed definition second (if at all).<br />
<br />
I think there are a lot of people who see language as static; that a word means something which can never change; that there is a correct usage which cannot grow or evolve. Euphemisms are one example that clearly show how languages are affected by the way that they are used. I think it is this feature of language, that they alter and self-correct over time to better reflect the people that use them to communicate, that makes language and etymology so interesting.Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com1tag:blogger.com,1999:blog-7790006377311932302.post-62392027153582295482012-05-08T10:16:00.000-04:002012-05-22T14:06:06.270-04:00Pagination with Spring MVC, Spring Data and Java Config<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-70xPL4lDHW0/T6dKS7-VZbI/AAAAAAAAAb0/7lXBCmxie60/s1600/book-pages.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="300" src="http://1.bp.blogspot.com/-70xPL4lDHW0/T6dKS7-VZbI/AAAAAAAAAb0/7lXBCmxie60/s400/book-pages.jpg" width="400" /></a></div>
Spring 3.1 has a lot of features for limiting the boiler-plate code you have to write for common functionality. One great example is pagination. Getting paged data from a database and presenting it to the user is one of those tasks that everyone seems to reinvent even though it's common functionality that is never specific to your business. Spring Data provides some facilities to add pagination to your application with a minimal amount of code. The <a href="http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/" target="_blank">documentation </a>on this is pretty good and it includes some steps on how to set up both in the data tier and the <a href="http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/#web-pagination" target="_blank">web tier</a>. Unfortunately, the documentation is unclear in a few places (see below) and doesn't explain at all how to use a Java Config. This post tries to fill those gaps.<br />
<br />
<a name='more'></a><br />
<div>
<span style="font-size: x-large;"><b>Data Tier</b></span><br />
<div>
The first thing we need is a data tier that can return paged data from a database. Spring Data provides a way to access the database without writing any code (almost). Instead of having to define and implement common CRUD methods you simply define an interface. You don't have to write the implementation because Spring will create a proxy that automagically fulfills your interface. You just wire the interface and Spring does the rest. I'm using JPA and here's a real example:</div>
<br />
<pre class="brush: java">package com.fawnanddoug.gmailsyncer.repository;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import com.fawnanddoug.gmailsyncer.domain.SyncJobDetail;
public interface SyncJobDetailRepository extends JpaRepository<SyncJobDetail, Long> {
public Page<SyncJobDetail> findBySyncJobIdOrderByLastUpdateDesc(long syncJobId, Pageable p);
}
</pre>
Most of the work occurs on line 9 where I extend the JpaRepository interface. Simply by extending that interface I get common CRUD methods like save(), findAll(), delete(), etc. I also give my Entity Type (SyncJobDetail) and the key (Long) so that I get a strongly typed interface. Now I have a repository that can create and modify Objects in my domain and all I did was extend an Interface.<br />
<br />
I get some additional help on line 11. There I define a method to grab the SyncJobDetails by their parent's Id and I sort them by the last updated time. Spring can automatically infer what the implementation should look like based on the method name (which is admittedly ugly) and once again I don't have to write any implementation. The first parameter will map to the SyncJobId in the query and the best part is it automatically adds support for Paging just by adding the Pageable parameter. Pageable has methods like "getPageNumber" and "getPageSize" so I can easily specify that I want pages with 20 items per page and give me page number 7. <b>The only trick here is the configuration which I'll describe below.</b><br />
<div>
<br /></div>
<div>
<span style="font-size: x-large;"><b>
Web Tier</b></span></div>
<div>
Now that we can pull the data from the database we need some way to serve it over the web. This also requires a minimal amount of code. Here's an example:<br />
<br /></div>
<pre class="brush: java">@Controller
public class HomeController {
private final SyncJobDetailRepository repo;
@Inject
public HomeController(SyncJobDetailRepository repo) {
this.repo = repo;
}
@RequestMapping(value = "/history/detail/{id}", method = RequestMethod.GET)
public String historyDetail(Model model, @PathVariable long id, Pageable p) {
Page<SyncJobDetail> page = this.repo.findBySyncJobIdOrderByLastUpdateDesc(id, p);
model.addAttribute("page", page);
return "historyDetail";
}
</pre>
<br />
Injecting the repository (line 6) and using the request mapping (line 11) are pretty basic Spring MVC concepts. In short, if the user requests a page like /history/detail/123 then the historyDetail() method will get automatically invoked. The real magic is on line 12 where the Pageable parameter is automatically inferred from the parameters that are passed into the request. <b>This also requires some configuration magic which is described below.</b> Once we have the Pageable we can just pass it on to our repository to get the appropriate data. The returned Page is then exposed to the View so that it can be displayed to the user. The page has useful information like which page we're looking at, how many total items exist and which items were returned in this page. Once again we have barely any code written and we get Paging essentially for free.<br />
<div>
<br />
<span style="font-size: x-large;"><b>
The View</b></span></div>
<div>
Now that the Page Object is exposed to the view we can use that Page to display useful information to the user. Here's an example JSP:</div>
<div>
<script class="brush: xml" type="syntaxhighlighter">
<![CDATA[
<util:pagination maxPages="${page.totalPages}" maxElements="${page.totalElements}" page="${page.number}"
size="${page.size }" />
<table>
<thead>
<tr>
<td width="2%"></td>
<td width="30%">GMail Name</td>
<td width="10%">Status</td>
<td width="58%">Message</td>
</TR>
</THEAD>
<tbody>
<c:set var="first" value="${page.size * page.number}"/>
<c:forEach items="${page.content}" var="row" varStatus="status">
<tr class="${status.count % 2 == 0 ? 'even' : 'odd'}">
<td>${first + status.count}</td>
<td>${row.googleName}</td>
<td>${row.status}</td>
<td>${row.message}</td>
</tr>
</c:forEach>
</tbody>
</table>
]]>
</script>
</div>
There's nothing particularly special here. I created a pagination util tag lib since I reuse pagination in a few places. It shows things like the total number of pages, the current page and how many elements are on the page. As you can see all of this information comes from the Page object on lines 1 and 2. The only other interesting item is that the content is exposed from the Page object at line 14. Pretty straight forward and I didn't have to manually calculate or even query any of this information.<br />
<br />
The only thing left is to post back to the controller when we want a new page. In this case we just have to specify the right parameters for the page size and the requested page and we're done. Here's an example of how I generate the URL for my "Next" button:<br />
<script class="brush: xml" type="syntaxhighlighter">
<![CDATA[
<spring:url value="" var="next">
<spring:param name="page.page" value="${page.number + 1}" />
<spring:param name="page.size" value="${page.size}" />
</spring:url>
]]>
</script>
In line 1 I use "" as the url value so that we post back to the same page. I then add the "page.page" parameter for the next page (current page plus one) and use the same size that we're currently using. Nothing particularly special here and all of the values come from the Page object that was returned from the data tier.
<br />
<br />
<span style="font-size: x-large;"><b>
The Gotchas</b></span><br />
<div>
The code, as you can see above, is pretty minimal and can apply generically to any domain. There are, however, a number of gotchas. Let's explore these gotchas by working backwards from the View to the Web Tier to the Data Tier.</div>
<div>
<br /></div>
<b><span style="font-size: large;">
View Gotchas</span></b><br />
<div>
The first one is related to the <a href="http://static.springsource.org/spring-data/data-jpa/docs/current/reference/html/#web-pagination" target="_blank">documentation</a>. In the above example I specify the next page parameter with the key "page.page". The gotcha is that the docs say the parameter name is just "page" so it took me a little while to catch. Technically you can specify any prefix you want (so it could be "foo.page"), but the important bit is that you must specify a prefix and then the parameter name. The prefix can be configured on the PageableArgumentResolver which is described below.<br />
<br />
The next gotcha is that the Java Doc for PageRequest (the Pageable implementation) says that it is 0-based but the Java Doc for Page says that it is 1-based. In other words, when you request data the first page is 0 but when it returns data the first page is 1. This can be pretty confusing and may lead you to try to compensate in your code for potential one-off issues. In fact, I think it's intentional and the goal is that when you display data it uses numbers that a User wants to see (e.g. page 1 is "1") but still plays nice with databases that think of the first page as 0. The PageableArgumentResolver (described below) will automatically handle the conversion which means you won't need code to compensate for this, but it can still be confusing. In short, it does the right thing but may be hard to wrap your head around.<br />
<br />
<span style="font-size: large;"><b>
Web Tier Gotchas</b></span><br />
I mentioned above that to get the Pageable parameter to work in your controller you need some configuration magic. In order for Spring to know how to convert the parameter to a Pageable object you need to configure a HandlerMethodArgumentResolver. Spring Data provides a PageableArgumentResolver but it uses the old ArgumentResolver interface instead of the new (Spring 3.1) HandlerMethodArgumentResolver interface. The XML config can handle this discrepancy for us, but since we're using Java Config we have to do it a little more manually. Luckily this can be easily resolved if you know the right magic incantation:<br />
<br />
<pre class="brush: java">@Configuration
@EnableWebMvc
public class WebMvcConfig extends WebMvcConfigurerAdapter {
@Override
public void addArgumentResolvers(
List<HandlerMethodArgumentResolver> argumentResolvers) {
PageableArgumentResolver resolver = new PageableArgumentResolver();
resolver.setFallbackPagable(new PageRequest(1, 10));
argumentResolvers.add(new ServletWebArgumentResolverAdapter(resolver));
}
</pre>
<br /></div>
The above is a standard Spring MVC Java Config. It extends WebMvcConfigurerAdapter so that it can override some common methods and add functionality. In this case we are overriding addArgumentResolvers so that we can add our PageableArgumentResolver. The key here is that on line 12 we wrap the PageableArgumentResolver in a ServletWebArgumentResolverAdapater. This lets us reuse the PageableArgumentResolver and still wrap it in the new Interface. Now we get the Pageable object injected into our methods for free. <br />
<br />
One other note is that I set the FallBackPageable to use Page 1 and a Page Size of 10. This means that if there is no Page parameters set it will use Page 1 and a Page Size of 10. However, if you were paying attention above you would note that Pageable is supposed to be 0-based. The magic here is that the PageableArgumentResolver tries to automatically compensate for 0-based and 1-based issues so it will treat both "0" and "1" as the first page. "2" is always the second page. This can be a little disconcerting at first, but as long as your view is passing in the values that the user wants (e.g. page "1" is "1") then you'll be fine.<br />
<br />
<span style="font-size: large;"><b>
Data Tier Gotchas</b></span><br />
I said above that this was about using Java Config but the sad news is that you can't use the JPA Data Repository scanning without XML. A typical approach is to import the XML using something like this:<br />
<pre class="brush: java">@Configuration
@ImportResource("classpath:com/fawnanddoug/gmailsyncer/config/jpa.xml")
public class MainConfig {
// beans go here
}
</pre>
<br />
<br />
Then you can have a simple xml config that looks something like this:</div>
<div>
<script class="brush: xml" type="syntaxhighlighter">
<![CDATA[
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:jpa="http://www.springframework.org/schema/data/jpa"
xsi:schemaLocation="http://www.springframework.org/schema/beans
http://www.springframework.org/schema/beans/spring-beans-3.1.xsd
http://www.springframework.org/schema/data/jpa
http://www.springframework.org/schema/data/jpa/spring-jpa-1.1.xsd">
<!-- find repos because there's no java config equivalent -->
<jpa:repositories base-package="com.fawnanddoug.gmailsyncer.repository" />
</beans>
]]>
</script>
</div>
<div>
<br /></div>
In the above, line 11 searches the specified package to find our repository interfaces. It then generates the appropriate implementations. Now we can wire our repository into the Java Config and use the cool new features. In our case the SyncJobDetailRepository will get autowired into the HomeController so we don't have to add any additional configuration.<br />
<br />
<span style="font-size: x-large;"><b>Conclusion</b></span><br />
<div>
And that's it. With a minimal amount of code you can add paging to your application and still use strongly typed methods and services. Just make sure to configure the appropriate parts and look out for 0-based / 1-based issues. </div>
<div style="display: none;">
<pre class="brush:java"></pre>
<pre class="brush:xml"></pre>
</div>
<script type="text/javascript">
function loadScript(url, callback){
var script = document.createElement("script")
script.type = "text/javascript";
if (script.readyState){ //IE
script.onreadystatechange = function(){
if (script.readyState == "loaded" ||
script.readyState == "complete"){
script.onreadystatechange = null;
callback();
}
};
} else { //Others
script.onload = function(){
callback();
};
}
script.src = url;
document.getElementsByTagName("head")[0].appendChild(script);
}
loadScript("http://alexgorbatchev.com/pub/sh/current/scripts/shCore.js",
function(){
loadScript("http://alexgorbatchev.com/pub/sh/current/scripts/shAutoloader.js",
function(){
SyntaxHighlighter.config.bloggerMode = true;
SyntaxHighlighter.autoloader(
'js jscript javascript http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushJScript.js',
'css http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushCss.js',
'java http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushJava.js',
'xml xhtml xslt html http://alexgorbatchev.com/pub/sh/3.0.83/scripts/shBrushXml.js'
);
SyntaxHighlighter.all();
}
);
}
);
</script>Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com13tag:blogger.com,1999:blog-7790006377311932302.post-77564226502670288812012-05-01T09:30:00.000-04:002015-12-08T21:49:07.690-05:00I wrote A Facebook To GMail Syncing App<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-BiejjUSgysc/T59AgOfY6_I/AAAAAAAAAZo/sQ_NdfGJNS0/s400/map.png" target="_blank"><img border="0" src="http://1.bp.blogspot.com/-BiejjUSgysc/T59AgOfY6_I/AAAAAAAAAZo/sQ_NdfGJNS0/s1600/map.png" /></a></div>
I wrote an app that syncs your Facebook Friends and GMail Contacts! Actually, I rewrote an app that syncs your Facebook Friends and GMail Contacts. The first version had some <a href="http://blog.fawnanddoug.com/2012/04/i-convinced-100s-of-people-to-give-me.html" target="_blank">flaws</a>. But this version is new and improved. Let me count the ways:<br />
<br />
<a name='more'></a><br /><br />
<b>Brand New User Interface:</b><br />
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="513" src="http://2.bp.blogspot.com/-m8AurOmm_fk/T5894GCkl7I/AAAAAAAAAZc/Ot8FT7yWyuw/s640/UI.png" width="640" /></div>
<br />
I tried to redesign the UI with an eye towards a few things:<br />
<br />
<ul>
<li>Clearly convey information with large text and bold icons</li>
<li>Feel consistent within Facebook's style while adding a touch of whimsy</li>
<li>Provide simple calls to action (errors are big and red with simple buttons that indicate the next step)</li>
<li>Limited options that respond immediately (e.g. you can change the Sync Frequency without reloading the page or clicking submit)</li>
</ul>
<div>
I'm really interested in your feedback! What do you think? Please post a comment!</div>
<div>
<br /></div>
<div>
<b>World Wide Appeal:</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-BiejjUSgysc/T59AgOfY6_I/AAAAAAAAAZo/sQ_NdfGJNS0/s1600/map.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="393" src="http://1.bp.blogspot.com/-BiejjUSgysc/T59AgOfY6_I/AAAAAAAAAZo/sQ_NdfGJNS0/s640/map.png" width="640" /></a></div>
<div>
I'm not sure how this is a new feature but the all new GMail Contact Syncer has been used in 47 countries in the first month. Above is a world map from Google Analytics (I'm big in India).</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<b>Cloud Based Development Platform</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.cloudfoundry.com/" target="_blank"><img border="0" height="60" src="http://2.bp.blogspot.com/--MhpTiqH-ww/T59Bf9JMF1I/AAAAAAAAAZw/cko40_E3KhM/s320/logo_header_cloudfoundry.png" width="320" /></a></div>
<div>
I moved the server out of my basement and now have a full data center with redundancy and and horizontal scaling. There's lots of other buzz words like "Platform as a Service", "Open Stack" and "Virtual Jabberwockies" (I made up some of those). Not that I really need it but it's cool (and free). Thanks <a href="http://cloudfoundry.com/">CloudFoundry.com</a>! </div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div>
<b><br /></b></div>
<div>
<b>Completely Rewritten Syncing Infrastructure:</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.springsource.org/" target="_blank"><img border="0" src="http://4.bp.blogspot.com/-lH9ECoPY4U4/T59EzaKtzpI/AAAAAAAAAZ8/Ehx25qf0Ivo/s1600/placeholder_video_spring_projects.png" /></a></div>
<div>
The original syncing infrastructure was written by hand. It put each Sync Job in a single transaction, was single threaded and periodically brought my Athlon 3200+ to its knees. The new infrastructure was rewritten using Spring Integration for Enterprise Integration and Spring Social for Social Networking Integration. It's fast enough that I actually had to artificially limit its speed so that Facebook would stop rejecting my requests for being too fast. Also it partitions each job into logical steps to make failover more convenient (e.g. when Facebook randomly fails requests).</div>
<div>
<br /></div>
<div>
<br /></div>
<div>
<b>Totally doesn't steal your passwords:</b></div>
<div class="separator" style="clear: both; text-align: center;">
<img border="0" height="198" src="http://3.bp.blogspot.com/-KJ4SfUUfZmY/T59GhSjA1hI/AAAAAAAAAaE/ETOkEzLNf3Q/s200/OAuth-Shine-300x298.png" width="200" /></div>
<div>
<b><br /></b></div>
<div>
The original version of the GMail Syncer required you to enter your GMail password and it stored it in the database. This is a <a href="http://www.blogger.com/"><span id="goog_1536151232"></span>bad<span id="goog_1536151233"></span></a> idea<span style="font-family: "verdana" , sans-serif; font-size: 12px; line-height: 16px;">™</span>. The new version never knows your passwords. Using a technology called OAuth your passwords stay with your services (Facebook & Google) and the GMail Syncer stays completely ignorant. You give access using the providers that you're comfortable with (Facebook & Google) and can revoke it at any time.</div>
<div>
<br /></div>
<div>
<b>Set it and forget it:</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-iqdxnZuoiio/T59J009s3dI/AAAAAAAAAaQ/4EljIt5MPug/s1600/491595737_31ee926a9a.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="218" src="http://4.bp.blogspot.com/-iqdxnZuoiio/T59J009s3dI/AAAAAAAAAaQ/4EljIt5MPug/s320/491595737_31ee926a9a.jpg" width="320" /></a></div>
<div>
<b><br /></b></div>
<div>
As of May 1st, 2012 the GMail Syncer has processed over 1000 jobs, but you don't have to care. Just set it up once, pick your sync frequency and forget about it. It'll sync forever (or until you revoke access).</div>
<div>
<br /></div>
<div>
<b>It's Free:</b></div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-iOeyRYWKeCo/T588VAEdEMI/AAAAAAAAAZU/Lpo3boxEm1w/s1600/thats+gold+jerry.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="233" src="http://4.bp.blogspot.com/-iOeyRYWKeCo/T588VAEdEMI/AAAAAAAAAZU/Lpo3boxEm1w/s320/thats+gold+jerry.png" width="320" /></a></div>
<div>
<b><br /></b></div>
<div>
And it's free. I aught to figure out a way to monetize this bad boy, but I'm not sure how to do that yet. So take advantage of my bad business sense and get in while the getting's good.</div>
<div>
<br /></div>
<div>
Please give it a try and give me any feedback you have in the comments or at doug [at] fawn and doug [dot] com.</div>
<div>
<br /></div>
<div>
Thanks!</div>
Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-46474491296942256872012-04-24T09:24:00.000-04:002012-04-24T21:00:05.539-04:00In which I accuse New Wave rockers "The Romantics" of defiling Rick James' Grave<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-GNRUhjOQ7fE/T5Ha6g8k42I/AAAAAAAAAV0/zeVQOA_h5jg/s1600/The_romantics-talking_in_your_sleep_s.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://4.bp.blogspot.com/-GNRUhjOQ7fE/T5Ha6g8k42I/AAAAAAAAAV0/zeVQOA_h5jg/s320/The_romantics-talking_in_your_sleep_s.jpg" width="317" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
<br /></div>
<div class="separator" style="clear: both; text-align: center;">
<i>Update: The below is officially facts because it is cited in <a href="http://en.wikipedia.org/wiki/Talking_in_Your_Sleep_(The_Romantics_song)#cite_note-1" target="_blank">Wikipedia</a>.</i></div>
<br />
In the low stakes world of accusing 80s rock bands of ripping off Rick James, few have the gumption to publicly post their assertions on the Internet. What with the rabid fanaticism of "The Romantics" die-hards and the Internet's strict requirements for rock solid proof, no one is willing to touch this red hot topic with a serial mouse. But people of the Internet, live in fear no more - for I shall lead you out of the dark as I raise my keyboard and shout for all to hear that The Romantics totally ripped off "Super Freak" in their modest 1983 hit "Talking in your sleep".<br />
<br />
<a name='more'></a>"What?" you say in exasperated disbelief. "Why would a hard rocking New Wave band whose fourth album totally went to number 14, stoop so low as to jack Rick James, the paragon of demure sensibilities, the very image of 80s sobriety? Well, the dastardly "The Romantics" might as well have been twirling their riff-stealing mustaches while they tied genteel Rick to the train tracks of financial oblivion. And gentle reader I'm getting all up in there because they totes did. Poor damsel Rick could only wait for a super hero to appear out of nowhere and save the day. And that's what I've done. All Rick had to do was wait 21 years and then die and then wait 8 more years and here I am with my <a href="http://www.youtube.com/watch?feature=player_detailpage&v=MrGaoSB0Eus#t=128s" target="_blank">Jones-like</a> ability to show up just in time and save the day.<br />
<br />
<b>The Facts:</b><br />
<ul>
<li>Fact: "Super Freak" was released in <a href="http://en.wikipedia.org/wiki/Super_Freak" target="_blank">1981</a></li>
<li>Fact: "Talking in your sleep" was released in <a href="http://en.wikipedia.org/wiki/Talking_in_Your_Sleep_(The_Romantics_song)" target="_blank">1983</a></li>
<li>Fact: Wikipedia is <a href="http://www.nytimes.com/2007/02/21/education/21wikipedia.html?_r=1&em&ex=1172466000&en=d1211c2d017e16b6&ei=5087%0A" target="_blank">indisputable</a></li>
</ul>
<br />
<b>The Damning Evidence:</b><br />
Everyone knows the famous "Super Freak" riff. Listen closely to seconds <a href="http://www.youtube.com/watch?feature=player_detailpage&v=QYHxGBH6o4M#t=2s" target="_blank">2-6</a> or the whole thing because it <a href="http://www.youtube.com/watch?feature=player_detailpage&v=QYHxGBH6o4M#" target="_blank">repeats</a> like a 1000 times. Now listen to the exact same thing as perpetrated by <a href="http://www.youtube.com/watch?feature=player_detailpage&v=HwT9ltDBG14#t=5s" target="_blank">The Romantics</a>. Bam! They don't call it "Gotcha Journalism" for nothing. Ok, no one would call this Journalism but still. Bam?<br />
<br />
<b>The Superfluous Conclusion:</b><br />
There's not much more to say. The Romantics are evil thieves what profited from an innocent man's paean to his love of skanks. It's time we brought these heartless bastards down and finally give a little praise to a humble man who quietly lived the bard's life, writhing and suffering for his art.<br />
<br />
p.s. If your spell checker doesn't recognize "skanks", don't try to check your spelling by Googling it. The results are disgusting.<br />
<br />Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-43932687500121667892012-04-18T12:36:00.000-04:002012-04-18T12:41:55.051-04:00I find interesting things interesting<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-vENURmn2VFU/T47uR9Ym-qI/AAAAAAAAAVg/y0AJwQl7nlo/s1600/Screen-Shot-2011-12-16-at-2.23.19-PM.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="225" src="http://4.bp.blogspot.com/-vENURmn2VFU/T47uR9Ym-qI/AAAAAAAAAVg/y0AJwQl7nlo/s320/Screen-Shot-2011-12-16-at-2.23.19-PM.png" width="320" /></a></div>
<br />
I'm fascinated by design in general and web design in particular. The domain of Design always seemed complex and inaccessible to me even though as a professional programmer I do a particular kind of design on a regular basis. Capital "D" Design has seemed to me like a far away land with it's own language, culture and secret societies that were all unapproachable to the lay person. However, as I started to read and learn more about the process of Design it seems in many ways to be just like programming (a wild land I am nevertheless at home in). <br />
<br />
<a name='more'></a><br />
In programming we often talk about "elegant design" meaning a simple solution to a complex problem. "Elegant Design" is a lofty goal that is often difficult to achieve. Programmers try to increase our odds of success by carefully limiting the scope of the issue we're trying to solve and then focusing on a smaller and thus more manageable problem. <br />
<br />
As one might expect, this is a guiding principal in creative pursuits of all kinds, from <a href="http://www.dailyblogtips.com/procrastinating-to-write-that-huge-article-break-it-down-in-small-parts/" target="_blank">Writing</a> to <a href="http://www.dummies.com/how-to/content/einsteins-general-relativity-theory-gravity-as-geo.html" target="_blank">General Relativity</a> to <a href="http://www.geek.com/articles/geek-cetera/a-brilliant-explanation-of-how-a-cars-differential-works-20111218/" target="_blank">Axle Differential</a>. Thus, the overwhelming topic of Design started to become approachable to me when I realized that I didn't need to understand everything at once and could just focus on the particular bite size portion I wanted to address that day like intro to Typography or beginning Color. As I expand my vocabulary and get introduced to the world of Design, I hope to further my education by posting about the various items that I learn about. Thus reinforcing my new knowledge and hopefully sharing it as well.Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-79144546307149575692012-04-10T21:51:00.001-04:002012-04-11T08:32:09.152-04:00Killing a Joke: Cannibal Joe's<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-NmsCe_Ppwd4/T4Ti7n0eQsI/AAAAAAAAAVI/JjRNZTTgDcw/s1600/CannibalCauldren.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="360" src="http://4.bp.blogspot.com/-NmsCe_Ppwd4/T4Ti7n0eQsI/AAAAAAAAAVI/JjRNZTTgDcw/s400/CannibalCauldren.jpg" width="400" /></a></div>
<br />
I have no idea how professional comedy writers work, but for some reason I'm convinced that one of their favorite things to do is come up with a simple set up and then write 200 punchlines for it. This isn't based on any facts or empirical research, other than sometimes in sitcoms you'll see a set up that every character in the room gets to riff on. Also, it's something I like to do. I think it's a fun mind exercise to see how many punchlines I can come up, especially when I'm bored. Here's an example from a recent Facebook post that none of my friends seemed to care about. I like the last one the best. Post your own slogans below!<br />
<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-pAeHmb1gRsw/T4Ti8Hks7tI/AAAAAAAAAVQ/jnFrTbCHq6U/s1600/jim.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="640" src="http://4.bp.blogspot.com/-pAeHmb1gRsw/T4Ti8Hks7tI/AAAAAAAAAVQ/jnFrTbCHq6U/s640/jim.png" width="616" /></a></div>
<br />
<br />Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-28306706104921235572012-04-03T09:11:00.000-04:002012-04-10T17:35:46.740-04:00I convinced 100s of people to give me their GMail passwords<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://4.bp.blogspot.com/-tDbBiEG9XJ4/T4SngTHuQCI/AAAAAAAAAVA/BOvV6O5gJko/s1600/Gmail_logo.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="288" src="http://4.bp.blogspot.com/-tDbBiEG9XJ4/T4SngTHuQCI/AAAAAAAAAVA/BOvV6O5gJko/s640/Gmail_logo.png" width="640" /></a></div>
<div class="separator" style="clear: both; text-align: center;">
</div>
Honestly, it wasn't intentional. My goal was just to sync my Facebook Friends with my GMail Contacts. I tried searching for an app that could do it, but I couldn't find one and so I figured this might be a good opportunity to learn a little about developing with Facebook & Google. My only intention was to scratch an itch that had been bothering me for a little while, but then it got <a href="https://gmailsyncer.cloudfoundry.com/about" target="_blank">popular</a>.<br />
<br />
<a name='more'></a>My first pass at the app was just for myself so I hard coded my Facebook username & password and my Google username & password. I figured it would just be for me and running on my machine at home so why bothering making it generic. Really I just wanted to play with the APIs and get something that worked. But then the Missus saw it, thought it was cool and asked if I could hook her up too. I could have continued with the hard coded approach, but now that I had multiple users, it seemed like a good excuse to make it a full fledged Facebook app.<br />
<br />
Rather than just have it run on my server at home I could make it an official apps.facebook.com app with a User Interface and everything. I could use the Facebook login mechanism so I didn't have to hard code my username & password and I could play with all of the cool Facebook APIs. I was particularly excited about posting a message to my wall every time it synced. Wouldn't everyone want to see how cool my app was? Every day? Surely they would.<br />
<br />
So I whipped it all up. Whipped is maybe overstating it. I had a young son and another on the way and a full time job. I worked on it when I could. Eventually, though, it was working. Anyone could sign up as long as they had a Facebook account. I didn't need to know your Facebook username & password , but the catch was that I did need your GMail username & password. The site had a nice clean interface (if I say so myself) with the slightest touch of whimsy and a great big text box for your GMail username & password.<br />
<br />
I filled it out. My wife filled it out. And I thought that would be the end of it. But a weird thing happened. Every day the app posted a message to my wall that said that I had just synced my Facebook & GMail contacts with a link to the app. That's when the questions started. My friends started asking me: "What is this thing?" "Does it really work?" "Can I try it?" Pretty soon a bunch of my friends had signed. up. People who never in a million years would have given me their GMail password if I had asked, were all too happy to type it into a website. Even though it was a website they knew I made. Then it got even weirder.<br />
<br />
I rarely ever checked the logs on my app. It was working fine for me so I didn't have much reason to care. Nevertheless, out of curiosity one day I checked the logs and noticed that I had over twenty users. Wow! Twenty of my friends checked this thing out? Wow, that's pretty cool. Then I noticed one of the users was someone I had never met. Apparently some friend of a friend had seen the app and signed up. That's pretty cool I thought. <br />
<br />
A few weeks later I checked again and this time I had over 100 users.80% of which I didn't know and all of which had given me their GMail username & password. "This is getting out of hand" I would have thought if I had time to think but my kids were yelling and the bills needed to get paid. <br />
<br />
By November of 2011 I had over 500 users all of whom gave me their GMail username & password, the vast majority of whom had absolutely no reason to trust me. It turns out that I'm not interested in exploiting these fine people but they didn't know that. They blindly entered their GMail username & password into a website and just like that could have given up tons of personal information and potentially financial information too.<br />
<br />
In November 2011, Facebook instituted a new security requirement that all Facebook apps support HTTPS. This does add some amount of security to your transactions but doesn't really help when you're giving a website your GMail password. Anyway, I was doing this as a hobby and wasn't really paying attention to new Facebook requirements. And even if I was, I wasn't about to shell out $100+ for an SSL certificate so that I could be HTTPS compliant. Especially for a service that I wasn't making money on. In fact, you could argue that I was losing money on it if you count the time/effort I put into it plus the cost of running my server. In fact, I wouldn't have even noticed that Facebook had stopped access to the service if people hadn't started complaining to me that the Syncer wasn't working any more. These were people I never met. People who had started relying on this service. It was crazy!<br />
<br />
I didn't have time to fix the service so I just let it languish. I started getting angry service complaints from people who wanted their Syncer but I didn't have the free time to figure out any solutions. I figured I would let it run so that people who already signed up could continue to sync and not get pissed off at me.<br />
<br />
Finally in March 2012 I decided to rewrite the application. This time my first priority was to avoid having anyone's username or password. There are actually standards for this and it wasn't terribly difficult to implement but I never thought the app would reach the kind of audience it did. And that was with all of the odds against it. The new version of the app doesn't require any usernames or passwords. It uses the Facebook / Google APIs to let you authenticate with those services directly and my app never sees your personal information. I also signed up with a service to provide HTTPS so that Facebook will play nice as well. You can find the new app at <a href="https://apps.facebook.com/gmailsyncer" target="_blank">https://apps.facebook.com/gmailsyncer</a>. However, the moral of this story is <b>NEVER GIVE YOUR GMAIL PASSWORD TO ANYONE OTHER THAN GMAIL!</b>Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com2tag:blogger.com,1999:blog-7790006377311932302.post-81359687291611424122012-02-24T21:08:00.002-05:002014-02-08T21:33:48.700-05:00A Letter to Ari's kids about Ametropes<i>This letter was requested by my friend Ari in response to my <a href="http://blog.fawnanddoug.com/2012/02/letter-to-my-toddlers-about-women.html" target="_blank">previous letter</a>.</i><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-T91dDqANPWg/T4Sj8dL5QVI/AAAAAAAAAUI/yLANHaLGaT4/s1600/large+image+of+red+glasses.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="http://3.bp.blogspot.com/-T91dDqANPWg/T4Sj8dL5QVI/AAAAAAAAAUI/yLANHaLGaT4/s320/large+image+of+red+glasses.jpg" height="320" width="320" /></a></div>
<br />
Dear Jacob & Abby,<br />
<br />
There are some things in this world that can be seen. There are also some things that cannot be seen. Unfortunately for you, your parents' compromised gene pools have likely condemned you to a life of stumbling around grasping for the latter. But despair not! There is a solution. In 1784 God bestowed upon Man the means with which to focus the unseen and render it knowable. He called them Glasses and were it not for them you may never have been born in the first place. Glasses will undoubtedly become a fixture in your life just as they were the fixture that made your life. You need to know and love glasses. Fear and respect glasses. Or at a bare minimum, don't pull them off your father's God damn face. The man needs to see and you need to live. You can accomplish those two goals by the simple act of not impairing your father's sight. Also, don't stab him with them once you've got them. That's just cruel. Plus he can't find you to stop you so he's got to yell until Mommy comes. And she's got better things to do than defend her husband from a toddler. So be a sport and leave the damn glasses alone.<br />
<br />
Love,<br />
Uncle Doug<br />
<div>
<br /></div>
Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-7508471128919101612012-02-23T20:34:00.002-05:002012-04-10T17:27:53.872-04:00A letter to my toddlers about women<div class="separator" style="clear: both; text-align: center;">
<a href="http://1.bp.blogspot.com/-rCpUUrTbcGA/T4Sly0d6XnI/AAAAAAAAAUo/SEj-4I2FOqI/s1600/frau.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="320" src="http://1.bp.blogspot.com/-rCpUUrTbcGA/T4Sly0d6XnI/AAAAAAAAAUo/SEj-4I2FOqI/s320/frau.png" width="157" /></a></div>
<br />
Dear Tyler & Aidan,<br />
<br />
There are some things in this world that can be known. There are also some things that are unknowable. Part of life is learning to accept that some things cannot be known. You will find and rebel and eventually accept that much about women is simply unknowable. It is frustrating and difficult and ultimately insurmountable. Accepting it will be one of the great triumphs in your life. But there is at least one thing about women that is knowable. And that, is that you do not fuck with their hair. If there is a woman that you like/respect/would enjoy ever seeing again, you are to compliment them on their hair and then shut the fuck up. It is simple, but it is critical to your future happiness. Women are not impenetrable magical sphinxes with mysterious vails and complex machinations. They are human, with everything that entails. So do yourself a favor, respect all women and show them that by at a bare minimum praising their hair and being quiet. It is hard fought wisdom that is as true today as it will be when you care to remember it. But for now, please stop pulling your mother's hair every time she gives you a hug. I'm sick of hearing her complain about it. Just give her a kiss and go away.<br />
<br />
Love,<br />
Dad<br />
<div>
<br /></div>Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com1tag:blogger.com,1999:blog-7790006377311932302.post-80124477549042893112012-01-18T09:30:00.000-05:002012-01-19T09:00:22.628-05:00I hate Children's Music, but are my kids ready for Hip Hop?<div>
<i>A little while ago my <a href="https://twitter.com/#!/urbanrhetoric" target="_blank">friends </a>at <a href="http://urbanrhetoricblog.blogspot.com/" target="_blank">Urban Rhetoric</a>, progenitors of all things cool, asked me to write a piece on being a parent and a fan of Hip Hop. Below is a reprint of the <a href="http://urbanrhetoricblog.blogspot.com/2012/01/i-hate-childrens-music-but-are-my-kids.html" target="_blank">original article</a>.</i><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcHHrb4mxJKpgwfjFFb9qQ-X8s_GxwG9dcgWy9dxYnZxK8pf96c-r1zvUkg9TxkrzMvprLZddqZbC3JpnvPatbmGxz8EE1NjB1hclYhk5JPs9zcCpznOkA1mB0Vagx9ghRmk-X3DHzIPpR/s320/dj_lance.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" src="https://blogger.googleusercontent.com/img/b/R29vZ2xl/AVvXsEgcHHrb4mxJKpgwfjFFb9qQ-X8s_GxwG9dcgWy9dxYnZxK8pf96c-r1zvUkg9TxkrzMvprLZddqZbC3JpnvPatbmGxz8EE1NjB1hclYhk5JPs9zcCpznOkA1mB0Vagx9ghRmk-X3DHzIPpR/s320/dj_lance.png" /></a></div>
Children's music is universally atrocious. It is trite and tedious. An endless sea of monotony that crushes ones humanity with unrelenting waves of banal tedium. At first it laps gently at the shores of your sanity. A barely felt current, swathing your new baby in passivity and lulling him to sleep. Slowly it seeps into diaper changes and car rides, please touch museums and toy stores. It's suddenly everywhere and you are caught in the swell of its undertow. You gasp for Nas, Mos Def, Kanye, but it's too late, your lungs are already saturated with Three Blind Mice, Itsy Bitsy Spider and something called "Raffi". Children's music is devious. It's born of platitudes and gentle hell fire. It's boring and insidious. It is the incarnate soul of the devil.<br />
<br />
<a name='more'></a><br />
Perhaps I've tipped my toe into hyperbole but while some hazards of parenthood are often discussed: the lack of sleep, the constant worrying, the lack of free time - no one ever talks about the terrible and omnipresent music. Car rides, play time, diaper changes, bath time, museums, stores - all come with the regular and insistent jack hammering of fairy tales and nursery rhymes and dead eyed automatons and all manner of sonic daemons numbing your humanity and sapping your will to fight back. But not entirely.<br />
<br />
Any music lover thusly assaulted, will, after some time, come to rebel against this aural malaise and ultimately arrive at the same cure: Either find children's music by artists you can tolerate (in my case nearly impossible) or introduce your children to the music you love. I wanted my kids to know Hip Hop (and all the music I love) but, for me, there were two key drivers. On the one hand, Hip Hop is such a looming and significant part of my life that it is important to me to introduce my kids to the culture and the music that I immerse myself in daily (or at least I used to). On the other hand, I can't listen to "Wheels on the bus" another damn time. So I made up my mind, but that's where the trouble started.<br />
<br />
My first thought was: "Just turn on the radio, dummy! The radio has good music and it's censored. It's a win-win". Actually, it's a lose-lose as I was wrong on both accounts. Here are the top five most played songs on Hot 97 (the local Hip-Hop & R&B station) for the week of December 27th 2011:<br />
<ol>
<li><i>Dance (Ass)</i><b style="font-style: italic;"> </b>- Big Sean featuring Nicki Minaj</li>
<ul>
<li>This track must have the record for the most times "Ass" is said in a single song. </li>
</ul>
<ol>
</ol>
<li><i>Ni**as in Paris</i> - Jay-Z & Kanye West</li>
<ul>
<li>This song is awesome but it seems like they're actively trying to do the worst censoring job they can.</li>
</ul>
<ol>
</ol>
<li><i>She Will</i> - Lil Wayne featuring Drake</li>
<ul>
<li>There is nothing redeeming about this song.</li>
</ul>
<ol>
</ol>
<li><i>Work Out</i> - J. Cole</li>
<ul>
<li>Same old "shake that ass girl" garbage that reminded me why I don't listen to the radio any more.</li>
</ul>
<ol>
</ol>
<li><i>Make me proud </i> - Drake featuring Nicki Minaj</li>
<ul>
<li>This passes for positive. *sigh*</li>
</ul>
<ol>
</ol>
</ol>
<div>
One good song in the top 5? And none I want my kids to hear? Radio is not the move. For the record, the Pop and Rock stations are just as unacceptable. That said, I don't want to begrudge any adult from listening to whatever they want. Even on the radio. I don't even care if they play uncensored screaming on the radio. Just have a way to warn me and let me shield my kids from it. And you can make judgments about what's appropriate for your kids. That said, I still want to listen to *my* songs and while I want to expose my kids to the music that I love, I'm wary of them getting over-exposed. So it occurred to me that maybe I should think through exactly what my criteria are for songs I want my kids to hear. I came up with what I thought were two reasonable rules:</div>
<ol>
<li>No words I didn't want to hear repeated at Grandma's house</li>
<li>No phrases or concepts I didn't want to explain</li>
</ol>
<div>
At first blush these seem reasonable and should leave me with a huge selection. I think of myself as a *real* Hip Hop fan. I love the golden age, backpack, underground, modern classics etc. I hate "Bitches & Bling", "Da Club" and all the crap that goes with that (with some exceptions). Surely it should be easy to find some tracks that meet my criteria. In truth, it's possible but it requires some serious leg work. Here are some classics I thought would obviously work until I re-listened to them with an ear to the above rules. Note that my kids are 2.5 and 6 months so my criteria might ease up in a few years:<br />
<ul>
<li><i>Don't Curse</i> - Various Artists</li>
<ul>
<li>How could this possibly offend? Kool G Rap: "Even made a record on how I'm doing on my B-I-T-C-H-es"</li>
</ul>
<li><i>I can </i>- Nas</li>
<ul>
<li>Great message but lots of drug and other references</li>
</ul>
<li><i>Definition</i> - Black Star</li>
<ul>
<li>"Mos Def & Kweli just, make a pussy freeze up, thinking of it, ease up"</li>
</ul>
<li><i>They Reminisce Over You</i> - Pete Rock & C.L. Smooth</li>
<ul>
<li>"We laughed all night about the hookers at the party"</li>
</ul>
<li><i>Me, Myself & I</i> - De La Soul</li>
<ul>
<li>Cool message until problems are resolved with violence: "I'll calmly punch them in the 4th day of July"</li>
</ul>
<li><i>Jesus Walks</i> - Kanye West</li>
<ul>
<li>Hell no</li>
</ul>
<li><i>My Philosophy - </i>Boogie Down Productions</li>
<ul>
<li>Great until: "How many MCs must get dissed, before somebody says 'Don't fuck with Kris'"</li>
</ul>
<li><i>Woo-Ha!! I got you all in check</i> - Busta Rhymes</li>
<ul>
<li>The chorus is: "I got that head nod shit to make you break your neck"</li>
</ul>
<li><i>Cha Cha Cha</i> - MC Lyte</li>
<ul>
<li>"Well Well Well, I'll be damned"</li>
</ul>
<li><i>It Takes Two - </i>Rob Base & DJ Easy Rock</li>
<ul>
<li>"I like the whopper, fuck the big mac"</li>
</ul>
<li><i>Public Enemy #1</i> - Public Enemy</li>
<ul>
<li>"I'll show you my gun; my Uzi weighs a ton"</li>
</ul>
<li><i>Case of the P.T.A.</i> - Leaders of the New School</li>
<ul>
<li>"What the Hell's going on"</li>
</ul>
</ul>
<div>
Even a song called "Don't Curse" fails the criteria? Wow. In all honesty though, some of these are not really that bad. The censoring in "It Takes Two" is pretty blatant but I don't think that's really a deal breaker and "Case of the P.T.A." and "Cha Cha Cha" are really kind of a stretch. That kind of stuff I think I can probably let slide. What hurts more is songs like "I can" which I think have a good message but too much other content that I'm not ready for my kids to start grappling with. </div>
</div>
<div>
<br /></div>
<div>
I think what it comes down to is that I was hoping for an easy solution. Unfortunately, like everything else with kids, I needed to step up and be a parent first. It's not easy to vet a ton of songs from my library but if I want to listen to them with my kids then it's something I've got to do. Of course, once I put in that initial work it's not so bad. I ended up making a playlist of songs that I thought were appropriate. It started out small (mostly instrumentals) and when I found time I added a song here and a song there. At this point its well over 2000 songs of both children's music and songs I like. It fits on my phone, so I can bring the list on car rides or play it during dinner time and I don't have to worry about what might come up next or hearing the same awful tune over and over. Much like the children it's intended for, the process was a huge pain in the ass but ultimately I think it's worth it.</div>
<br />
<br /></div>Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com3tag:blogger.com,1999:blog-7790006377311932302.post-70865186445568469752012-01-11T11:00:00.000-05:002012-01-11T12:54:49.717-05:00Simple Photoshop Star Burst Effect<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh3.ggpht.com/-3_uS5OFzafs/Tvt_VUn6GLI/AAAAAAAAAPk/9axmPxq4EvQ/s1600-h/star%25255B2%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="star" border="0" height="312" src="http://lh5.ggpht.com/-8OI2ELx06Wk/Tvt_Vm77gII/AAAAAAAAAPs/vk6KkFhJ_FA/star_thumb.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="star" width="400" /></a></div>
<br />
One effect that I really like, especially for a splashy or retro type feel, is the star burst effect. I think the spinning color adds a lot of energy and can be a fun way to add a little pep. There are many tutorials on the <a href="https://www.google.com/search?sourceid=chrome&ie=UTF-8&q=photoshop+star+burst+effect" target="_blank">web</a> but they seem to either be <a href="http://www.youtube.com/watch?v=rJ9l9zDXTgE" target="_blank">complicated</a> or <a href="http://www.photoshopessentials.com/photo-effects/starburst-background/" target="_blank">time consuming</a>. That said, the two linked above do provide additional flexibility. The only advantage to this technique is that it is simple and fast. So let’s start:<br />
<br />
<br />
<a name='more'></a><span style="font-family: inherit;"><span style="font-size: large;">1. Create a new file</span></span><br />
Here’s an example:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh5.ggpht.com/-Mh1Df_qUzV8/Tvt_Vk23aZI/AAAAAAAAAP0/hQJYPr5xDwM/s1600-h/image%25255B3%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="245" src="http://lh5.ggpht.com/-6CJKCFTC65s/Tvt_V9x2eTI/AAAAAAAAAP8/nkmR5lGcBAU/image_thumb%25255B1%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="400" /></a></div>
<br />
<br />
<span style="font-size: large;">2. Fill the background layer with a color</span><br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh6.ggpht.com/-M-PmYdg77qo/Tvt_WPdCTwI/AAAAAAAAAQE/JSMK8MaXmgY/s1600-h/image%25255B27%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="303" src="http://lh5.ggpht.com/-lYAv7czC2TA/Tvt_Wcfg5MI/AAAAAAAAAQM/8FE9ANVZfOE/image_thumb%25255B9%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="400" /></a></div>
<br />
I chose this color:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh3.ggpht.com/-AnGCbRjXxqs/Tvt_WikhMXI/AAAAAAAAAQU/5F-6kKw0QQg/s1600-h/image%25255B30%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="270" src="http://lh5.ggpht.com/-M5AgLV240ug/Tvt_W_Y-TRI/AAAAAAAAAQc/MwF9yHOMNrY/image_thumb%25255B10%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="400" /></a></div>
<br />
<br />
<span style="font-size: large;">3. Get our magic shape</span><br />
<br />
3a. Create a new layer<br />
3b. Set the foreground color to White<br />
3c. Select the custom shape tool<br />
<a href="http://lh4.ggpht.com/-fgrXW91mBRQ/Tvt_XGCMrmI/AAAAAAAAAQk/O2GvfbiZDXM/s1600-h/image%25255B41%25255D.png" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img alt="image" border="0" height="400" src="http://lh4.ggpht.com/-c7MTvBGRqJ8/Tvt_XfzHaAI/AAAAAAAAAQs/PuEKei8bEbc/image_thumb%25255B19%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="150" /></a><br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
<br />
3d. Select these options:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh5.ggpht.com/-6USWgYEewKc/Tvt_XbHoQSI/AAAAAAAAAQ0/gLYVUUrouNk/s1600-h/image%25255B46%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="53" src="http://lh6.ggpht.com/-3yGEPPTLJG4/Tvt_Xky8C5I/AAAAAAAAAQ8/8HBNGO4fXDY/image_thumb%25255B26%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="420" /></a></div>
<br />
<br />
The key option is the shape. You want the “Symbols” shape list and the “Registration Target 2” shape.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh3.ggpht.com/-Wb22sHft6nY/Tvt_XzlYUkI/AAAAAAAAARE/SL_P3t7ZI6k/s1600-h/image%25255B51%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="337" src="http://lh5.ggpht.com/-MrXCB2jKPV4/Tvt_YAZFRQI/AAAAAAAAARM/NlI-N8Y4J3M/image_thumb%25255B32%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="400" /></a></div>
<br />
<br />
<span style="font-size: large;">4. Drag the shape over the layer so the Star Burst covers the whole image</span><br />
I hold “Shift” so it’s an evenly distributed burst.<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh3.ggpht.com/-eEAbUHs7Jiw/Tvt_YV0y1jI/AAAAAAAAARU/L40p2GU4RSc/s1600-h/image%25255B58%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="304" src="http://lh3.ggpht.com/-hR_9ZOMB9Lk/Tvt_YteNQMI/AAAAAAAAARc/7vYupK0Z4ok/image_thumb%25255B38%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="400" /></a></div>
<br />
<br />
<span style="font-size: large;">5. Set the layer Blending Mode to “Overlay” and the Opacity to taste</span><br />
I like ~75% Opacity in this case<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh4.ggpht.com/-qpBMbh35abw/Tvt_YxZVRJI/AAAAAAAAARk/tlz56R7kqy8/s1600-h/image%25255B59%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="304" src="http://lh3.ggpht.com/-W5cbU0u1fiQ/Tvt_ZCj7E7I/AAAAAAAAARs/rzc4pEUhAAg/image_thumb%25255B42%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="400" /></a></div>
<br />
<br />
<span style="font-size: large;">6. Slap an image over the center</span><br />
Like this one:<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://2.bp.blogspot.com/-mfGXeX0EO4g/TvuBUalO7-I/AAAAAAAAASc/kJkCeyISHak/s1600/star+alone.png" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="155" src="http://2.bp.blogspot.com/-mfGXeX0EO4g/TvuBUalO7-I/AAAAAAAAASc/kJkCeyISHak/s200/star+alone.png" width="200" /></a></div>
<span style="font-size: large;"><br /></span><br />
And you’re done!<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://lh5.ggpht.com/-MULNH-X1iRM/Tvt_ZdOcY1I/AAAAAAAAAR0/2l8XJ3GGs-4/s1600-h/image%25255B62%25255D.png" style="margin-left: 1em; margin-right: 1em;"><img alt="image" border="0" height="303" src="http://lh6.ggpht.com/-IlG-0MzIxTI/Tvt_ZqFkHbI/AAAAAAAAAR8/BjjNN5AgsBo/image_thumb%25255B43%25255D.png?imgmax=800" style="background-image: none; border-bottom-color: initial; border-bottom-style: initial; border-bottom-width: 0px; border-left-color: initial; border-left-style: initial; border-left-width: 0px; border-right-color: initial; border-right-style: initial; border-right-width: 0px; border-top-color: initial; border-top-style: initial; border-top-width: 0px; display: inline; margin-bottom: 0px; margin-left: 0px; margin-right: 0px; margin-top: 0px; padding-left: 0px; padding-right: 0px; padding-top: 0px;" title="image" width="400" /></a></div>Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-23579329954848248412011-12-28T10:54:00.000-05:002012-04-10T18:10:32.728-04:00Greatest Game Ever Still Pretty Great<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-Q3AyZ0woRQQ/T4SmtR8UbpI/AAAAAAAAAU4/mNPnvrr86Z4/s1600/meet_the_christmas.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="180" src="http://3.bp.blogspot.com/-Q3AyZ0woRQQ/T4SmtR8UbpI/AAAAAAAAAU4/mNPnvrr86Z4/s320/meet_the_christmas.jpg" width="320" /></a></div>
<br />
There are a number of criteria that reviewers use to mistakenly calculate the greatness of a game. By "game", of course, I mean Video Game since I am writing this after 1982. Some of these mistaken criteria are "Graphics Quality", "Story", or "Fun". All of these are <a href="http://www.youtube.com/watch?v=d3nzlJD2rC8">crocker</a>. There is only one criterion by which you can judge any game's true Greatness: hours played.<br />
<br />
<a name='more'></a>At first blush, "Hours played" seems awful as it doesn't take into account for the factors that reviewers routinely write about like "Maximum Resolution" and "Frames Per Second" and other similarly quantifiable values. And to make it even worse Reviewers always gussy up the statistics with wobbly gushy feelings like "playability" and "character exposition". But all of these are bunk because they muddle up the only thing that truly identifies greatness. How long are you willing to put up with it? You might have had a hot girlfriend or a sweet girlfriend but the one you're willing to spend your life with is by your own actions the one you think is greatest. Even if her graphics appeal and audio fidelity average lower than girl A or girl B, the one that's actually great is the one that you want to spend your time with, not the one with the cool effects that gets boring after a while.<br />
<br />
This is why the greatest game ever is <a href="http://www.teamfortress.com/">Team Fortress 2</a>. I've logged over <a href="http://steamcommunity.com/profiles/76561197963683883/stats/TF2/?tab=stats">500 hours</a> in this bad boy and it's still bringing me joy. Valve has made a huge effort to continually add additional content (<a href="http://www.teamfortress.com/119/">for free</a>) and this has gone a long way toward keeping the community attuned and vibrant. They've even recently started adding content from the community into the game and this has reinvigorated the cycle with more to do and more people involved who make more to do and so on and so forth.<br />
<br />
<div class="separator" style="clear: both; text-align: center;">
<a href="http://www.teamfortress.com/screamfortress/images/bg_top.jpg" imageanchor="1" style="clear: left; float: left; margin-bottom: 1em; margin-right: 1em;"><img border="0" height="320" src="http://www.teamfortress.com/screamfortress/images/bg_top.jpg" width="265" /></a></div>
<br />
<br />
One of my favorite annual updates is <a href="http://www.teamfortress.com/screamfortress/">Scream Fortress</a>, which is a limited time Halloween themed update with events and achievements intended to embrace the holiday. It's silly and gratuitous but every time I think I'm out, they give me a new reason to stick around and try out the new stuff and revisit my old TF2 friends. Congratulations Team Fortress you're still the Greatest Game Ever.Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com0tag:blogger.com,1999:blog-7790006377311932302.post-40099504239398303392011-12-21T11:00:00.000-05:002012-04-10T17:29:28.485-04:00Resurfacing: It's not just for parking lots and Joan Rivers' face<div class="separator" style="clear: both; text-align: center;">
</div>
<div class="separator" style="clear: both; text-align: center;">
<a href="http://3.bp.blogspot.com/-gn9GMzWQoB4/T4SmKnUb74I/AAAAAAAAAUw/aoQ3_ZmeEBU/s1600/joan-rivers.jpg" imageanchor="1" style="margin-left: 1em; margin-right: 1em;"><img border="0" height="297" src="http://3.bp.blogspot.com/-gn9GMzWQoB4/T4SmKnUb74I/AAAAAAAAAUw/aoQ3_ZmeEBU/s320/joan-rivers.jpg" width="320" /></a></div>
<span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: 13px; line-height: 18px;"><br /></span><br />
<span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: 13px; line-height: 18px;">Many years ago in a land <a href="https://home.fawnanddoug.com/mambo/" target="_blank">far away</a> I had a super long commute and a pressing need for the constant adulation that can only be provided by strangers commenting on my thoughts. So, I started a blog. As it turns out, the pressing need persisted but the strangers' comments did not. Actually, the strangers' comments never materialized (unless you count <a href="https://home.fawnanddoug.com/mambo/index.php?set_albumName=garden&id=P1010014&option=com_gallery&Itemid=&include=view_photo.php" target="_blank">comment spam</a>) and as you might expect the flow of unread posts dried up while the ads for fake Canadian Cialis kept popping up. </span><br />
<span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: 13px; line-height: 18px;"></span><br />
<a name='more'></a><br />
<span class="Apple-style-span" style="color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: small;"><span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; font-size: 13px; line-height: 18px;">Nevertheless, a pressing need will need pressing and so I'm back again to blog at the internetz until you respond in the way that sates my hunger for attention. This time however, I'm armed with a better spam filter and some significantly more modest goals. Specifically, I want to try out </span></span><span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: 13px; line-height: 18px;">different writing styles like <a href="http://www.avclub.com/">those</a> <a href="http://www.lifehacker.com/">in</a> <a href="http://kotaku.com/">the</a> <a href="http://gizmodo.com/">blogs</a> <a href="http://www.pcgamer.com/">I</a> <a href="http://www.engadget.com/">read</a> and more importantly I'm not going to </span><span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: 13px; line-height: 18px;">commit to posting a lot (and thus fail and stop posting at all). The blog is resurfacing, let's see how it goes. And by "let's" I probably mean "I'll". And by "see how it goes" I mean "delete a bunch of spam and then give up".</span><br />
<span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: 13px; line-height: 18px;"><br />
</span><br />
<span class="Apple-style-span" style="color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: small;"><span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; font-size: 13px; line-height: 18px;">For posterity's sake, here's the original post from the original blog:</span></span><br />
<span class="Apple-style-span" style="color: #333333; font-family: 'trebuchet ms', verdana, arial, sans-serif; font-size: small;"><span class="Apple-style-span" style="-webkit-border-horizontal-spacing: 2px; -webkit-border-vertical-spacing: 2px; border-collapse: collapse; font-size: 13px; line-height: 18px;"><br />
</span></span><br />
<blockquote>
Hey all! Well, I got a Treo from the 'rents for my bday - and what with its keyboard and my two hour daily commute it seems inevitable that <a href="http://fawnanddoug.com/"><span class="Apple-style-span" style="color: black;">fawnAndDoug.com</span></a> will soon be overrun with trite (and yet verbose) missives on my various musings, opinions and incindiary votives. So let's begin. For starters the keyboard on this badboy is kinda tight so my usual explitive laced comments may be slightly censored since my thumbs aren't nearly as quick with the curses as my ten fingered assault. Damn! I dont really have much else to say today - so I'll just suggest you check out sahara hotnights (specifically a jam called kicks) and I'm out! Wow - we're already at my stop - I guess i'll continue offending you tomorrow . </blockquote>Anonymoushttp://www.blogger.com/profile/15855570654788531731noreply@blogger.com3