General
Nov 15, 2008
I upgraded my Ubuntu installation and was having problems getting my web applications to start correctly in Tomcat 5.5. I'm not using the standard location for the webapps.
I was getting ajava.security.AccessControlException.
I had to modify my /etc/tomcat5.5/policy.d/50user.policy and had to add this:
grant codeBase "file:/home/timb/webapps/-" {
permission java.security.AllPermission;
};
General
May 19, 2008
I have an association between the scout table and the rank table but this is a many to many relationship. I didn't want to use HABTM because I wanted to keep the rank_date for each rank.
My model now has three tables for this relationship: scout, rank_scout, and rank. Frequently I want to list the current rank of a scout and with DRY, I don't want the find method all over the place. I added a method to the scout model to get the current_rank and also the current_rank_scout model.
To get the current_rank_scout, I need to select the row with the maximum rank_date.
I tried lots of different combinations (in all of these, 'scout' is the current scout model)
scout.rank_scouts.maximum(:rank_date)but this gave me the maximum value of rank date for the scout, close but I need the whole row.
scout.rank_scouts(:max => :rank_date)Nope, not even close there.
scout.rank_scouts.find(:conditions => {'max(rank_date)'})
but :conditions must be a hash (and even if it was a hash, this doesn't work right.
Of course, I'm just making this too hard. Thinking of it a different way, if I order the rows by rank date, I want the first row.
scout.rank_scouts.find(:first, :order => 'rank_date DESC')Ah Ha! That's what I needed. On to the next task.
General
Apr 26, 2007
I'm at the
Mapping Your Future annual conference today presenting about Blogs and Wikis. My presentation is available
here (MS ppt file). There is also a video available about educators using Wikis. It is available
here (9 MB mpg).
General
Feb 15, 2007
Hmm, on the whole I'm fan of
Google. I use
gmail as my only mail service and practically live by the
calendar and
maps. But I came across a blog posting from
Dare Obasanjo talking about the "hot, hot, hot" MSN
Live Search for Mobile. Thought I'd give it a quick whirl to see what's there. I fired up my trusty browser in my Treo 700p. This is a Palm based phone so I didn't expect a lot from MSN Mobile. But the pages are (thankfully) very lightweight and clean. I searched for the weather in Fredericksburg and got this:
"We're sorry, there is no weather for your location. Please try again later."
Glad to hear there is no weather at all. I was getting tired of the ice and snow. Guess I'll have to stick with
weather.com for now. Maybe I'll get around to actually trying the search sometime too.
General
Jan 17, 2007
I've been using
Unison to synchronize my Linux laptops for quite a while. It works great since I have two laptops that I use interchangeably. However, on one of the laptops I still have a Windows partition for a client that is using Windows servers. The VPN software and testing of the code just works better that way. But when I'm using Windows I don't have access to any of my documents. They're all on the Linux partitions.
Most references for getting Unison to work on Windows require running cygwin (which is a minor pain) or generating keys with openssh. I really didn't want to go through that hassle. All of the machines are local to my network and I just don't need the higher level of security that the openssh keys would give. Here's a quick tutorial to get Unison working on Windows.
Step 1) Download Unison for Windows. I'm using version 2.13.16 because that's the version I'm using on Linux. Make sure the versions are consistent across all of your platforms.
Step 2) Run Unison once to set up the profile. Put in the directories you want to synchronize.
Root 1: C:\Documents and Settings\timb\My Documents
Root 2: ssh://192.168.0.254//home/timb/LaptopBackup
When you try to connect you may get the message "Uncaught exception Unix.Unix_error(20, "create_process", "ssh")". A more common error if you've tried to use
plink as the ssh utility is "Fatal Error" "Lost connection with the server". Plink.exe doesn't support all of the commands that the full ssh supports.
Step 3) In order to overcome the different command line options between plink.exe and ssh we will need to create a custom batch file to execute plink
@plink %1 %2 %3 -pw yourpassword -ssh "unison -server -contactquietly"
The @ at the beginning of the line is important. @echo off is not sufficient for this script. Save this script somewhere in your path. I called mine plink-cmd.bat and put it in c:\Windows.
Step 4) Modify the .prf file to use the custom command we just created. Go to c:\Documents and Settings\yourusername\.unison There is a file called default.prf (or something else if you named your profile something. Edit that file and add the following:
sshcmd=plink-cmd.bat
Step 5) Rerun Unison and it should work. Happy synchronizing!
General
Aug 15, 2006
I was asked to present at the Department of Education's Software Developers Conference in early August and I received a lot of positive feedback. You can find the presentation on my Presentations page.
The session was called "eAuthentication in Higher Education" and I talked about some of the current federations that are in production:
The basic purpose of the session was to educate the developers and hopefully show them that federations are not a radical idea anymore. Their customers, the schools, are already implementing federations and they can integrate it into their software or else their competitors will.
What is Transitive Trust
Let's start with a basic math lesson. The transitive property of equality states:
If a = b and b = c then a = c.
But let's carry that over to trusting people.
If Chris trusts Nick and Nick trusts Stephanie then Chris will (probably) trust Stephanie.
The transitive property isn't quite so absolute whenever humans enter the equation.
What is a Federation
So we have the trust relationships but these only extend so far. I may trust a friend of a friend but I wouldn't carry that to a friend of a friend of a friend of a friend. There are not indefinite levels of trust.
The boundaries of our trust make up our federation. A federation is a group of organizations that have agreed to trust each other.
What is eAuthentication
eAuthentication is one particular federation that is managed by the US federal government and specifically by GSA. There are many other federations such as those that I listed above. The one key difference with the eAuthentication federation is that the federal government has mandated that every agency will participate in eAuthenticaiton. Every citizen who interacts with the government may potentially use the federation.
Java
May 23, 2006
We originally deployed code with a client-config.wsdd but this is a bit unweildy when deploying the client to many different systems. What we really wanted to to instead was to call the setClientHandlers(Handler request, Handler response) method on the Call object.
We are using custom serializers for all of our data types but this caused problems. We need to register the TypeMappings so Apache Axis knows how to serialize the objects into XML and back. The original code looked like this:
Call call = (Call) new Service().createCall();
QName qn = new QName(IDTSConstants.DTS_DEFAULT_NAMESPACE, “DTSResponseSignature”);
call.registerTypeMapping(DTSResponseSignature.class, qn,
new BeanSerializerFactory(DTSResponseSignature.class, qn),
new BeanDeserializerFactory(DTSResponseSignature.class, qn));
And everything worked fine when the client handlers were defined in the client-config.wsdd.
However, as soon as we removed the configuration from the client-config.wsdd and added the line:
call.setClientHandlers(this.getRequestHandler(), this.getResponseHandler());
We would get stack traces like this:
java.io.IOException: No serializer found for class com.aes.dts1.serializable.DTSRequestRouting in registry org.apache.axis.encoding.TypeMappingDelegate@9cfec1
at org.apache.axis.AxisFault.makeFault(AxisFault.java:101)
at org.apache.axis.SOAPPart.writeTo(SOAPPart.java:317)
at org.apache.axis.SOAPPart.writeTo(SOAPPart.java:269)
at org.apache.axis.SOAPPart.saveChanges(SOAPPart.java:530)
at org.apache.axis.attachments.AttachmentsImpl.getAttachmentCount(AttachmentsImpl.java:554)
at org.apache.axis.Message.getContentType(Message.java:486)
at org.apache.axis.transport.http.HTTPSender.writeToSocket(HTTPSender.java:343)
at org.apache.axis.transport.http.HTTPSender.invoke(HTTPSender.java:138)
at org.apache.axis.strategies.InvocationStrategy.visit(InvocationStrategy.java:32)
at org.apache.axis.SimpleChain.doVisiting(SimpleChain.java:118)
at org.apache.axis.SimpleChain.invoke(SimpleChain.java:83)
at org.apache.axis.client.AxisClient.invoke(AxisClient.java:165)
at org.apache.axis.client.Call.invokeEngine(Call.java:2784)
at org.apache.axis.client.Call.invoke(Call.java:2767)
at org.apache.axis.client.Call.invoke(Call.java:2443)
at org.apache.axis.client.Call.invoke(Call.java:2366)
at org.apache.axis.client.Call.invoke(Call.java:1812)
at com.aes.dts1.client.DTSCoreClient.invoke(DTSCoreClient.java:68)
at DTSClientApplication.main(DTSClientApplication.java:86)
Caused by: java.io.IOException: No serializer found for class com.aes.dts1.serializable.DTSRequestRouting in registry org.apache.axis.encoding.TypeMappingDelegate@9cfec1
at org.apache.axis.encoding.SerializationContext.serializeActual(SerializationContext.java:1505)
at org.apache.axis.encoding.SerializationContext.serialize(SerializationContext.java:978)
at org.apache.axis.encoding.SerializationContext.serialize(SerializationContext.java:678)
at org.apache.axis.message.MessageElement.outputImpl(MessageElement.java:1247)
at org.apache.axis.message.SOAPHeaderElement.outputImpl(SOAPHeaderElement.java:250)
at org.apache.axis.message.MessageElement.output(MessageElement.java:1208)
at org.apache.axis.message.SOAPHeader.outputImpl(SOAPHeader.java:328)
at org.apache.axis.message.SOAPEnvelope.outputImpl(SOAPEnvelope.java:476)
at org.apache.axis.message.MessageElement.output(MessageElement.java:1208)
at org.apache.axis.SOAPPart.writeTo(SOAPPart.java:315)
… 17 more
However, after a few days of debugging (argh!!!) it was a simple fix.
Change the registration of the TypeMapping to look like this:
MessageContext ctx = call.getMessageContext();
TypeMapping tm = ctx.getTypeMapping();
QName qn = new QName(IDTSConstants.DTS_DEFAULT_NAMESPACE,
“DTSResponseSignature”);
tm.register(DTSResponseSignature.class, qn,
new BeanSerializerFactory(DTSResponseSignature.class, qn),
new BeanDeserializerFactory(DTSResponseSignature.class, qn));
XML
Oct 21, 2005
In migrating to the XML Registy and Repository for the Education Community we needed to make sure that the data in the registry accurately reflected all of the elements in the static PESC Core Main schema. In the XML Registry there are now almost 1,000 data elements. This is way too hard to manually compare 1,000 elements against a static schema.
I thought it would be an interesting little trick to use the data within the files themselves to build a document showing the similarities and differences. Hey, both source documents are XML (actually XSD) so XSLT is the logical choice for me to do this.
There are a few key points necessary to make this work:
- The XSL document() function allows you to read in another separate file from the main one being processed.
- When displaying the XML Schema snippets to a web page I need to encode everything so that the browser will display it correctly. I found an XML to HTML Verbatim Formatter with Syntax Highlighting to do the job for me.
Start the XSL by declaring the XML Schema namespace and also importing the XML Verbatim stylesheet.
<xsl:transform
xmlns:xsl=”http://www.w3.org/1999/XSL/Transform”
xmlns:xs=”http://www.w3.org/2001/XMLSchema”
version=”2.0″>
<xsl:import href=”xmlverbatim.xsl”/>
The main loop of the xsl looks like this (simplified a bit to remove other html cruft):
<xsl :template match=”/”>
<html>
<body>
<table border=”1″>
<tr><th>Core Component Name</th><th>CoreMain</th><th>Registry Value</th></tr>
<xsl :apply-templates select=”/*/xs:complexType” />
</table>
</body>
</html>
Now match on the xs:complexType element to print the matched element and the element from the other document.
<xsl:template match=”xs:complexType”>
<xsl:variable name=”elementName” select=”@name”/>
<tr><td>
<xsl:value-of select=”$elementName”/>
</td><td>
<xsl:apply-templates select=”.” mode=”xmlverb”/>
</td><td>
<xsl:variable name=”ad” select=”document(’registry/aid-disbursement.xsd’)”/>
<xsl:apply-templates select=”$ad//*[@name=$elementName]” mode=”xmlverb”/>
</td></tr>
</xsl:template>
Voila! Using a bit of CSS there in the html I can get nice color coded XML and display the old document and the new document side by side in an HTML page. This was really a lot simpler that I expected it to be.
General
Sep 10, 2005
I’ve been a fan of Tom Peters for many years. I got the book “In Search of Excellence” from my mother several years ago and it really struck a chord.
I’ve been reading several manifestos from Change This and Tom Peters’ manifesto “The “PSF” is Everything” is simply great. It is one of those things that both fires me up and motivates me while at the same time I say to myself “of course, this stuff is obvious”.
He talks of making a dramatic difference and being a “dream merchant.” One of my favorite passages, life is “far too short … I don’t have the time to be bored.” Wow. I’ve never understood people’s acceptance of mediocrity. If you aren’t striving to be the absolute best at what you do then why even bother getting out of bed in the morning.
General
Aug 14, 2005
I had an unfortunate set of events happen in the last few weeks. The Windows XP system that I use at home was getting pretty flaky. I had hooked up my sister’s hard drive to backup / recover a few files for her and when I ran a virus checker I was a little shocked. The HDD had about 275 distinct viruses! I didn’t think that was possible. So I cleaned everything up (including spyware) and thought all was well. Unfortunately my own system that I used for the recovery started acting up. I thought that maybe one of the viruses (virii?) got onto my system somehow and was messing things up. I now know that it was just a bad coincidence. The hard drive was in the final process of dying a slow and painful (to me) death.
Anyway, long story short (maybe not short enough), I had lots of files that needed to be recovered. I had backed up all of the critical files like documents and budgets and such but I never backed up the photos from my digital camera or the approximately 35 GB of music. I don’t have any real way to back up 35 GB of music anyway. I have access to the original sources on CD or from archive.org for live concerts, but that is such a hassle to re-rip the CDs.
I started to copy files to the new hard drive but got lots of CRC errors. This was going to be painful. I needed a utility to copy the files and ignore the CRC errors. Of course I know that the section that has the CRC error is toast, gone, kaput. But that’s OK I suppose. It is better than manually copying every single file individually.
I came across Copy It Anyway! which will copy a file or set of files with read errors. Usually the whole file is not bad, just enough for one sector. With MP3 or videos this will usually be OK.
There are a few problems with this utility. First and foremost, it can’t copy subdirectories. There are a few other minor quirks I’ll explain at the end but the inability to copy whole subdirectories made this a very time consuming problem to recover my files.
I needed a batch file to loop through the old hard drive and build the command line to call CIA (Copy It Anyway!).
I have a controller batch file that coordinates the rest of things.
doit.bat:
@echo off
dir "f:\My Music" /b /s /a:d > filelist.txt
:START
copy fragment.txt + filelist.txt temp.txt > nul
type temp.txt | find "set filename=" > temp.bat
echo call process.bat >> temp.bat
call temp.bat
type temp.txt | find /v "set filename=" > filelist.txt
copy filelist.txt nul | find "0" > nul
if errorlevel 1 goto START
Let’s look at what this is doing line by line.
dir "f:\My Music" /b /s /a:d > filelist.txt
Hard coded locaton for my source files (f:\My Music). The power of this line is the /b which tells the dir command to only list bare filenames. When used in conjunction with the /s it will also display the full path. The “/a:d” tells dir to only display directories.
copy fragment.txt + filelist.txt temp.txt > nul
This is an old DOS trick to concatenate two files. The listing below this has the contents of fragment.txt. Note that there is no line feed after the = or this won’t work.
the end part “> nul” just suppresses any error messages.
type temp.txt | find "set filename=" > temp.bat
Now we want only the first line of the file. “find” will give us this. Unfortunately there isn’t a clean way to do this without a lot of temporary files.
echo call process.bat >> temp.bat
Here we are echoing the literal string “call process.bat” to the end of temp.bat. We’re not executing it yet.
call temp.bat
Execute temp.bat. Remember that when calling batch files from another batch file you must use “call”. Otherwise, the calling script will end as soon as the called script is finished.
type temp.txt | find /v "set filename=" > filelist.txt
Now take the temp.txt and list all files that do NOT have “set filename=” in them. We’re writing it back out to filelist.txt. So each time through the loop filename.txt will be one line smaller.
copy filelist.txt nul | find "0" > nul
if errorlevel 1 goto START
These two lines go together. Basically, if we type filelist.txt and there is still something left in the file then goto START and loop the whole thing again. If the file is empty then it will stop.
fragment.txt:
set filename=
process.bat:
@Echo off
echo "%filename%" > dest.txt
c:\Development\sed\sed.exe "s/f:/c:/g" dest.txt > dest2.txt
copy fragment2.txt + dest2.txt outfile.bat > nul
call outfile.bat
md %outname%
echo Processing "%filename%\*.*" %outname%
"c:\Program Files\Copy It Anyway 2.4\CIA.exe" "%filename%\*.*" %outname%
Let’s look at what process.bat is doing.
echo "%filename%" > dest.txt
%filename% was populated when we ran temp.bat in the calling batch file.
c:\Development\sed\sed.exe "s/f:/c:/g" dest.txt > dest2.txt
Sometimes there is no real substitute for a good unix utility. Sed is a classic utility to edit the incoming stream (sed=Stream EDitor). The regular expression “s/f:/c:/g” says search for all “f:” and globally (g at the end) replace it with “c:”. If your source or destination are different then you’ll need to modify this.
copy fragment2.txt + dest2.txt outfile.bat > nul
Just like in the previous file, prepend a SET onto the first line of the file.
call outfile.bat
This will set the environment variable %outname% for later use.
md %outname%
Make sure that the destination directory is created. Technically this isn’t required, but if I tried to copy files to a directory that didn’t exist CIA.exe would prompt me to create it. I put this here so I wouldn’t have to babysit the script.
echo Processing "%filename%\*.*" %outname%
Output what’s happening.
"c:\Program Files\Copy It Anyway 2.4\CIA.exe" "%filename%\*.*" %outname%
This will run CIA with the source and destination.
fragment2.txt:
set outname=
There are a few problems with this script:
- If there are no files in the destination directory CIA would display a popup that I had to click on. This meant that I did have to babysit it a little bit.
- The script didn’t end correctly. When there were no files left in the filelist.txt it went into an endless loop. That was no big deal. Ctrl-C fixed that.
- If I needed to restart the script, CIA would prompt me to overwrite existing files. So I had to manually click “No to all”. This was a bit tedious but on the whole not too bad.
- If the last file in a directory had errors then CIA would display a summary of files copied. This had to be closed manually
- The whole process was damn slow! It took almost 4 days to copy 35 GB of data. A lot of time was because I had so many CRC Read errors on the drive. It was also because of unfortunate timing on prompts from CIA.
On the whole it was an interesting experience getting all the data off the old drive. I think I recovered the vast majority of the files. Some of the MP3s are trashed because the first few bytes were corrupt. Oh well, that’s better than ripping everything again.
Shibboleth
Jun 20, 2005
Before I get started I want to point out the purpose of this document. I’m trying to get the Shibboleth 1.3 Identity Provider running with as few extra components as possible. This means no LDAP, no SSL, no pubcookie and really for now, no real WAYF and no real Service Provider. This is a cut down installation guide to get it going. We’ll explore more options once everything is working. Just to reiterate, the setup described here is not secure in any way!! Don’t use this as a guide for your production site.
Download the shibboleth-1.3??.tar.gz file from http://wayf.internet2.edu/shibboleth/beta/ (I happen to be using the b5 build for now but obviously you should download the latest one)
Extract the file to a directory of your choosing and run the ant build script. This is a pretty straightforward process. One note, the first directory you are prompted for is the location of keystores, tools and other such things and should not go in the Tomcat/webapps folder.
Fire up tomcat and see what happens out of the box. Ok, not much happens out of the box. I guess I need to start reading some of the documentation.
So IMO the best place to start reading about a java app is the web.xml within the WEB-INF directory. Looking there are the following things:
- context-param contains the parameter IdPConfigFile with the location to the idp.xml
- One servlet called IdP
- Three servlet-mapping elements all pointing to the one IdP servlet. These are for the URLs ‘/SSO’ ‘/AA’ and ‘/Artifact’
- One mime-mapping for css files.
One thing that is missing is the security-constraint to tell Tomcat that these are protected resources. There is an example here: https://mail.internet2.edu/wws/arc/shibboleth-users/2005-03/msg00020.html . This example is for using LDAP but I’m going to go even simpler and use the static tomcat-users.xml file. This isn’t scalable but it will allow the IdP to get up and running then I can migrate it to use LDAP in a little bit. By default, Tomcat comes configured to read users from the tomcat-users.xml and the user ‘tomcat’ with the password ‘tomcat’ will work for our purposes. This user will have one role of ‘tomcat’.
In the example the protected resource is in the URL ‘/HS’. As of Shibboleth 1.3 this has been renamed ‘/SSO’ so make the appropriate modifications to your web.xml.
Copy the security-constraint (changing the url-pattern to /SSO) and copy the login-config to your web.xml.
After restarting Tomcat, the IdP servlet was throwing an exception about not having the right kind of XML parser so I renamed the default tomcat/common/endorsed jars and copied all of the jars from the shibboleth-idp-install/endorsed to tomcat/common/endorsed and restarted tomcat.
Ooh, that was bad. Now XML parser errors are spewing all over the place. In tomcat’s server.xml I had the xmlValidation=”true” attribute set in the Host tag. Set it back to false and it started working. (Working in as much as Tomcat started)
When I go to /SSO or /AA I get the following Shib Exception:
No protocol handler registered for location (http://localhost:8080/timb-idp/SSO).
org.opensaml.SAMLException: Request submitted to an invalid location.
By modifying the idp.xml to bump up the logging to DEBUG we can see what is happening.
The Identity Provider was trying to register a URL at example.org. So modify the AAUrl attribute of the idp.xml to point to my AA (http://localhost:8080/timb-idp/AA)
Ooh!! Lots more messages came out of the tomcat console before it blew up. Still got the same error message, but it took longer to get there. That’s progress!
Within the idp.xml, change providerId attribute to point to my webapp and change the three ProtocolHandler/Location tags to correspond to what I’m doing.
Now I get the error org.opensaml.SAMLException: Invalid data from Service Provider: no target URL received.
That makes sense. I didn’t tell my SSO webapp where I was trying to go once I got authenticated. I need to pass some parameters to the SSO servlet so it knows what to do once everything is working.
According to the latest copy of the specification guide the HTTP request to the SSO must contain the following parameters on the query string:
- providerId
- shire
- target
- time (optional)
Providing all of those now works and it redirected me to the URL provided by the shire parameter.
We now have a more-or-less-working-yet-toally-insecure Identity Provider.
Next step is to put up a Service Provider that can consume these credentials.
Shibboleth
Jun 19, 2005
So I’m going to try to install Shibboleth. I’m going to try a pure java installation that is now available with Shib 1.3.
A great diagram for the architecture and components of Shibboleth is available from Howard Gilbert’s Wiki here