BibleGateway.com Verse Of The Day

Thursday, November 29, 2007

Opening JAR/WAR/EAR With Windows XP 'Compressed Folders'

Why do I keep doing Google searches to find this site, when I can just post a link here? I'm an idiot.

More importantly, why do I really keep needing to look this up? I'm an idiot.

Wednesday, November 28, 2007

Splunkin' Like A Madman And Log Management

Splunkin'

After playing with Splunk over a year ago (and liking what I saw) , we finally got the green light to go forward with an implementation at work. Splunk is a log aggregation and search tool. Basically it's a really fancy way of grep'ing through log files without logging in to each server and grep'ing through logs. One search will search logs across all the servers.

Right now I am in the process of estimating our peak daily volume, which more of less means I am ssh'ing in to each app server and Apache server and looking at the file sizes for the various logfiles. The licensing for Splunk is tiered based on peak daily volume, with a free version that will index up to 500MB a day. Their pricing model seems like a real bargain when you compare it to other enterprise software vendors out there, especially when you see the license fee is a "perpetual license" -- you pay once and it is good forever. If your logging volume increases, you can upgrade the license without screwing around with the software installation, and the cost is prorated as the difference between the old and new.

Everyone I have dealt with over at Splunk has been really cool to work with so far as well. Not a bunch of pushy sales-droid types, and they seem more than willing to work with you for unique configurations.

This should make it a lot easier to find and pinpoint issues across our 3 Jboss clusters and the handful of stand-alone Jboss and Tomcat servers and multiple Apache servers.

Apache Log File Management

And speaking of logs, we had an issue where the mod_jk.log file hit the 2 gig limit (32 bit Linux) on one of our Apache servers the other day. That was fun to track down, since some stuff seemed to work and other stuff did not. Our web applications were working fine, but web services calls into our boxes were failing. According to our Jboss logs, we were processing the WS calls and returning valid responses, but the clients were getting errors about the connection being closed while reading the response, or null objects being returned, depending on which platform they were connecting from.

So now I am working on getting logrotate setup for our Apache installations. While I am in this mode, I think I'll whip up some Nagios checks to alert if the filesizes grow to say 1.5G (which shouldn't happen with daily log rotation and compression).

Setting it up is pretty easy. I edited the /etc/logrotate/httpd file to add the following:

