26 January 2009

Windows PowerShell: FAIL

What I wanted:
find . -name *.class -exec rm {} \;

What I was told would work:
Remove-Item .\* -recurse -include .class

What I would have settled for (if it had worked):

set fso = CreateObject("Scripting.FileSystemObject")
set fileExpr = new RegExp
fileExpr.Pattern = "class$"
fileExpr.IgnoreCase = True
set curDir = fso.GetFolder(".")
CleanDir curDir, fileExpr

Sub CleanDir(Dir, Expr)
for each File in Dir.Files
if Expr.test(File.Name) then
fso.DeleteFile(Dir.Name & File.Name)
end if
next
for each SubDir in Dir.SubFolders
CleanDir SubDir, Expr
next
end sub


What I finally wound up doing:
[In windows explorer]
Right-click on directory->Search
Click in "Enter all or part of the file name" text box, type ".class"
Click "Search"
Wait
Hit Ctrl-A to select all
Hit "Delete"
Wait (for almost a full minute! WTF?)
Click close button.

Apparently they're shooting for a consistent level of difficulty no matter what the task. Replicate an enterprise database? Five minutes. Import it into Excel, flag outliers and print a formatted report? Five minutes. Delete the file after printing? Five minutes...

11 January 2008

Logitech SetPoint == teh suxx0r!

I like Logitech's trackballs. People were amazed that I would actually spend my own money for a Logitech trackball for my work computer when I had a perfectly good mouse. At last count I had two of the original Trackman Marble, two corded and one cordless Trackman Marble Wheel, and a Marble Mouse. I got the Marble Mouse because my thumb started to be tired if I worked on the computer too long at night (after working on one all day at the office). So, I have the Marble Mouse at work.

The Marble Mouse has two large buttons on either side, and it also has two small buttons just above the large ones. Logitech's SetPoint utility can be used to map those to a keypress or action. When I originally got the MM, I set it up so that the left small button paged down, and the right one paged up. I used it this way for so long, it's become second nature. So when they aren't mapped properly, it drives me nuts.

I just started a new job, with a new computer. I unpacked the MM and hooked it up and downloaded the latest SetPoint from the Logitech site. Launched it up and went to customize the buttons and WTF? I could map the left one to page up, but there was no page down listed. I checked out the installation directory, and there was this XML file (default.xml) with all these "handler sets" configured. It certainly looked like both page down and page up were supported, so why weren't they available? Further digging showed a ton of configuration files in the All Users/Application Data/Logitect/SetPoint directory, but they all had helpful names like "1000001F". So I STFW to see if anyone else had posted a solution to this. Someone had written an "uberButton" utility that claimed to enable all features for all buttons, but it was an 8M executable, which I thought was a little much for my purposes. So I poked around some more and found a directory named "Logitech/SetPoint" in my profile's application data directory. I checked, and lo and behold, there were the current mappings in a file named user.xml. They were references to the handler sets I had noticed in the default.xml file. So I whipped out Vim and changed the references to point to the page up and down handlers. There were also some application-specific "overrides" for these functions, so I set those up too. Logged out and back in and voila! The MouseMan rides (properly) again!

Here's what the configuration file looks like:


