Tuesday, May 13, 2008

Playing with the Struts 2 Dispatcher


Due to the nature of our permission handling on a project at work I am in need of a way to pass URLs in to the Struts dispatcher and get back the class name and method of the action to invoke (possibly the results as well...). See we have our classes implement a "SecurityAware" interface that returns an array of [OR'd] permission names required to view a page. We also have a permission based menu system that sometimes requires different permissions than the page the menu item points to. I had run into a problem in trying to find all those needed permissions between the database and the source code which was a lot of manual work. So you can see why I'd want to just pass in the URLs from the menu and get the action, call the security method and display the needed permissions for the resulting page.


What I did was start taking a trip through the Struts 2 source and the test classes for an idea of what was going on. I started at the FilterDispatcher since that's the one class that I define and is the starting point of my (any?) Struts2 applications.


The filterDispatcher creates a Dispatcher object. This seems to be the core configuration beast of the system. This has an ActionMapper injected into it. This is where my problems begin.


How are things injected into the FilterDispatcher since I'm "creating" it in the web.xml? There's a ConfigurationManager that is created inside that loads the struts config files and I've had luck printing out parameters that I've set in my struts.xml so I think it's loading correctly. What I'm now having trouble with is the ActionMapper. I'm playing with this in jRuby and I have no idea how to deal with Java5 Generics (I guess I could Google it...) so I took the route of just injecting my created actionMapper.


actionMapper = DefaultActionMapper.new
dispatcher.configurationManager.configuration.container.inject(actionMapper)


This does have an effect in that I can set struts.enable.SlashesInActionNames to true and then call actionMapper.slashesInActionNames? and have the result be true (it defaults to false, so there is a change).


I should mention that I'm running this code in the struts blank example app's WEB-INF/classes directory. I can create a MockServletRequest and pass in URLs that should and shouldn't work. It errors out appropriately when the URL doesn't end with ".action" and I can get it to parse the namespace portion correctly, but I'm having no luck with the Method. For example /example/Login_input.action should result in an ActionMapping with:

  • Name: Login_input

  • Namespace: /example

  • Method: input


Or at least that's what I'm expecting, but I never get anything for the method.


Here's my code so far.


require 'java'

import 'org.apache.struts2.dispatcher.Dispatcher'
import 'org.apache.struts2.dispatcher.FilterDispatcher'
import 'org.apache.struts2.dispatcher.mapper.DefaultActionMapper'

import 'org.springframework.mock.web.MockServletContext'
import 'org.springframework.mock.web.MockFilterConfig'
import 'org.springframework.mock.web.MockHttpServletRequest'
import 'org.springframework.mock.web.MockHttpServletResponse'


filterConfig = MockFilterConfig.new
servletContext = MockServletContext.new


dispatcher = Dispatcher.new(servletContext, {})
dispatcher.init
Dispatcher.instance = dispatcher

actionMapper = DefaultActionMapper.new
dispatcher.configurationManager.configuration.container.inject(actionMapper)


request = MockHttpServletRequest.new(servletContext, 'GET', '/example/other.action');
response = MockHttpServletResponse.new;

actionMapping = actionMapper.getMapping(request,dispatcher.configurationManager)
puts "Name: #{actionMapping.name}"
puts "Namespace: #{actionMapping.namespace}"
puts "Method: #{actionMapping.method}"
puts "Result: #{actionMapping.result}"


Wednesday, April 16, 2008

MythTV allowing schedules but showing none

My MythTV is on the fritz. Maybe because I keep screwing with it... It would let me "schedule" things through mythweb or by Manage Recordings -> Schedule Recording and would save the recording schedule I picked but would not show anything in the upcoming recordings section. This had happened to me once before and luckily I vaguely remembered how to fix it.

If this happens to you, or to me again, check that your recording device is hooked up to your listings. I think it's under Input Devices in setupmythtv, aka the backend setup. In my case I had my card and I had Schedules Direct but my card's television input was not pointing to anything. I pointed it to Schedules Direct and exited out, went back into the frontend and all my upcoming shows were there.

Sunday, April 6, 2008

Maemo Mapper paths.db break down


When connected to GPS mapper has a track log in the paths.db file in /home/user/.maemo-mapper/. If you take a look at the file with the sqlite3 client you'll see a table named track_path. This is where the data points are stored but they're not in degrees. A .schema reveals that the columns are called unitx and unity.


A dig through the internet found me source that was using a function called unit2latlon. So I downloaded the source from the garage and grep'd for the definition of this function. It turns out to be a macro in defines.h.


Here's the macro converted to ruby for easy use. It only does long and lat. I still have to find what the altitude is in...



TILE_SIZE_P2 = 8
MAX_ZOOM = 20

WORLD_SIZE_UNITS = 2 << MAX_ZOOM + TILE_SIZE_P2

MERCATOR_SPAN = -6.28318377773622
MERCATOR_TOP = 3.14159188886811


def unit_2_lat_lon(unitx, unity)
lon = ((unitx) * (360.0 / WORLD_SIZE_UNITS)) - 180.0
lat = (360.0 * (Math.atan( Math.exp ((unity * (MERCATOR_SPAN / WORLD_SIZE_UNITS)) + MERCATOR_TOP)))) * (1.0 / Math::PI) - 90.0;
return lat,lon
end



It's line for line the same as the Maemo-Mapper team's code. There was a lot more in the defines.h file, but this is all that's needed to run the function. I ran the last point in the database and checked the output in Google maps (straight copy and paste!) and it is where it stopped logging!



irb(main):001:0> lat,lon = unit_2_lat_lon(157094175,199470581)
=> [41.9127500226541, -74.6601469069719]

Friday, April 4, 2008

Google Maps, Maemo Mapper, GPX, KML all get me to the rally!

I'm a bit of a rally fan. Not the political kind, but the motorsports kind (you may have see it in the x-games...). This weekend is the 2008 Rally New York. If you've ever been to a rally before you know what a pain it is finding and getting to spectator points. If you haven't, I suggest you do, and believe me, it's a pain. There's usually a spectator guide and if you dig around the site some scanned maps that have been marked but these always leave me a little in the dark. So each time I go to one of these I usually take a few hours checking out google maps and figuring out where the spectator points are and how to get there.


It started out with just printing out the pdf's and the maps, but then I got a psp and I started downloading Google maps and drawing on them and loading them as pictures. Then I got the Nokia N800, suddenly there was no need for print outs (yay! I'm going green while driving > 300 miles to a fuel burning frenzy). The N800 can handle PDFs and with the help of Maemo Mapper can handle maps for me. That coupled with a bluetooth GPS receiver gives me a really great tool for navigating to the different points. Now I have an iPhone as well which has maps and PDF abilities (if it only had GPS!)
So what I did this year was plot everything on Google Maps:


View Larger Map

The cameras are spectator points. The orange lines are the closed road courses and the blue lines are the recommended approaches to the spectator areas. While editing a map you can download the kml file to view in Google Earth. This file just contains a link to the actual .kml file that has the lines and points in it. I tried putting this file on a server and hitting it in my iPhone's maps.app. But after getting a polite error about not being able to find anything matching the search I looked on the web and found that the only kml elements Google Maps for Mobile (which maps.app is based on I hear) is the point. So lines were out and my iPhone was out.


Next I looked up what format Maemo Mapper supported, which is GPX. I fired up gpsbabel and converted my real kml file to gpx only to have Maemo Mapper choke on it. It turns out that Mapper wants only one type of dataset at a time. The kml had lines and points which were converted to "trk"'s and waypoints. But I really only needed to store my spectator areas as Points of Interest (POI). Mapper stores it's POI in a sqlite3 database in /home/user/MyDocs/.documents/Maps/poi.db by default. By getting that file I was able to write a ruby script that took out the "Point" elements from the kml file and create sql insert statements. Putting that db back and fixing permissions (I was ssh'd in as root so the file became pwn'd) gave me the spectator points. Since they're the only things in there it makes for very easy viewing!


Then came the "approaches", I have them as line fragments on the google maps but Mapper only supports Routes and tracks. I don't know if tracks would have helped me as I think they're in a db called paths.db but the routes are stored in an xml file in gpx format. Gpsbable dropped the names and descriptions when it converted the lines so I created a new ruby script that took the LineStrings from kml and created trkpt's from them. Reloading the route file in Mapper gave me my route to the place plus the route fragments to the spectator areas. Since they were all in the same route file I could then download tiles around the routes and have everything internet free. I also added a description to some of the points in the line for turn directions/hints. You can count the bends in your google map to know which one to add it to and remember it goes at the beginning of a line not the end. So if you have a right turn between segment 2 and 3 add the turn description to segment 3.