/app/j2http/apache-2.0.55/logs/*log {
daily
rotate 10
copytruncate
notifempty
compress
}

Tuesday, November 27, 2007

Chicken Farming Comes To An End

Our first experiment in raising chickens for food ended this past weekend. We had 5 Rhode Island Red hens left, and I really didn't want to take care of them through our harsh upstate winter. Since I was already spending my weekend butchering and packing my deer, making venison jerky, and grinding venison into burger, I figured I would go ahead and take care of the last 5. So now, with 5 chickens in the freezer and none in the coop, our first year as chicken farmers has officially ended.

We learned a lot and had some fun raising chickens, and the neighbors didn't seem to mind too much. The meat and extra eggs were nice as well. This is definitely something we will do again in the spring, but there are a few things I will do differently.

  • Make the chicken run around the coop bigger. It was originally supposed to be bigger but I ran out of time and ran into big rocks on the one corner. So I let them free range a lot, which wasn't terrible, but led to some minor annoyances here and there.
  • Make a nest box. I meant to do this but again ran out of time. Then I read about chickens not laying until they are 6-8 months old, so figured they would all be gone by then anyways. Then my neighbor Dave started finding eggs in his flower garden, and we started finding eggs in our barn. If we start them with a nest box and minimize the free ranging, we should have to go on Easter egg hunts all the time.
  • Make the chicken run higher or put a top on it. Even when I closed the chickens in, they literally flew the coop and got out anyways.
  • Get some bigger meat chickens. The Buff Orpington's and Rhode Island Reds we got are known as dual purpose birds, good eating size plus good layers. It seemed to take forever for these birds to reach a good eating size, so next time I will get some Cornish X Rocks or other big meaty type birds as well as the Buff's and Red's.
  • Get an open watering trough. The watering jug I got is a pain, since you have to take it apart and turn it over to fill, then put it together and turn it back over. It would be so much easier (and drier) to just pour water into a trough instead of messing with that thing.

Monday, October 01, 2007

Let's Rock

All right, my wife's at work, my kids are in bed, Lexi is sleeping. I've got Dead Poetic on my iPod, XAMPP on my thumb drive, and a brilliant new MySQL database schema created for my new bastard stepson of a side project. Time to generate some scaffolding! For this top secret project, I will write the back-end admin stuff using Ruby on Rails. For the front end, I haven't quite decided yet. I'm just not sure how RoR scales up for an external facing website, and for what I am doing it might be easier to use Java anyways, as I can write a few JSP tags to use on any of the pages, and use Velocity as the templating engine to build the special configuration page.

Then again, I just got paged for a work issue, and that trumps side projects. DAMMIT!

Any Given Sunday

The Bills pulled out their first win of the season against the Jets, which was the first clue that something was "wrong".

Over half of my Last Man Standing pool (myself included) picked San Diego since they were so highly favored to win, and THEY LOST!

On top of that, my 3-0 Steelers went 3-1, against the freaking CARDINALS!

Must be a bad dream, must be a bad dream, must be a bad dream!

Don't Bother Me

The national "do not call" registry expires every 5 years, so first wave is about up. To re-register.... https://www.donotcall.gov/Register/Reg.aspx

Thursday, September 27, 2007

Rhythm's Gonna Get You

Last night was Raina's big 4th birthday, so we went to Chuck E. Cheese. On the way home, our Sirius radio was, of course, tuned to "Kid's Stuff", and annoying little Dora The Explorer was singing some cheesy 80's song, Rhythm's Gonna Get You. The song was bad enough the first time around, did we really need Dora to "sing" it for us?

Anyway, that's not even the point. Half way through the song, Raina asked us if Rhythm is a bad guy? Took us a few seconds to realize what she was getting at. In the Disney movies they've seen, the bad guys "get you", so Rhythm must be a bad guy since it is trying to "get you". It was funny, and also kind of scary. As adults, we see or hear things but don't notice anything odd, but the kids are processing it and picking it apart, not missing a single thing.

Word, Character, Line Counter Ruby Script

I've been on a few projects lately that involve loading data files that I get from other teams. When I get a file, it's nice to see how big it is to get an idea of how long it will take. Opening the file in a text editor and going to end of file to see the row number can be a pain, especially for big files. Also, I'm on a Windows desktop, so tools like "wc" aren't built-in, and launching Cygwin and getting to the right directory can be a pain in itself (cd /cygdrive/c/Documents\ and\ Settings/ran488/Desktop), so I whipped up this quick Ruby script to give me all the file info I want.

Here's the Ruby script (fileinfo.rb) to get the count of lines, characters, and words in a text file. It also spits out the min and max line size as well. The source can be downloaded at http://www.frontiernet.net/~nicholson150/fileinfo.rb.


#!/usr/bin/env ruby

if __FILE__ == $0
if ARGV.length < 1 then
puts "Usage: #{$0} filename"
exit
end

results = []
words = 0
chars = 0
minline = 0
maxline = 0

filename = ARGV.first
File.new(filename, "r").each { |line| results << line }

puts "#{filename} has..."
puts " -> #{results.size} lines."


results.each do |line|
chars += line.length
words += line.split.length

if line.length > maxline then
maxline = line.length
elsif line.length < minline then
minline = line.length
end

end
puts " -> #{words} words."
puts " -> #{chars} characters."
puts " -> #{minline} character shortest line length."
puts " -> #{maxline} characters longest line length."

end


Here's an example of the output....





C:\cvs\MqThrottler>fileinfo.rb PA_CSS_Test_Load_0911.txt
PA_CSS_Test_Load_0911.txt has...
-> 10008 lines.
-> 58021 words.
-> 4356721 characters.
-> 0 character shortest line length.
-> 489 characters longest line length.

Monday, September 17, 2007

Lexi


Here's an updated picture of Lexi the Wheaten Terrier. She is about 7 months old here, mostly white and wheat color with just hte black face remaining.

Thursday, September 06, 2007

PDFCreator

Apps like OpenOffice.org have built-in PDF export functions, and Mac OS X has a built in PDF export in the print dialog.

But if you're running apps on Windows that don't have this feature, you can download and install PDF Creator. It installs itself as a "printer" on your PC. When you choose to print from any application, choose this printer instead of your normal printer.

Bulk File Renamer in Ruby - file_rename.rb

My updated bulk file renamer script is listed here. (http://www.frontiernet.net/~nicholson150/file_rename.html) along with the Ruby source code.

I first started messing with this concept in this post, but have refined it a bit since.

Along with the Ruby compiler I mentioned yesterday, this could be compiled and dropped in your PATH somewhere (e.g. Windows dir), and then you could run it anywhere as if it were a native Windows/DOS command. I may end up doing that on my home PC to help rename the imported digital photos-- "DCP_05483.JPG" just isn't as descriptive as "Jeep Camping Trip 2007 - 012.jpg"

Tuesday, September 04, 2007

RubyScript2Exe - The Ruby Compiler

I've been playing with RubyScript2Exe (http://www.erikveenstra.nl/rubyscript2exe/index.html) lately, and it's pretty sweet. It's a Ruby "compiler" to generate executables from your Ruby scripts. Since it embeds the Ruby runtime and any libraries in the executable, even simple scripts can "bloat" up to bigger sizes than you might expect, but if distribution is your thing, so what?

Maybe you want to share some of your cool scripts and development tools you've created, but can't count on your intended audience having Ruby installed (what's wrong with them, anyways?), or maybe it would take too long to explain what Ruby is and how to run the scripts. Or maybe you just don't want people screwing with your perfect source code. Or perhaps, you are ashamed of your trashy, hack-n-whack coding?

Whatever the reason, this thing is the nuts. Just download a single rubyscript2exe.rb script and run it against your app. I've only tried it on single files on Windows so far, but it is supposed to create Linux executables as well, and will allegedly work on more complex, multi-*.rb-file apps.

Tuesday, August 28, 2007

Crazy Women Drivers

We went to a picnic a few weeks back, and my wife got to "drive" a horse-drawn cart. Here she is with our friend Anita driving the horses.....
Yee-haw!

Wednesday, August 08, 2007

A Few Notes on my Load Tester Ruby Script

A few days ago I posted a Ruby script I use to simulate multiple users hitting a web application. I have a few notes regarding that script:
  1. There is a minor bug in it, that doesn't affect functionality so much as the "logging". I used a global var like a dummy and then reference that in the threads.
  2. I also added a 3rd command line param to tell the script how many times each thread should run through the list of URL's.
  3. I also added a sleep() feature so each thread will pause between each run through the list of URL's. Right now that is just a global var instead of a command line param.
I will have to repost the source to get these changes up here. (Actually the *.rb source file is up, I just haven't regenerated the HTML yet.)

On a related note, I wanted to do a better simulation of the types of requests coming through to our prod box. My clever idea, take the actual requests out of Apache's access_log or (in my case) ssl_request_log.

So how to easily grab all those requests to build my file? The format of my ssl_request_log looks a bit like this (some stuff renamed to protect the innocent):

[08/Aug/2007:14:42:23 -0400] 192.237.32.76 SSLv3 RC4-MD5 "GET /appcontext/en/US/CZN2/css/main.css HTTP/1.1" -

Well I just want the URI starting after the GET, so from the Linux/Unix/Cygwin command line do something like this....
$ cat ssl_request_log | cut -d' ' -f 7 2>&1 | tee URIs.txt
Then in vi, I can just put the "http://myserver" portion of the URL at the beginning of each line with a global replace, and I'll have a file with every request, including those for images, stylesheet *.css files, javascript *.js files, etc.

This is much easier than sitting here trying to piece together URL's by hand.

Monday, August 06, 2007

A Great Interview Question

I had to do a few interviews last week, and some of my standard questions were getting a bit stale. So I went out on Google and searched for some new ammo.

I came across this question: "What is the difference between final, finally, and finalize()?"

At first I thought that was too easy, mostly because I knew the answer, and it seemed fairly obvious. Even so, I broke it out in an interview, and the guy nailed the first two, missed the last one.

I was relating that tale to a coworker afterward, and she said, "what was the question he missed?" She nailed the first two, didn't know the last. She's a good programmer; I've worked side by side with her every day for the past few years, and she missed it. That's when I came to the conclusion this is a great question.

Even good programmers don't know everything, and not many Java programmers really have to get into the bowels of memory allocations and garbage collection. This is a great question, not because I want the guy who can answer all 3, but because I want to see how people handle themselves when they don't know the answer. Sometimes that will tell you more about the person than if they just know all the answers.

The first guy I asked this question got the first two 100% correct, then he flat out said he didn't know what finalize was, hadn't run across it, and then he wrote it down in his notebook and said he would have to look it up.

The second guy I asked, he missed it, but didn't seem to know he missed it. For finally, he stammered about a bit and mentioned it was for garbage collection, then switched gears a little and said you usually use it with a try/catch block. Then for finalize, he said he didn't know what it was.

So both missed it, but was one answer better than the other?

In my mind, first guy won. He admitted what he didn't know and showed some level of ambition to look it up. The second guy tried to dazzle me with a little BS for a minute before admitting he didn't know. He didn't write anything down or mention looking it up, he just racked it up as a missed question.

They both went on to answer other technical questions to my satisfaction, but I came away with a slightly difference perspective of each. If they both just nailed every tech question I threw at them, I wouldn't have that added bit of perspective. And how do you know it isn't just "book smarts" -- a lot of those guys that rattle off every new buzzword and argue about architectures and design patterns can't write code to save their lives.


Other Interview Follies:

A lot of funny things do happen when interviewing. Everything I have ever learned about interviewing seemed like common sense, until I met some of these folks:
  • One of my manager's standard questions: "How would you structure your ideal work day?" (e.g. 50% development, 20% support, etc.) One guy told us he would sleep in, get to work late, of course he would stay late also, but his brain just doesn't work in the morning unless he has 10 cups of coffee, so he would rather sleep. Honesty is NOT always the best policy.
  • One lady told us she mostly does front-end work. When I asked her what she was using to build the interfaces (Swing, JSP, JSF, etc.), she told me she mostly does back-end server work. Nice 180, thanks for coming in today!
  • Another guy just got done telling us about all these web applications he wrote, JSP's, servlets, etc. I asked if he was using STRUTS, and he told me he wasn't sure, he might be using it. Then when I asked what application server he was using, he said UNIX and Windows. I tried to clarify, asking him which J2EE server he was using, he looked confused; so I changed my terminology to "servlet and JSP container", he still looked confused; so I made it multiple choice: "Are you using Websphere, Weblogic, Tomcat, Jboss?" His final answer: "Tomcat sounds familiar."

Wednesday, August 01, 2007

HTTP Load Tester Script in Ruby

Here it is, after running my newly acquired Ruby code to HTML converter script, I am finally able to post decent looking Ruby code. Just not directly on Blogger (yet).

Here's the link to my HTTP "Load Runner" script I have been using to debug certain issues (memory leaks, database connection leaks, etc) with a certain un-named inherited application. It's written in Ruby.

For the more hard-core stuff I use JMeter, but this served two purposes:
1. learning more about Ruby
2. having a quick and simple script for small stuff instead of the heavyweight JMeter.


HTML: http://www.frontiernet.net/~nicholson150/http_load_tester.html
Ruby Source: http://www.frontiernet.net/~nicholson150/http_load_tester.rb

Making Code Pretty For The Web

Any time I post code to this stupid blog, I always run into the same issue. How to make it look like readable code? Seems that just putting code inside <PRE> tags doesn't seem to work -- something about the Google / Blogger engine mangles the crap out of the end if it gets "too long".

For Ruby at least, I just found this blog entry that helps out immensely. It's missing a step, as it assumes you have Rio already, but it was easy enough to figure out.
> gem install rio
> gem install syntax
Then save that Ruby script off locally and run it against your script file. Include the CSS or just add it to the HTML inside <STYLE> tags and you're golden.

Of course, the braindead Blogger engine doesn't seem to be picking up the stylesheet, so the code doesn't appear as pretty as it should -- no syntax coloring or anything. I'll have ot figure that out before I post my HTTP load runner script.

Tuesday, July 31, 2007

Using JConsole To Connect To Remote JVM

We have been having issues with a certain Java web application we inherited. Actually, we've had a few problems with it, but that's another story.

One issue was that, even though there is nothing running on this Linux box except a single instance of Tomcat (5.5) with this single web application deployed to it, we will get into frequent issues where we get paged for "high CPU utilization". Log in to the machine, and the Java process is taking 100% CPU. It might stay like that for a few hours, or even a few days. Occasionally it clears up on it's own, but usually the server stops responding and we have to restart Tomcat.

We have had another issue where we would get Out of Memory exceptions (Heap space), and the JVM would stop running. The two issues weren't necessarily related, but we couldn't rule out the possibility either.

We don't have much visibility into the application and what's going on since it is a vendor built "black box". We have some customized source code, but most of it is off limits to us. They rolled their own database connection pool, MVC framework, persistence framework, etc.

In comes Jconsole. JConsole is an awesome tool that comes bundled with JDK 1.5 and above. It connects to the JVM and gives you all the info you could want on the various JVM memory pools, garbage collection, threads, classloading, and lets you manage anything exposed via JMX. It also has lots of pretty graphs, such as memory usage and garbage collections over time, for any or all of the memory pools (heap, non-heap, or individual pools, like permgen and eden space). Same goes for threads and loaded classes --current number, peak, total created.

The best thing about JConsole is the ability to connect to remote JVM's so you don't add too much overhead on the box being monitored. I have JConsole hooked up to my test and production servers, and it helped me prove that the two issues above were connected. There is some condition in the application (yet to be found, actually, first step was proof of what's really happening) that causes a substantial memory leak, and once the memory usage gets at it's ceiling, the garbage collection thread basically runs constantly, trying to regain some trivial amount of memory, then filling it up, and running GC again.

With an average of 100 active sessions at a time plus full garbage collection running non-stop, the CPU gets consumed quickly. I monitored the app for about a week and a half, and memory and CPU looked great -- there were a bit over 200 full GC's in that time period. Then this past weekend, we had to restart because of a DNS issue. That was Friday evening, and by Monday morning there were over 2000 full GC's performed, and I was restarting a non-responsive server by lunch time.

Below are two screenshots, one is of several days of "normal" memory usage, notice gradual rise and then sharp decrease at full GC, all the while keeping well below the JVM's allotted memory ceiling. Second is this past weekend's issue, where memory is hovering at ceiling and a full GC doesn't do much. Also notice the old generation memory pool is quite full.




MAKING IT HAPPEN

To set up JConsole to run on a local JVM, you only need ot pass one extra argument to the JVM:
-Dcom.sun.management.jmxremote
To set up remote (with no security), it is a matter of adding a few more parameters to the remote JVM at startup:
-Dcom.sun.management.jmxremote.port=8004 -Dcom.sun.management.jmxremote.authenticate=false -Dcom.sun.management.jmxremote.ssl=false
Then when you start up JConsole, go to the remote tab, enter the server name and the port that you specified (in this case, 8004).

Simple as that. It starts collecting stats immediately and the graphs appear. As you explore the JMX tree, you will notice yo ucan click on some of the stats and the simple integer displays "opens up" into a full graph display. I'm doing this to watch the active sessions patterns through the day and week.

Monday, July 09, 2007

I Feel So Naughty

Most of my development experience has been with structured languages such as C and Java.

Given that, I feel almost naughty working with Ruby's loose typing. It's like I am getting away with something, doing something oh so wrong. For some reason it never seemed odd with shell scripts, but does here. It's both liberating and a little scary at the same time.

No compiler errors, no warnings, no runtime exceptions, no type mismatches, nothing. You can make x = 3 then turn around and make x = "dude", and then set x = FileRenamer.new with out any issues.

Sunday, July 08, 2007

And The Tree Came Crashing Down

We went to church this morning, and then off to pick up the kids. When we got home, everything was wet, so we knew it had rained. Then for some reason, I felt the need to go over and check on the chickens. What I saw amazed me. 3 big branches of the maple tree had come crashing down on the coop and the fenced in run. I went back to check it out, and the chicken run was completely full of branches and leaves -- I couldn't see the chickens, but I could hear them clucking and running around through the leaves. The back part of the fence was completely down to the ground.

Luckily, no damage was done to the roof of the coop, no chickens were hurt, and once I cleared out all the branches, I was able to repair the fence with minimal effort. Now it's mostly a cleanup effort.

Another Picture of Lexi


Here's an updated picture of Lexi the Wheaten Terrier. She's growing up and getting a lot lighter now. You can see a lot more of the white hair around her face and her belly is almost completely white now.

Friday, July 06, 2007

Black Raspberry Time

It's that time of year again. The black raspberries are starting to ripen in the back yard and in the woods behind the house. The berries seem to be quite a bit smaller than normal, and a little less abundant than normal, probably because we've had a lack of rain so far this summer. But the berries that are out there are quite yummy. We picked a small bowl on the 4th, and I picked a handful today after work. In the morning, I plan on taking my big bowl out again for round two. Normally I can fill a large mixing bowl in one pass, and this time I just got manybe 1/5 of a bowl the first time through.

Tuesday, July 03, 2007

Office Art, Part 1

I drew this on the back of one of my project folders and snapped the picture with my ViewSonic v36 Pocket PC. This is what happens when I get bored in meetings.

Tuesday, June 26, 2007

And 20 Became 19.

fresh chicken and ribs on the barbieUnce upon a time, five Buff Orpington roosters lived among others in a backyard flock, somewhere in upstate New York. A lone Speckled Sussex was even more the outsider, for the flock was mostly made up of Rhode Island Reds.

One sunny day in the summer of 2007, the largest rooster of the flock, a Rhode Island Red who goes by no name, was set aside to become dinner. By "set aside", what I really mean is he was placed into the fenced in backyard, separated from the other chickens and their food, until the time came. But this fenced in yard also happens to be inhabited part-time by a Soft Coated Wheaten Terrier named Lexi.

And so it was, Lexi was let outside and chased the RIR rooster before I could grab either one of them. The rooster ran into some high weeds, the remnants of what used to be a nice flower garden. Lexi ran in after, and came out seconds later, but the rooster did not appear. The chicken must have found escape from the fenced yard, for nobody could find the rooster after much searching.

So the next in line was desperately chosen, as dinner time was drawing nearer with no poultry ready to grill. It was a tough choice, but finally one Buff Orpington rooster stood out as the second largest, and soon succumbed to the meat cleaver. One chop and that's all she wrote.

That evening, dinner plates were stacked high of barbequed pork ribs and a fresh chicken, right from the back yard. Wegman's Memphis Style Barbeque sauce and Frank's Red Hot sauce complimented the smoked meats well.

Soon thereafter, the orginal RIR rooster showed himself. A master of camoflage he proved himself to be, as nobody could find him a few hours earlier, but there he was, right where he had last been spied. He managed to avoid his fate on that June day. But alas, he shall become dinner on another eve, when the 20 will become 18.

Thursday, June 21, 2007

NYS Child Car Seat Inspection

If you're a New Yorker, this is a good deal. It's one of those rare instances where you feel like you're actually getting something back for all the insane taxes you pay. You can go to these free child car seat inspections where officers will inspect your car seats to see if they meet current regulations, are installed properly, and to see that your child fits properly.

If you child has outgrown the seat, or if the seat fails to meet current regulations for any reason, they hand you a new replacement seat. Yes, a FREE replacement seat. They also let you know about the various height and weight guidelines for the different types of seats so you know better what you should be doing. We went to one last month and ended up getting 2 new seats because Raina had just outgrown the seat she was in, and Taryn was very close to outgrowing hers.

Another bonus was free popcorn, hotdogs, and pop. Can't go wrong there even if your seats are up to code.

Check out http://www.nysgtsc.state.ny.us/seat-cal.htm to see when the inspections will be held near you.

Robb's Salsa Recipe

I made a batch of salsa last night, and figured I would share my "recipe", as if there's anything to making salsa -- put crap in food processor and chop the hell out of it.

This is how I did it last night, but it usually varies a bit depending on what's available at Wegmans.

Ingredients
  • 2-3 lbs. "Tomatoes on the Vine"
  • 1-2 Vidalia onions, depending on size
  • 2 -3 Jalapeno peppers
  • 1 big Poblano pepper
  • 1 tbsp. Fresh of chopped Garlic
  • 1 bunch of fresh Cilantro
  • 2 nectarines or peaches
Directions
  • Heat cast iron skillet on high, get it really hot. Add a little dab of oil.
  • Cut poblano pepper and onion into quarters and place in skillet to sear outside. You don't want to completely cook the onions and peppers, just sear them on the outside to bring out a nice roasted smoky flavor and soften them up just a tad.
  • Place everything into a food processor and chop the crap out of it. May need to do multiple batches in the chopper depending on its size. Chop to a consistency you like, some like it chunky, some smooth, and everything in between.
  • Pour into a sealable bowl and store in refrigerator. Let the flavors meld together for awhile before enjoying for best results.
  • If you choose to share, DON'T DOUBLE DIP! That's nasty.
Variations
  • The peaches sound odd or even downright gross if you've never tried it, but they add a certain sweetness without being overbearing. BTW, peaches and nectarines are pretty much the same thing, except peaches have fuzz and nectarines don't. I prefer the non-fuzzy nectarine.
  • Roasting the vegetables on the grill would probably be even tastier than the cast iron skillet method.
  • Add corn and black beans, and maybe even a bit of cumin for a bit of southwestern flair
  • To turn up the heat, add more Jalapenos, Habaneros, or some of your favorite hot sauce. I have young children now, so I try to not get carried away with heat.
  • Of course, green peppers are popular in salsa. I find them a bit overbearing though when raw.

Tuesday, June 19, 2007

Monkey Butt

Sometimes I like to enter random word combos into Google to see what comes up. Tonight I entered "monkey butt" and found an interesting product. The funniest part is, it's a real product.

Thursday, June 14, 2007

Getting Thread Dump Of Running Java Process

Of course you can kill -3 a process to get the thread dump, but for Java apps you can also get thread dumps of running processes using the JDK 5 tools jps and jstack. For example, on a server where I am running Tomcat 5.5, typing jps gets a list of all Java processes running:
-bash-3.00$ jps
25515 Bootstrap
26912 Jps
Then using that process id, run jstack:

jstack 25515

Tuesday, June 12, 2007

My First Vaguely Useful Ruby Script

Alright, anyone with directories full of digital photos, mp3's, or other docs might appreciate an easy way to rename all the files in a directory, either by appending a common prefix, postfix (or extension), or giving each file the same basename with a sequence appending to the filename. There have been times I would have found that useful, and it turns out it's very easy to do with some simple Ruby scripting....


file_renamer.rb


#!/usr/bin/env ruby

require 'fileutils'
require 'find'

class FileRenamer

attr_accessor :directory

# constructor
def initialize(directory=".")
@directory = directory
FileUtils.cd(@directory, :verbose => true)
end

#add prefix to each file in dir, ignoring directories
def add_prefix(prefix)
Dir.foreach(@directory) {|x| rename_file(x,"#{prefix}#{x}") }
end

#add postfix to end of each file in dir, ignoring directories
def add_postfix(postfix)
Dir.foreach(@directory) {|x| rename_file(x,"#{x}#{postfix}") }
end

#rename a file unless it is a directory
def rename_file(from, to)
print "#{from}"
if FileTest.directory?(from)
puts "/ remains unchanged"
else
FileUtils.mv(from, to, :verbose => false)
puts " changed to #{to}"
end #if
end

# rename all files in directory to base_name + a sequence
def rename_with_base(base_name)
idx = 0
Dir.foreach(@directory) do |x|
if !FileTest.directory?(x)
rename_file(x,"#{base_name}#{idx}")
idx += 1
end
end
end

def list_dir
Dir.foreach(@directory) do |x|
print "#{x}"
if FileTest.directory?(x)
puts "/"
elsif FileTest.executable?(x)
puts "*"
elsif FileTest.symlink?(x)
puts "->"
end #if
end #do
end
end

# test it...
fn = FileRenamer.new("C:/test")
fn.list_dir
fn.add_prefix("test")
fn.rename_with_base("start")
fn.add_postfix(".txt")

Monday, June 04, 2007

Set The Captives Free!

My original plan was to let my chickens free-range during the days so they could eat weeds and bugs off my property. I've read that free-ranging chickens will find enough food that you really don't even need to feed them in the summer months. I'm not ready to test that one yet, but I did try a little free ranging this weekend.

On Saturday, I opened up the gate while I went in to get them fresh water, and by the time I got back with the water, they were all out in the yard scratching and pecking and making happy little chicken noises. I let that go on for a few hours while I played outside with the girls.

On Sunday I left them out for a few hours again. Actually I left them out twice Sunday, for a few hours the first time and just about 20 minutes the second time. The second time was a fluke. They used to never try to leave even with the gate open. But now they know what it's like out there, and they leave whenever they can.

I liked seeing the chickens roam about on their own. They mostly stayed right near the coop, foraging in the high weeds for bugs and making the occasional foray into the garden. I still only do it when I am outside with them, though, because some of our neighbors don't feel the need to control their dogs, and just let them roam about in other people's yards.

Lexi got out of the yard at one point Sunday and had a good chase with the chickens. She was within about 4 inches of a Buff Orpington dinner at one point right before I caught her. If a small puppy can be that fast and aggressive toward the chickens, I hate to think what would happen if full grown dogs came into our yard with the chickens out. So I still leave them "ccoped" up when I'm not right there watching and still keep them full of 20% grower/starter and cracked corn.

Wednesday, May 30, 2007

Ruby On Rails On Oracle Hurdles, Part 2

I've hit my first bumps in the road to hooking up RoR to an existing Oracle database schema. Like I posted yesterday, designing an RoR app from the ground up would be easier because you can build your database schema to RoR's expectations and limitations. And now I explain in more detail why...

Other than getting connected to an Oracle database (officially "Hurdle #1"), which I discussed yesterday, I've hit a few more snags.

Hurdle #2: Table naming. I'm trying to hook up to an in-house authentication/authorization database that is used by several of our application platforms. For RoR's "coding by convention" to work properly, you would ideally have plural table names, e.g. reps, rep_roles. Then when you generate your scaffolding, you would use singular names for the controller and view, e.g. rep, rep_role.

The existing database had singular table names. Generating scaffolding would come up with an error about the reps table not existing. Of course not, it's rep. My initial thought is, "how can I fool RoR?" And the answer was obvious, create views and see what happens.


create view reps as select * from rep;
create view rep_roles as select * from rep_role;


With newly created views sitting in from of the tables, I go back to the trusty command line and try to generate my scaffolding again. PRESTO!! It worked. It's a hack, it's trashy, it's shameful. But it works, and it was quicker than ripping apart the framework and touching code in inappropriate fashion.

So, Hurdle #2 has been overcome.

Hurdle #3: This one is what I alluded to yesterday about simple keys. These tables don't have simple "id" type keys. In MySQL when I was starting an app from scratch, of course I would use an auto-increment id. Works great. But according to the RoR on Oracle article (http://www.oracle.com/technology/pub/articles/saternos-rails.html), there are expectations for the primary key column being "id" and having a sequence with a naming convention of "singular-table-name"_seq, e.g. rep_seq for table reps.

Well, that's not going to happen here, not for this database. And despite the tagline of the article ("Learn techniques for creating a Ruby on Rails Web application that utilizes a legacy schema."), the article really doesn't address natural primary keys, whether they are simple one-column keys or complex multiple-column keys.

I can generate the scaffolding and launch the server. I get pages displaying all the rows in the table. But clicking on the show, edit, etc. links fail. The error, obviously is that it couldn't find an "id" column to look up the record by. I also noticed the PK fields aren't showing up on the list view or in the "create new" record view.

I've been working on other projects most of the day, so haven't had a chance to work on it beyond identifying what doesn't work.

So, Hurdle #3 remains a hurdle for now.

And I just had a few minutes to Google, and found this article (http://www.oracle.com/technology/pub/articles/tate-activeerecord.html) which basically says the same thing I am realizing. To quote:
But Active Record is not for every application. It does not handle legacy schemas nearly as well. For those, you need a mapping framework, not one that merely wraps tables with simple classes.

But then there is this blog (http://paulmwatson.com/journal/category/ruby-on-rails/) that has some encouraging words about setting the table name and PK column with the set_primary_key and set_table_name methods of the ActiveRecord model. (The specific post is at http://paulmwatson.com/journal/2006/11/02/wrangling-rails/)

Now that is worth a try, but still not so sure it will work for a VARCHAR primary key field, and still not going to cut it for multiple-column primary keys.

Tuesday, May 29, 2007

Hooking Up Ruby To Oracle

Well, I have to admit this one stumped me for a few days. Actually it stumped me one day, and I got pulled to work on actuall work stuff, and just got back to it a few days later.

I started by following the examples at http://www.oracle.com/technology/pub/articles/haefel-oracle-ruby.html
to get the Ruby Oracle libraries installed. That part was easy, download the OCI8 Ruby package at http://rubyforge.org/projects/ruby-oci8 and run it.

Then I whipped up a script to test the connection from an example I found online (forget the URL to reference -- sorry if I stole your crap, dude!) That script basically looks like this (with usernames and passwords changed to protect the innocent.

test-conn.rb


require 'oci8'

OCI8.new("user", "password", "oracle_sid").exec("select * from rep") do
|r| puts r.join(", ");
end



I was pumped! That seems easy, and then I can get out of the MySQL realm and start hooking up Rails to existing enterprise DB's and really show this stuff to be useful in our environment. Sorry MySQL fanatics, we are (mostly) an Oracle shop -- I have nothing against MySQL for my personal projects.

Anyways, she didn't work out so well. I figured it would work since I have Oracle installed on my PC, but got the following popup when I ran that script.

There was also a Ruby stack trace on the command line. Basically I did some Google'ing and found this posting (http://blade.nagaokaut.ac.jp/cgi-bin/scat.rb/ruby/ruby-talk/238003) which at least gave me the clue I needed to look for OCI.dll on my system. My thought was it was getting confused between the Oracle 8 and Oracle 9 versions loaded on my machine.

Searching and searching, I don't have OCI.dll on my machine. Back to Google (a developer's best friend IMO, it always confuses me when other developers don't try this first when they get stumped) That's when I came across this page (http://www.dll-files.com/dllindex/dll-files.shtml?oci) that let me download OCI.dll. Dropped that in C:\WINNT\system32 (I'm on Windows 2000 at work, might be WINDOWS instead of WINNT for you), and re-ran the above test-conn.rb script.

IT WORKS!
Ruby on Rails on Oracle, here I come (again)

I'm guessing my next big hurdle (or potentially a roadblock instead of a hurdle) will be the lack of support for complex primary keys. Starting an application from scratch, you can design your database with that limitation in mind, but building applications for existing databases becomes another issue. I'm sure someone smarter than me has already addressed these concerns, so it might just be a matter of more Google searches.

Monday, May 28, 2007

Thursday, May 24, 2007

Ruby Syntax Sampler Script

Alright, I got a few different Ruby On Rails applications off the ground with the help of some tutorials, examples, and just plain screwing around. But I came to the scary conclusion that these apps were cool, but I still don't know much about Ruby the language. Rails makes it almost too easy! I only injected a few lines of code here and there based on the samples and tutorials to do things like define the foreign keys to get a selection list instead of text entry box for referenced tables.

So here's my lame attempt at playing with "Ruby proper" -- the language, devoid of frameworks. This is my trashy syntax_sampler.rb script that demonstrates some of the basics of Ruby.


require 'net/http'

#define a class
class SyntaxSampler
# define a constant
Dude = "Constants are capitalized. They cannot be declared in methods"
# the constructor method in Ruby is always "initialize"
def initialize( name )
@name = name # instance var starts with at sign
end

def print_name
puts @name
end

# big ugly method
def dostuff
print "x = "
x = 5
puts x
print "x + x = "
puts x + x
print "x * x = "
puts x * x
print "x**x = "
puts x**x
print "x/2 is "
puts x/2
print "but x/2.0 is "
puts x/2.0
names = ['robb', 'paula', 'raina', 'taryn'] #assign array to 'names'
print "crappers reversed is "
puts "crappers".reverse
print "\nPrint the Array: #{names}" # embed values right in print instead of 2 lines
puts "\nReverse the Array: #{names.reverse}"
puts "for methods with no args, parenthesis optional..."
puts names.reverse()
print "\nCapitalize Each Array Item: "
names.each {|name| print "#{name.capitalize} " }
print "\nUpper case Each Array Item: "
names.each {|name| print "\n\t - #{name.upcase} " }
puts "\n\n'5' as an integer = #{"5".to_i}"
puts "\n19.0 as a string = #{19.0.to_s}"
puts "\n'a19' as an integer = #{"a19".to_i}"
puts "\n'19a3' as an integer = #{"19a3".to_i}"
puts Dude
# define a global var
$wicked = "Global vars start with a dollar sign"
puts $wicked
end

def anothermethod
puts $wicked # make sure that var is really global scope
end

def mirror( something )
puts "#{something} #{something.reverse}"
end

def mirrordefval( something = "dude")
puts "#{something} #{something.reverse}"
end
end

class DiffClass
def initialize
end

def rundiff
puts $wicked # really is global, even across classes
10.times {print " x "} # everything is an object
puts
# use the above "required" module to do something...
Net::HTTP.start( 'www.ruby-lang.org', 80 ) do |http|
print( http.get( '/en/LICENSE.txt' ).body )
end

# shortcut for string arrays, no more pesky commas and quotes
food = %w{elk deer caribou squirrel pig chicken cow}
puts food

# basic if/else control structure
count = 5
if count > 10
puts "Try again"
elsif count == 5
puts "You lose"
else
puts "Enter a number"
end

# a while loop
xx = 0
while xx < 10
print "#{xx}..."
xx += 1
end
print "done"
end
end

# classes and methods are defined, now run them....
ss = SyntaxSampler.new("Robb")
ss.print_name
ss.dostuff
ss.anothermethod
ss.mirror("hello, I'm passing params baby") # with parens
ss.mirrordefval "yo yo yo" # no parens
ss.mirrordefval # no params sent in, use default value
puts $wicked # still don't believe me? it IS GLOBAL DAMMIT!
dc = DiffClass.new
dc.rundiff()
#now a little introspection for your sorry ass
puts "\n\nMethods of SyntaxSampler class...."
SyntaxSampler.instance_methods.each {|mm| print "#{mm} "}
puts "\n\nNow eliminate inherited methods...."
# this looks like a simpler way to print out a list, eh?
puts SyntaxSampler.instance_methods(false).join(" ")
exit # not really needed here

Friday, May 18, 2007

Getting Started With Ruby On Rails

The steps I took to install and get Ruby on Rails up and running. This is working on Windows (2000 at work, XP at home). I basically followed the steps on http://www.rubyonrails.org/down.

  1. Download and install Ruby. For Windows, there is a .exe installer. Make sure you pick the installer that comes with the "gems" package updater. I installed at c:\ruby to keep things simple. Installers are at http://www.ruby-lang.org/en/downloads/
  2. Once installed, you made need to add your Ruby bin directory to your PATH environment variable. On 2000, I had to do that, on XP, it just worked from the command line without doing that.
  3. In a command line window, cd into c:\ruby and run: gem install rails --include-dependencies
  4. To create the application, I created a webapp directory under c:\ruby.
  5. Change directory into c:\ruby and type: rails webapp
  6. Change directory into c:\ruby\webapp and type ruby script/server
  7. Once that comes up, open up a browser and check out http://localhost:3000
  8. There you have it, Rails is up and running on Webrick server.
  9. I already had MySQL installed, so next step was quick for me. If you don't already have it, go download and install MySQL.
  10. While you're in a downloading kind of mood, you might as well grab the Eclipse plugin for Ruby if you are an Eclipse guy. You could use any editor, like Notepad++, FreeRIDE (is that still supported? not in latest Ruby release, but it was in last release I downloaded), etc.
  11. With MySQL up and running, create a database and some tables.
  12. Edit your c:\ruby\webapp\config\database.yml file to point at your database.
  13. Run ruby script/generate to create controllers, models, etc.
  14. Edit your c:\ruby\webapp\routes.rb file to add your "routes" for your app.
I'm still on step 14, so can't tell you much more about that. Sitting on a conference call doing "real work"....

To create a controller, I did this....

C:\ruby\webapp>ruby script/generate controller recipe list
exists app/controllers/
exists app/helpers/
create app/views/recipe
exists test/functional/
create app/controllers/recipe_controller.rb
create test/functional/recipe_controller_test.rb
create app/helpers/recipe_helper.rb
create app/views/recipe/list.rhtml


And then enter http://localhost:3000/recipe/list into your browser. So far I got the screen below running without writing a single line of code. Not bad for about 30-45 minutes' worth of work.


It's amazing what you learn when you actually look at a tutorial. Instead of generating models blindly and getting the above, I should have been creating scaffolding to get something useful. I created a table call "robbs" in my database and added a single row (from MyEclipse DB Explorer). Notice my table is "robbs" plural, and the scaffolding controller and view is simply "robb" singular. Then by running the following command, I get this....



C:\ruby\webapp>ruby script/generate scaffold robb robb
exists app/controllers/
exists app/helpers/
exists app/views/robb
exists app/views/layouts/
exists test/functional/
dependency model
exists app/models/
exists test/unit/
exists test/fixtures/
identical app/models/robb.rb
identical test/unit/robb_test.rb
identical test/fixtures/robbs.yml
create app/views/robb/_form.rhtml
create app/views/robb/list.rhtml
create app/views/robb/show.rhtml
create app/views/robb/new.rhtml
create app/views/robb/edit.rhtml
create app/controllers/robb_controller.rb
create test/functional/robb_controller_test.rb
create app/helpers/robb_helper.rb
create app/views/layouts/robb.rhtml
create public/stylesheets/scaffold.css


Thursday, May 17, 2007

My Ruby Dilemna

To Ruby or not to Ruby?

I looked at Ruby a while back, maybe 2004/5-ish, when it was just starting to make a buzz. It seemed interesting because it was an object oriented scripting language. Loosely typed, to-the-point syntax, etc. I liked the concept, but ultimately I couldn't find a use for it to get me off the ground. When I really needed a shell script, it was quicker to just use ksh or bash and the standard array of unix utilities--awk, sed, cut, grep, etc.

Spare time is precious, and there are a lot of things besides programming to fill it with. And how often do you really get time at work to sit down and learn a new technology, pure R&D and personal development type stuff?

And if I do sit down to learn something new, should I really be spending time on a "toy" like Ruby when I could be brushing up on new Java stuff? Afterall, Java/J2EE are paying the bills, so shouldn't I be the best Java guy I can be, instead of dabbling in flavor-of-the-month technologies?

Then I saw a demo for Ruby on Rails. Wow, that blows the mind of a STRUTS/JSP/Servlet kinds of guy like me. It looks so cool, so fast, so easy, can it really be like the demo? It sparked my interest, but again, the buzz was that this was more of a "toy" than a real enterprise alternative, and lots of negative press was spent decrying lack of security and scalability, blah blah blah. Short attention span, drifting off, blah blah blah.

So now a friend of mine is diving head first into Ruby on Rails along with a few of his friends at work. His genuine enthusiasm has piqued my interest for a 3rd time. He is enthusiastic without being one of those zealots that are screaming loudly about how Ruby is going to kill Java or kill .Net or kill Python or whatever. No languages have to die here, people, it's just one more tool in the toolbox -- do you throw away your drill when you buy a new hammer?

And after all this time, Ruby is still out there and gaining support, so I can't really label it "flavor-of-the-month" or "toy" anymore. To be honest, I thought the same thing about Java back in '96/'97-ish when it was just being used to write applets embedded in HTML pages. I dabbled with some Java, but as a C/SQL/Motif programmer, Java seemed completely useless for real apps. I wasn't convinced until I saw the proof-of-concept office suite Corel wrote in Java -- whoa, real applications like drawing and word processing and spreadsheets running in a graphics "desktop" with icons and stuff! Now I write Java every day. So given that my judgement on this sort of thing sucks goat, I think Ruby, esp. Ruby on Rails, deserves another, more serious, look.

Using the instructions on this link, I was able to use Ruby GEMS to download and install Rails. Then a few commands and I got my sample skeleton project up and running. About 20 minutes worth of "work" while I sit here trying to figure out a production support issue for work. And here it is, my local server running my skeleton project....

And now, some links.....

Ruby Programming Language

Ruby On Rails

Ruby in 20 Minutes tutorial

Learning Ruby by comparing to languages you already know

More Ruby documentation

RDT Ruby IDE plugin for Eclipse

Why's Poignant Guide To Ruby

Using Oracle with Ruby

Wednesday, May 16, 2007

Mini KISS

Couple of friends saw these guys playing at the JavaOne After Dark bash. It's at once disturbing and intriguing at the same time. Mini KISS is a KISS tribute band made up of midgets, er, little people, or whatever they like to be called nowadays.

http://www.minikissonline.com/

Tuesday, May 15, 2007

Freaky Word Tricks For Your Shriveled Little Mind

An interesting email I got recently. Must be some amount of truth to it because I could read it without much difficulty...


Only great minds can read this


This is weird, but interesting!

fi yuo cna raed tihs, yuo hvae a sgtrane mnid too
Cna yuo raed tihs? Olny 55 plepoe out of 100 can.
i cdnuolt blveiee taht I cluod aulaclty uesdnatnrd waht I was rdanieg. The phaonmneal pweor of the hmuan mnid, aoccdrnig to a rscheearch at Cmabrigde Uinervtisy, it dseno't mtaetr in waht oerdr the ltteres in a wrod are, the olny iproamtnt tihng is taht the frsit and lsat ltteer be in the rghit pclae. The rset can be a taotl mses and you can sitll raed it whotuit a pboerlm. Tihs is bcuseae the huamn mnid deos not raed ervey lteter by istlef, but the wrod as a wlohe. Azanmig huh? yaeh and I awlyas tghuhot slpeling was ipmorantt! if you can raed tihs forwrad it

FORWARD ONLY IF YOU CAN READ IT.

Monday, May 14, 2007

More Chicken Pictures

The chickens have more than tripled in size since we got them a few weeks ago.

The buff orpington's seem to be the beefiest of them all right now, and a few of the reds are still kind of scrawny looking. They are all almost fully feathered out now, though -- with just their undersides and necks looking downy now.

I picked one up the other day and could not believe how thick the legs are already. These things went from fluffy little balls to something that resembles actual chickens now. Every day I go back there I notice a difference. I think if I just sat back there in the coop, I would actually see them grow. Well, maybe not.

Friday, May 11, 2007

I've Got Some Time

I've been increasingly paranoid about not having time to build my nest boxes for the chickens. So I asked myself, "self, how early do chickens start laying eggs?"

And when I had no answers, I asked Google instead. According to the Murray McMurray hatchery site, this is the answer:

"Most hens will start laying between 5-7 months of age. They will lay best at 1 to 2 years of age. All pullets (female chicken under 1 year of age) lay small eggs at first and after a while will lay larger eggs. Younger hens will lay 1 egg every 3-4 days. A hen 30 weeks old can lay 2 eggs every 3 days. Some have been known to lay an egg a day. All breeds have different laying abilities."
So I have a little time before I really need to panic. They are getting pretty big though, and when I fed them this morning, one of them was actually up on the perch. They are almost fully feathered out now as well. By the end of May we should be grillin' up our first chickens.

MMMmmmm, chickens!

Thursday, May 10, 2007

9 Week Old Lexi

This is what Lexi, our Wheaten Terrier, looks like at about 9 weeks old. Chewing on a toy. Play! Play! Play! Crash and sleep! Repeat.....

Wednesday, May 09, 2007

Bear Kills Moose in Alaska Driveway as Residents Watch

Bear Kills Moose in Alaska Driveway as Residents Watch
FOX News

I've seen stray cats hunting for mice in my driveway, but never anything as exciting as a brown bear killing a moose. If I had been in this situation, I probably would have grabbed the camera AFTER grabbing my rifle and skinning knife.

Click on the URL below for the rest of this story:
http://www.foxnews.com/story/0,2933,270936,00.html

Monday, May 07, 2007

Winchester 1300 - Possible Issue Shooting Slugs?

My father just bought the rifled slug barrel for his Winchester 1300 12 gauge, and brought it up this weekend to try it out. I gave him a couple of my Lightfield Hybrid EXP sabot slugs to shoot through it, and the when he fired, the action came all the way open and ejected the shell without any intervention.

He thought maybe he was inadvertently pulling the slide open because he wasn't expecting that much recoil, so I tried it. I rested the gun over the "V" of my pointer finger and thumb, not holding the forestock at all. The action came all the way open and ejected the spent casing for me as well.

Now we aren't sure if the barrel is keeping the bolt from fully locking properly, or if this is a side effect of the Winchester "Speed Pump" feature (I never really knew what that feature was supposed to do anyways), or if it is a matter of too much shell for the gun to handle?

Each shot was dead accurate at about 70 yards, and it didn't seem to be an "unsafe" condition as it was firing accurately and ejecting the spent casing, but it could definitely catch you off guard in a hunting situation if you are used to doing all the work of pumping a new round in.

I've fired everything from light target loads to heavy 3" magnum turkey and goose loads to rifled slugs through my Mossberg 500 and never had this happen, so I can't image a 2-3/4 slug being too much for a 1300 to handle.

My initial research on the web found another guy in a forum that this happened to, but haven't been able to find too much more info on it. I'll have to keep looking, and in the meantime he was going to have a gunsmith look at it to see if there is an issue with the bolt locking mechanism.

Thursday, May 03, 2007

Lexi Is OURS!!! HAHAHA

We picked her up Tuesday evening because the breeder was going to be out of town delivering a few puppies on Wednesday (a few hours early, yeah!). We'll have to post more pictures when we get a chance. She does a lot of hyper play-play-playing, followed immediately by crashing into a sleep. That's what puppies do.

Monday, April 30, 2007

2 More Days And Lexi Is Ours!

Here she is, at almost 8 weeks old. She turns 8 weeks Wednesday, May 2, so we can pick her up then.

Sunday, April 29, 2007

The Chickens Are Home

Today I put the finishing touches on the chicken run. Paula cleaned out the building that will serve as the coop while I finished putting the gate on. We put the perch in there, carried the chickens outside in laundry baskets, and hung up the brooder heat lamp. I got bigger feeder and waterer and set those up, and Paula went and got more corncob bedding to put down on the floor. So the chickens our our of our home and into their home.

And it was about time. They got to the point where they could fly out of the wading pool even with the 12" cardboard walls surrounding it. This morning one of them was running around the living room chirping because it was lost and couldn't get back in the pool. Later in the morning I saw a couple of them fly out when they got excited about something. They also started to do a little picking on one of the Buff Orpingtons, which meant it was time for them to get more space per bird.

Of course, now that they have so much more room, I went out to take pictures and they are all huddled together in the corner. When we got home from dinner, I peeked in the side window and they were all laying down right under the brooder lamp. But they have the space if they need it, so I feel better. And they aren't running around in my living room, so my wife feels better.

By the end of May they should be right about eatin' size, and it will be time to fire up the grill and have some friends over. Nice and fresh.

I still need to build or buy nest boxes, but I should have a few weeks before they get to that point, so it isn't an emergency yet.

Tuesday, April 24, 2007

More Puppy Pix 4/24

More pictures of the Wheaten puppies.
We get to pick ours up May 2!!

Thursday, April 19, 2007

The Other Chick - A Mystery!

This is the extra chick we got with our order from Murray. Still not sure what kind of chicken it is. Once the wing feathers start coming in more it should be a lot easier to tell, or I could just wait until it's fully grown.

Wednesday, April 18, 2007

Go Ahead And Make Yourself At Home

These 3 deer were among the 7 that visited our feeder the other day. It's not all that uncommon for them to be in our yard with the apple trees and the fact that we back up to an abandoned apple orchard. It's not even uncommon for them to poke their noses in the squirrel feeder and eat out of there. But this particular deer decided to make herself right at home and just layed down right smack dab in the middle of our yard.

Obviously she doesn't know who lives here. Hard to not think of a full freezer with scenes like this.

Who Says Programmers Can't Get Chicks?


Here's a close up of a Rhode Island Red chick, about 5 days old, sitting on my kitchen table.

Tuesday, April 17, 2007

The Chicks Have Arrived

The chicks have arrived! I finally got to bed around 6am after working on that file upload all night, and drifted off to sleep around 6:20 -- just in time for my cell phone to ring at 6:30. I jumped up and answered assuming it was work calling about the failed upload, but it was the post office letting me know the chickens have arrived. I rolled over andwent back to sleep for a few hours, then picked them up around lunch time. One had died in transit, but there were still 26 birds in there, so they must have put in 2 extra instead of just the one extra. We got 5 Buff Orpington roosters, 20 Rhode Island Red straight-run (as they hatch, no sexing), plus the one "exotic" chick Murray McMurray throws in for free. It is black with a stripe, so not sure what kind it is yet. Looking through the catalog it could be any number of breeds -- we'll see once it gets bigger.

We have our old kiddie pool set up in the back living room for them right now. We got the starter kit from Murray McMurray that has the cardboard draft shield, brooder lamp and red heat bulb, thermometer, 2 waterers, and 2 feeders. It seemed like a good deal versus buying all the individual pieces separately.

Right now our house if full of the CHEEP CHEEP CHEEP of 26 baby chickens. The girls think they are so cute and wanted to know what we would name them. Our answer of course NO NAMES, these are LIVESTOCK not PETS!

The dog on the other hand is a pet, she will have a name (Lexi -- we picked her out last night, and get to bring her home in a few more weeks) -- we won't eat the dog! Try to explain that to a 2 and 3 year old and it suddenly becomes clear how arbitrary the whole thing is -- how certain animals can share our homes as pets (dogs, cats, guinea pigs), other animals get killed for sharing our homes without asking (mice, rats, moles), and other animals get raised solely to eat (chickens, cows, pigs).


Thursday, April 12, 2007

Working With Velocity Templates

I used Velocity as the templating engine for my persistence code generator project. It is really simple and clean to set up and use, which is refreshing compared to a lot of the Apache projects which are obtuse and over-abstracted for the sake of inflating programmer's egos.

Why use Velocity? I use it for code generation -- connect to a database, get the meta data, and generate the DAO, VO, and DaoFactory Java classes to work with that database. But you could use it to generate any sort of text, whether it is HTML, JSP, ASP, emails, documentation, source code, etc.


Below is a very simple stub to show how easy it is to work with Velocity. This is the bare minimum Java code you need to merge your data into a template and generate an output file. Of course, you would have more than one bind variable in the context would probably have to deal with more complex data structures, but this gives you the basics...


import java.io.File;
import java.io.FileWriter;
import java.util.Properties;

import org.apache.velocity.Template;
import org.apache.velocity.VelocityContext;
import org.apache.velocity.app.VelocityEngine;
import org.apache.velocity.exception.MethodInvocationException;
import org.apache.velocity.exception.ParseErrorException;
import org.apache.velocity.exception.ResourceNotFoundException;

public class VelocityExample
{

public void execute() throws Exception
{
//Set up Velocity engine and context
Properties p = new Properties();
VelocityEngine ve = new VelocityEngine();
VelocityContext vctx = new VelocityContext();

p.setProperty("file.resource.loader.path", "/path/to/your/velocity/templates");
ve.init(p);

// put any number of properties in the context. These are
// used as bind vars in the Velocity template. For example
// In template any instance of ${someProperty} will be replaced
// with someValue.
vctx.put("voClassName", "ExampleVo");

// Get your template
Template templ = ve.getTemplate("YourTemplate.vm");
// and a writer to new file to be generated
FileWriter writer = new FileWriter("/path/and/filename/of/generatedfile");

// merge template with context and write to your output file
templ.merge(vctx, writer);
writer.flush();
writer.close();
}
}


Ok, so that's the code, what does the template look like? Below is the template I use to generate VO's (or DTO's) -- a class with properties and getters/setters methods for each property.

Notice is looks a lot like a Java class already, except for all those pesky $var's and #directives. Anything you put in the velocity context is available from the template. You reference the properties using $property or ${property}, and if it is a complex type you can reference fields using dot notation like ${property.name}, or loop through List's and arrays using #foreach directives.


// ====================================================================
// DO NOT EDIT THIS FILE. IT HAS BEEN AUTO-GENERATED BY DAO-GEN.
// YOUR CHANGES WILL BE LOST NEXT TIME THE CODE IS GENERATED!
// ====================================================================

package ${voPackageName};

import java.io.Serializable;
import java.util.ArrayList;
import java.util.List;

import java.sql.Date;
import java.sql.Timestamp;


/**
* @author DaoGen
* @version cvs: $Id: VoTemplate.vm,v 1.2 2006/12/12 20:13:23 ran488 Exp $
*/
public class ${voClassName}
extends BaseVo
implements Serializable
{

#foreach( $field in $persistableFields )
/** $field.colName */
private $field.javaType $field.jFieldName ;

#end


/** Constructor */
public ${voClassName}()
{
super();
}


// Getters and Setters
#foreach( $field in $persistableFields )


/**
* Setter for DB column $field.colName
* @return void
* @param $field.typeName
*/
public void set${field.jProperFieldName}( $field.javaType $field.jFieldName )
{
this.${field.jFieldName} = $field.jFieldName;
}


/**
* Getter for DB column $field.colName
* @return value
*/
public $field.javaType get${field.jProperFieldName}()
{
return this.${field.jFieldName};
}
#end
}