<Source>
<UserOptions>
<UserOption Name="BeepKeyboardSettings">0</UserOption>
<UserOption Name="ShowKeyboardPopupSettings">1</UserOption>
<UserOption Name="ShowKeyboardTraySettings">1</UserOption>
<UserOption Name="ShowTrayIcon">1</UserOption>
<UserOption Name="ShowKeyboardSettings">1</UserOption>
</UserOptions>
<Apps>
<App>c:\\progra~1\\micros~2\\office11\\outlook.exe</App>
<App>c:\\program files\\outlook express\\msimn.exe</App>
<App>c:\\progra~1\\micros~2\\office11\\winword.exe</App>
<App>c:\\progra~1\\micros~2\\office11\\powerpnt.exe</App>
<App>c:\\progra~1\\micros~2\\office11\\excel.exe</App>
</Apps>
<Devices>
<Device DisplayName="Marble Mouse" Class="PointingDevice" Model="16777328" NumberOfButtons="4" ConnectionID="">
<Param ACCELERATION="1" AIRACCELERATION="4" AirSpeedX="50" AirSpeedY="50"
AppSpecific="0" ButtonSwapped="0"
DefaultGameModeResolutionPresets=""
DefaultGameModeResolutionPresetsY="" DistinctAirCursor="1"
EnableCentering="1" GameDeviceSuggestGameMode="0"
GameDeviceSuggestOS="0" GameModeButtonAddGame="0"
GameModeButtonResDown="0" GameModeButtonResUp="0"
GameModeButtonResUpWrap="0" GameModeKeepAcceleration="1"
GameModeKeepKeypress="1" GameModeKeepOther="0"
GameModeKeepRightLeft="1" GameModeKeepSpeed="1"
GameModeNotification="0" GameModeResolutionNbPreset="0"
GameModeResolutionPresets="" GameModeResolutionPresetsY=""
GameParticipate="0" GameParticipateDefault="0" GamePowerMode="0"
GameReportEnabled="1" GameReportRate="2" GameResIndex="2"
GameUseAdvanced="0" GamingType="0" LowBatPercentLevel="5"
MenuLocBottom="-1" MenuLocLeft="-1" MenuLocRight="-1"
MenuLocTop="-1" MenuSize="0" OfficePowerMode="0"
ShowButtonsPage="1" SmartMove="0" SpeedX="25" SpeedY="25"
StromboliMice="0" TouchPadMice="0" TrackballMice="1" Trails="0"
TrailsLength="0" UserBatteryMode="0" VScrollAcceleration="0"
VScrollSpeed="0" VoIPClient="" smartshiftmode="0"/>
<Buttons>
<Button Number="1" Name="1">
<Param IconLoc="" Type=""/>
<Trigger Class="ButtonPress">
<Param Button="1" FirstRepeatDelay="0" RepeatDelay="0"
Silent="1" Type="0"/>
<TriggerState Name="ButtonDownUp" HandlerSet="LeftClick">
<Handler Class="MouseButton">
<Param ButtonName="PrimaryButton"/>
</Handler>
</TriggerState>
</Trigger>
</Button>
<Button Number="2" Name="2">
<Param IconLoc="" Type=""/>
<Trigger Class="ButtonPress">
<Param Button="2" FirstRepeatDelay="0" RepeatDelay="0"
Silent="1" Type="0"/>
<TriggerState Name="ButtonDownUp" HandlerSet="RightClick">
<Handler Class="MouseButton">
<Param ButtonName="SecondaryButton"/>
</Handler>
</TriggerState>
</Trigger>
</Button>
<Button Number="4" Name="3">
<Param IconLoc="" Type=""/>
<Trigger Class="ButtonPress">
<Param Button="4" FirstRepeatDelay="0" RepeatDelay="0"
Silent="0" Type="0"/>
<TriggerState Name="ButtonDownUp" HandlerSet="PageDown">
<Handler Class="KeyStroke">
<Param KeyName="{PGDN}"/>
</Handler>
<AppOverride App="mm.exe" HandlerSet="AppOverride_MindMapperBackward"/>
<AppOverride App="winword.exe" HandlerSet="AppOverride_WordBackward"/>
<AppOverride App="msimn.exe" HandlerSet="AppOverride_OutlookExpressBackward"/>
<AppOverride App="outlook.exe" HandlerSet="AppOverride_OutlookBackward"/>
<AppOverride App="powerpnt.exe" HandlerSet="AppOverride_PowerPointBackward"/>
<AppOverride App="nlnotes.exe" HandlerSet="AppOverride_LotusNotesBackward"/>
<AppOverride App="excel.exe" HandlerSet="AppOverride_ExcelBackward"/>
</TriggerState>
</Trigger>