Hopefully that'll be everything I need. I'm bringing my old phone just in case I need to tether (damn you iPhone and your lame bluetooth support) to the N800 for data. Now about that battery life...

Sunday, March 30, 2008

Building source on Ubuntu

If you're trying to build anything from source on Ubuntu don't forget to sudo apt-get install build-essential before you start!

I was trying to build the iphone toolchain on Ubuntu 7.10 and was getting this error when trying to ./configure --enable-optimized

checking for C compiler default output file name... configure: error: C compiler cannot create executables
See 'config.log' for more details.


I knew there was some basic install stuff you needed before hand but I had to search for it. Once installed your configure step will work.

[update]
If you have to build ruby gems with native extensions then also add


sudo apt-get install ruby1.8-dev

Monday, January 21, 2008

HTML as a programming language

I read an article a couple days ago about robots that were programmed by genetic algorithms where one of the patterns that evolved was that of a liar. I've been looking for something to try out with genetic algorithms and thought it would be interesting to see if I could create a liar bot as well.
Since I've been reading Programming Collective Intellegence (along with everyone else!) and Dave always telling me this stuff needs to be done as a tree ("LISP, NICK! LISP!") I started thinking down that path.
Then I realized that HTML is a tree, and quickly coded up this useless calculating proof of concept: HTML programming!

<body>
<div class="add">
<div class="power">
<div>x</div>
<div>2</div>
</div>
<div class="multiply">
<div>5</div>
<<div>x</div>
</div>
<div>4</div>
</div>
</body>

This is the "Code". It's a polynomial, x^2+5x+4. And then the "interpreter":

function writeExpression(action, actors) {
//console.debug(action,actors);
if (action == 'add') action = '+';
if (action == 'subtract') action = '-';
if (action == 'multiply') action = '*';
if (action == 'power') action = 'Math.pow(';

var openParen = action.indexOf("(");
if (openParen > -1) {
action += actors.join(",")+")";
return action;
} else {
return actors.join(action);
}
}

function depthFirst(el) {
if (el.childElements().length == 0) return el.innerHTML;
var children = [];
for(var i = 0; i < el.childElements().length; i++) {
children.push(depthFirst(el.childElements()[i]));
}

var expression = writeExpression(el.className, children);
//console.log(expression);
return expression;
}

function parse() {
var code = depthFirst($(document.body));
$(document.body).insert("<br/>"+code);
x = prompt('X = ?');
eval("fun = function(x) {return "+code+";}; alert('f(x) = "+code+"\\nf("+x+") = '+fun(x));");
}

It's just a depth first search that then decodes the class name into an operator and returns the equation for that branch.

Sunday, January 6, 2008

Reflecting off a cd case 2


Reflecting off a cd case 2
Originally uploaded by joecagit

Progress has been made!
Using GPSD's python wrapper I'm connecting to GPSD running on my desktop machine. I also fixed the spacing of the lines on the speed chart and the number of numbered lines on the compass. I also dropped in the current speed value.

The first test revealed how tiny the font at 25 (pixels? points?) is. So I bumped it up to 50 and doubled the draw size of the two gauges. Either by luck or some math that I some how got right, the fonts fix perfectly.

When trying to take this picture I noticed that the display goes away completely when the any part of the light from the GPS is behind it. That means that the HUD will disappear in other car's lights or street lights, etc. I hope that won't be too much of an issue.