Thursday, November 24, 2011

Escaping Java classpath wildcard on Windows command line!

I had a ridiculous battle with a set of .bat scripts that I wrote to launch java command line applications on Windows. I'm working on a suite of related little tools that I bundle into a zip file with a bunch of .jar files under a /lib/ folder, and launch scripts under a /bin/ folder that each look something like this:

@echo off

if not defined JAVACMD set JAVACMD=java.exe
if defined JAVA_HOME set JAVACMD="%JAVA_HOME%\bin\java.exe"

%JAVACMD% "-Djava.util.logging.config.file=%~dps0..\config\logging.properties" -cp "%~dps0..\lib\*" run.my.App %*

So I'm trying to use the nifty java classpath * wildcard introduced with java 6, but I had a terrible time with windows insisting on expanding /lib/* in the shell regardless of how I tried to quote or escape the thing! I was driving myself crazy, when I finally got the genious idea to just add a semicolon, so /lib/*; doesn't look like a path to Windows Powershell or CMD (I guess), and the following works:

@echo off

if not defined JAVACMD set JAVACMD=java.exe
if defined JAVA_HOME set JAVACMD="%JAVA_HOME%\bin\java.exe"

%JAVACMD% "-Djava.util.logging.config.file=%~dps0..\config\logging.properties" -cp "%~dps0..\lib\*;" run.my.App %*

What an amazingly stupid waste of time!

Sunday, October 23, 2011

Java RMI over SSL and a single port

I was lucky with my latest freelance contract that the clients were happy to build on littleware to implement some of their back-end services, and they let me bill a little time to implement a few features - like securing the RMI-based procedure-calls over SSL. Fortunately java includes an RmiSSLSocketFactory, but there are a few integration tricks.

I outlined in a previous post how to configure RMI to export all its objects over a single firewall-friendly port. That was easy to do once I knew the trick. Unfortunately SSL has a few moving parts - some of which I disabled as I really just want the data going over the wire encrypted, and am less concerned over end-point certificate verification.

Anyway, in the end it's not too complicated - it's just a matter of knowing the tricks - which are annoying to track down. Most of the code is in littleware's LittleRemoteObject class that specializes java's UnicastRemoteObject. The server builds a RmiSSLServerSocketFactory using an SSLContext that loads a certificate from a keystore on the classpath. The client builds and RmiSSLClientSocketFactory with a no-op TrustStore that bypasses verification of the server's certificate.