</Button>
<Button Number="5" Name="4">
<Param IconLoc="" Type=""/>
<Trigger Class="ButtonPress">
<Param Button="5" FirstRepeatDelay="0" RepeatDelay="0"
Silent="0" Type="0"/>
<TriggerState Name="ButtonDownUp" HandlerSet="PageUp">
<Handler Class="KeyStroke">
<Param KeyName="{PGUP}"/>
</Handler>
<AppOverride App="mm.exe" HandlerSet="AppOverride_MindMapperForward"/>
<AppOverride App="winword.exe" HandlerSet="AppOverride_WordForward"/>
<AppOverride App="msimn.exe" HandlerSet="AppOverride_OutlookExpressForward"/>
<AppOverride App="outlook.exe" HandlerSet="AppOverride_OutlookForward"/>
<AppOverride App="powerpnt.exe" HandlerSet="AppOverride_PowerPointForward"/>
<AppOverride App="nlnotes.exe" HandlerSet="AppOverride_LotusNotesForward"/>
<AppOverride App="excel.exe" HandlerSet="AppOverride_ExcelForward"/>
</TriggerState>

</Trigger>
</Button>
</Buttons>
</Device>
</Devices>
</Source>


You can browse through the default.xml file and check out what kind of functions are available. This file appears to cover all Logitech keyboards and mice, so there are a lot of things available (zoom in/out, volume up/down, standby/restart/shutdown/logoff, VoIP w/Skype, browser next/previous/search...). From the looks of it, you should also be able to create your own handler sets to handle app-specific functions, or even launch external programs. So if you have to perform some obnoxious series of commands on a regular basis (like, say, FTP an HTML file to a server and launch a browser to look at it), you could script those and bind them to a mouse button! Could be really handy, given a suitably bizarre set of circumstances...

15 October 2007

Selling out -- Round II

For my first "real" job, I was hired out of college before I'd finished my degree. I left to move to The Big City (Chicago) and to make triple what I'd been earning with two half-time computer programming jobs in Iowa. It was rough, as move-to-the-big-city-from-Hicksville-and-get-a-real-job first jobs often are, but the company I worked for was good, I really liked the people I worked with, and I was both challenged to come up with innovative solutions and given the leeway (if not the budget) to implement whatever solution I deemed best. However, the company was a non-profit, and I got discouraged whenever I talked with any of my old college friends who had likewise gotten real jobs and were making significantly more than I was. So, after a couple of years I jumped ship, selling out for a 20% raise and a change of scenery (to Philadelphia). The people at my new job were nice enough, but I was now a junior developer, and had to follow corporate standards and have a lot of my projects designed by some one else. I missed my old job, but there was no way to go back.

Fast forward to today. I'm working for a company I like, with people I really get along with, and I have the freedom to implement whatever solutions I like using whatever technologies I like. It's taken me a good four years of consulting and networking to get where I'm at, and I'm finally happy about my job for the first time in a long time. So I probably should have expected the phone call.

As it turns out, somebody (a recruiter) I'd worked with when I was consulting had kept a copy of my resume. He'd never been able to place me because I was only looking for jobs in Lincoln, and there weren't many companies in Lincoln that used Java. Now, it seems, one of the few that did was looking for new senior-level staff. So, under the misapprehension that "it never hurts to talk", I sent an updated copy of my resume to the recruiter. "Just to see what they had to say". Well, what they said was basically "20+% pay raise, team technical lead, possible promotion to junior architecture position within a year". So, I did what anyone who had been cast aside by his corporate overlord at some point in his career would do. I jumped. Sold out. Turned my back on the folks who had given me the freedom to exercise my technical judgement and the support to make me successful at it, and headed on down the line.