For a more real-world example of the Java code, here is the method I use to generate the VO from the above example. This method actually generates the DAO implementation and interface and the VO for each table in a database.



/**
* @param db
* @param basePath
* @param ve
* @param vctx
* @throws ResourceNotFoundException
* @throws ParseErrorException
* @throws Exception
* @throws IOException
* @throws MethodInvocationException
*/
private void generateTablePersistentObjects(Database db, String basePath, VelocityEngine ve, VelocityContext vctx) throws ResourceNotFoundException, ParseErrorException, Exception, IOException, MethodInvocationException
{
Template voTempl = null;
Template daoIntfcTempl = null;
Template daoImplTempl = null;
voTempl = ve.getTemplate("VoTemplate.vm");
daoIntfcTempl = ve.getTemplate("DaoTemplate.vm");
daoImplTempl = ve.getTemplate("DaoImplTemplate.vm");

// The MEAT and POTATOES
for (Table table : db.getTables())
{
// put everything from table into Velocity context
vctx.put("voClassName", table.getJProperFieldName() + "Vo");
vctx.put("daoImplClassName", table.getJProperFieldName() + "DaoImpl");
vctx.put("daoInterfaceName", table.getJProperFieldName() + "Dao");
vctx.put("dbTableName", table.getTableName());
vctx.put("javaTableName", table.getTableName());
vctx.put("persistableFields", table.getColumns());
vctx.put("primaryKeyFields", table.getPrimaryKeys());
vctx.put("schema", table.getTableSchema());
vctx.put("tableType", table.getTableType());
vctx.put("numPkFields", table.getPrimaryKeys().size());

FileWriter voWriter = new FileWriter(basePath+"vo"+File.separator+table.getJProperFieldName()+"Vo.java");
voTempl.merge(vctx, voWriter);
voWriter.flush();
voWriter.close();

FileWriter daoIntfcWriter = new FileWriter(basePath+"dao"+File.separator+table.getJProperFieldName()+"Dao.java");
daoIntfcTempl.merge(vctx,daoIntfcWriter);
daoIntfcWriter.flush();
daoIntfcWriter.close();

FileWriter daoImplWriter = new FileWriter(basePath+"dao"+File.separator+table.getJProperFieldName()+"DaoImpl.java");
daoImplTempl.merge(vctx,daoImplWriter);
daoImplWriter.flush();
daoImplWriter.close();
}
}


For more info check out http://velocity.apache.org/

There is also an Eclipse plugin for the templates at http://veloedit.sourceforge.net/.