One final trick - the server boots up its own RmiRegistry with its custom RmiSSLServerSocketFactory, and the client connects via LocateRegistry.getRegistry - which accepts the client's socket factory - (littleware's RemoteRetryHelper implements the client's connection). Littleware's client actually exposes its own API that integrates a cache and authentication building on top of the service API that the server exposes via RMI (I'm working on a REST implementation).

Anyway - a boring post about a technology nobody uses. Sweet!

Friday, September 09, 2011

Mishaps and Disasters

The last month and a half has been a parade of mishaps and disasters. The morning of July 27 I was looking forward to my flight out to California where I would join my old friends for a camping trip in the eastern Sierras. Our three dogs and I were walking through the front yard as we returned from our morning walk when we all realized we were being attacked by freakin' yellow jackets. I usually get along fine with stinging insects, but yellow jackets are freakin' mean if they decide you're too close to their nest, and we had a freakin' nest in some gopher holes in the front yard. Yellow jackets can sting repeatedly, and they do sting repeatedly - it's not like "sometimes they sting repeatedly, but usually just once" - I mean they just latch onto you, and repeatedly sting - freakin' mean! The dogs freaked out, but we managed to run to the back yard, and I spent the next hour dressed up for a Chicago winter in Alabama July trying to drown and burry that freakin' nest.

Anyway, we survived, and I was glad when I was finally sitting uncomfortably in coach flying to LAX. My friend Hiro organized a 4 day (3 night) backpacking trip. I packed light - just a few changes of cloths and enough food to keep me hiking through the freakin' arid eastern Sierras in late July. Hiro set me up with a tent and sleeping bag, and I rented a pack. We drove up 9000 ft outside of Bishop, and started hiking. I was such an idiot. Everybody got altitude sickness the first day - freakin' headaches and exhaustion, but that comes with the territory, and we made it over a high ridge the first day, and were on our way the second day on a much less taxing hike. By mid afternoon we were getting rained on pretty steadily, but I thought, "no problem, nice summer rain", but there's no such thing as a nice summer rain above 10000 feet. The rain didn't stop, then it turned to hale, and it got cold! Freakin' cold, and I was not the only one not prepared for rain or cold. Finally my friend, Mike, decided to hell with it, and put up his tent - by then it was a freakin' down pour of freezing rain that had been getting worse for hours. I was shaking like a leaf. Fortunately, my other friend, Steve, got his tent up, and let me crawl in. We both dove into our sleeping bags, and I felt much better for a while, but soon I was freezing again, and Steve noticed, "you don't have a matte under your bag ?". Frick - I just had the bag Hiro gave me. Freakin' Steve saved my life letting me sit on his matte. We sat with our faces jammed into the tent wall for about an hour when the rain finally stopped. Steve let me crash in his tent that night, and Hiro lent me an extra matte, so all was well, but we were all pretty annoyed when it started raining the next night, and I was especially upset, because my tent leaked. I was freakin' miserable in this mini tent with puddles all around me, but my friends, Stewart and Chika, took pitty on me, and I squeezed in with them. Everyone on that trip must hate me now.

So fun trip, and I made it home, and all was cool for a week or two, when Joanne's poor Aunt died, and we drove to Atlanta for the funeral - leaving our dogs at the kennel. We have three dogs that I love and spend a ton of time with, but they don't get along all the time, especially Ponzi and Moose, and especially when squeezed in close quarters - like our Volkswagon hatch-back driving to the kennel. We have a crazy system where Ponzi sits in the front passenger seat, and we have this fence thing separating the front seats from the back, and Joanne, Moose, and Shugs sit in the back. Anyway, that worked pretty well, and we made it to the kennel ok, and the dogs were all excited and nervous. I put Ponzi on the leash, and circled around back to let Moose and Shug out, and there's some confusion, and the next thing Ponzi and Moose were trying to bite each other's head off - literally. There's a lot of luck involved in separating two fighting dogs without coming away bleading yourself, but somehow I managed it. I noticed Ponzi holding his head to the side, but didn't see any obvious injury, so we left the boys at the kennel, and went to Atlanta for some family drama. Next afternoon we headed back, and found Ponzi with a very red eye - Moose must have got a tooth into Ponzi's eye. Freakin' mess. Fortunately Joanne got through to the Auburn Vet School, and Dr. Newcomer was good enough to let us squeeze in an appointment just after closing time. Ponzi's eye was ok - the surrounding tissue was injured, and Newcomer set us up with some eye ointment to help him heal. Soon after that I noticed we had a new nest of hornets in another gopher hole in the front yard. What the frick ?

Finally, Ponzi healed up, and I took the dogs to the park to run around - we did that twice a week. Kiesel park is a really cool park in Auburn. It used to belong to some farmer named Kiesel who raised cattle. I guess he donated the land to the city, and it's a great park with a cow fence all the way around, a little pavilion for concerts, a few miles of walking path along the parks edge, and several little woods separating parts of the path. Unfortunately, Shug has a love for going where he's not supposed to go, and a gift for finding gaps in the fence, and sure enough - soon after we arrived he ran across a stream, and under the fence, and into the woods across the street. I followed, and so did Ponzi. I nearly caught up with him when Shug decided to walk out into the road - in front of a GMC Jimmy. The next hour was a mess. Shug survived the accident - dragging himself to the side of the road. I tied him up there, so I could get the car, dragging Ponzi along and yelling for Moose, but when I drove back I couldn't find Shug - he had hidden in some bushes. Fortunately Moose showed up, and found Shug for me. I lifted my poor broken and bloody dog into the back of the Jeep. Moose and Ponzi amazingly didn't fight, and we headed to the vet school. I think it was only about 7am, but the vet school has an emergency service, and they took Shug in. I was a freakin' mess. Long story short, the vets were able to save Shug. He had a fractured right femur and another fracture on the left side of his pelvis, but the orthopedics crew was able to do some magic with a bunch of steel rods, pins, screws, wires, and plates. Shug is three weeks along his way to recovery now - so far so good. I enjoy keeping him company - writing code at the table next to his crate; hiding from whatever else is coming my way.

Oil Buffet

The other day I listened to a short discussion on the PBS News Hour podcast about the proposed oil pipeline from the Alberta tar sands mines to feed our oil hunger here in the U.S.A. The news hour likes to have little mini debates where they have a spokesperson from each side sit down and talk about an issue. The whole tar sands thing and global warming in general boils down to whether we should risk our environment for the economy, or risk the economy for the environment. Of course if the environment goes to hell, then the economy is screwed anyway; and the tar sands wouldn't be viable if the cost of a gallon of gas wasn't already heading north of $4, but whatever.

Anyway - one thing the pro-pipeline guy said (and many others too about this and similar issues) that frustrates me is that the Alberta tar sands will be developed whether the United States consumes the oil or not - the oil will just go to other markets, so we might as well take the oil, because there's no stopping China and India or wherever. I mean per capita we consume over 5 times the amount of energy as China, and 10 times the energy of India (thanks, Wikipedia!). Sure there are a lot more Chinese and Indians, but since we led the way to our energy-fat lifestyle; we ought to also lead the way to a more sustainable future. The cost of energy will go up, and we'll have to change the way we do things, but we'll be better off in the end.

The picture I have in my mind is of big old fat America at the energy buffet - freakin 500 pound energy fatty half blind from diabetes, blood pressure through the roof, butter for blood, zooming around the buffet in one of those fat-people scooters, because America can't walk more than a few steps without getting winded. So fatty America has already eaten 5 times the amount of food as any of the skinny Chinese at the buffet, and ten times more food than any of the Indians; but America is getting pissed off, because the buffet is starting to run out of food, and even though any one Chinese or Indian has eaten a lot less food than America, when you add up all the food that all the Chinese and Indians have eaten - that almost competes with the amount of food America has eaten, and America needs that food! Freakin' China and India! Now America notices Canada just set a giant chocolate cake on the buffet table. Is America going to freakin' go on a diet and start exercising and let China and India and Europe have the freakin' cake ? No way, baby!

Tuesday, July 26, 2011

Writing the Congressman

I got a little fired up about the debt ceiling nonsense, and decided to e-mail my Congressman, Mike Rogers. I always feel so empowered after writing a politician - which is ridiculous, because it has never made a difference.

Hello Congressman!

I'm writing today to complain about your part in the House majority's role in taking the United States to the brink of default. I cannot take your "Cut, Cap, and Balance" bill seriously. It is exactly in times of economic recession that we need the federal government to run a deficit - lowering taxes, bolstering the social safety net, and absorbing the lost revenue from a diminished tax base. Our short term debt increase is a result of the economic downturn and stimulus spending; it is not a result of "out of control" spending. Our long term debt mostly results from the projected increase in medicare spending. I know you understand this, so I find your press announcements disingenuous.

The debt ceiling should be raised as a matter of course, and not used as a gun to the nation's head to push through the minority agenda. Please do your part to ease the debt-ceiling grid lock. It would be great while your at it if you could help restore funding to the FAA, and contribute to the plans to save the postal service from bankruptcy.

Thanks for your service.

Cheers,
Reuben Pasquini

Sunday, July 24, 2011

Giving GWT a try

When I decided to implement a webapp browser for littleware's node database I began a debate with myself whether to implement the project in javascript or to give GWT a try. I have a little experience with javascript - I've enjoyed working with YUI on several projects, but javascript really sucks - no strict type checking, no module system, weirdo prototype object system ...

On the other hand - I feel like if I'm going to build a browser-based application, then I should develop with web technologies - javascript, HTML, CSS, ... It's great that GWT let's me develop in java, but it's a weird technology - not javascript, not really java - it's its own thing, but GWT is open source, has a community, is used and sponsored by Google (this blogger editor uses GWT), so might as well give it a try.

Anyway, I downloaded GWT and Eclipse, and was quickly running the demo application that the GWT Eclipse plugin creates in a new project. The demo is great for demonstrating GWT's remote service infrastructure and asyncrhonous support, but the simple UI assembly does not take advantage of GWT's new declarative UIBinder infrastructure. I want to use that UIBinder mojo, so I decided a good first task would be to refactor the demo to use UIBinder. I had a few false starts and a few different parts, but one part of the code replaces this html segment:


...
    <table align="center">
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter your name:</td>        
      </tr>
      <tr>
        <td id="nameFieldContainer"></td>
        <td id="sendButtonContainer"></td>
      </tr>
      <tr>
        <td colspan="2" style="color:red;" id="errorLabelContainer"></td>
      </tr>
    </table>
    ...
, and this java code:
	public void onModuleLoad() {
		final Button sendButton = new Button("Send");
		final TextBox nameField = new TextBox();
		nameField.setText("GWT User");
		final Label errorLabel = new Label();

		// We can add style names to widgets
		sendButton.addStyleName("sendButton");

		// Add the nameField and sendButton to the RootPanel
		// Use RootPanel.get() to get the entire body element
		RootPanel.get("nameFieldContainer").add(nameField);
		RootPanel.get("sendButtonContainer").add(sendButton);
		RootPanel.get("errorLabelContainer").add(errorLabel);
                ...
         }
with this UIBinder XML:
...
	<g:HTMLPanel>
		
    <table align="center">
      <tr>
        <td colspan="2" style="font-weight:bold;">Please enter your name:</td>        
      </tr>
      <tr>
        <td> <g:TextBox ui:field="nameField" /></td>
        <td> <g:Button ui:field="sendButton" /></td>
      </tr>
      <tr>
        <td colspan="2" style="color:red;"> <g:Label ui:field="errorLabel" /></td>
      </tr>
    </table>
		
	</g:HTMLPanel>
   ...
that pairs with this code:
public class DemoPanelView extends Composite {
	private static DemoPanelViewUiBinder uiBinder = GWT
			.create(DemoPanelViewUiBinder.class);

	interface DemoPanelViewUiBinder extends UiBinder<Widget, DemoPanelView> {
	}

	@UiField
	public Label errorLabel;
	@UiField
	public Button sendButton;	
	@UiField
	public TextBox  nameField;
...
Anyway I'm having fun with GWT, so I'll try to implement the database browser in GWT until I run into something that stops me ...

Sunday, July 03, 2011

Powershell Tabexpansion

There must be a better way to do this, but one of the funny things I do is setup variables in Powershell for paths that I frequently navigate to. So my profile.ps1 file has lines like this:
$GLOBAL:g1 = "C:\whatever\some\path"
$GLOBAL:l1 = "C:\whatever\some\other\path"
, and I issue a lot of commands like cd $g1
ls $l1
cp $l1/bla $g1
... that kind of thing.

One thing that drives me crazy is that I often want to do something with a sub-directory or file under a path referenced by one of my jump variables, but Powershell tabexpansion won't expand the variable for me ... until now! I finally decided to look into how to customize Powershell's tab expansion, and sure enough there's a TabExpansion function that I can override. This bLog post has the details.
Anyway, I defined my own TabExpansion function in profile.ps1 with a patch that just adds one line to the default function. The default has this regex-switch case that expands variable names:

            foreach ($_v in Get-ChildItem ($_varName + '*') | sort Name)
            { 
                $_nameFound = $_v.name
                $(if ($_nameFound.IndexOfAny($_varsRequiringQuotes) -eq -1) {'{0}{1}{2}'}
                else {'{0}{{{1}{2}}}'}) -f $_prefix, $_provider, $_nameFound
            }
, and my patch just adds a line to the end of the loop that also returns the value of the expanded variable:
            foreach ($_v in Get-ChildItem ($_varName + '*') | sort Name)
            { 
                $_nameFound = $_v.name
                $(if ($_nameFound.IndexOfAny($_varsRequiringQuotes) -eq -1) {'{0}{1}{2}'}
                else {'{0}{{{1}{2}}}'}) -f $_prefix, $_provider, $_nameFound
                $_v.value
            }
Now when I enter 'ls $g', then for each tab Powershell scrolls through a series of possible expansions: $g1, value of $g1, $gr, value of $gr, then back to $g1. If I want to list some sub-directory of $g1, then when the value of $g1 shows up, I just enter a '/' and start tabbing again to find the sub-directory. Hopefully that makes sense. Let me know if there's a better approach!

Saturday, June 04, 2011

Quick Powershell Background Function

My blogging energy is way low these days, but I'm forcing myself to throw together a quick post - whipping myself like that crazy albino masochist monk assassin in the DaVinci Code.

First, here's a handy function to change the background color of a powershell terminal. Just throw it into profile.ps1, and you're all set to colorMe 'DarkGreen' or whatever. I found the code at http://technet.microsoft.com/en-us/library/ee156814.aspx.

function colorMe( $color ) {
    (Get-Host).UI.RawUI.BackgroundColor = $color
}

Next, I'm working on a project now that involves a little SVG, and I was surprised to discover that my NetBeans IDE didn't recognize the file type. It turns out that NetBeans' SVG support is in its Mobility extension, so I'm all set now after installing that.

Sunday, May 22, 2011

littleware a little chill

I just updated littleware's ivy repository with new versions of the core littleware artifacts. I completely reworked the way the asset system manages client-server interaction, and also reworked the asset type-tree and IOC injection. I think it's a big improvement, but I'm too lazy to get into it tonight. The core littleware.asset APIs pass regressions, but the supplement tools under littleware.apps (data buckets, image managers, task tracking) need patches to work with the updated core APIs, so I didn't release new versions of those artifacts yet.

I'm busy now with some consulting work (whoot!), so littleware is going to have to simmer on the back burner for a while. I still laid out some goals for littleware's next milestone. Rather than complete the port of the supplemental modules to the new core api's, I plan to work on a couple services for services.frickjack.com that do not require the asset system.

The first service is a simple Google-calendar based appointment manager. A client supplies a calendar with events marking out when he or she is available for appointments, then other users from the general public may visit the frickjack service to view the calendar, and schedule an appointment - which just appears as another event in the calendar. This service will take advantage of the openId authentication service already deployed. I think this service would be useful for the owners of those "For Sale By Owner" homes to let potential buyers schedule home visits online.

I also want to deploy a simple barcode-image service where a client may submit a numeric barcode to a ReST-like URL, and receive in reply a .png barcode image. I've had this service in mind from when I worked at Auburn University's library, and a coworker in circulation mentioned how annoying it was to search for the record in the ILS associated some e-mail or web-based request. He suggested that it would be nice if the e-mail or web page had an embedded barcode-image that he could just scan with the hand-held barcode scanner that integrates with the ILS system.

We'll see how it goes.

Tuesday, May 17, 2011

Keep up with the bLog!

My list of blogging topics is piling up, but I've been putting off my latest post trying to finish several littleware changes, then a small contract.

Anyway, time to take a break from code, and complain about platforms. It's great that there are so many great tools and frameworks out there to take advantage off - application servers ( glassfish, tomcat, jetty, jboss, geronimo, ... ), distributed SCM (Mercurial, Git ), IDEs (Netbeans, Eclipse), java web component libraries (JSF, MyFaces, RichFaces, PrimeFaces, struts, ...), build tools (ANT, IVY, Maven, Scala SBT, Gradle), databases (MySQL, Postgres, Cassandra, ...) ORM ( Eclipselink hibernate, ...), javascript (YUI, JQuery, GWT, Flash, ...), languages (PHP, java, scala, python, ruby, ...), platforms (Spring, JavaEE, Guice, AWS) ... !

It's nice except when it's not nice. It always takes me a while to learn how to use a tool well - the right and wrong way to do things, the corner cases to avoid. So I like to use ant with ivy, glassfish, netbeans, mercurial, YUI, ...; and I'm not going to spend time learning jboss, maven, eclipse, and git unless they have something major to offer me. Of course - other people feel the same way. I can't argue with a project proposal where the client wants to use tomcat, struts, git, maven, and eclipse; because that's what the client is familiar and productive in, and there's no reason for that client to switch to glassfish, jsf, mercurial, ivy, and netbeans. It's a little annoying to spend time learning a new tool that does the same thing as a tool I know how to use, but in a slightly different way - even though I always learn something in the process.

End of rant!

Friday, May 06, 2011

Rejected by Google!

I was lucky enough to interview with Google in Santa Monica last week, but got the "thanks, but no thanks" call today. Ugh! On the one hand they would be scraping the bottom of the barrel to make me an offer, and on the other hand a company that can't talk itself into making me an offer has bigger problems than I can help with. Still sucks though.

Sunday, April 24, 2011

Happy Earth Day!

Earth Day was last Friday, April 22, so I thought I'd throw up some pictures of our backyard herb garden for the occasion. One of the benefits of living in Alabama is that you can get a nice house on a quarter acre in town for a lot less than the cost of a one bedroom condo in L.A. The only drawback is that also comes with a few crabby neighbors that complain if you don't mow the grass, and call the cops if your dog gets out of the fence and runs through their yards. Most people are cool though, plus Alabama doesn't believe in property taxes, so it's easy to be a squatter. We'd rather have stupid kids than property taxes here. We do tax groceries, because the poor should pay for government. The grocery tax hasn't made a dent in the obesity problem anyway.

If you live out of town, then you can afford a bunch of land, so the neighbors aren't a problem, and you can have farm animals! Farm animals aren't allowed in town, but I love the idea of having chickens. A lot of people have horses out here - crazy, right ? All the cool little girls in Alabama have a horse and take riding lessons.

Anyway, back to our back yard - the dogs love it (2 of the dogs love it, the other dog loves to sleep on the bed), and there's room for a little herb garden and a compost pile (which fertilizes the garden). My garden is kind of a mess. I don't really know what I'm doing, so I was especially pleased when some seeds I threw in the ground early March actually grew. Alabama is warm enough to have two or three growing seasons if you're ambitious enough to take advantage of it - cool crops in early spring, hot crops through the summer, then cool crops again in the fall. My garden doesn't have the best location - there are a lot of trees around our back yard, and the house shades the back yard in the afternoon, so the garden really only gets three or four hours of good sunlight each day.

Last fall I scattered some cilantro and swiss chard seeds in the garden. The swiss chard was great last year and sprang to life this spring too, but the cilantro didn't do much until this spring when it popped up and went crazy. They're both great in pesto and in beans with rice.

Cilantro From Garden
Swiss chard From Garden

I also scattered some broccoli, carrot, and leek seeds in March. The broccoli is growing like crazy, and I think the carrots are doing ok (it's either carrots or weeds - I'll find out when I dig them up next month). I think it's actually summer squash (probably from seeds left from last year's garden) growing where I planted the leeks, so I don't know what's up with that.

Broccoli From Garden
Summer squash From Garden

I stopped by Home Depot the other day too, and bought some tomato and pepper plants - 16 plants for about $7! Jalapeno peppers did great in the garden last year, but Joanne hates jalapenos, so the neighbor and I were constantly eating them. This year I got some banana peppers - which should be more mild for Joanne, and cayenne too - we'll see how those do. I had a few bell pepper plants last year, but they didn't do well. I also tried egg plant last year, but some beetle munched most of it - I was too lazy to try to figure out how to defeat the beetle.

Pepper plant From Garden

Of course owning a house is not very Earth Day green. When we first moved to Auburn we lived in a big apartment complex. The complex consisted of several buildings along a loop road on wooded land by a big pond - pretty nice. Our building had eight two-bedroom apartments that the complex management kept in good shape. The house we live in now probably has about the same carbon footprint as the whole building we used to live in. At least we recycle and compost and try not to run the air conditioner or heat or set the thermostat as high or low as we can stand it (Joanne hates me for that). Plus I bought (actually it was a gift from mom) this funky little mechanical lawn mower with the blades that spin when you push it. I love that little mower even though I only use it once a month or so, because I actually like the look of the yard with the wild flowers and weeds and uneven growth, but sometimes neighbors complain about that. The soil is mixed with clay around here, and there's a lot of shade, so the grass doesn't get high in our yard anyway. The neighbors with the manicured, irrigated, chemical treated lawns bless us with the noise and pollution from their two-stroke mowers and leaf blowers every week, and we don't complain (except on the bLog!).

HBO is going crazy advertising for Lady Gaga's Monster Ball Tour that they somehow get to broadcast from Madison Square Garden. I'm almost tired of seeing her breasts and ass ... almost. I wonder if Gaga cloths would be comfortable for programming?

Wednesday, April 13, 2011

nice AWS!

I spent time over the last several days setting up a free amazon web services account, and manged to deploy a simple openId helper service that I've been working on the last couple weeks. I couldn't be happier with AWS. I have a glassfish app server running custom java webapp online for free rather than paying between $50 and $100 a month to some hosting provider. If people actually start using my apps, then I'll just scale up my AWS instance, and start paying Amazon for the resources I use. That's just what I need.

The AWS web console is very to use; I was able to get up running with an Amazon Linux AMI backed by an EBS disk by just following the instructions in the "getting started" guide. I'm new to AWS, but I've already picked up a few tricks.

  • "Terminating" an EC2 instance not only shuts down the server - it also deletes the instance and its data - even an EBS backed instance. Just stop (don't terminate) an EBS AMI if you want to be able to start it back up again.
  • This bLog has a nice overview of how to register the AWS security keys with the Putty ssh client for Windows.
  • The public DNS name on a load balancer persists, but the public DNS name on an EC2 instance is reset after reboot. A good way to publish a service is to deploy it behind a load balancer, then map a DNS C-record to the load-balancer's public DNS name. You can do the same thing with an "elastic IP", but I don't think the AWS free service tier includes elastic IPs.

I'm also really happy with my experience with openId. The littleId code includes a simple openId consumer I wrote with the very nice openid4java library, and supporting javascript, PHP, and scala/java client libraries. The littleId openId consumer can run in the same java webapp that requires authentication, or the webapp can access littleId as a simple web service. In fact, a webapp can just use http://services.frickjack.com as its littleId service provider.

The littleId client API includes support for javascript, java, scala, and a little PHP, and should be easy to port to other languages. The javascript code manages openId popup-authentication windows in the browser. The java client code includes JAAS integration, and a simple LoginServlet and request-filter that easily integrate into a java .war webapp, and the PHP code is currently just a single-method class with which a PHP client can verify the credentials delivered by littleId's openId consumer. There's a littleId demo online that might help show what I'm talking about.

I'm not sure how much that all makes sense, but I'm all set to write webapps that authenticate with Google and Yahoo openId credentials, plus there's a small chance other developers might find the littleId service useful. It would be great to get a little traffic to http://services.frickjack.com, and setup the site with adwords or whatever to make a few bucks!

I hope to make the littleId service more useful to a developer compared to just coding an openId consumer directly by integrating support for live.com and Facebook connect authentication. I also plan to setup a workflow for authentication by non-web GUI clients. We'll see how long it takes me to get around to that!

Finally, is it just me, or is iTunes unstable on Windows 7 ?

More communication ...

One of the many things I constantly struggle with is how to better communicate the current state of the littleware dev machine. What is littleware? How can I use it? What are the current development goals? Fortunately Google code is there to host the littleware project site, source code repositories, download bundles, wiki, and issue tracker; but over the last few weeks I've added a few small improvements to my communication effort.

First, I added a "news" section to the frickjack.com home page just to report the latest goings on. I'll get in the habit of throwing the news up on twitter too @catdogboy; although twitterfeed has turned my twitter account into a garbage stream of code commit messages and issue tracker updates.

I also moved littleware's SCRUM documentation over to frickjack.com (it's a Google site). I made that move mainly to integrate littleware's Google calendar , but it cost me some head banging to get the issue tracker gadget working there. The whole SCRUM and calendar thing is pretty aspirational anyway considering I'm the only one that works on the project, but it helps me keep track of which direction I'm moving in.

Finally, I've made littleware's jenkins server available online. Actually, the server is only available when I'm running my laptop at home - beta.frickjack.com maps to my home! Jenkins (formerly known as Hudson) is a great continuous integration server. It was easy to configure jenkins to automatically build and test littleware commits thanks to littleware's IVY build system. It's important to secure jenkins before publishing a server for public access, but that's not hard to do.

I've spent a lot of time the last week on system administration deploying an AWS AMI and tweaking jenkins, and project management updating docs and moving things around on the web sites. I hate wasting time on non-coding stuff like that! This kind of work always reminds me that a software project needs a good combination sysadmin, project-admin, configuration, release, and documentation manager. It's not just all about the coders!

Thursday, April 07, 2011

Google code Mercurial SCM / issue-tracker integration

I just patched a littleware bug, and I wondered whether Google code had some SCM integration with its issue tracker, so that I could update the bug's issue with the patch's commit message. The answer is yes ! One limitation is that the SCM message only propagates to the issue-tracker after being pushed to the main Mercurial repository - pushing to a clone won't do it. That's probably the best way to implement the feature anyway.

I posted a new littleware download bundle online today too. The new code includes openId support, and separates out the base littleware ivy module from a new littleAsset module that also includes the old littleApps module. That probably sounds meaningless, but basically I pushed the code around, so that an application can more easily link against the littleware.base packages without also dragging in the littleware.asset stuff that a lot of apps don't need.

Anyway, I'll try to put together a "release notes" bLog post soon with more details on the openId stuff. I intend to get back into the discipline of updating the bLog at least once a week - there's always something to talk about. For example, I actually have a little openid-helper web service that I want to get up online with Amazon web services. I'm not sure whether the openId helper is really all that useful, but AWS is giving away 12 months of free use, so I might as well give it a try - see what the fuss is about.

Thursday, March 24, 2011

cops and gangstas

We watched the penultimate episode of The Wire season 3 last night (on Netflix DVD). Omar and Brother Mazon doin' what they do. I love it!

Friday, March 18, 2011

Seafood Watch Pocket Guide

I was just listening to Barton Seaver's TED Talk, and he mentioned Monterey Bay Aquarium's seafood pocket guide, so I checked it out. The guide recommends ocean-friendly seafood choices. What a great idea! Just print a guide out, keep it in my wallet, and check it the next time I buy seafood at a restaurant or the grocery.

By the way - a great way to organize podcasts in iTunes is to setup a "smart playlist" where:
"Genre contains Podcast AND Plays is 0".

Thursday, March 10, 2011

Facebooking the bLog

I got the brain wave today to add a Facebook Like button to the bLog, and spent a couple hours banging my head against the dev docs before I tracked down Yaniv Kessler's bLog laying the whole thing out. Thanks, Yaniv!

Of course, as I'm implementing Yaniv's instructions I come across this block in the bLog's design template:

     <b:includable id='shareButtons' var='post'> ...
Freakin' blogger has a config checkbox that turns on a bunch of social-sharing buttons. Just call me Reuben "day late, dollar short" Pasquini.

Wednesday, March 09, 2011

Cookie Monster loves cookies!!!

C is for cookie - the peoples dessert; anyone can make it. The hardest part of making a cookie is creaming the butter, and that just requires muscles or machines - no brains needed. That's good enough for me.

Of course, some cookies are better than others. Everyone has his opinion, but here's some tips for how I like my chocolate chip cookies. These ideas evolved over years of criticism from Joanne complaining that I don't make the cookies right, but she's mostly happy now.

  • First, only make half the recipe. The recipe on the back of the bag of chocolate chips calls for half a pound of butter, and makes about 36 cookies. Who wants 36 cookies ? Just halve the recipe, and make 9 at a time. The dough stays good in the fridge for 5 days or so. I usually start with the ghirardelli's recipe, and go from there.
  • Next, only use half the chips. Whoever sold that bag of chips wants to stuff you full of chocolate, but I like a cookie with some chips, not chips with some cookie. So when making half the ghirardelli recipe, only use 1/2 cup of chips - not a full cup. That was Joanne's idea.
  • I'm a nuts guy. I like a half cup of chopped pecans (walnuts are ok too) to go with the half cup of chips. Joanne doesn't like nuts, but I make the cookies, so nuts are in. I'm the man!
  • Cover the dough, and let it sit in the fridge for a half a day. I don't know why, but the cookies are better. That's a tip from an old NY Times article.
  • Joanne likes her cookies "crispy" - cooked all the way through - brown on top - no goo in the middle. Cook chocolate chip cookies at 375 degrees (ghiradelli got that right), but go for 12-13 minutes depending. Just keep an eye on the cookies after 10 minutes - pretend you're the Oracle in the Matrix. I used to be a crispy on the outside goo in the middle guy, but I've been converted.
  • Most people like a warm from the oven cookie, but I actually like a cookie best an hour or two after it comes out of the oven. Somehow the cookie settles down chemically after an hour - not too hot, holds together well, nice texture - good "mouth feel" (isn't that a disgusting phrase?). At any rate - let the cookie cool a little bit on the sheet, then on a rack - outside of dogs' reach (very important). You'll just piss yourself off if you don't wait, and you try to pickup a cookie off the sheet, and it just collapses under it's own weight, because it's still goo, but you shove the goo into your mouth, and burn your mouth, because it's too hot. Not that I ever did that ...

Tuesday, March 08, 2011

Moving glassfish install

When I setup a new server virtual machine it's not unusual for me to install something in one place, then decide later that I don't like that layout, and start moving directories around. That almost always causes me trouble, and I ran into problems today when I moved a working glassfish install from /A to /B - after the move glassfish hung on startup. Fortunately, I was able to figure it out. Here's the trick if there are any other frickjacks out there that also like to pointlessly make things hard for themselves - just erase the felix osgi bundle cache files under glassfish/domains/domain1/osgi-cache/felix/. Those files cache the path to osgi bundle jars at the time of the last glassfish startup.

Sunday, March 06, 2011

little plans

Last week I updated the littleware project site with a fresh binary code drop - including the first little distro zip bundle download. The little distro unzips into littleware/client, littleware/server, and littleware/webapp folders. The server runs an embedded derby (javadb) database and exports littleware's API services via RMI to standalone and web clients. There are more details on the getting started page.

C:\Users\pasquini\Apps\littleware\server\bin
> .\littleServer.bat
Mar 5, 2011 2:56:10 PM littleware.base.DynamicEnum registerMemberIfNecessary
FINE: Registering new new DynamicEnum type: littleware.asset.AssetType
Mar 5, 2011 2:56:11 PM littleware.base.DynamicEnum registerMemberIfNecessary
FINE: Registering new new DynamicEnum type: littleware.security.auth.ServiceType
Mar 5, 2011 2:56:11 PM littleware.bootstrap.AbstractLittleBootstrap bootstrap
FINE: Check bootstrap module: littleware.apps.filebucket.server.BucketServerModule
Mar 5, 2011 2:56:11 PM littleware.bootstrap.AbstractLittleBootstrap bootstrap
FINE: Check bootstrap module: littleware.apps.tracker.server.TrackerServerModule
Mar 5, 2011 2:56:11 PM littleware.bootstrap.AbstractLittleBootstrap bootstrap
FINE: Check bootstrap module: littleware.bootstrap.server.AssetServerModule
Mar 5, 2011 2:56:11 PM littleware.bootstrap.AbstractLittleBootstrap bootstrap
FINE: Check bootstrap module: littleware.bootstrap.server.SimpleServerBuilder$Bootstrap$1
Mar 5, 2011 2:56:11 PM littleware.bootstrap.server.AssetServerModule configure
INFO: Configuring JPA in standalone (hibernate) mode ...
Mar 5, 2011 2:56:11 PM littleware.base.PropertiesLoader loadProperties
FINE: Attempting to load properties for: littleware.properties, true
Mar 5, 2011 2:56:11 PM littleware.base.PropertiesLoader loadProperties
FINE: Loaded props: 2
Mar 5, 2011 2:56:11 PM littleware.base.PropertiesLoader loadPropsFromFile
INFO: Loading config\littleware.properties
Mar 5, 2011 2:56:11 PM littleware.base.PropertiesLoader loadProperties
FINE: Attempting to load properties for: littleware_jdbc.properties, true
Mar 5, 2011 2:56:11 PM littleware.base.PropertiesLoader loadProperties
FINE: Loaded props: 2
Mar 5, 2011 2:56:11 PM littleware.base.PropertiesLoader loadPropsFromFile
INFO: Loading config\littleware_jdbc.properties
Mar 5, 2011 2:56:11 PM littleware.db.DbGuice bindDataSource
INFO: Binding DataSource datasource.littleware to jdbc:derby:littleware
Mar 5, 2011 2:56:11 PM littleware.asset.server.db.jpa.AbstractGuice configure
FINE: Configuring JPA database access
Mar 5, 2011 2:56:11 PM littleware.base.PropertiesLoader loadProperties
FINE: Attempting to load properties for: littleware.properties, false
Mar 5, 2011 2:56:12 PM littleware.base.PropertiesLoader loadProperties
FINE: Attempting to load properties for: littleware.properties, false
15 [main] INFO org.hibernate.cfg.annotations.Version - Hibernate Annotations 3.5.0-Final
37 [main] INFO org.hibernate.cfg.Environment - Hibernate 3.5.0-Final
39 [main] INFO org.hibernate.cfg.Environment - hibernate.properties not found
44 [main] INFO org.hibernate.cfg.Environment - Bytecode provider name : javassist
52 [main] INFO org.hibernate.cfg.Environment - using JDK 1.4 java.sql.Timestamp handling
183 [main] INFO org.hibernate.annotations.common.Version - Hibernate Commons Annotations 3.2.0.Final
202 [main] INFO org.hibernate.ejb.Version - Hibernate EntityManager 3.5.0-Final
316 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: littleware.asset.server.db.jpa
.AssetEntity
376 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity littleware.asset.server.db.jpa.AssetEntity on t
able asset
468 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: littleware.asset.server.db.jpa
.AssetAttribute
469 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity littleware.asset.server.db.jpa.AssetAttribute o
n table asset_attr
480 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: littleware.asset.server.db.jpa
.AssetDate
481 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity littleware.asset.server.db.jpa.AssetDate on tab
le asset_date
484 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: littleware.asset.server.db.jpa
.AssetLink
488 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity littleware.asset.server.db.jpa.AssetLink on tab
le asset_link
495 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: littleware.asset.server.db.jpa
.TransactionEntity
498 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity littleware.asset.server.db.jpa.TransactionEntit
y on table littleTran
506 [main] INFO org.hibernate.cfg.AnnotationBinder - Binding entity from annotated class: littleware.asset.server.db.jpa
.AssetTypeEntity
510 [main] INFO org.hibernate.cfg.annotations.EntityBinder - Bind entity littleware.asset.server.db.jpa.AssetTypeEntity
on table x_asset_type
586 [main] INFO org.hibernate.cfg.annotations.CollectionBinder - Mapping collection: littleware.asset.server.db.jpa.Asse
tEntity.attributeSet -> asset_attr
586 [main] INFO org.hibernate.cfg.annotations.CollectionBinder - Mapping collection: littleware.asset.server.db.jpa.Asse
tEntity.dateSet -> asset_date
587 [main] INFO org.hibernate.cfg.annotations.CollectionBinder - Mapping collection: littleware.asset.server.db.jpa.Asse
tEntity.linkSet -> asset_link
600 [main] INFO org.hibernate.cfg.AnnotationConfiguration - Hibernate Validator not found: ignoring
675 [main] INFO org.hibernate.cfg.search.HibernateSearchEventListenerRegister - Unable to find org.hibernate.search.even
t.FullTextIndexEventListener on the classpath. Hibernate Search is not enabled.
684 [main] INFO org.hibernate.connection.ConnectionProviderFactory - Initializing connection provider: org.hibernate.ejb
.connection.InjectedDataSourceConnectionProvider
688 [main] INFO org.hibernate.ejb.connection.InjectedDataSourceConnectionProvider - Using provided datasource
2821 [main] INFO org.hibernate.cfg.SettingsFactory - RDBMS: Apache Derby, version: 10.5.3.0 - (802917)
2821 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC driver: Apache Derby Embedded JDBC Driver, version: 10.5.3.0 -
 (802917)
2867 [main] INFO org.hibernate.dialect.Dialect - Using dialect: org.hibernate.dialect.DerbyDialect
2886 [main] INFO org.hibernate.transaction.TransactionFactoryFactory - Transaction strategy: org.hibernate.transaction.J
DBCTransactionFactory
2889 [main] INFO org.hibernate.transaction.TransactionManagerLookupFactory - No TransactionManagerLookup configured (in
JTA environment, use of read-write or transactional second-level cache is not recommended)
2889 [main] INFO org.hibernate.cfg.SettingsFactory - Automatic flush during beforeCompletion(): disabled
2895 [main] INFO org.hibernate.cfg.SettingsFactory - Automatic session close at end of transaction: disabled
2903 [main] INFO org.hibernate.cfg.SettingsFactory - Scrollable result sets: enabled
2904 [main] INFO org.hibernate.cfg.SettingsFactory - JDBC3 getGeneratedKeys(): disabled
2911 [main] INFO org.hibernate.cfg.SettingsFactory - Connection release mode: auto
2914 [main] INFO org.hibernate.cfg.SettingsFactory - Default batch fetch size: 1
2916 [main] INFO org.hibernate.cfg.SettingsFactory - Generate SQL with comments: disabled
2919 [main] INFO org.hibernate.cfg.SettingsFactory - Order SQL updates by primary key: disabled
2923 [main] INFO org.hibernate.cfg.SettingsFactory - Order SQL inserts for batching: disabled
2926 [main] INFO org.hibernate.cfg.SettingsFactory - Query translator: org.hibernate.hql.ast.ASTQueryTranslatorFactory
2934 [main] INFO org.hibernate.hql.ast.ASTQueryTranslatorFactory - Using ASTQueryTranslatorFactory
2935 [main] INFO org.hibernate.cfg.SettingsFactory - Query language substitutions: {}
2936 [main] INFO org.hibernate.cfg.SettingsFactory - JPA-QL strict compliance: enabled
2939 [main] INFO org.hibernate.cfg.SettingsFactory - Second-level cache: enabled
2942 [main] INFO org.hibernate.cfg.SettingsFactory - Query cache: disabled
2944 [main] INFO org.hibernate.cfg.SettingsFactory - Cache region factory : org.hibernate.cache.impl.NoCachingRegionFact
ory
2947 [main] INFO org.hibernate.cfg.SettingsFactory - Optimize cache for minimal puts: disabled
2949 [main] INFO org.hibernate.cfg.SettingsFactory - Structured second-level cache entries: disabled
2963 [main] INFO org.hibernate.cfg.SettingsFactory - Statistics: disabled
2963 [main] INFO org.hibernate.cfg.SettingsFactory - Deleted entity synthetic identifier rollback: disabled
2965 [main] INFO org.hibernate.cfg.SettingsFactory - Default entity-mode: pojo
2967 [main] INFO org.hibernate.cfg.SettingsFactory - Named query checking : enabled
2968 [main] INFO org.hibernate.cfg.SettingsFactory - Check Nullability in Core (should be disabled when Bean Validation
is on): enabled
3054 [main] INFO org.hibernate.impl.SessionFactoryImpl - building session factory
3447 [main] INFO org.hibernate.impl.SessionFactoryObjectFactory - Not binding factory to JNDI, no JNDI name configured
Mar 5, 2011 2:56:16 PM littleware.asset.server.db.jpa.SimpleJpaTransaction endDbUpdate
FINE: Transaction complete, rollback: false
Mar 5, 2011 2:56:17 PM littleware.bootstrap.server.AssetServerModule$Activator start
INFO: Looking for RMI registry on port: 1239
Mar 5, 2011 2:56:17 PM littleware.bootstrap.server.AssetServerModule$Activator start
INFO: littleware RMI start ok
Mar 5, 2011 2:56:17 PM littleware.bootstrap.AbstractLittleBootstrap bootstrap
FINE: Waiting for OSGi startup ...
Mar 5, 2011 2:56:17 PM littleware.bootstrap.AbstractLittleBootstrap$SetupActivator$1 frameworkEvent
FINE: Triggering startup barrier ...
< littleware RMI server bootstrap
< hit any key to shutdown
>

The littleware/client folder currently just holds the lgo (littleware go) tool - which is a launcher for a small suite of littleware.lgo.LgoCommand derived tools that include a Swing-based GUI for browsing the littleware node tree database, and the command line version of the Voyager to Vufind harvester tool we developed at Auburn.

C:\Users\pasquini\Apps\littleware\client\bin
> ./lgo.bat help
Mar 5, 2011 3:07:40 PM littleware.security.auth.client.ClientLoginModule login
INFO: User authenticated: pasquini
Mar 5, 2011 3:07:41 PM littleware.base.PropertiesLoader loadPropsFromFile
INFO: Loading C:\Users\pasquini\.littleware\edu\auburn\library\voyager\Voyager.properties
Mar 5, 2011 3:07:41 PM littleware.db.DbGuice bindDataSource
Command use:
     lgo [+user name] [+mode client|app] [+url http://server:port] [+profile cli|swing] command options
        +mode [local|client] -- client establishes session with littleware server, default is client
        +profile [cli|swing] -- specify AppProfile to pass to AppModuleFactory modules at bootup time
        +user [username] -- user to authenticate as in client mode, defaults to current OS user
        +url  [server-info] -- url specifies host/port information for the littleware server in client mode

command.not.found:
   No help found for command: , available commands:
    littleware.apps.lgo.GetAssetCommand [get, cat] Get the asset at a given path
    littleware.apps.lgo.ListChildrenCommand [ls] List the children under a given path
    littleware.apps.lgo.DeleteAssetCommand [delete, rm] Delete as asset at a given path
    littleware.apps.lgo.GetByNameCommand [byname, nget] Get the asset at a given path
    littleware.lgo.XmlEncodeCommand [xencode, xenc] lgo xmlencode
    littleware.apps.lgo.CreateUserCommand [mkuser, adduser] Create a littleware.USER under /littleware.home/Users/
    littleware.apps.lgo.SetImageCommand [setimage] Associate an image with an asset at a given path
    littleware.lgo.EzHelpCommand [help] lgo help <command>
    littleware.apps.lgo.GetRootPathCommand [abspath] Convert the given path to a rooted path
    littleware.apps.lgo.LgoBrowserCommand [browse] launch littleware browser
    littleware.apps.lgo.CreateFolderCommand [mkdir] Create a littleware.GENERIC asset at a given path
    littleware.apps.lgo.CreateLockCommand [mklock] Create a littleware.LOCK asset at a given path
    edu.auburn.library.tool.vygr2vfind.view.V2vLgoCommand [v2v] Copy records from Voyager to a Vufind Solr index
C:\Users\pasquini\Apps\littleware\client\bin
> ./lgo.bat help v2v
Mar 5, 2011 3:09:45 PM littleware.security.auth.client.ClientLoginModule login
INFO: User authenticated: pasquini
Mar 5, 2011 3:09:45 PM littleware.base.PropertiesLoader loadPropsFromFile
INFO: Loading C:\Users\pasquini\.littleware\edu\auburn\library\voyager\Voyager.properties
Mar 5, 2011 3:09:45 PM littleware.db.DbGuice bindDataSource
Command use:
     lgo [+user name] [+mode client|app] [+url http://server:port] [+profile cli|swing] command options
        +mode [local|client] -- client establishes session with littleware server, default is client
        +profile [cli|swing] -- specify AppProfile to pass to AppModuleFactory modules at bootup time
        +user [username] -- user to authenticate as in client mode, defaults to current OS user
        +url  [server-info] -- url specifies host/port information for the littleware server in client mode

edu.auburn.library.tool.vygr2vfind.view.V2vLgoCommand:
   Copy records from Voyager to a Vufind Solr index

Copy records from Voyager to a Vufind Solr index.

      v2v -min [min] -max [max] [-halt [HH:mm] -continue [stubfile]] -solr /path/to/solr/index/biblio
      v2v -age [age] -solr /path/to/solr/index/biblio
      v2v -idfile [file full of bib-ids] -solr /path/to/solr/index/biblio

      Note: loading vufind.properties from  DocsFolder/vygr2vfind
      Note: specify jdbc URL for Voyager database in
           ~/.littleware/edu/auburn/library/voyager/Voyager.properties -
             datasource.voyager = jdbc:oracle:thin:user/password@host:port:vger
browser screen

There's not much useful there really, but it's nice to at least have something to show. The code drop also updates the IVY artifacts and web-start apps hosted by littleware's ivy2maven2 repository as well as the online javadoc and scaladoc API docs.

I was pretty happy with how smoothly the release process went thanks to the ivy build setup. The root build.xml file includes buildAll and buildAndTest rules that a local jenkins continuous integration server executes whenever I push a change to my personal repositories. Everything would have been great if not for the problems that with java 6 update 24 web-start security that plagued me. Ugh!

Anyway, I now need to decide what littleware tasks to tackle next. On the one hand there are several API and infrastructure design flaws that I need to eventually address, but on the other hand it would be good to build out some useful littleware-based applications to demonstrate the systems utility. On a 3rd hand the more application level code I write the more annoying an API-level refactoring will be to propagate, and a 4th hand realizes that application level development is the best way to get ideas for improving an API, and a 5th hand points out that the experience and learning I gain is the main benefit of working on littleware in the first place. I'll probably just continue with the tasks outlined in the current SCRUM sprint, and continue to develop the scala tools in littleware's catalog repository.

The academic library is a terrible market to work for as a library is amazingly cheap, and even if the library had money - it wouldn't want to change anything. A true librarian would turn the clock back to card catalog days in a heart beat. If a librarian ever gets the bomb, then we're all in trouble; he'll blast us back to the stone age smiling ear to ear. These negatives are positives for a loan frickjack that can bundle a suite of open source tools in a cloud-hosted library-in-a-box, and make money charging $200- a month. We'll see what I think next month.

Saturday, March 05, 2011

Chinese breakfast!

It's a rainy day in Auburn. The dogs and I were drenched on our morning walk, and it felt like a good day to cook some breakfast when we got home. Cong you bing (葱油饼 - green onion pancake) is one of the few Chinese dishes I make on a regular basis. Just mix a cup of water with some flour and a little oil and salt to make a dough, split it into 4 parts, roll out, jelly roll with salted diced green onions, stretch out to a pancake, and fry it up. I'm always surprised when something so simple (flour, water, green onions, salt, oil) is so good.

jellyroll

One trick when rolling out the pancake is - don't roll it out much. Pressing too much with a rolling pin on the dough with green onions mixed in squeezes water out of the onions, and the dough turns to mush. Just roll the dough out a little bit with a rolling pin, then pick the bing (pancake) up, and stretch it with your hands - like pizza dough.

stretch

The bing is great with a fried egg.

bingfry
on plate

fight the tyranny of basil pesto!

I love pesto, but, frickjack that I am, I always assumed pesto was made with basil, and it's hard to get my hands on a big pile of basil without spending a fortune, so I didn't eat pesto much ... until now! I can't remember how I finally got a clue, but I think I was wistfully looking over the ingredients of some pesto jar at the grocery, and I saw spinach instead of basil. A freakin' light bulb lit up above my head (very low watt is all my head can manage), and I realized I no longer had to be a slave to basil - that tyrant!

Anyway, spinach is plentiful this time of year at the grocery, and it makes great pesto. Just mince up the spinach, then blend with olive oil, parmesan, salt, pepper, - pine nuts if you got 'em; toss with pasta ... oh so good!

pesto
pasta
picasa

Friday, March 04, 2011

Silly user, tricks are for programmers!

At the one and (thankfully) only systems department meeting I attended at the library over a month ago there was a short conversation about how systems installed an obscure version of perl (not ActiveState or Cygwin) on a cataloging department Windows XP box, so a cataloger could run a magic perl script handed down from the ages. The magic perl script processed a file of MARC records from our "books on order" vendor to update each record with the correct Auburn location code for where the record's book would be shelved.

I volunteered at the time that Jack or I could whip out a java or C# program to replace the perl script. I was warned that this was complex code that tied together purchase orders, bib, holdings, and item records. I had to see this amazing piece of work!

When I finally got around to looking at the perl script and a sample input I found about 50 lines of perl and a simple file full of BIB records. I knew it!

Anyway, I wrote a little webstart app (MarcConvert - more info here) that could probably replace the perl script. MarcConvert reads an input file of records in MARC or MARCXML format, and writes the records back out to an output file in MARC, MARCXML, or SIMPLE (readable) format while optionally applying a "location filter". I'll e-mail a few people at the library to see if they want to give it a try. MarcConvert should be useful in a few ways.

  • First, MarcConvert can transform a file of MARC records to a readable format, so a cataloger can take a quick look at what is actually in the file.
  • Next, if a cataloger wants to make a simple change to a file of MARC records, then he/she can just convert the file to XML, make the changes to the XML in a standard editor, then convert the XML file back to MARC.
  • Finally, a cataloger can apply the "location filter" to a MARC file without installing perl.

Thursday, March 03, 2011

jre6 update 24 sucks

It's always annoying when something outside my control changes, and breaks my code. I received an e-mail the other day from my former coworker at the library reporting that one of the little tools I wrote stopped working on some machines. I suggested a few things to try, but I assumed that something ridiculous happened at the library, and they would fix it up. By the end of the day they let me know that web start with the new java 6 update 24 threw a SecurityException when launching my tool.

I at first could not reproduce the problem running a copy of the library tools, but that was just because I was running a pre-update version of java. My Windows 7 laptop has 3 java installs - a 64 bit jre, a 64 bit jdk, and a 32 bit jre - only the 32 bit jre has update 24. My netbeans developer environment and powershell command line both point at the pre-update jdk, but Chrome's java plugin runs the 32 bit jre with update 24. I was already annoyed at this point. Ugh.

Google helped me narrow the problem down to web-start's interaction with the Apache felix OSGi implementation that I use. I wrote up a little test case (below), and submited a ticket to Oracle's java team.

package littleware.demo;

import java.io.PrintWriter;
import java.io.StringWriter;
import java.net.URL;
import java.net.URLClassLoader;
import java.sql.Connection;
import java.util.HashMap;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.swing.JFrame;
import javax.swing.JOptionPane;
import javax.swing.JScrollPane;
import javax.swing.JTextArea;
import javax.swing.SwingUtilities;
import javax.swing.WindowConstants;
import org.apache.felix.framework.Felix;


public class JavaToy {
    private static final Logger log = Logger.getLogger( JavaToy.class.getName() );

    public static class AppRunner implements Runnable {
        public void run() {
            final StringWriter swriter = new StringWriter();
            final PrintWriter pwriter = new PrintWriter( swriter );
            pwriter.append( "Class path: " ).append(
                    System.getProperty( "java.class.path" )
                    ).append( "\n\n-------------------------\n" );
            final ClassLoader classLoader = this.getClass().getClassLoader(); //Thread.currentThread().getContextClassLoader();
            if ( classLoader instanceof URLClassLoader ) {
                pwriter.append( "URLClassLoader:\n" );
                for ( URL url : ((URLClassLoader) classLoader).getURLs() ) {
                    pwriter.append( url.toString() ).append( "\n" );
                }
                pwriter.append( "\n--------------------------------\n" );
            }
            try {
                Class.forName( "bogus.DoesNotExist" );
                pwriter.append( "No exception thrown on bogus class load\n" );
            } catch ( Exception ex ) {
                pwriter.append( "Caught exception loading bogus class: " ).append( ex.toString() ).append( "\n" );
                ex.printStackTrace(pwriter);
            }
            pwriter.flush();
            final JFrame jframe = new JFrame( "Webstart test" );
            final JTextArea jtext = new JTextArea( swriter.toString(), 20, 40 );
            jframe.add( new JScrollPane( jtext ) );
            jframe.pack();
            jframe.setDefaultCloseOperation(WindowConstants.EXIT_ON_CLOSE);
            jframe.setVisible(true);
        }
    }
    public static void main( String[] args ) {
        try {
            log.log( Level.INFO, "Launching felix!" );
            (new Felix(new HashMap<String, Object>())).start();
            Thread.sleep( 2000 );
        } catch (Exception ex) {
            log.log(Level.SEVERE, "Caught exception", ex);
            System.exit(0);
        }
        SwingUtilities.invokeLater( new AppRunner() );
    }
}

Web-start with a pre-update-24 jre throws the expected ClassNotFoundException and yields this result:

Class path: C:\Program Files\Java\jre6\lib\deploy.jar

-------------------------
URLClassLoader:
file:/C:/Users/pasquini/Documents/Code/JavaToy/dist/JavaToy.jar
file:/C:/Users/pasquini/Documents/Code/JavaToy/dist/lib/felix-2.0.4.jar
http://felix.extensions:9/

--------------------------------
Caught exception loading bogus class: java.lang.ClassNotFoundException: bogus.DoesNotExist
java.lang.ClassNotFoundException: bogus.DoesNotExist
...

An update-24 jre throws a troublesome SecurityException with this result:

Class path: C:\\Program Files (x86)\\Java\\jre6\\lib\\deploy.jar

-------------------------
URLClassLoader:
file:/C:/Users/pasquini/Documents/Code/JavaToy/dist/JavaToy.jar
file:/C:/Users/pasquini/Documents/Code/JavaToy/dist/lib/felix-2.0.4.jar
http://felix.extensions:9/

--------------------------------
Caught exception loading bogus class: java.lang.SecurityException: Permission denied: http://felix.extensions:9/bogus/DoesNotExist.class
java.lang.SecurityException: Permission denied: http://felix.extensions:9/bogus/DoesNotExist.class
...

The bug robot sent me a nice response for my trouble.

Dear Java Developer,

Thank you for your interest in improving the quality of Java Technology.

Your report has been assigned an internal review ID of 1990127, which is NOT visible on the Sun Developer Network (SDN).

Please be aware that the large volume of reports we receive sometimes prevents us from responding individually to each message.

If the information is determined to be a new Bug or RFE, or a duplicate of a known Bug or RFE, you will receive a followup email containing a seven digit bug number. You may search for, view, or vote for this bug in the Bug Database at http://bugs.sun.com/.

If you just reported an issue that could have a major impact on your project and require a timely response, please consider purchasing one of the support offerings described at http://developers.sun.com/services/.

Bla bla bla ...

Fortunately, the felix user mailing list had a thread on the issue, and some cool Karl Pauls guy pointed me in the right direction to throw together a little patch (see below) that got me back in business.

Hurray for open source! Of course now its 3 days of head banging, trying random hacks, and considering dropping felix or web-start later. I'm too relieved to be bitter! Oracle actually closed a related ticket as "Not a bug", because the http://... URL in the ClassLoader path violates the web-start single-origin policy - now more strictly enforced in update 24. That looks like some lazy Oracle developer dodging work to me, because a signed web-start app granted all-permissions can do almost anything (open network ports, erase the disk, write and launch a program, whatever), so what's the deal with the SecurityException scanning the class path ?

Re: Java 6 Update 24 breaks Felix
Reuben Pasquini
Thu, 03 Mar 2011 10:00:46 -0800

Here's an ExtensionManager patch that got me back in business with webstart

(don't know what's up with the sun.org.moz... import in Felix.java):

---------------------

C:\Users\pasquini\Documents\Code\felix-framework
> svn diff -x -w
Index: src/main/java/org/apache/felix/framework/Felix.java
===================================================================
--- src/main/java/org/apache/felix/framework/Felix.java (revision 1076687)
+++ src/main/java/org/apache/felix/framework/Felix.java (working copy)
@@ -75,8 +75,8 @@
 import org.osgi.framework.hooks.service.ListenerHook;
 import org.osgi.service.packageadmin.ExportedPackage;
 import org.osgi.service.startlevel.StartLevel;
-import sun.org.mozilla.javascript.internal.UintMap;

+
 public class Felix extends BundleImpl implements Framework
 {
     // The secure action used to do privileged calls
Index: src/main/java/org/apache/felix/framework/ExtensionManager.java
===================================================================
--- src/main/java/org/apache/felix/framework/ExtensionManager.java
(revision 1076687)
+++ src/main/java/org/apache/felix/framework/ExtensionManager.java
(working copy)
@@ -87,6 +87,10 @@

     static
     {
+        // Allow system-property based disable of ExtensionManager.
+        // Adding http://felix.extension:9 to the class path causes
problems
+        // in webstart which enforces a "single origin" policy as of jre 6
update 24.
+        if( System.getProperty( "felix.extensions.enabled", "true"
).equalsIgnoreCase( "true" ) ) {
         // pre-init the url sub-system as otherwise we don't work on
gnu/classpath
         ExtensionManager extensionManager = new ExtensionManager();
         try
@@ -113,7 +117,10 @@
             extensionManager = null;
         }
         m_extensionManager = extensionManager;
+        } else {
+            m_extensionManager = null;
     }
+    }

     private final Logger m_logger;
     private final Map m_headerMap = new StringMap(false);

---------------------------



Then add

           <property name="felix.extensions.enabled" value="false" />
in the .jnlp <resources> block, and I'm back in business.

Thanks for pointing me in the right direction.

Hope something like this makes it into the  next release ... ?



Cheers,

Reuben


On Thu, Mar 3, 2011 at 10:52 AM, Reuben Pasquini <reu...@frickjack.com>wrote:

> I agree it sounds like an Oracle bug, but a workaround would be great.
>
> A config parameter that disables extension bundles works for me too.
>
> I embed felix in a very limitted way to manage the startup/shutdown
> lifecycle
>
> via BundleActivators, so I don't think my apps will miss the extension
> bundles.
>
> Let me know when you have a patch checked in, and I'll give it a try ...
>
> or if you think it's a small change, then point me at the right part of the
> felix
>
> repo, and I'll try to throw together a patch for you.
>
> Thanks for the help!
>
>
>
> Cheers,
>
> Reuben
>
> On Thu, Mar 3, 2011 at 3:16 AM, Karl Pauls <karlpa...@gmail.com> wrote:
>
>> On Thu, Mar 3, 2011 at 10:08 AM, Bauersachs Ingo
>> <ingo.bauersa...@fhnw.ch> wrote:
>> >> Problem is this doesn't make sense to me as the app has allpermission
>> >> (or sets the security manager to null even). Why enforce this then? It
>> >> sounds like a bug (and there are several at oracle by now). Lets see
>> >> what they do
>> >
>> > Indeed, this makes no sense.
>> >
>> >> I'll try to find a workaround on the weekend and if
>> >> not, I'll probably add a property to disable extension bundles (which
>> >> would make the url go away - only extension bundles will not work).
>> >
>> > 6u24 is out and I needed a workaround fast. Changing the extension url
>> from http to file seems to satisfy the same origin policy. If my change
>> doesn't cause other side-effects it might even be a permanent solution. (I
>> have far to less experience with OSGi/Felix to really comment on this)
>>
>> If it works for you it is ok for now - it is not the permanent
>> solution as there will be other issues (there is a reason we do what
>> we do there - related to extension bundles). But if i can't find
>> something better the solution will be close (i.e., provide a way to
>> disable extension bundles -> don't register this url at all).
>>
>> regards,
>>
>> Karl
>>
>>
>> > Regards,
>> > Ingo

Monday, February 28, 2011

Tweaking Google Code's Issues Gadget

I'm a big fan of Google Code where I host the littleware project. I've been playing around with gcode's issue tracker trying to figure out some way to embed the tasks associated with a code sprint into the sprint's overview documentation, and I eventually stumbled upon a nice issue-tracker gadget in a Gadgets for Google Code bLog entry that almost does what I want, but not quite!

Fortunately, it turns out that the issue-tracker gadget is one of the gadgets developed by google's code-hosting team, and the code is open source here. I cloned a copy of their issue-tracker gadget under a littleware 'gadgets' repository, and was able to add a milestone parameter to the gadget with only a little bit of trouble. I think I'm all set!


I submitted an issue with the gadgets for google code project with some information about my patch. It would be cool if they fold the patch into their code - we'll see what happens.

Wednesday, February 23, 2011

Success tastes like pizza!

I think I finally have the blogger podcast thing working. To recap - I started by hosting my .mp3 media file with Google docs, and that works great, except Google docs publishes the .mp3 file to a URL that does not include the .mp3 file extension (something like https://docs.google.com/uc?id=0B-82pgvnWy-8OWM2ZWJhYzktOWU5OC00MTI4LWFiZWYtZWNhOTQ0ZjlmMDA2&export=download&hl=en), and iTunes requires that a podcast's enclosure references a URL with a valid media extension (more details here).

So I went looking for some other free place to host my .mp3 files, and found Microsoft's sky drive, which preserves a file's extension in its download URL (ex: http://public.blu.livefilestore.com/y1p5DNvAssqO8iaDVAKdFChzS01Pj_v3M9B68-VJdTj9sQr43GHTr8pThRglPvVeIzJlbU4AE3nQbW7NiocbkkOLA/shCore.js?download&psid=1). Unfortunately, that download URL does not persist - a file's URL changes from day to day.

Finally, I tried Google sites, and (so far) I think that works - with persistent iTunes-friendly URLs like https://sites.google.com/site/frickjack/projects/podcasts/Podcast20110223.mp3?attredirects=0&d=1

This picture shows our dog, Moose, guarding a pizza from our other dog, Ponzi - Moose wants it all for himself! One tip for home made pizza - if you don't have a pizza stone, then an upside down iron skillet works too.

From Food
Picasa logo

Skydrive lets me down!

Ugh!

I was pretty happy when I was able to setup a blogger based podcast with mp3 files served from Microsoft's sky drive, so I went ahead and linked my blogger template to a copy of Gorbachev's syntax highlighter on sky drive too, but it turns out the freakin' sky-drive URL's do not persist, so neither of those nifty tricks work. I just inlined the syntax highlighter into the blogger template, but I'll have to figure something else out for audio-file hosting. There must be some way to get Google Docs URL's to work with iTunes.

This is from Apple's "Making a Podcast" page:
The URL before the GET-style form values (before the first ?) must end in a media file extension (e.g. mp3). To work around this, the feed provider can alter their URL from this: http://www.podcaster.com/load.php?f=&Wipeout.php to this: http://www.podcaster.com/load.mp3?f=&Wipeout.mp3 Notice how it says load.mp3 instead of load.php. It should be possible to accomplish this via various means, such as web server rewrites. iTunes looks at the extension of the path part of the url, i.e. the part before the"?".

Ugh!

JScrollPane a JPanel !

If you're a frickjack like me, then you might believe that a JScrollPane only works with widgets like JTextArea that are specially designed to be scrollable, but (as with so many other beliefs) you would be wrong! Turns out JScrollPane works just fine with JPanel, which is a big deal when you're trying to layout a bunch of widgets on a big form or whatever. Anyway, the scala code below works, so I hope scrolling works for more complicated panels too. I'll let you know if I find out otherwise.

package littleware.demo

import javax.swing._

object Toy {
    def main( args:Array[String] ):Unit = {
        SwingUtilities.invokeLater( new Toy )
    }
}

class Toy extends Runnable {
    override def run():Unit = {
        val jbox = new Box( BoxLayout.Y_AXIS )
        (0 until 40).foreach( (index) => {
            jbox.add( new JLabel( "Test" + index ) )
        }
        )
        val jframe = new JFrame( "Toy" )
        jframe.setDefaultCloseOperation( JFrame.EXIT_ON_CLOSE )
        val scroll = new JScrollPane( jbox )
        scroll.setVerticalScrollBarPolicy( ScrollPaneConstants.VERTICAL_SCROLLBAR_ALWAYS )
        scroll.setPreferredSize( new java.awt.Dimension( 700, 400 ))
        jframe.add( scroll )
        jframe.pack
        jframe.setVisible( true )
    }
}

How do you like that slick syntax highlighting ? If it works (I won't know till I post, so frick), then it's all thanks to Alex Gorbachev's syntax highlighter - which I copied over to Skydrive, and hooked into the bLog template. Alex is probably an evil James Bond villain, but I'll have to paypal him $10- when I get motivated.

That's all pretty exciting stuff. Our dogs were very excited too ...

the pack is bored

Friday, February 18, 2011

Pound cake!

 

What to do with 1/2 pound butter, 5/3 cups sugar, 2 cups flour, ... Good old Fannie Farmer!

 

I also accidentally uploaded this pretty cool picture of my dog, Shug, while playing with Picasa. Go, Shug!

Posted by Picasa

Thursday, February 17, 2011

Blogger + Google Docs Podcast

I often enjoy listening to podcasts while I'm playing around with the computer. The java posse, TED, Entrepreneurial Thought Leaders, and the New Yorker Political Scene are some of my favorites.

I recently began wondering how I could publish my own podcast of (for example) a lecture at the university or whatever with as little expense and effort as possible. It turns out that cheap and easy podcast publishing can be a simple 3 step process thanks to great free online services like Google's docs and blogger, and the very useful open source audacity audio program (or Garage Band is probably even better on the Mac).

  • First, record your podcast with audacity or garage band or your iPhone or whatever. You can get as professional as you want - I'm just a hack for now.
  • Second, publish your audio file online. Google docs gives you 1 GB of storage for free, but Microsoft's skydrive provides 25 GB for free!
  • Third, setup a podcast feed that links to your audio file. A podcast feed is just a specialized RSS feed, which is just an XML file that readers know how to interpret. You could just edit an rss xml file, and publish that via Google docs or skydrive, but Google's blogger platform makes it easy to associate audio files with blog entries, so the blog feed also acts as a podcast feed.
  • Finally, register the podcast's feed url (ex: http://blog.frickjack.com/feeds/posts/default?alt=rss) with iTunes or your favorite podcast player.

Even an idiot like me can pollute the internet with worthless media!

Update ...

I wrote this post before I actually had the podcast feed working, and I wound up driving myself crazy trying to get iTunes to recognize the podcast when hosting the .mp3 file on Google docs. The feed worked fine with Google Reader, but I think iTunes looks at the enclosure URL to decide what kind of media file a feed item references rather than trusting the enclosure's mime type attribute. Google docs generates nonsensical urls that do not preserve the file extension (ex: https://docs.google.com/uc?id=0B-82pgvnWy-8OWM2ZWJhYzktOWU5OC00MTI4LWFiZWYtZWNhOTQ0ZjlmMDA2&export=download&hl=en is a .mp3 file), so iTunes throws a
There are no playable episodes ... The URL might point to text-only episodes, or contain file types that iTunes cannot play.
error when loading the podcast.

Fortunately, Skydrive came to my rescue with an iTunes friendly URL for my audio file: http://m8cb3q.blu.livefilestore.com/y1pCrRuW5NqZivMGgB7_oAUy_g8m_83domL0-7pAkoVezoGWOATDPuuLQNxECsNAK60JSxlYwkgHW2WycExfTMGXBxd4-gM0bqR/Podcast20110217.mp3?download&psid=1.

It also turns out that Google Reader is nice about letting random frickjacks embed its audio player!

Another Update ...

Freakin' sky-drive url's don't persist (there's some kind of cache timeout mojo), so the .mp3 links are now bogus. Back to the drawing board!