[Actually, I'm still on good terms with them, and agreed before I left to make myself available to provide whatever knowledge transfer I could once they hired my replacement, but still...]

So now, here I am, with my big paycheck and my serious-sounding title (not quite "Lord of the Realm, Defender of the Faith, Emperor of the Enterprise Service Bus", but better than "Spec Programmer II") and I'm wondering how I'm going to fit in around here. Everybody's very nice, and I've been able to contribute and I have projects and everything, but I still don't feel like I did the right thing. I mean, I did (it wasn't just for the money, there were significant career-direction factors that played a part), it just doesn't feel that way yet.

13 June 2007

The JAXP hits!!--More--

I had a spare moment, so I thought I'd shoot myself in the foot. Well, the shooting was unintentional, but it happens so often under these circumstances, you'd think I'd have figured out when to flinch by now.

Standard near-end-of-project clean-up, replacing any remaining "err.printStackTrace()" calls with proper logging statements and other tidying-up. I had in my notes "Add input XML validation". We hadn't had any problems with XML formatting before, but it seemed like a good thing to have when we went into production (can you hear the warning siren going off?). So I cons up a quick XMLValidator class with an inputIsValid() method and get that working. Plug it into the project and test all the various inputs we have, everything is happy, so I deploy it to our test server and have our external development partner have a go at it.

On the first run, he reports "Hey, it gives me a validation error." So we check, and sure enough, he's got one of the attributes misspelled. We fix that and re-submit and he gets another one. We check, and find another one. Fix and re-submit, get another validation failure. Only this time, we can't figure out why. The error message he's getting is "cvc-elt.1: Cannot find the definition of 'SchoolInfo'" (which is one of the elements in our schema). We can't find the problem, so I cut the XML from the server log and submit it on my test system where I can step through the validation with the debugger. Lo and behold, no problem! :/

So what's different between dev and test? Same app server, same jar file, whoops! I'm running Java 1.5 on my dev server, but the test server is 1.6 (why?) . Poke around a bit (OK, a lot) and find out that J2SE1.5 uses JAXP 1.3, while 1.6 uses JAXP1.4. Hmmm, so what's the difference? Well, it's not exactly documented anywhere, but when validating under JAXP1.4, you have to explicitly make the DocumentBuilderFactory (from which you create the parser that creates the document that you pass to the validator...) namespace-aware, and tell it that the resulting parser will be used for validation. Which makes sense, I guess, it's just that all this (and so much more!) is included with the JDK, and there's no real migration guide or set of comprehensive release notes that spells out these changes. And let's face it, if there were, it would be so big nobody would read it anyway. It would be nice if they could provide total backwards compatibility, but I know that's not always possible.

I dunno, I finally got Ruby 1.8.5 installed, and got Rails set up, and I'm working through Dave Thomas's Rails book, and I'm finding it's much quicker to develop in Rails than JEE. Of course, I haven't done anything much in Rails yet (and I don't know Ruby at all), but I've got a project that I had to shelve because it was taking too long to write in Java, and I'm going to dust off those notes and see if I can get it done in Ruby. If so, then I may have to roll out some Ruby utilities here at the orifice and see if I can get them entrenched. From what I can see, .NET isn't going to make web development any quicker than JEE, so I might as well push for something that is.

16 January 2007

Back from Web Services Hell

Well, what a long, strange, nasty, standards-ridden trip it's been! I started working on my WS project, using NetBeans and the Sun app server (aka the Glassfish Project). Everything's fine, I'm able to create a web service, deploy it on the server, create a WSDL file, create a test client, everything happy little web-service developers do. Obviously, things were going too well. So I get ready to make the service available to a third-party company we're working with. Since I can't just stick my development machine outside the firewall, I find out where we deploy our other web services. Well, it turns out all of our other web services are written in either .NET (our "approved" development platform) or LotusScript. Obviously, neither of these were going to work. However, we had a couple of legacy apps that were written in Java, so there was an app server available, it just wasn't accessible to outside users. I was assured that opening a hole in the firewall wouldn't be a problem, so I should just go ahead and test my service and once it was ready they'd open it up. OK, sounds good, I thought, and handed over a copy of my WAR file. "Oh, we can't use that." Um, what? "We need an EAR file." My blinking was met with a blank stare. OK, fine, whatever, it is but the work of a moment to create an EAR file that has my WAR file in it. I hand it over to the powers that be (who are really pretty good developers, but not sysadmins) and ask that they let me know when it's been deployed.

A couple of minutes later, I get a log file. A big one.

Seems our app server only supports Java up to version 1.4.2 (yeah, three guesses which one that is...), and I made heavy use of JAX-WS (which uses JSR-181 annotations, all of which are part of Java 1.5). Being the clever, resourceful person I am, I immediately went bitching to my manager. Turns out that even the latest version of The App Server That Time Forgot didn't support Java 1.5. However, as soon as I started looking, I discovered that they were releasing a new new version, which allegedly did support 1.5. So I downloaded it and immediately discovered that its support was pretty much limited to running 1.4.2 apps on a 1.5 JVM ("No JEE 5 support for you!").

So I got a copy of the popular open-source IDE that the app server company bases its tools on. Turns out the IDE doesn't have a "server profile" for the new new app server version, so it wasn't able to talk to it (even though it supported the new release, and the new new version was a minor release of that). So I got the "toolkit" that was supposed to work with the new new version. Turns out it didn't support JAX-WS. So I got the Web Services clip-on/extension/module. Turns out it was only mostly compatible with JAX-WS (full compatibility coming RSN). It further turns out that once the Web Services c/e/m was installed, the toolkit was only marginally compatible with the app server.

So I fudged and poked and tweaked and Googled and grepped and read and read and read. I loaded updates, I loaded patches, I got beta code, I got alpha code, I got third-party code. I got add-ons and clip-ons and extensions and modules. Finally, I caught myself humming "One Piece at at Time" by Johnny Cash, and that's when I decided it wasn't going to work.

Hmmmm, what to do, what to do... Do I scrap my current implementation and step back to those golden days of yesteryear and rewrite my code using JAX-RPC interface semantics? Or do I take that idle Oracle server I built and put the Sun app server on it and have them stick that out in the open? A quick conversation with my VP and it all started falling into place. It boiled down to the fact that I was proposing we use hardware we already owned, so we didn't have to coordinate with anyone else, and it was an already-proven solution. I was a month past my original "I could probably write this in a week or so" estimate, so expediency ruled the day and we did the deed. I was able to finish the work quickly once that was done (Total time creating web service with four methods: 2.5 wks. Total time trying to figure out how to make it work on app server X: 3 wks.).

I just deployed the service yesterday, and I'm waiting for the "We can't use this — it's just what we asked for!" call. Guess I'll just cement the curse by writing up the documentation...

19 October 2006

XSL FTW!

Just got a new project at work. We need to interface to another company's information system, and they want us to use web services. OK, cool, I'm all about the SOA (or will be once I do a little research), let's see what they've got. Interface specs, XML-RPC, all looks good until I get to the sample data they're sending. All their data is expressed as XML attributes. I know there's no hard consensus on elements vs. attributes, but I personally hold to the idea that attributes are for meta-data, and data itself should be in elements. I tried to ignore it, but it kept bugging me. I know that for the project I'm on, I'll just wind up working with what they give me, and suck it up. But I deperately wanted to convert it. Hey, it should be easy with XSL, right? Well, I suppose if you actually knew XSL, it probably would be. It took me a couple of hours to figure out the proper incantations of selectors and elements to get it done, but finally I did. And just on the off chance there's some other attribute-hater out there, here it is (sorry about the formatting!):

<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">

<xsl:template match="*">
<xsl:variable name="tagName" select="name()"/>
<xsl:element name="{$tagName}">
<xsl:text>
</xsl:text>
<xsl:for-each select="@*">
<xsl:variable name="tagName" select="name()"/>
<xsl:element name="{$tagName}"><xsl:value-of select="."/></xsl:element>
<xsl:text>
</xsl:text>
</xsl:for-each>
<xsl:apply-templates/>
</xsl:element>
</xsl:template>

<xsl:template match="@*|processing-instruction()|comment()">
<xsl:copy>
<xsl:apply-templates
select="@*|node()|processing-instruction()|comment()"/>
</xsl:copy>
</xsl:template>

</xsl:stylesheet>

Invoke it with Xalan (or your XSLT processor of choice). I use the command "java org.apache.xalan.xslt.Process -in stuff.xml -xsl cvtAttrs.xsl" (where the script is in 'cvtAttrs.xsl', of course).

Share and enjoy!

24 July 2006

Hybrid Opinion

Well, just hit a new high in the Honda: 53.7 MPG over 550 miles. These were all in-town miles, average speed was probably somewhere around 30-35. Not bad, all things considered. For some reason, my mileage always seems to improve in the summer. I assume it's got something to do with "summer blend" gas, either more or less ethanol, I don't know. I do know that I'm hard-pressed to break 47 during the winter.

Random thoughts on the hybrid debate:

I've been seeing a fair amount of commentary on how economical hybrids are, with a lot of people opining that you'll never make back the "premium" you pay to buy a hybrid. I've read analyses that show that it takes longer to recoup the additional money spent to buy a hybrid than that average driver owns a car. That the published numbers don't reflect the actual mileage you'll get. Well, that just doesn't seem to be the case, at least here at Chez Random (which has a three-car garage).

First, the hybrid wasn't really more expensive than the non-hybrid. Yes, it was more expensive than a base-model Civic, but it also comes with a lot of features you didn't get on the base model: anti-lock brakes, side-impact air bags, and an AM/FM/CD radio with subwoofer are the ones that jump immediately to mind. Add it all up, and you're close to the starting price for the high-end model. One thing I have to mention, though, is that you really don't have any choice on the hybrid options: I think the only choices I had were the color, whether I wanted a CD changer in the trunk and whether I wanted splash guards. No sunroof, no electric seats, no leather interior, nada. Oh, I did get some nifty mag wheels, but only because they're a special lightweight alloy that contributed to the gas mileage. Anyway, the point is, the price was right in line for a similarly-configured gas-only model, so I don't think I paid any more for the car (certainly not after the hybrid-purchase tax rebates, which totalled a cool couple thousand).

And mileage not making the published figures? Um, frankly I can't remember what the advertised mileage was on the Civic, but I doubt it was much over 50 (I seem to recall that the Insight, Honda's cramped two-seat serious hybrid, gets about 60, so I'm sure the 4-seat, 4-door Civic gets less than that). And that's about what I'm getting, so there's no problem there. I do know that when I gas up, it's considerably cheaper than filling either of the other two cars (which can break $50 with today's prices). Of course, a big part of that is the Civic's tank size (about twelve gallons), which is about two-thirds the size of the tanks in the other cars. But if I drive either of the other two daily, I wind up filling them about once a week. I fill they Honda maybe once a month, and then it's more of an inconvenience than a trial.

I am a bit worried about resale value. The battery pack is supposed to last for about eight years, and cost around $3000 to replace (2004 prices). So if I sell it after seven years, I expect the price drop to be considerable. Hopefully, this won't be a problem, since I tend to keep cars for awhile (my other car is an '87), and I bought the extended warranty expressly because it will cover the battery pack replacement. But if for some reason I do have to sell it, then that may be an issue. Or not, depending on how much the battery packs cost by then. I will be surprised if they haven't come up with an idea or two on making it more economical, but I also won't be surprised if the gummint hasn't slapped some kind of disposal or recycling fee on the new packs. If worst comes to worst, I hear that the pack is really just a couple hundred rechargable D batteries, maybe I'll just fire up the ol' soldering iron and fix it myself! Of course, a couple hundred rechargable D batteries will cost a bit, maybe I should start collecting them now...